Skip to content
Go back

AI Agent 工程地图——从 LLM 到可编程 Agent

AI Agent 工程地图——从 LLM 到可编程 Agent

结论先行

先给一句话定义,后面所有内容都是这句话的展开:

Agent = LLM + 工具循环 + 记忆 + 编排。

LLM 只负责「读和想」;Agent 之所以是 Agent,是因为它多了一个循环——调用工具、观察结果、再决定下一步;而这个循环要跑得远、跑得稳,靠的是记忆(上下文)和编排(规则、流水线、多智能体调度)。

这篇是「AI Agent 工程」系列的总纲。它不讨论「AI 是不是未来」这种问题——那不需要工程师回答。它要回答三个更具体的工程问题:

  1. 一个 Agent 系统在架构上到底分几层,每层解决什么问题?
  2. 把 Agent 做「可编程」而不是「会聊天」,要攻克哪五个主题?
  3. 为什么 Claude Code 和 Codex 走的是两条不同的编排路线,它们分别把复杂度放在哪里?

一、Agent 的分层架构

把 Agent 拆开看,是四层,自下而上:

┌────────────────────────────────────────────────────────────┐
│ ④ 编排层 Orchestration                                      │
│    路由 · 流水线 · 多 Agent 调度 · 状态机 · 验收闸门           │
├────────────────────────────────────────────────────────────┤
│ ③ 记忆层 Memory                                             │
│    短期上下文窗口 · 长期存储 · 检索 · 压缩/淘汰               │
├────────────────────────────────────────────────────────────┤
│ ② 工具循环层 Tool Loop                                       │
│    函数调用 → 执行 → 观测结果 → 决策 → 再次调用               │
├────────────────────────────────────────────────────────────┤
│ ① 模型层 LLM                                                │
│    推理 · 规划 · 生成 · 指令遵循                              │
└────────────────────────────────────────────────────────────┘

下面自下而上说清楚每一层在工程上的真实含义。

① 模型层:只负责「读和想」。 它输出两样东西:文本,以及「我要调用哪个函数、传什么参数」的意图。模型不保存状态,不知道上一轮发生过什么,也不会主动执行任何操作——它只会「说」它想调用工具。很多人对 Agent 的误解就在这:以为是模型变聪明了,其实是外面的循环让模型「看起来」在干活。

② 工具循环层:Agent 的引擎。 这是 Agent 和 ChatGPT 聊天窗口的本质区别。循环是:模型说「调用 Read 读这个文件」→ 运行时真的去读 → 把结果塞回上下文 → 模型看到结果决定下一步。这个「想—做—看」的闭环就是 ReAct 范式(Think → Act → Observe)。这一层落到 Claude Code 源码上非常直观:主循环集中在 QueryEngine.ts(约 1300 行的查询与编排核心),而「手」则来自一个工具注册表——源码 tools/ 目录下挂着 40 多个一级工具,包括 BashTool(执行 shell)、FileReadTool/FileEditTool/FileWriteTool(读/改/写文件)、GrepTool/GlobTool(搜索)、WebFetchTool/WebSearchTool(联网)与 MCPTool(协议接入);上面那句「调用 Read」对应的正是 FileReadTool 这样一个具体工具。循环的核心矛盾是每一次迭代都在消耗上下文窗口,所以循环不能无限跑——这就是第 ⑤ 个主题「长任务状态管理」要解决的。

③ 记忆层:让循环「记得住」。 上下文窗口是易失的、有限的、且不便宜。所以工程上要把记忆分两类:短期记忆(当前上下文窗口里能直接看到的东西)和长期记忆(写进外部存储、跨会话可检索的东西)。记忆不是「让模型记性更好」,而是「把外部存储接到循环上」——会话开始时检索注入相关上下文,会话结束时把有价值的内容沉淀下来。Claude Code 的 CLAUDE.md 项目记忆文件、以及 MCP(Model Context Protocol)接入外部数据源,都是这层的手段。

④ 编排层:让循环「别跑偏」。 单个 Agent 循环能做的事有限:跑久了会忘、会飘、会做出危险操作。编排层负责三件事——路由(这个意图该用哪个能力单元)、流水线(多个步骤按依赖关系串联)、闸门(哪些操作必须人确认、哪些结果必须达标才能继续)。Claude Code 的 skill(可复用技能)、subagent(子代理)与 hook(生命周期钩子)都属于这一层的构件;它甚至把「人怎么调度 Agent」也做成了注册表——源码 commands/ 目录下有 86 个一级命令(如 /review/commit/mcp/memory/tasks/permissions/resume)。Codex 则把编排落在一个更「像操作系统」的 agent 子系统上:core/agent 里的 registry.rsAgentRegistry 维护一张 agent 树、限制每个会话能派生多少子代理线程,并从一份历史人物名字池里给子代理起昵称)、role.rs(角色在 spawn 时选定,作为「高优先级配置层」叠加到子代理配置上,内置 default/explorer/worker 三种角色),而真正的「派生」动作在 codex_delegate.rs——一次性拉起一个子 Codex 会话、塞入初始输入、通过 channel 转发事件与操作,完成后自动关闭,且被委托者从不向人弹审批。

这四层里,LLM 是别人(模型厂商)给你的,而工具循环、记忆、编排这三层,才是你作为工程师真正要写代码的地方。这也是这个系列的重心所在。

💭 思考:为什么是四层,而且「模型层」被单独划出去?—— 一步步推:Agent 要干活,得有「手」(工具循环);要记得住上下文,得有「记性」(记忆);要别跑偏,得有「纪律」(编排)。这三层都不是模型自带的——模型是无状态的推理函数,厂商只交付「读和想」。于是约束就变成了:你需要写代码、写测试、埋日志的地方,恰恰是循环/记忆/编排这三层;而模型层你只能调参、换型号。所以「四层」不是一个审美选择,而是「控制权边界」的投影——把你能工程化的部分和厂商交付的部分切开,才不会每次模型升级都推倒重来。如果不拆,你会发现自己一边改调度逻辑、一边祈祷模型别变,那三层就永远没法独立迭代。

二、五大核心主题一句话勾画

这张地图上有五个核心主题,是「把 Agent 做可编程」绕不开的。这里先一句话勾画,后续每篇展开一个。

1. Tool Loop 编排 —— 工具调用不是「给模型几个函数就完了」,而是「循环怎么收敛」:工具描述怎么设计、调用结果怎么裁剪后回填、失败怎么重试、什么时候该停下。一个收敛不了的循环,就是一个烧 token 的死循环。

2. 提示词工程 —— 在 Agent 里,提示词不是「聊天开场白」,而是系统契约:角色、约束、工具的语义、失败处理规则,全部是提示词。System Prompt 定义了「这个 Agent 是谁、能做什么、不许做什么」,模型每一次推理都受它约束。把规则写清楚,模型才能稳定遵守。

3. 上下文压缩与 Memory —— 上下文窗口是 Agent 的第一资源约束。压缩(摘要、淘汰旧消息)和记忆(外置存储 + 检索)是一体两面:压缩解决「窗口装不下」,记忆解决「窗口之外还要记得」。Claude Code 的自动压缩(/compact)和 CLAUDE.md 持久记忆就是这主题的现成例子。

💭 思考:为什么把「上下文窗口」抬到「第一资源约束」的位置,而不是算力或模型能力?—— 算力是钱、是弹性可扩的,模型能力是厂商在涨的,唯独上下文窗口是「每一轮都要全量重发历史」这个循环结构里最硬的天花板:它同时决定了一轮能带多少信息、能跑多少圈、以及每圈要付多少 token。循环每多转一圈,窗口里就多一份要重发的历史——所以窗口不是一个「参数」,而是「循环能跑多远」的物理边界。这解释了为什么压缩和记忆被并成同一个主题:压缩解决「这一圈里窗口装不下」,记忆解决「窗口之外还想记得」——两者合起来,才是把窗口当成可经营资源、而不是一次性耗材。

4. Subagent 与 Skill 注入 —— 单个 Agent 什么都做 = 什么都很烂。正确做法是「注入式分工」:把专门能力拆成可注入的单元(subagent / skill),主 Agent 按需加载,用完即弃。这样既保证每个单元足够专注,又不让所有能力同时挤爆上下文窗口。

💭 思考:为什么「单 Agent 什么都做 = 什么都很烂」,而不是「能力多所以更强」?—— 反着推:模型的能力不是装在「数据库」里随时可取,而是靠 prompt 把它「读进来」才生效。于是能力越多,意味着启动时要么全塞(token 爆炸 + 注意力稀释),要么让一个 Agent 同时扮演多个身份(上下文互相污染)。约束就落到了「上下文窗口一次只能装当下这一份」:正确解法不是让模型更全能,而是把每个专门能力做成可注入的独立单元,谁需要谁加载、用完即弃。所以「什么都做」的烂,本质是「把几十份说明书和一个多面人格同时塞进一个窗口」的烂——拆开,不是为了更聪明,是为了让窗口里只装一份。

5. 长任务状态管理 —— 一个 Agent 任务动辄几十步、跨多个会话,中途断了怎么办?答案不是「重新跑」,而是状态快照 + 断点续传:把进度、中间产物、未决决策序列化保存,断了能从快照继续。这本质是把「Agent 的一次运行」从「进程」升级成「可恢复的持久任务」,背后是状态机的思想。

三、Claude Code vs Codex:两种编排哲学

把上面五件事组合起来,业界出现了两条不同的路线。这里只做提纲挈领的对比,详细拆解留给后续文章。

Claude Code:上下文即编排。 它把「规则、工具、流程」全部写进模型能读到的文件里——CLAUDE.md(项目记忆与规范)、subagent 定义、skill(可复用技能)、settings.json 里的 hooks 与权限,还有 Plan 模式(先规划再执行)。它的哲学是:让模型「读」工程知识,而不是被代码「驱动」。约束是自然语言写成的,人机同读;编排逻辑不在某个调度器里,而在这些文件里,由模型在上下文中自主执行。

Codex:循环即编排。 它把重心放在更自动化的 agentic loop 和云端沙箱上——把任务切块、丢进隔离沙箱并行跑、自动读改写测试迭代,直到产出可合并的 PR。它的哲学是:把「跑循环」这件事本身做成产品,让循环足够鲁棒,人只需要给任务和验收标准。这个哲学在源码里体现得相当直白:委托出的子代理(codex_delegate.rs)被强制为「从不弹审批」(approval policy 必须为 never),意味着子任务一旦派出就全自动跑完,人只守在验收那一端。

用一句话概括这场分野:

Claude Code 把复杂度放在「提示词与文件」上(声明式,人要写清规则);Codex 把复杂度放在「循环与沙箱」上(执行式,系统要跑得稳)。

这两者不是对错之分,而是「编排的复杂度放在哪一层」的不同选择。如果你要解决的是「一堆规则和能力怎么被一个模型正确调用」这种知识密集型编排问题,文件声明比代码调度更经济;如果你的问题是「并行跑 100 个独立任务」,沙箱并行哲学就更合适。

💭 思考:为什么同样是 coding agent,Claude Code 和 Codex 会走向「复杂度放不同层」的两条路?—— 从各自要解决的约束反推:Claude Code 面对的是「一堆规则、能力、权限怎么被一个模型正确调用」这种知识密集问题——瓶颈不在「跑得快不快」,而在「模型读没读懂规则」,所以它把编排写进模型能读的文件里(声明式),让人机同读、让模型在上下文里自主执行。Codex 面对的是「并行跑 100 个独立子任务」这种吞吐密集问题——瓶颈在「循环和沙箱够不够鲁棒、能不能无人值守跑完」,所以它把编排写进调度器与沙箱(执行式),人只守在验收端。换句话说,这不是谁对谁错,而是「你的瓶颈在知识密度还是在吞吐」决定了复杂度该放在哪一层。若反过来——用文件声明去跑 100 个并行任务、或用沙箱调度去解知识密集编排——都会把简单问题做复杂。

四、这张地图怎么用

这个系列后续会沿着五个主题逐个展开,每篇回答一个可落地的问题:

主题要回答的工程问题
Tool Loop 编排循环怎么收敛?工具描述、结果裁剪、重试与终止条件
提示词工程怎么把「规则」写成模型能稳定遵守的契约?
上下文压缩与 Memory窗口装不下、跨会话记不住,怎么办?
Subagent 与 Skill 注入怎么把「专门能力」按需注入而不挤爆上下文?
长任务状态管理几十步跨会话的任务断了,怎么断点续传?

结语

最后把本文压缩成一段话,方便你记住这张地图的骨架:

Agent 不是「更聪明的 LLM」,而是「LLM 套上一个会循环的外壳」:工具循环给它手,记忆给它记性,编排给它纪律。把这三层做扎实,你就从「用 AI 的人」变成了「造 Agent 的人」。 下一篇,从最底层、也是最容易写出死循环的地方开始:Tool Loop 编排。

章末提问

追问 1:一句话说清 Agent 和 LLM 的区别?

结论先行:LLM 是无状态的「读和想」,Agent 是给它套上一个「想—做—看」循环的外壳,多出来的三样东西是工具循环、记忆、编排。

因为:模型输出只是文本或「我要调哪个函数」的意图,它自己不会执行、不保存状态、也不受纪律约束;是外面的循环真的去执行工具、把结果喂回去、再决定下一步,是记忆层把易失的窗口接到外部存储上,是编排层用路由/流水线/闸门让循环别跑偏。所以 Agent 不是「更聪明的模型」,而是「模型 + 循环 + 记忆 + 编排」四层,其中后三层才是工程师写代码的地方。

追问 2:为什么「循环」是 Agent 的引擎,而不是模型?

结论先行:因为模型本身是无状态的,是循环把「调用工具、观察结果、再决策」这个闭环串起来,才让模型「看起来在干活」。

因为:模型一次调用只会输出文本或 tool_call 意图,真正让任务推进的是「执行 → 喂回结果 → 再请求」这个 ReAct 循环。而这个循环每一次迭代都在消耗上下文窗口——所以「循环能不能收敛」直接决定了 Agent 是省 token 地干活还是烧 token 地死循环。把引擎放在循环而不是模型上,你才能把护栏(max_iter、去重)、可观测性(hooks)都挂到正确的地方。

追问 3:Claude Code 和 Codex 的编排哲学,本质差别在哪?

结论先行:本质差别不在「谁更可编程」,而在「编排的复杂度放在哪一层」——Claude Code 放在提示词与文件(声明式),Codex 放在循环与沙箱(执行式)。

因为:Claude Code 把规则、工具、流程写进模型能读的 CLAUDE.md、skill、subagent 定义里,让模型在上下文里自主执行,适合「知识密集型」编排(一堆规则怎么被正确调用);Codex 把重心放在 agentic loop 和云端沙箱上,子任务被派出去后强制全自动跑完、人只守在验收端,适合「吞吐密集型」的并行任务。所以选型问的不是谁强,而是「你的瓶颈在知识密度还是在并行吞吐」。

追问 4:为什么要把「上下文窗口」当成第一资源约束,进而带出压缩和记忆?

结论先行:因为 Tool Loop 每轮都要全量重发历史,上下文窗口同时卡住了「能带多少信息、能跑多少圈、每圈多少钱」,它是循环能跑多远的物理边界。

因为:窗口是易失、有限、且不便宜的。压缩(摘要、淘汰)解决「这一圈窗口装不下」,记忆(外置存储 + 检索)解决「窗口之外还要记得」,二者一体两面。把窗口当资源而非耗材,才会在设计上主动做结果裁剪、自动压缩、prompt caching、子任务隔离——这些都是围绕同一个约束的不同止损阀。


Share this post on:

Previous Post
Agent 的本质:LLM + Tool Loop
Next Post
不均匀硬币如何公平决策——抛两次的巧妙解法