← 返回写作
12 分钟阅读

十几个微服务,怎么让 AI Agent 真正参与系统设计和编码?

把十几个微服务放进同一个 workspace,只是 Agent 工程的第一步。真正让它可靠起来的,是高质量上下文、服务边界地图、协议活文档,以及一套能让 Agent 自己跑起来的验证闭环。

十几个微服务,怎么让 AI Agent 真正参与系统设计和编码?

十几个微服务,怎么让 AI Agent 真正参与系统设计和编码?

前两天看到一张图,讲怎么在很多微服务之间使用 AI Agent。

里面有个问题很典型。

一个公司有十几个微服务,现在想让开发用 Agent 来做系统设计和编码。但一个 user story 经常不是改一个服务就结束了,它可能从前端进来,穿过用户服务、订单服务、库存服务、支付服务,最后还要落到消息队列和异步任务里。

这时候你把任务丢给 Agent,它要理解的就不是某一段代码。

它要理解边界。

哪个服务拥有用户状态,哪个服务只是缓存一份投影;哪个字段是外部合同,哪个字段只是内部实现;哪段逻辑能改,哪段逻辑一改就会影响下游对账。

问题就在这里。

单体应用里,Agent 至少还能在一个代码树里一路 grep 下去。微服务系统里,真正麻烦的是那些没有写在一个文件里的知识:职责边界、业务概念、调用约定、历史债、异常语义、重试策略、幂等规则。

所以很多团队的第一反应是,把所有微服务放到一个 workspace 下面,每个服务配自己的文档,让 AI 自己读。

这个方向对吗?

我自己的判断是:对,但远远不够。

把仓库放在一起,只是把门打开了。Agent 能不能真的干活,取决于你有没有给它一张地图,以及它改完以后能不能自己验证。

一张微服务地图,多个服务仓库放在同一个 workspace 中,根目录 AGENTS.md 像导航台一样指向各服务文档、OpenAPI spec 和 contract tests

一个 workspace 是必要条件,但不是充分条件

先承认一点,把多个服务放到一个 workspace 下,确实是目前很务实的做法。

如果你本来就是 monorepo,那很适合 Agent。

schema 定义、API 协议、共享类型、服务实现、测试代码,都在一个地方。Agent 不需要在十几个仓库之间来回切换,也不需要靠你一段段复制上下文。它可以自己搜索调用链,自己找接口定义,自己对照测试。

这对 Agent 很重要。

因为 Agent 最怕的不是代码多。

它最怕的是缺关键上下文。

一个用户故事跨了三个服务,如果 Agent 只看到了其中一个服务,它就很容易做出局部正确、系统错误的设计。它可能在订单服务里加了一个状态,却不知道支付服务已经用另一个字段表达同一件事;它可能改了一个返回结构,却不知道前端 BFF 和下游数据同步任务都依赖这个结构。

如果历史原因不方便合成 monorepo,也没必要硬迁移。

一个折中办法是虚拟 monorepo:把多个仓库 clone 到同一个本地目录里,外面套一层统一的 workspace。代码仍然是多个 repo,日常发布和权限边界不变,但 Agent 工作时能看到一个相对完整的系统视图。

这一步能解决很多基础问题。

但它解决不了全部。

因为“看得到”不等于“看得懂”。

十几个服务全部摊在 Agent 面前,有时候不是帮助,而是噪音。你给它一整座城市,它也许能找到路,也许会迷路。真正有用的不是把所有东西都塞进上下文,而是告诉它从哪里开始,哪些东西相关,哪些东西暂时不用看。

这就是 Context Engineering 的核心味道。

Anthropic 在讲 context engineering 的时候,有个判断我很认同:Agent 不是上下文越多越好,而是要在正确时间拿到正确上下文。上下文窗口再大,也不应该变成垃圾场。

放到微服务里,这句话尤其要紧。

Agent 需要的不是文档堆,而是服务地图

很多团队会说,我们有文档。

但你点进去一看,经常是这种形态:Confluence 里几百页,接口文档散在几个目录,架构图是三年前的,某个 README 写着“详见新版本文档”,新版本文档又链接回旧页面。

人都不愿意看。

Agent 更不会神奇地看懂。

给 Agent 的文档,最好不要从“大而全”开始,而是从“地图”开始。

我会建议做三层。

第一层,根目录放一份总索引。

可以叫 AGENTS.md,也可以叫 CLAUDE.md,名字没那么重要,关键是它的职责要清楚:告诉 Agent 这个 workspace 里有哪些服务,每个服务负责什么,改某类需求时应该先看哪些目录,哪些命令可以跑,哪些命令不能随便跑。

它不是百科全书。

它是机场指示牌。

比如:

1
2
3
4
5
6
7
8
9
10
/identity-service
负责用户身份、登录态、权限校验
业务概念:User、Account、Session、Role
深入文档:identity-service/AGENTS.md
API spec:identity-service/openapi.yaml
契约测试:identity-service/contracts/

/billing-service
负责订阅、账单、支付状态同步
注意:不拥有用户权限,只消费 identity 的 account projection

这种信息对 Agent 的价值非常高。

它不需要一上来读完所有服务的所有文档。它先读根索引,判断当前 user story 可能涉及 identity、billing、notification,然后再去加载这几个服务的细节。

第二层,每个服务目录里放自己的边界说明。

这份文档不要写成“本服务采用 Spring Boot + MySQL + Redis”。这些 Agent 读代码就能知道。

真正该写的是代码里不容易直接看出来的东西:

这个服务拥有哪些业务事实?

哪些概念属于它,哪些概念只是从别的服务同步过来的?

它对外承诺哪些 API 语义?

哪些字段不能随便改名?

哪些状态转换有业务含义?

哪些下游调用依赖幂等、重试、顺序或最终一致性?

这其实就是 DDD 里的 bounded context,只是你不用把它写得像 DDD 教材。Agent 不需要口号,它需要边界。

第三层,给 Agent 明确上下文路由规则。

比如:

1
2
3
4
5
6
如果任务涉及“用户是否有权限购买套餐”,先读:
1. /identity-service/AGENTS.md
2. /billing-service/AGENTS.md
3. /contracts/identity-billing/

不要直接修改 notification-service,除非 contract test 指向它。

这类规则很土。

但很有用。

它相当于把资深工程师脑子里的“先看哪里、别碰哪里、这里以前踩过坑”写下来。对人有帮助,对 Agent 更有帮助。

三层上下文结构,根 AGENTS.md 是索引,每个服务 AGENTS.md 是边界说明,OpenAPI/contract tests 是可执行证据

文档最大的问题,是它会老

但只靠文档还有一个硬伤。

文档会老。

而且微服务文档老得特别快。

一次接口新增字段,一次错误码调整,一次异步事件 payload 改动,一次数据库状态枚举扩展,都可能让文档和代码开始偏离。人类工程师有时候还能靠记忆和群聊补齐,Agent 没有这种组织八卦能力。

它会相信你给它的上下文。

过期文档对 Agent 的伤害,比没有文档还大。没有文档时,它至少会去读代码、跑测试、问你确认;过期文档会给它一种虚假的确定感。

所以我越来越倾向于一个原则:

能从代码或规格生成的,就不要手写。

OpenAPI spec 就是典型例子。

一份写得好的 OpenAPI,不只是给人看的接口文档。它还可以生成 SDK,生成 mock server,生成 contract test 的输入,甚至作为 Agent 理解服务边界的机器可读材料。

它比一段“用户服务提供登录接口”的自然语言可靠得多。

因为它会告诉 Agent:

这个 endpoint 是 POST /sessions

请求体有哪些字段。

哪些字段必填。

响应状态码有哪些。

错误结构长什么样。

这就从“描述”变成了“规格”。

更进一步,如果你已经有 Pact 契约文件,或者有成熟的 consumer-driven contract testing,那就更好。

因为 contract test 本身就是活文档。

它不是某个人写在 wiki 里的愿望,而是调用方真实依赖的接口形状。调用方说,我实际用了这些字段、这些状态码、这些交互序列。被调方每次变更,都要验证自己仍然满足这些契约。

这东西对 Agent 很值钱。

它可以读契约,知道服务之间真正发生了什么;也可以改完代码以后跑契约测试,知道自己有没有破坏别人。

微服务里最容易出问题的地方,恰好就是“我以为没人用”和“你怎么会这么用”。

契约测试把这两句话提前变成测试失败。

这比事后开会体面多了。

真正难的是验证,不是生成代码

很多团队引入 Agent 的时候,最先关注的是代码生成质量。

这个当然重要。

但在微服务场景里,我觉得更应该先问另一个问题:Agent 写完以后,它怎么知道自己没搞坏?

单服务还好办。

跑单元测试。

跑类型检查。

跑本地集成测试。

最多再起一个数据库和 Redis。

跨微服务就麻烦了。一个 user story 可能涉及三五个服务,外加消息队列、对象存储、第三方支付、权限系统。你不可能让 Agent 每改一次代码,就把完整生产级环境在本地跑起来。

就算能跑,成本也太高。

于是很多 Agent 工作流会卡在这里:它能写代码,但验证不了;它能解释方案,但不知道方案在系统里能不能跑通。最后还是人来做集成验证,人来盯日志,人来判断哪里不对。

这就回到了老问题。

人,重新变成循环里的慢部件。

要让 Agent 真正参与系统设计和编码,你得给它一个更轻的验证环境。

我的建议是,每个服务至少提供两类东西。

一类是 mock server。

它可以手写,也可以基于 OpenAPI spec 自动生成。关键是 Agent 在改某个服务时,不需要把所有依赖服务都真实启动起来。它可以面对一个足够稳定的模拟对手,验证自己的请求格式、响应处理和异常分支。

另一类是 contract test。

它验证的不是“整个业务流程是不是完全正确”,而是“服务之间约定的协议有没有被破坏”。

这个边界很重要。

端到端测试当然有价值,但它太重,不适合作为 Agent 高频自我修正的主循环。contract test 更轻、更快、更贴近服务边界,适合放在 Agent 的本地工作流里。

一个比较实用的闭环是这样:

Agent 读根 AGENTS.md

定位相关服务。

读取服务边界文档、OpenAPI spec、契约文件和相关测试。

提出设计。

修改代码。

启动 mock server。

跑 contract test。

根据失败信息自我修正。

再跑。

直到测试通过,或者明确停下来告诉人类:当前契约和需求冲突,需要你做产品或架构判断。

这才是 Agent 真正能干活的地方。

不是一次把代码写对。

而是它写错了以后,有一个系统能让它知道错在哪里。

Agent 在本地工作台中循环执行 write-test-fix,旁边是 mock server、OpenAPI spec、contract tests 和测试失败日志

不要让 Agent 直接面对整个系统

这里还有一个容易被忽略的点。

很多人把“让 Agent 理解全局”理解成“让 Agent 读取全部”。

这两个不是一回事。

一个资深工程师做跨服务设计,也不会把十几个服务每一行代码都读完。他会先建立系统地图,找到相关边界,再深入关键路径。Agent 也应该这么工作。

所以 workspace 的设计目标,不是让 Agent 一次性吞掉整个系统,而是让它能按需导航。

这也是我觉得根 AGENTS.md 特别重要的原因。

它不是为了喂满上下文。

它是为了减少上下文。

好的索引,会把 Agent 从“全仓库漫游”拉回“任务相关路径”。好的服务文档,会把 Agent 从“读代码猜业务”拉回“先理解边界,再看实现”。好的契约测试,会把 Agent 从“我觉得这样没问题”拉回“协议有没有被破坏”。

这三件事组合起来,才像一个可运行的 Agent 工作台。

Anthropic 在 long-running agents 那篇文章里提到一个很实在的做法:长任务要给 Agent 搭 harness,包括进度记录、验证脚本、干净的工作状态,让它能跨长时间任务继续推进。

放到微服务协作里,harness 不是一个抽象概念。

它就是这些东西:

AGENTS.md

服务边界文档。

OpenAPI spec。

mock server。

contract tests。

本地启动脚本。

测试命令。

失败时该看哪里的日志。

哪些目录只读。

哪些变更必须人工确认。

这些东西看起来不酷。

但它们决定 Agent 是“会写代码的聊天框”,还是“能在你们系统里工作的工程同事”。

一个更靠谱的三层搭法

如果让我给一个十几个微服务的团队设计 Agent 工作方式,我不会从“买哪个 Agent 产品”开始。

我会先搭三层。

第一层,统一 workspace。

能 monorepo 就 monorepo。不能就虚拟 monorepo。至少让 Agent 在一个地方看到相关服务、共享协议、测试和文档。

这一层提供全局视图。

第二层,分层上下文。

根目录有系统索引,每个服务有边界说明,协议规格尽量机器可读。人写的文档只写代码和 spec 难以表达的内容,比如业务语义、历史决策、例外规则、危险区域。

这一层提供精准上下文。

第三层,验证闭环。

每个服务提供 mock server 或模拟依赖,核心服务之间建立 contract test。Agent 改完以后,必须能在本地跑一组足够快、足够准的验证,而不是把所有风险都留给人类集成测试。

这一层提供自我修正能力。

这三层搭起来以后,Agent 的工作方式会发生变化。

以前你可能是这样用:

“帮我改一下订单状态逻辑。”

然后你把相关代码贴给它,它改一版,你再告诉它哪里不对。

新方式应该更像这样:

“根据根 AGENTS.md,处理 story-123。先识别涉及的服务和契约,提出设计,不要修改无关服务。改完后启动 billing 依赖的 mock server,跑 identity-billing 和 billing-notification 的 contract tests。失败就自我修正,三轮仍失败再停下来说明原因。”

这个 Prompt 不一定优雅。

但它工程上更靠谱。

因为它把 Agent 放进了一个可验证的循环里。

三层 Agent 微服务工程体系,底层统一 workspace,中层分层上下文和协议活文档,上层 mock server 与 contract test 验证闭环

写在最后

我现在看 Agent 工程,越来越觉得它和新员工入职有点像。

你不能把一个新同事拉进几百个群,丢给他十几个仓库,然后说:“你自己理解一下系统,明天开始做跨服务设计。”

这不叫授权。

这叫放生。

真正靠谱的团队,会给他系统地图,会告诉他服务边界,会指明哪些文档可信,哪些接口有契约,哪些测试必须跑,哪些地方以前出过事故。

Agent 也是一样。

它读得快,写得快,跑命令也快。

但它不天然知道你们公司的业务边界,不天然知道某个字段背后的历史包袱,也不天然知道一个接口变更会把哪个下游打穿。

所以问题不是“要不要把十几个微服务放到一个 workspace 下”。

要。

但更关键的问题是:你们有没有把一个复杂系统整理成 Agent 可以导航、可以理解、可以验证的工作环境。

统一 workspace 给它眼睛。

分层文档和协议测试给它判断。

mock server 和 contract test 给它反馈。

三层都在,Agent 才有机会从“帮我补几行代码”,走到“参与跨微服务系统设计”。

这件事说到底不是 AI 魔法。

还是工程。

只不过这一次,我们不是只给人类工程师搭工程体系。

我们也要给 Agent 搭。

参考资料

  1. Anthropic, Effective context engineering for AI agents
  2. Anthropic, Effective harnesses for long-running agents
  3. Datadog Frontend Platform, Steering AI Agents in Monorepos with AGENTS.md
  4. Pact, Consumer driven contract testing

Ask Leslie

从本站公开文章中寻找答案。当前版本在浏览器本地检索,不上传问题,也不会编造不存在的经历。

输入一个问题,我会把你带到 Leslie 写过的相关内容。

微信联系

Leslie Zhang 的微信二维码

扫码添加 Leslie,建议备注你的名字与来意。