ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

Agent项目九周上线实战:MCP协议与分层架构的复用策略

Agent项目九周上线实战:MCP协议与分层架构的复用策略 1. 为什么第二个 Agent 项目能九周上线1.1 从零造轮子的代价做过第一个 Agent 项目的人大概都有体会真正花在业务逻辑上的时间可能连三成都不到。剩下的时间全耗在基础设施上——消息怎么传、工具怎么调、状态怎么存、失败怎么重试、日志怎么打、权限怎么控。这些东西每一个单独拎出来都不算难但堆在一起就是无底洞。我第一个 Agent 项目从立项到勉强能跑花了将近五个月。其中前两个月基本都在搭架子自己写工具调用协议、自己设计上下文管理、自己搞会话状态持久化。等到真正开始写业务逻辑的时候人已经疲了代码也乱了。上线之后各种边界问题层出不穷改一个地方崩三个地方。所以做第二个 Agent 的时候我给自己定了一条铁律能复用的一律不自研只做真正属于业务差异化的那部分。结果就是九周上线而且稳定性比第一个项目好得多。这篇文章就把这套“先复用再自己做”的思路完整拆开讲包括选了什么现成基础设施、怎么组装、踩了哪些坑。1.2 九周上线的核心逻辑九周这个时间不是拍脑袋定的是倒推出来的。一个 Agent 项目要上线必须完成的事情无非几大块模型接入、工具调用、会话管理、业务逻辑、前端交互、部署运维。如果每一块都自研按我的经验至少四到五个月。但如果把其中通用性强的部分交给成熟方案只保留业务逻辑和必要的胶水层时间就能压缩到两个月出头。这里的关键判断是哪些东西是“所有 Agent 都需要但跟我的业务无关”的。这类东西就是应该复用的。比如工具调用的协议层、模型 API 的封装、流式输出的处理、会话上下文的存储这些不管你做的是客服 Agent 还是数据分析 Agent需求都差不多。自己写一遍除了感动自己没有任何收益。反过来哪些东西是“只有我的场景才需要的”那必须自己做。比如特定的业务规则、专有的数据处理流程、跟内部系统的对接逻辑。这些才是项目的真正价值所在也是别人抄不走的部分。九周的时间分配大致是这样的第一周确定技术选型和基础设施方案第二到四周搭通主链路第五到七周写业务逻辑和工具第八周联调和测试第九周部署和灰度。这个节奏能跑通的前提就是基础设施那部分没有拖后腿。2. 现成基础设施的选型与拆解2.1 MCP 协议解决了什么问题MCP 是这两个月被问得最多的词之一。很多人第一次听到会懵它到底是软件协议还是硬件协议简单说MCP 是一套让模型和外部工具之间标准化通信的协议。你可以把它理解成 Agent 世界的 USB 接口——以前每个工具都要单独写一套对接代码现在只要工具实现了 MCP任何支持 MCP 的 Agent 都能直接调用。这个价值在第二个项目里体现得特别明显。第一个项目我接了七个工具每个工具的调用逻辑都是手写的参数格式、返回结构、错误处理各不相同维护起来极其痛苦。第二个项目直接用 MCP 协议对接工具那边只要暴露标准的 MCP Server我这边用统一的客户端去调就行。新增一个工具的时间从原来的大半天缩短到半小时。MCP 的核心概念其实就几个Server 端暴露工具Tools、资源Resources和提示模板PromptsClient 端负责连接和调用。传输层支持标准输入输出和 HTTP 两种方式本地工具用前者远程服务用后者。理解了这一层剩下的就是照着文档接。2.2 为什么选 Python 作为主力语言Agent 开发的语言选择Python 和 TypeScript 是两大主流。我最终选 Python理由很实际生态成熟度和团队熟悉度。Python 在 AI 领域的库支持是最全的模型 SDK、向量数据库客户端、数据处理工具几乎都是一等公民。而且团队里几个人 Python 都熟不需要额外的学习成本。TypeScript 在类型安全和前端集成上有优势但如果你的 Agent 主要是后端逻辑这个优势就没那么关键。Python 环境配置这块我建议直接用 3.11 或 3.12别用太老的版本。3.10 以下在异步和类型提示上有一些限制写起来别扭。安装方式上Windows 用户直接官网下载安装包勾选“Add to PATH”Linux 用户用系统包管理器或者 pyenv 都行。虚拟环境一定要建python -m venv .venv然后激活这是基本操作不建虚拟环境后面依赖冲突会让你怀疑人生。2.3 基础设施层的分层设计热词里出现了“表示层 应用层 领域层 基础设施层”这个分层概念这其实是领域驱动设计DDD的思路。放到 Agent 项目里我是这么对应的层级职责我的实现方式表示层对外接口、前端交互FastAPI SSE 流式输出应用层编排流程、协调各组件自研的 Agent 调度器领域层业务规则、专有逻辑项目核心完全自研基础设施层模型调用、工具协议、存储复用现成方案这个分层的好处是边界清晰。基础设施层的东西可以随时替换不影响上面的业务逻辑。比如模型从一家换到另一家只需要改基础设施层的适配器领域层的代码一行不动。工具协议从自研换成 MCP也只动基础设施层。我见过很多 Agent 项目把模型调用代码直接写在业务逻辑里结果想换个模型要改几十个文件。这种耦合是项目后期最大的技术债。3. 核心环节的实操与配置3.1 工具调用的标准化接入MCP Server 的接入是整个项目里最值得展开讲的部分。我以最常见的几个场景为例。本地工具用 stdio 方式。比如一个读取本地文件的工具MCP Server 就是一个独立的进程通过标准输入输出跟 Agent 通信。配置大概长这样from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client server_params StdioServerParameters( commandpython, args[-m, my_file_server], env{DATA_DIR: /path/to/data} ) async with stdio_client(server_params) as (read, write): async with ClientSession(read, write) as session: await session.initialize() tools await session.list_tools()这段代码的关键点是initialize()必须先调它会完成协议握手和能力协商。很多人第一次写忘了这步然后调工具一直报错查半天查不出来。远程工具用 HTTP 方式。如果工具部署在另一台机器上就用 HTTP 传输。配置里填好服务地址和认证信息就行。这里要注意的是超时设置远程调用一定要设合理的超时不然网络抖动的时候 Agent 会卡死。工具描述要写清楚。这是最容易被忽视但影响最大的点。模型是根据工具的名称和描述来决定调不调、怎么调的。描述写得含糊模型就会乱调或者不调。我的经验是描述里要包含这个工具做什么、什么情况下用、参数的含义和格式、返回什么。宁可写长一点也别图省事。3.2 会话状态与上下文管理Agent 跟普通 API 最大的区别就是有状态。多轮对话里上下文怎么存、怎么截断、怎么检索直接决定了 Agent 的智商上限。我的方案是分三层存短期上下文放内存就是最近几轮对话直接拼进 prompt中期记忆放 Redis存整个会话的完整历史需要的时候按相关性检索长期知识放向量库存业务文档和沉淀的经验。上下文截断是个技术活。简单粗暴地按 token 数截断会丢掉关键信息。我的做法是保留最近 N 轮完整对话加上从历史里检索出的相关片段再留一部分给系统提示和工具定义。具体分配比例要看模型上下文窗口大小128K 的窗口可以宽松些8K 的就得精打细算。注意上下文里工具定义占的 token 往往比你想的多。接了十几个工具的话光工具描述就可能吃掉几千 token。工具多的时候要考虑按场景动态加载别一次性全塞进去。3.3 流式输出与前端交互Agent 的响应时间通常比普通 API 长因为中间可能有多轮工具调用。如果等全部完成再返回用户会以为卡死了。所以流式输出是必须的。我用的是 SSEServer-Sent Events比 WebSocket 简单单向推送够用了。实现上就是把 Agent 的执行过程分阶段推给前端正在思考、正在调用工具 X、工具返回结果、正在生成回答。前端根据这些事件显示不同的状态用户体验会好很多。这里有个细节工具调用阶段可能要好几秒前端最好显示一个“正在查询 XX”的提示而不是干等。用户知道系统在干活耐心会好很多。4. 常见问题与排查实录4.1 工具调用失败的排查思路工具调用失败是最高频的问题原因五花八门。我整理了一个排查顺序基本能覆盖九成情况现象可能原因排查方法模型不调用工具工具描述不清检查 description 是否说明了使用场景调用参数错误参数 schema 不明确检查参数的 type 和 description调用超时工具执行太慢加日志看工具内部耗时返回结果模型看不懂返回格式太复杂简化返回结构加自然语言说明连续调用同一工具模型陷入循环加调用次数上限和去重逻辑我踩过最坑的一次是工具返回了一个嵌套很深的 JSON模型理解不了反复调用同一个工具想拿到“更清楚”的结果结果死循环。后来把返回改成扁平结构加一句自然语言总结问题就没了。4.2 模型输出不稳定的应对同一个 prompt模型有时候输出 JSON有时候输出带 markdown 代码块的 JSON有时候还加一句“好的这是结果”。这种不稳定在解析的时候很要命。我的应对是三层防护第一层prompt 里明确要求输出格式给出示例第二层解析的时候先做清洗去掉代码块标记和多余文字第三层解析失败时触发重试把错误信息反馈给模型让它重新输出。三层下来解析成功率能到 99% 以上。还有个技巧是用结构化输出功能。现在主流模型 API 大多支持指定 JSON Schema让模型按 schema 输出稳定性比纯 prompt 约束高一个档次。能用就用别硬扛。4.3 部署与灰度上线的经验Agent 的部署跟普通服务不太一样因为它的行为有一定不确定性。我的做法是先灰度再全量而且灰度期间要重点监控几个指标工具调用成功率、平均响应时间、异常终止率、用户重试率。异常终止这个指标特别重要。热词里有个“agent execution terminated due to error”说的就是执行中途挂掉。这种情况用户看到的就是“服务出错”体验极差。我的处理是加了一层兜底任何未捕获的异常都转成友好的提示同时记录完整堆栈到日志方便事后排查。灰度期间我还发现一个问题某些工具在并发高的时候会超时。原因是工具内部有共享资源竞争。解决办法是给工具加连接池和限流别让并发把下游打垮。5. 复用与自研的边界怎么划5.1 判断标准与决策清单“先复用再自己做”说起来简单做起来最难的是判断什么该复用、什么该自研。我总结了一个决策清单每次遇到新组件就过一遍这个组件是不是所有 Agent 都需要是的话优先找现成方案。现成方案能不能满足 80% 的需求能的话就用剩下 20% 用适配层补。自研的成本和收益是否匹配如果自研要两周但只提升 5% 的效果不值。这个组件会不会成为核心竞争力会的话必须自研。现成方案的社区活跃度如何不活跃的慎用出问题没人管。按这个清单过一遍大部分决策就清晰了。我第二个项目里模型接入、工具协议、会话存储、日志监控全是复用的只有业务规则引擎和专有数据处理是自研的。这个比例大概是 7:3我觉得比较健康。5.2 复用带来的隐性成本复用不是没有代价的。最大的隐性成本是学习成本和适配成本。一个现成的框架你要读懂它的设计、搞清它的边界、处理它跟你业务不匹配的地方这些都要时间。我的经验是选复用方案的时候要看文档质量和示例完整度。文档差的方案省下的开发时间全搭在摸索上了。宁可选一个功能少一点但文档清楚的也别选功能全但文档稀烂的。另一个隐性成本是版本升级。复用的组件升级了你的适配层可能要跟着改。所以适配层要写得薄越薄越好改。我一般会把适配层控制在几百行以内超过这个量就说明耦合太深了得重新考虑。5.3 从第一个到第二个项目的迁移经验第一个项目虽然慢但也不是白做的。它帮我摸清了 Agent 开发的完整链路知道了哪些地方是坑。第二个项目能快很大程度上是因为知道坑在哪提前绕开了。具体来说第一个项目踩过的坑包括工具描述写得太随意导致模型乱调、上下文管理没做好导致长对话崩溃、没有流式输出导致用户以为卡死、异常处理不完善导致服务频繁挂掉。这些在第二个项目里全都提前处理了。所以如果你正在做第一个 Agent 项目别嫌慢把每个坑都记下来。这些经验在第二个项目里会变成实打实的时间节省。第一个项目五个月第二个项目九周这个压缩比不是靠运气是靠第一个项目交的学费。6. 一些实操中的小技巧工具调用的日志一定要打全。我见过太多人只打“调用工具 X 成功/失败”出问题的时候完全不知道参数是什么、返回是什么。我的做法是把工具名、入参、出参、耗时全打出来排查效率高很多。模型的选择别死磕一家。不同模型在不同任务上表现差异很大有的擅长工具调用有的擅长长文本理解。我的做法是准备两三个模型按任务类型路由。工具调用密集的场景用一个纯文本生成的场景用另一个效果和成本都能优化。PR 流程要规范。Agent 项目的代码变更往往涉及多个组件review 的时候容易漏。我的做法是每个 PR 都要求写清楚改了什么、为什么改、怎么测试的。CI 里加上工具调用的集成测试确保改动不会破坏现有功能。最后分享一个关于上下文的小技巧在系统提示里明确告诉模型“你有以下工具可用但只在需要时调用”比单纯列工具列表效果好。模型有时候会为了“表现”而调用不必要的工具加这句话能明显减少无效调用。这个项目后续还可以扩展的方向是把工具调用做成可配置的不同场景加载不同的工具集。现在工具是写死的加新工具要改代码。如果做成配置驱动运营同学自己就能配开发就解放了。这是我下一步打算做的事。
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进