从iOS老炮到Prompt工程师:一次MCP集成的血泪踩坑实录
六年前,我写的第一行 Swift 代码还是用 var 声明变量、手写 delegate 回调。如今在家远程办公,一边调试 SwiftUI 预览器抽风的问题,一边在 Jupyter Notebook 里跑 Prompt 工程实验——这世界变化太快,连我家猫都学会了在我敲 Cmd + R 时跳上键盘。
去年底,团队接了个“智能客服助手”的需求,产品经理拍着胸脯说:“就加个 AI 功能,两周上线,双十二前搞定。” 我当时差点把刚泡好的挂耳咖啡喷屏幕上——这哥们怕不是以为 AI 是 npm install 一下就能跑的?但转念一想,反正居家办公通勤时间省了,不如趁机深入搞搞最近火出天际的 MCP(Model Context Protocol) 和 Prompt 工程。毕竟,现在不学点 AI,简历都不好意思投。
起因:一个“简单”的需求,一堆复杂的上下文
我们的 App 是个电商导购平台,用户经常问“这件衣服适合梨形身材吗?”、“能不能搭配牛仔裤?” 这类问题传统规则引擎根本扛不住。于是技术总监拍板:接入大模型,用自然语言理解用户意图,再结合商品知识库生成回复。
关键来了:不能直接把用户 query 扔给 LLM。一来涉及用户隐私(比如身高体重),二来商品数据分散在多个微服务里,三来……我们可不想因为 prompt 写得不好,让 AI 建议用户“穿睡衣去参加婚礼”。
所以架构上我们决定采用 MCP —— 这玩意儿说白了就是一套标准化协议,让 LLM 能安全地调用外部工具(比如查库存、读用户画像),同时把上下文信息结构化传递,避免 prompt 泄露敏感字段。
听起来很美,对吧?现实很快教我做人。
踩坑第一站:MCP 的“安全上下文”其实不安全
我们一开始图快,直接用 OpenAI 的 function calling 模拟 MCP。把用户 ID、订单历史一股脑塞进 system prompt:
{
"role": "system",
"content": "你是XX商城的智能导购。当前用户ID: U12345, 最近购买: [连衣裙, 高跟鞋], 身高: 165cm..."
}
结果 QA 测试时发现:当模型 token 不够时,会直接把 user ID 当作回复内容吐出来!
“根据U12345的历史偏好,推荐这款...”
线上要是这么干,分分钟被用户投诉到 GDPR 罚款。我当时后背一凉——这哪是智能客服,这是人肉数据泄露器啊!
教训:MCP 的核心不是“传数据”,而是“可控地暴露能力”。正确的做法是只暴露工具接口,让 LLM 通过工具调用来获取必要信息,而不是把原始数据塞进 prompt。
于是我们重构了 MCP 层:
- 定义
UserProfileTool:LLM 可请求“获取用户体型特征” - 定义
ProductMatcherTool:输入商品ID,返回适配建议 - 所有工具调用走内部鉴权网关,用户原始数据永不进 LLM 上下文
// 伪代码:MCP 工具注册
MCP.register(tool: UserProfileTool.self) { request in
guard let userID = request.context.userID else { return .unauthorized }
let profile = UserDB.fetchProfile(for: userID)
// 只返回脱敏后的特征
return ToolResponse(body: ["bodyType": profile.bodyType])
}
这一步改完,安全基线才算达标。顺便吐槽一句:有些开源 MCP 实现默认把整个 context 序列化进 prompt,简直是埋雷。
踩坑第二站:Prompt 工程 ≠ 拼夕夕式堆砌指令
解决了安全问题,以为能松口气?Too young。
初期 prompt 写得像产品经理的需求文档:
“你是一个专业的时尚顾问,请根据用户的身材、风格偏好、当前季节、商品材质、价格区间、历史购买记录、社交平台点赞数据……给出不超过3条、每条不超过50字、带emoji、语气亲切但不过分热情的建议。”
结果模型要么输出超长小作文,要么干脆罢工:“抱歉,我无法访问您的社交数据。”
问题在哪? 我们把所有约束都塞进 system prompt,导致 LLM 注意力分散,关键指令反而被忽略。
后来学乖了,采用 分层 Prompt 设计:
角色定义层(固定不变)
"你是XX商城的AI时尚顾问,只回答穿搭相关问题"能力声明层(由 MCP 动态注入)
"可用工具:[getUserBodyType, getProductDetails]"任务约束层(每次请求动态生成)
"请用中文回复,最多2条建议,每条≤40字"
最关键的是:把格式要求转化为工具输出规范。比如我们自定义了一个 FormatResponseTool,LLM 只需调用它并传入原始建议文本,工具内部做截断+emoji处理。
# Prompt 工程实践:动态注入约束
def build_prompt(user_query: str, tools: List[str]) -> str:
return f"""
角色:专业时尚顾问
能力:{', '.join(tools)}
任务:针对"{user_query}"生成建议
注意:仅调用工具获取必要信息,勿猜测用户未提供的数据
"""
效果立竿见影——输出稳定性提升 70%,连测试同学都说“这次终于不像机器人了”。
开发心得:老 iOSer 的跨界生存指南
作为一个写了六年 UIKit/SwiftUI 的开发者,突然扎进 AI 领域,最大的冲击不是技术,而是思维模式。
在 iOS 开发中,我们追求确定性:某个按钮点击必然触发某个回调;而在 Prompt 工程里,你面对的是概率分布——同一个 prompt,可能今天输出完美,明天就胡言乱语。
我的三条血泪心得:
不要迷信“一次写好”
Prompt 和 UI 一样需要 A/B 测试。我们现在用 LangSmith 记录每次 LLM 调用的 input/output,每周 review bad case。上周就发现一个 bug:当用户问“显瘦吗?”,模型总回答“显胖”,后来发现训练数据里“显瘦”和“显胖”的样本比例失衡……MCP 不是银弹,但能救命
别指望 LLM 自己搞定一切。把确定性逻辑(如权限校验、数据格式化)放在 MCP 工具里,让 LLM 专注它擅长的——理解模糊意图。这就像 iOS 里把业务逻辑放 ViewModel,View 只负责展示。安全必须前置
曾经有同事提议:“先上线再加安全策略”。我直接甩出 OWASP LLM Top 10 文档——Prompt Injection、训练数据污染、工具滥用……这些风险在设计阶段就要堵住。现在我们所有 MCP 工具强制要求:- 输入参数类型校验
- 输出内容过滤敏感词
- 调用频次限流
效果与反思:从“能用”到“敢用”
经过三轮迭代,新客服助手终于在双十二前灰度上线。数据还不错:
| 指标 | 旧规则引擎 | 新 MCP+Prompt 方案 |
|---|---|---|
| 用户满意度 | 68% | 82% |
| 平均响应时长 | 1.2s | 2.4s |
| 敏感信息泄露 | 0.3% | 0% |
虽然延迟翻倍(主要花在工具调用链路上),但安全性和体验提升值得。最让我欣慰的是,上周收到一条用户反馈:“你们的 AI 终于不说‘亲亲’了!”
写在最后:别做技术的观光客
回看这段历程,从最初对 MCP 一脸懵,到能设计安全的 Prompt 架构,最大的感悟是:技术没有边界,只有认知的牢笼。
以前我觉得“iOS 开发”是个身份标签,现在明白——我们首先是问题解决者。无论是用 Auto Layout 对齐像素,还是用 Prompt 工程对齐语义,本质都是在混沌中建立秩序。
最近团队又在讨论把 MCP 用到 App 内搜索场景。我一边啃《Hands-On Prompt Engineering》电子书,一边想:或许明年这时候,我会写一篇《从 Prompt 工程师到 iOS AI 插件开发》?
谁知道呢。反正我家猫已经适应了我在深夜对着终端喃喃自语:“这个 embedding 为啥 cosine 相似度这么低……”
(完)
P.S. 如果你在搞类似项目,记住两件事:
- 永远假设 LLM 会犯错——它确实会
- 安全不是功能,是底线
共勉。

评论 0