如何设计出优秀的 AI Agent:从架构原则到实战模式
一篇从 Software Spec、三层架构、Latent vs. Deterministic 分界线到自我进化系统的完整指南。基于对 2025-2026 年 AI coding agent 领域最前沿实践的深度解读。
引言:AI Agent 时代,工程师的角色变了
2025-2026 年,AI coding agent 经历了一场质变。Claude Code、Codex CLI、Cursor、Kiro——这些工具不再只是补全下一行代码的自动完成器,它们已经进化为能理解整个代码仓库、执行多文件修改、运行测试并自主迭代的自主代理。Google 的 Addy Osmani 将这种新模式定义为"AI-augmented software engineering"——AI 增强的软件工程,而非 AI 自动化的软件工程。
这个区分至关重要。开发者的角色正在从"写代码的人"转变为"指挥 AI 写代码的人"。 Andrew Ng 在一张广为流传的 slide 中指出了这个转变的深层含义:当 AI coding 把"构建"这个环节极大加速之后,产品管理反而成了新瓶颈——工程师一天能出三个原型,但 PM 来不及给反馈。他的结论是:Engineer:PM 比例正在趋向 1:1,能自己定义产品方向(shape product)的工程师会跑得非常快,因为他们不需要等任何人,自己就能完成"构建 → 反馈 → 迭代"的完整闭环。
这意味着 AI 时代最值钱的工程师,不是写代码最快的,而是能同时扮演工程师和半个 PM 的人。而这个能力的技术载体,就是本文要讨论的核心:如何设计出优秀的 AI agent 系统。
一、从 Spec 说起:你和 AI 之间的"合同"
在讨论架构之前,先讲一个更基础的问题:你要怎么告诉 AI 你想要什么?
1.1 Software Spec 不是 Prompt
Software Spec(软件规格说明书)在 AI coding agent 的语境下,本质上是你写给 AI 的"需求合同"——它精确描述你要什么、不要什么、边界在哪里,让 AI 在最少的来回沟通中产出最接近你预期的代码。
但 Spec 不是 prompt。Prompt 是一条消息,发完就消失在聊天历史里;Spec 是一个持久化的、版本控制的文档,它锚定在你的代码仓库中,跨会话存在,随项目演进而更新。GitHub 在 2025 年 9 月开源了 Spec Kit,正式提出了 Spec-Driven Development(SDD)的方法论,一个清晰的行业共识正在形成:在 AI agent 时代,specification 正在取代 code 成为"源头真相"(source of truth)。
1.2 为什么 Spec 变得前所未有地重要
AI coding agent 就是你的"超级初级工程师"——执行力极强、速度极快,但它不会主动猜你的意图。当 Spec 有空白地带时,AI 不会停下来问你,它会自己填补——而它填补的方式往往不是你想要的。你说"写一个用户认证模块",它可能给你搞一个 JWT + OAuth2 + 社交登录的全套方案,而你只需要一个 API key 验证。Overengineering 是 AI 的默认模式。 Spec 写得越好,AI 的产出越接近你脑中的画面。
更关键的是,研究表明当你往 prompt 中塞入过多指令时,模型对每一条指令的遵循率会显著下降——这被称为"指令诅咒"(Curse of Instructions)。你不能把所有需求堆到一条消息里,你需要一个结构化的、分层的 spec 来管理这些信息。
1.3 好的 Spec 覆盖六大领域
GitHub 的 AI 团队分析了超过 2,500 个 agents.md 文件后,发现最有效的 spec 覆盖六个核心领域:命令(Commands)——带标志和参数的完整可执行命令,放在 spec 的早期位置;测试(Testing)——如何运行、什么框架、覆盖率要求;项目结构(Project Structure)——显式声明每个目录的用途;代码风格(Code Style)——一个真实的代码示例比三段文字描述更有效;Git 工作流(Git Workflow)——分支命名、提交信息格式;边界(Boundaries)——agent 绝对不能碰的东西。
其中"边界"是最容易被忽略却最有效的控制手段。因为 AI 添加你不想要的东西的返工成本,远高于它漏掉你想要的东西。一条"不要引入当前 requirements.txt 中没有的新依赖",可能帮你省掉两小时的排查时间。
1.4 Spec 的工作流:四阶段门控模型
GitHub 的 Spec Kit 提出了一个经过实战验证的四阶段工作流。核心思想是在每个阶段设置人工检查点,直到当前阶段被验证后才进入下一阶段:
Specify(定义意图)→ Plan(技术设计)→ Tasks(任务分解)→ Implement(执行)
这个门控工作流解决了"纸牌屋代码"的问题——那种看起来能运行但一推就倒的脆弱 AI 产出。你不再 review 千行代码的巨大 diff,而是审查解决特定问题的小变更。Agent 知道要构建什么(spec)、如何构建(plan)、当前在做什么(task)。
二、三层架构:Fat Skills, Thin Harness, Deterministic Tooling
有了 spec 之后,下一个问题是:AI agent 系统本身应该怎么设计?
一套优秀的架构思想把 AI agent 系统分解为三层,并给出了一个极其精炼的设计原则:把智能向上推入 skill 层,把执行向下推入确定性工具层,让中间的 harness 保持薄。
2.1 底层:Deterministic Tooling(确定性工具层)
底层是你的应用提供的确定性能力:QueryDB、ReadDoc、Search、Timeline——SQL 查询、文档读取、搜索执行、时间线获取。这些工具的特征是:同样的输入永远产出同样的输出。 它们不需要任何"智能",只需要精确执行。
这一层对应的是传统软件工程师最熟悉的领域。如果你有 Java 后端经验,这就是你以前写的 DAO 层和基础设施代码——API 调用、数据库查询、文件操作。这些代码的价值在于可靠性和可预测性。
2.2 中间层:Thin Harness(薄调度层)
中间层是运行 LLM 的那个外层程序——也就是 Claude Code、Codex CLI 这类工具的"壳"。它只做四件事:循环运行模型(agentic loop)、读写文件、管理上下文(context window)、执行安全策略。 就这些。
一个最简的 agentic loop 核心逻辑大致是这样的:
循环 {
收集上下文(用户输入 + 系统指令 + 工具结果)
调用模型
如果模型要求调用工具 → 执行工具,继续循环
如果模型给出最终回答 → 输出,结束
}
这个核心循环加上上下文管理、安全策略、错误处理,大约 200 行代码就能搞定。关键在于 harness 不包含任何领域知识(那在 skill 里)、不包含任何具体的工具实现(那在底层应用里)、不包含任何业务判断(那在 LLM 的 latent space 里)。
用 Java 后端架构来类比:Harness 就像 Spring Boot 框架本身。你不会把 DispatcherServlet 的路由逻辑和你的业务代码混在一起——框架负责调度,业务代码负责逻辑,两者清晰分离。
反模式是"fat harness + thin skills"——你见过那种场景:40+ 个 tool definition 吃掉一半的 context window,每个 MCP 工具调用要 2-5 秒的网络往返,REST API 的每个 endpoint 都被包装成独立的 tool。结果是三倍的 token 消耗、三倍的延迟、三倍的失败率。正确的做法正好反过来:skill 要胖,harness 要瘦,工具要专而快。
2.3 顶层:Fat Skills(胖技能层)
顶层是 Markdown 写成的操作流程,里面编码了判断力、流程和领域知识。作者说 90% 的价值在这里。
注意:这里的"胖"不是指 skill 文件很长,而是指 skill 承载的知识密度很高。一个好的 skill 文件详细编码了"在什么情况下做什么判断、按什么流程操作、使用什么领域知识"。比如 last30days-skill 的 SKILL.md 里写了完整的多平台数据采集流程——每个平台用什么 API、怎么做查询扩展、结果怎么评分排序、输出什么格式。这些不是代码,是自然语言指令,但它们编码了真正的专业知识。
2.4 这个架构最优雅的性质
“当你这样做的时候,模型的每一次进步都会自动提升每一个 skill,而确定性层保持完美可靠。”
因为 skill 是用自然语言写的判断逻辑,当底层模型从 Sonnet 升级到 Opus、或者从当前版本升级到下一代时,skill 里编码的判断力会自动变得更好——更强的模型能更好地理解和执行同样的指令。你不需要改任何一行代码。同时,底层的确定性工具不受模型更换的影响——SQL 查询就是 SQL 查询,不管上面跑的是什么模型。
系统的两半以不同的方式进化:上层随模型进步自动变好,下层保持完美稳定。 两者叠加产生复利效应。
用传统后端架构来做一个精确的类比:Controller 层(harness)很薄,只做调度;Service 层(skills)很胖,承载所有业务逻辑和判断力;DAO 层(deterministic tooling)做确定性的数据操作。结构是同构的,只是"业务逻辑"变成了"判断力",“代码"变成了"自然语言指令”。
三、Resolver:上下文的路由表
当你的 skill 越来越多、领域知识越来越丰富时,一个新的问题出现了:怎么让模型在正确的时间加载正确的知识?这就是 Resolver 要解决的问题。
3.1 核心概念
Resolver 是一个上下文的路由表——当出现 X 类型的任务时,先加载 Y 文档。Skill 告诉模型"怎么做",Resolver 告诉模型"什么时候加载什么"。 两者是不同层次的东西。
举个例子:一个开发者改了一个 prompt,没有 resolver 的话就直接上线了。有了 resolver,模型会先去读 docs/EVALS.md,里面写着"改了 prompt 之后要跑 eval suite、比较分数、如果准确率下降超过 2% 就回滚并调查"。开发者甚至不知道这个 eval suite 存在——resolver 在正确的时机加载了正确的上下文。
3.2 Resolver 解决的根本矛盾
AI agent 架构中有一个最根本的矛盾:模型知道得越多越好,但上下文窗口塞得越满效果越差。
一个真实的案例完美地说明了这一点:一位开发者的 CLAUDE.md 曾经膨胀到 20,000 行——他把遇到的每个 quirk、每个 pattern、每个经验教训全塞了进去。结果模型的注意力反而退化了,Claude Code 甚至主动建议他删减。最终他把 CLAUDE.md 砍到大约 200 行,里面只放指向各个文档的指针(索引),让 resolver 在需要时按需加载。
20,000 行的知识依然存在,只是不再污染上下文窗口。这就是 lazy loading 的思想——你不会在应用启动时把整个数据库加载到内存里,你会在需要某条数据时才去查询。Resolver 就是 context window 的 lazy loading 机制。
3.3 Claude Code 的内置 Resolver
Claude Code 有一个内置的 resolver 机制:每个 skill 都有一个 description 字段,模型会自动把用户意图和 skill 描述做匹配。你不需要记住某个 skill 的命令名——description 本身就是 resolver。
这隐含了一个重要的设计原则:resolver 的质量取决于 description 写得好不好。 如果你的 skill description 写得太模糊(比如"处理数据相关任务"),模型可能在不该触发时触发,或在该触发时漏掉。Description 实际上是 resolver 的路由规则——它需要足够精确才能让路由正确工作。
这跟 MCP Server 开发中的 tool description 是完全一样的问题。MCP tool 的 description 同样充当 resolver——模型根据 description 来决定什么时候调用哪个 tool。写好 tool description 和写好 skill description,底层是同一个能力。
3.4 实操建议:索引+路由的 AGENTS.md 设计
随着项目越来越多、积累的 pattern 和 gotcha 越来越多,你的 CLAUDE.md 或 AGENTS.md 会面临膨胀压力。提前做的准备是:把它设计成一个索引+路由系统,而不是一个知识百科。
比如针对某个项目,AGENTS.md 里只写一行"该项目的开发规范见 docs/AGENTS-project.md",然后在那个文件里放 API 的 quirks、框架的特殊处理、测试策略等等。Agent 在处理该项目的任务时会自动去读那个文件,但在处理其他项目时不会被这些信息干扰。
让正确的知识在正确的时间出现在正确的位置,其余时间不要占用宝贵的注意力预算。
四、最关键的设计决策:Latent vs. Deterministic
如果整篇文章只能记住一个概念,那就是这个。
4.1 一条必须画清的线
每一个 AI agent 系统中的步骤,都属于且仅属于两类中的一类:
Latent space(潜在空间) 是 LLM 擅长的——阅读、理解、判断、综合、模式识别。这些任务没有唯一正确答案,需要"智能"来处理。
Deterministic(确定性) 是传统代码擅长的——同样的输入永远产出同样的输出。SQL 查询、编译后的代码、算术运算、组合优化。
混淆两者是 agent 设计中最常见的错误。
一个极其生动的例子:LLM 可以安排 8 个人的晚宴座位(考虑性格和社交关系的小规模判断问题,latent space 能处理),但让它安排 800 人的座位,它会生成一个看起来合理但完全错误的方案——因为 800 人的座位安排本质上是一个组合优化问题(deterministic),被强行塞进了 latent space。
4.2 两个方向的错误
错误方向一:把 deterministic 的工作塞进 latent space。 比如让 LLM 计算两个日期之间相差多少天。这有唯一正确答案,一行代码就能精确算出。但 LLM 有时对、有时错、有时差一天——因为它本质上不是在"计算",而是在"猜一个看起来合理的数字"。
错误方向二:把 latent space 的工作硬编码成 deterministic。 比如用 if-else 和关键词匹配来判断用户的一句话是"抱怨"还是"建议"。这种做法脆弱且覆盖率低,因为自然语言的表达方式无穷无尽——这个任务天然需要语义理解能力。
最差的系统充满了这两种错误——到处让 LLM 做算术、做精确检索(它不擅长的),同时又用硬编码规则做语义理解、做内容综合(它擅长的)。最好的系统对这条分界线毫不留情。
“毫不留情”(ruthless)的意思是:在做这个分类决策时,不允许任何模糊地带和偷懒。实际开发中人是有惰性的——当你已经在跟 LLM 对话了,顺手让它算个数、做个格式转换,感觉很方便。但这种偷懒积累起来,就是系统不可靠的根源。好的工程师会抵抗这种惰性,就像你在 Java 项目中坚持"业务逻辑不要写在 Controller 里"一样——这种"毫不留情"不是固执,而是工程纪律。
4.3 实战中的分界线
以一个 Telegram bot 的记忆关联推送功能为例,每个步骤都必须想清楚放在线的哪一侧:
步骤一:从 Mem0 中检索与新记忆语义相似的历史记忆。 → Deterministic。 调 API、传入 query、拿回 top-K 结果。这是一个有确定性输入输出的 API 调用,不需要 LLM 的判断。
步骤二:判断检索到的历史记忆和新记忆之间是否存在有意义的关联。 → Latent space。 “有意义的关联"是一个需要语义理解的判断问题——两条记忆可能用词完全不同但讲的是同一个概念的两个侧面。
步骤三:根据关联记忆的数量和相关度分数,决定是否推送通知。 → Deterministic。 “如果关联记忆数 ≥ 2 且平均相关度 > 0.7 就推送”——用 if 语句就够了。如果让 LLM 来决定,它可能每次给出不同的答案,导致用户体验不可预测。
五、Diarization:LLM 最不可替代的价值
在画清 latent vs. deterministic 的分界线之后,一个自然的问题出现了:latent space 最独特、最不可替代的能力到底是什么?答案是 Diarization(综合提炼)。
5.1 什么是 Diarization
这个词原本来自语音处理领域(speaker diarization,说话人分离),但在 AI agent 架构中被赋予了新的含义:模型阅读大量分散的信息,然后蒸馏出一页结构化的判断——一份浓缩自几十甚至几百份文档的分析简报。
核心论断是:没有任何 SQL 查询能产出这个。没有任何 RAG pipeline 能产出这个。 模型必须真正地去阅读、在脑中同时持有矛盾的信息、注意到什么在什么时候发生了变化、然后综合出结构化的洞察。这是"数据库查询"和"分析师简报"之间的区别。
5.2 为什么 RAG 做不到 Diarization
这里有一个常见的误解需要澄清。很多人以为 RAG(检索增强生成)可以做到 diarization——毕竟 RAG 也是"从多个文档中提取信息然后生成回答”。但两者有本质区别:
RAG 的核心是检索——给定一个 query,找到最相关的几个文档片段,然后让模型基于这些片段生成回答。它解决的是"哪些信息与我的问题相关"的问题。
Diarization 的核心是综合判断——不是基于一个 query 去检索,而是把一个实体(一个人、一个项目、一个趋势)的所有相关信息全部读完,然后发现信息之间的矛盾、变化轨迹、隐含模式,最终产出一份有洞察力的分析。它解决的是"这些信息放在一起意味着什么"的问题。
一个真实案例:一位创业者在申请表上说自己在做"AI agent 的 Datadog"(可观测性工具),但她 80% 的 GitHub 提交都在计费模块。这意味着她实际在做的是 FinOps(金融运维)工具,只是包装成了可观测性的外壳。
发现这个差距需要同时读三份不同的材料(GitHub 提交历史、申请表、顾问对话记录),并在脑中交叉比对。向量搜索能帮你找到"与 FinOps 相关的文档",但它不能帮你发现"一个人嘴上说的和手上做的不一致"。这就是 diarization 的独占领地。
5.3 Diarization 的应用场景
Diarization 的 pattern 可以迁移到大量场景中:用户行为分析(发现用户说的和做的之间的差距)、竞品情报(综合多个信息源判断竞品的真实战略方向)、代码审查(读完整个 PR 的改动后给出架构层面的判断而非逐行检查)、知识管理(从用户的碎片记忆中综合出结构化的认知 profile)。
核心 pattern 始终不变:检索(deterministic)→ 阅读全部材料(latent)→ 综合判断(latent)→ 输出结构化洞察(deterministic)。 检索和输出是确定性的,阅读和判断是 latent space 的。分界线依然清晰。
六、会学习的系统:从一次性工作到永久升级
前面五个概念(spec、三层架构、resolver、latent vs. deterministic、diarization)是静态的设计原则。真正让系统产生质变的,是把它们组合成一个会自我进化的闭环。
6.1 学习循环的设计
以一个活动匹配系统为例。这个系统为 6,000 名参会者做智能分组匹配——按行业亲和度聚类、跨行业"意外邂逅"匹配、实时配对等。
活动结束后,一个 /improve skill 阅读 NPS 调查结果,执行 diarization——不是分析差评(那通常是个别极端情况),而是专门分析"还行"的评价——那些系统差一点就成功但没完全对的地方。然后它提取 pattern,提出新规则,写回匹配 skill 文件中:
当参会者说"AI 基础设施"
但创业公司 80%+ 的代码是计费相关:
→ 分类为 FinTech,而非 AI 基础设施。
当同一组中的两个参会者已经互相认识:
→ 惩罚亲近度。优先新颖的介绍。
这些规则下次运行时自动生效。Skill 改写了它自己。
第一次活动:12% 的"还行"评价。下一次:4%。系统在没有任何人重写代码的情况下变得更好了。
6.2 闭环的结构
整个学习循环的结构是:运行 → 收集反馈 → 分析"差一点"的地方 → 提炼新规则 → 写回 skill → 下次运行时自动使用。
注意这个循环中 latent vs. deterministic 的分界依然清晰:分析 NPS 反馈、提取 pattern、提出新规则——这些是 latent space 的工作;把规则写入文件、下次运行时加载——这些是 deterministic 的工作。架构原则在每一个层面都保持一致。
6.3 “如果我不得不向你要求同一件事两次,你就失败了”
这条指令精准地表达了一种架构纪律:
你不被允许做一次性的工作。如果我让你做某件事,而它是那种将来还需要再做的事,你必须:先手动做 3 到 10 个样本。给我看结果。如果我批准了,把它固化成一个 skill 文件。如果它应该自动运行,把它挂到定时任务上。
这不是 prompt engineering 技巧,而是一种系统设计哲学。你写的每一个 skill 都是对系统的永久升级。 它永远不会退化,永远不会遗忘,它在你睡觉时凌晨三点运行。而当下一代模型发布时,每一个 skill 都会立刻变得更好——latent space 步骤中的判断力自动提升,deterministic 步骤保持完美可靠。
实操启示:如果你在用 AI coding agent 做项目开发时,发现自己反复给 agent 下达类似的指令(“检查代码风格是否符合规范”、“更新 CHANGELOG”、“跑完测试后总结失败原因”),那就应该把它固化成 skill 文件或 AGENTS.md 中的一条永久规则。每一次这样的固化,都是系统的一次永久升级。
七、一个完整的思维模型
把前面所有概念串起来,一个优秀的 AI agent 系统的设计思维模型是这样的:
第一步:用 Spec 定义意图。 在写任何代码之前,先写清楚你要什么、不要什么、约束条件是什么。Spec 是你和 AI 之间的合同,也是整个系统的"源头真相"。
第二步:画清 Latent vs. Deterministic 的线。 审视系统中的每一个步骤,冷酷地判断它属于哪一侧。需要判断力的交给 LLM,需要精确性的交给代码。不允许任何模糊地带。
第三步:构建三层架构。 把智能向上推入 skill 层(自然语言编码的判断力和领域知识),把执行向下推入确定性工具层(可靠的 API 调用、数据库查询、算法),让中间的 harness 保持薄(只做调度,不做业务)。
第四步:用 Resolver 管理知识加载。 不要把所有知识塞进一个文件。设计一个索引+路由系统,让正确的知识在正确的时间出现在正确的位置。
第五步:让 Diarization 发挥 LLM 的独特价值。 在需要综合判断的环节,让模型读完所有材料、持有矛盾、发现变化、产出洞察——这是它最不可替代的能力,不要用 SQL 或 RAG 来替代。
第六步:构建学习循环。 运行 → 收集反馈 → 分析"差一点"的地方 → 提炼新规则 → 写回 skill。让系统自我进化,让每一个 skill 成为永久升级。
这六步不是线性的流水线,而是一个持续运转的设计思维框架。你在设计 AI agent 系统的每一个决策中,都可以回到这个框架来检验:我的 spec 写清楚了吗?这个步骤放在线的正确一侧了吗?知识加载的时机对吗?这个功能可以被固化成 skill 吗?
结语:AI 时代工程师的核心能力
回到文章开头的问题:AI 时代最值钱的工程师是什么样的?
不是写代码最快的(AI 已经把这件事加速了 10 倍),而是能做好以下这些事的人:写出清晰的 spec 来定义意图,画清 latent vs. deterministic 的分界线来做架构决策,设计出让 AI 的智能和代码的确定性各司其职的系统,构建起能自我进化的学习循环。
这些能力的共同特征是:它们都不是"写代码"本身,而是关于代码的判断力——什么该写、什么不该写、什么交给 AI、什么交给代码、什么固化成规则、什么保持灵活。
如果你有传统后端工程经验,这些能力并不陌生。你在 Java 项目中坚持"业务逻辑不写在 Controller 里"的纪律,和你在 AI agent 系统中坚持"deterministic 工作不塞进 latent space"的纪律,本质上是同一种工程素养——关注点分离、分层设计、在正确的抽象层级做正确的事情。 只是现在你的系统多了一个新的组件——LLM——而你需要学会如何精准地划定它的职责边界。
构建一次。永远运行。系统会复利增长。
参考资料
- Addy Osmani, “How to write a good spec for AI agents”, January 2026
- Addy Osmani, “My LLM coding workflow going into 2026”, December 2025
- GitHub Blog, “Spec-driven development with AI”, September 2025
- GitHub Blog, “How to write a great agents.md: Lessons from over 2,500 repositories”, November 2025
- Martin Fowler (Birgitta Böckeler), “Understanding Spec-Driven-Development: Kiro, spec-kit, and Tessl”, 2025
- Kiro Documentation, “Specs”, 2026
- Andrew Ng, “Building at speed: The Product Management Bottleneck”, 2025