Loading

【大模型Wiki】16 · Agent 与多 Agent 与 Coding Agent

Agent、多 Agent 与 Coding Agent

新增篇目。Agent 是 2025-2026 大模型落地的主战场。
经典综述:Lilian Weng《LLM Powered Autonomous Agents》https://lilianweng.github.io/posts/2023-06-23-agent/

一、Agent 是什么

Agent = LLM(大脑) + 工具(手脚) + 循环(生命) + 记忆(经验)。与"一问一答"的 Chatbot 的本质区别:Agent 在一个循环里自主决策——观察环境、调用工具、根据结果继续行动,直到完成任务。

Lilian Weng 的经典定义:

Agent = LLM + Planning(规划) + Memory(记忆) + Tool Use(工具)

1.1 为什么"循环"是灵魂

普通 Chatbot 是一次性函数调用:input → LLM → output。Agent 则是把 LLM 包在一个 while 循环里:每一次迭代,模型可能决定"我还需要查点东西",于是调用工具、拿到结果、把结果塞回上下文,再让模型决策下一步。这个"自主决断 + 环境交互"的闭环,让模型从"会说话"变成"能办事"。

四个要素缺一不可:

  • LLM(大脑):负责推理、规划、生成行动决策;
  • 工具(手脚):让模型能影响/读取外部世界(搜索、代码执行、API);
  • 循环(生命):把"思考—行动—观察"串成可持续的过程;
  • 记忆(经验):让长任务中模型"记得"之前做过什么、学到了什么。

1.2 Agent 与 Chatbot 的能力边界

维度 Chatbot Agent
交互方式 一问一答 自主多步循环
能否改外部世界 否 能(调 API、跑代码、写文件)
是否需要工具 不需要 核心依赖
上下文长度 单轮/短对话 长任务、需记忆管理
失败处理 重问即可 需自我修正/回滚

二、Agent 循环:ReAct 范式

ReAct(Reasoning + Acting, 2022)是几乎所有 Agent 的骨架:模型交替输出思考与行动,观察行动结果再继续思考:

Agent 循环与 Harness

用户:帮我查北京今天天气,如果下雨提醒我带伞
──────────────────────────────────────────────
Thought: 我需要先查询北京的天气
Action: get_weather(city="北京")
Observation: {temp: 22, condition: "小雨"}
Thought: 今天有小雨,需要带伞。任务完成,回复用户
Final Answer: 北京今天小雨 22°C,出门请带伞。
──────────────────────────────────────────────

落到 API 层面,这就是第 04 篇的 tool calling 外面套一个 while 循环:

messages = [{"role": "user", "content": task}]
while True:
    resp = llm.chat(messages, tools=tools)
    if resp.tool_calls:                       # 模型要求调用工具
        for tc in resp.tool_calls:
            result = execute_tool(tc)          # 真正执行
            messages.append(tool_result_msg(result))
    else:                                     # 模型给出最终答案
        return resp.content

工程必备件:最大步数限制(防死循环)、人工确认(危险操作审批)、错误回传(工具报错要把错误信息还给模型让它自我修正)。

2.1 ReAct 的 Prompt 结构拆解

ReAct 之所以有效,是因为它把"推理"和"行动"显式地写在同一个轨迹(trajectory)里,让模型能"边想边做"。一个典型的 ReAct prompt 包含三部分:

  1. 指令(Instruction):告诉模型"你可以交替使用 Thought / Action / Observation,必要时调用工具,最终用 Final Answer 收尾";
  2. 少量示例(Few-shot demos):给 2–3 个完整 Thought→Action→Observation→Final Answer 样例,教模型格式;
  3. 当前轨迹(Trajectory):已发生的 Thought / Action / Observation 历史,模型基于它续写下一步。
# 系统指令(示意)
你是一个能用工具的智能体。每一步先写 Thought(你的推理),
再写 Action(调用工具:工具名(参数)),系统会返回 Observation。
循环直到你能在 Final Answer 中给出最终答复。

# 示例
Question: 珠穆朗玛峰比科罗拉多河高多少?
Thought: 我需要先查两座地标高度
Action: search("珠穆朗玛峰 海拔")
Observation: 8848.86 米
Thought: 再查科罗拉多河
Action: search("科罗拉多河 海拔")
Observation: 约 0 米(河流源头海拔不起决定作用,这里仅为示例)
Thought: 两者相减
Action: calculator(8848.86 - 0)
Observation: 8848.86
Final Answer: 约 8848.86 米

# 当前问题
Question: {user_question}
Thought:

注意:现代模型(GPT-4 等)通常不再用纯文本 ReAct 格式,而是改用 API 原生的 tool_calling(结构化输出),由框架把 Observation 回填。但 ReAct 的思想——"思考与行动交错、观察驱动下一步"——仍完整保留在 tool-calling 循环里。

2.2 Function / Tool Calling 实现原理

Tool Calling(也叫 Function Calling)是模型原生能力,让模型以结构化 JSON 表达"我想调哪个函数、传什么参数",而不是靠正则从文本里扒 Action:。

2.2.1 原理

  1. 你向 API 声明一组工具的 schema(函数名、描述、参数 JSON Schema);
  2. 模型读用户问题后,若认为需要调用,就返回一个结构化的 tool_calls,含函数名与已填好的参数;
  3. 你(Host)在本地真正执行函数,拿到结果;
  4. 把"工具名 + 参数 + 结果"作为一条 tool 角色消息回填进对话,再请求模型,模型据此继续。
import openai  # 以 OpenAI 兼容接口为例,思路通用

tools = [{
    "type": "function",
    "function": {
        "name": "get_weather",
        "description": "查询指定城市当前天气",
        "parameters": {
            "type": "object",
            "properties": {
                "city": {"type": "string", "description": "城市名,如 北京"}
            },
            "required": ["city"]
        }
    }
}]

messages = [{"role": "user", "content": "北京今天天气怎么样,要带伞吗?"}]
resp = client.chat.completions.create(
    model="gpt-4o", messages=messages, tools=tools
)

# 模型决定调用工具:返回结构化 tool_calls
if resp.choices[0].message.tool_calls:
    tc = resp.choices[0].message.tool_calls[0]
    print(tc.function.name)       # 'get_weather'
    print(tc.function.arguments)  # '{"city": "北京"}'  —— 已是合法 JSON
    # 本地执行
    result = get_weather(**json.loads(tc.function.arguments))
    # 回填工具结果
    messages.append(resp.choices[0].message)              # 先保留模型的 tool_calls 消息
    messages.append({
        "role": "tool",
        "tool_call_id": tc.id,
        "content": json.dumps(result, ensure_ascii=False)
    })
    # 再次请求,模型据此给出最终自然语言答复
    final = client.chat.completions.create(model="gpt-4o", messages=messages, tools=tools)
    print(final.choices[0].message.content)

2.2.2 关键工程细节

  • 参数必为合法 JSON:现代模型对 JSON 输出做了微调,基本能稳定产出可解析参数;但仍要 try/except json.loads 兜底,解析失败可把错误回传让模型重试。
  • tool_call_id 必须对应:回填的 tool 消息要带 tool_call_id,框架据此把结果拼回对应的那次调用(并行调用时尤其重要)。
  • 并行 tool call:模型一次可返回多个 tool_calls,Host 可并发执行再批量回填,提速明显,但注意结果回填顺序与 id 对应。
  • schema 写得好坏决定成败:函数 description 和参数 description 是模型"决策是否调用、怎么填参"的主要依据,务必写清楚、写准确,避免歧义。

三、Memory 与上下文工程

上下文窗口有限,长任务必须管理"该让模型看到什么":

  • 短期记忆:对话历史,直接放上下文;过长则压缩/摘要(compaction)
  • 长期记忆:向量库检索(RAG 式记忆)、结构化事实库("用户偏好 X")
  • 工作区记忆:Codex 类 Agent 的 AGENTS.md、任务待办清单文件(todo list 作为持久记忆)
  • Append-only 日志:DeepSeek Harness 的 session log 是典型——所有"模型所见即所录",可回放可分叉(见 10 篇)

"上下文工程(Context Engineering)"已取代早期"提示工程"成为一门显学:决定每一步把哪些系统提示、工具 schema、历史、检索结果、子 Agent 报告塞进有限窗口。

3.1 记忆系统的三种形态(深入)

3.1.1 短期记忆(Short-term / Working Memory)

就是当前上下文窗口里的内容:系统提示、对话历史、工具调用与结果。问题是窗口有限(哪怕 128K/200K 也有上限),长任务会溢出。

常用压缩策略:

  • 截断(Truncation):丢弃最旧的消息——简单但可能丢关键信息;
  • 摘要压缩(Compaction):定期对早期消息调用 LLM 做摘要,用摘要替换原文,典型如 Claude Code 的 compact;
  • 滑动窗口 + 关键保留:保留最近 N 轮 + 始终保留"任务目标、用户偏好、重要结论"。

3.1.2 长期记忆(Long-term Memory)

让 Agent 跨会话记住东西,典型三类:

  1. 向量库记忆(RAG 式):把历史对话/知识切块 embedding 入库,需要时语义检索回填;
  2. 结构化事实库:显式存"用户偏好:喜欢简洁回答""项目名称:X"等键值/图结构事实;
  3. 文件/工作区持久化:直接写文件(笔记、todo、实验记录),模型下次读文件即可恢复。
# 长期记忆最简实现:向量库检索(伪代码)
def recall(memory_db, query, k=5):
    q_vec = embed(query)
    hits = memory_db.search(q_vec, top_k=k)   # 语义检索
    return "\n".join(h.text for h in hits)

def remember(memory_db, text):
    memory_db.insert(embed(text), text)        # 把经历写进长期记忆

3.1.3 工作区记忆(Workspace Memory)

Agent 把"待办清单、约束、发现"持续写进工作区文件,相当于给模型一块"外部大脑"。AGENTS.md(项目级指令)、todo.md(任务清单)、notes.md(调研笔记)是常见形态。好处:不受上下文窗口限制,可审阅、可版本化(进 git)。

3.2 上下文工程(Context Engineering)是什么

Andrej Karpathy 等提出的观点:写好一个固定 prompt 已经不够,每一步"给模型喂什么上下文"本身就是一个需要设计和优化的系统。核心决策:

  • 系统提示里放多少"世界观/规则"?
  • 暴露多少工具 schema?(schema 越多,模型越容易选错/越占 token)
  • 历史保留多少、摘要到什么粒度?
  • 检索结果怎么排序、截断?
  • 子 Agent 的报告摘要到几行?

好的上下文工程 = 在有限窗口里塞进"恰好够决策的信息",同时剔除噪声。这是 Agent 质量的分水岭,往往比换更强模型收益更大。

四、Planning 与 Self-Reflection

4.1 规划(Planning)技术

技术 思路
CoT 思维链 让模型先写推理再作答
任务分解 把大任务拆成子任务清单(plan-and-execute)
Reflection 执行后自我批评、重试
长思考(Reasoning 模型) o1/R1/Qwen3-Thinking 类模型把"规划"内化为测试时计算

4.1.1 Plan-and-Execute

把"规划"和"执行"分成两个阶段/两个模型调用:

  1. 先规划:让模型一次性产出完整子任务清单(plan);
  2. 再逐条执行:每步执行后把进展写回 plan,执行完一条划掉一条;
  3. 必要时重规划:遇到意外阻塞,重新调用规划器调整剩余步骤。

优点:plan 在上下文里稳定可见,模型不易"跑偏";适合长任务。

4.2 Reflexion(自我反思)

Reflexion(2023,Shinn et al.)是让 Agent 从失败中学习的关键技术。核心:每轮结束后,让模型基于"轨迹 + 环境反馈"生成一段口头反思(verbal reflection),存进记忆,下一轮决策时读它,避免重复犯错。

第 1 轮:尝试 → 失败(环境回报负值)
   ↓
反思模块:基于"我做了什么 / 为什么失败"生成反思
  "我刚才没先查数据库就直接写 SQL,导致字段名错。
   下次应先 DESCRIBE 表结构再写查询。"
   ↓
反思存入长期记忆
第 2 轮:模型读取反思 → 改变策略 → 成功

实现要点:

  • 用一条固定反思 prompt 让模型总结"哪里错了、下次怎么改";
  • 反思是文本、可解释、可审计;
  • 与 3.1 的长期记忆结合,跨 Episode 积累经验。

4.3 长思考模型对 Agent 的影响

o1 / DeepSeek-R1 / Qwen3-Thinking 这类"推理模型"把规划内化为测试时计算(test-time compute):模型在内部做大量隐式推理后再输出行动。这显著提升了复杂多步任务的规划质量,但代价是延迟和成本更高。实践中常"简单步用快模型、难规划用推理模型",做分层调度。

五、多 Agent(Multi-Agent)

单 Agent 的痛点:上下文装不下一个庞大任务;一个模型既要规划又要执行容易"分心"。多 Agent 把系统组织成分工协作的智能体集合。

5.1 常见拓扑

模式 结构 例子
Orchestrator-Worker 一个主 Agent 拆解任务、派发子 Agent、汇总结果 Claude Code 子 Agent、DeepSeek Harness 的 subagent
流水线 A 输出 → B 输入,按序加工 需求→编码→测试→评审
辩论/评审 多个 Agent 各持立场互相挑错 代码 review 对抗、提升推理质量
群聊 共享消息总线,自由发言 AutoGen group chat

5.1.1 各拓扑适用场景

  • Orchestrator-Worker:最常用。主 Agent 负责"派活 + 收活 + 兜底",子 Agent 各自专注一块(如一个查文档、一个写代码)。上下文隔离好,主 Agent 不被子任务细节淹没。
  • 流水线(Pipeline):任务天然有先后依赖且每一步职责清晰,如"需求分析→编码→测试→评审"。
  • 辩论/评审(Debate):用"多视角互相挑错"提升推理/代码质量,代价是多倍调用成本。
  • 群聊(Group Chat):角色在共享总线自由发言,灵活但难控制、易发散,多用于探索型协作。

5.2 主流框架角色分工对比

框架 出品方 特点 心智模型
LangGraph LangChain 图编排、状态机、checkpoint,工程最成熟 把 Agent 画成"有状态的状态图"
AutoGen / AgentChat 微软 多 Agent 会话式协作、group chat 多个 Agent 像人一样聊天协作
CrewAI 开源 角色化团队(CEO/工程师/QA 隐喻),上手快 组建一支"有职位的团队"
MetaGPT 开源 SOP 驱动的软件公司模拟 用标准作业流程编排"软件公司"
Swarm → Agents SDK OpenAI 极简 handoff 模式 Agent 之间"交接"对话

5.2.1 怎么选框架

  • 要强可控、可回放、可 checkpoint 的生产系统 → LangGraph(状态机 + 持久化最扎实);
  • 要快速搭多 Agent 对话/群聊原型 → AutoGen;
  • 要用"角色剧本"讲故事式组队 → CrewAI(CEO/Researcher/Writer 隐喻直观);
  • 要模拟软件公司瀑布流(需求→设计→编码→测试) → MetaGPT(内置 SOP);
  • 要轻量 handoff、和 OpenAI 生态紧耦合 → Agents SDK。

5.3 多 Agent 的核心难题:状态与上下文隔离

多 Agent 的核心难题不是"怎么对话",而是状态与上下文隔离:子 Agent 之间传什么、主 Agent 记住多少、失败如何回滚——这正是 Harness 层(09 篇)要解决的问题。

具体痛点:

  • 上下文污染:子 Agent 的脏输出(报错、冗长日志)直接灌给主 Agent,挤占窗口还带偏决策;
  • 信息丢失:子 Agent 该回传"结论摘要"而非"全部过程",否则主 Agent 被淹没;
  • 失败传播:一个子 Agent 崩了,要不要整体回滚?还是换一个子 Agent 重试?需要显式错误处理;
  • 成本爆炸:N 个 Agent 各自带全量工具 schema + 历史,token 消耗乘倍。

对策:子 Agent 只读必要上下文、只回传结构化摘要;主 Agent 持有全局 plan 与最终裁决;用 Harness 统一管理调度、隔离、回滚。

六、Coding Agent:落地最成功的 Agent

编程是 Agent 的最佳场景:目标可验证(编译/测试)、反馈明确(报错信息)、工具天然存在(文件系统、shell、git)。

6.1 代表产品(2026)

产品 形态 特点
Claude Code 终端 Agent Anthropic 官方,agentic coding 事实标杆
DeepSeek Harness 开源运行时 Everything is a plugin,源码可读(见 10 篇)
OpenAI Codex CLI 终端/云 与 GPT-5 协同,沙箱执行
Gemini CLI 终端 超大上下文
Cursor / Windsurf IDE 编辑器内多文件编辑
Qwen Code / GLM Coding 终端 国产开源平替

6.2 共同的工具集设计

几乎所有 Coding Agent 都收敛到一套最小而完备的工具:

read_file / str_replace_editor   # 精确读改文件(diff 式编辑)
bash / shell(持久会话)          # 跑构建、测试、git
glob/grep                        # 代码检索
web_search / fetch               # 查文档
subagent / task                  # 派发子任务

工作流:探索(grep 读代码)→ 计划(todo list)→ 执行(编辑+测试循环)→ 验证(跑测试)→ 汇报。"计划→执行"分离、测试驱动闭环,是从"能改代码"到"可靠改代码"的关键。

6.2.1 工具设计细节

  • str_replace_editor(diff 式编辑):模型不是"重写整文件",而是给出 old_string → new_string 的精确替换。好处:改动可追溯、失败易定位、不会因一处错毁全文件。是 Coding Agent 比"整文件生成"可靠的核心。
  • 持久 shell 会话:多次命令共享同一 shell 状态(环境变量、工作目录、虚拟环境),否则 cd / source activate 在下一条命令失效。
  • glob/grep 检索:让模型先"看懂仓库结构"再动手,避免凭空编造不存在的文件/函数。

6.2.2 SWE-agent 类工作流(以基准为参照)

SWE-bench 等基准催生的 Coding Agent 范式,可抽象为:

  1. 复现(Repro):先跑通能触发 bug 的最小测试,确认问题存在;
  2. 定位(Localize):用 grep/调用图/报错栈定位相关文件与函数;
  3. 修复(Patch):生成最小 diff;
  4. 验证(Verify):跑测试套件,失败则把报错回传模型重试;
  5. 提交(Submit):通过则提 PR。
输入: issue 描述 + 仓库
  → 检索相关代码 (grep / 调用图)
  → 写修复 diff (str_replace_editor)
  → 跑测试 (bash: pytest)
  → 失败? 把报错喂回模型再修 (Reflexion 式循环)
  → 通过? 提 PR

这一范式已被 Claude Code、Codex、DeepSeek Harness 等吸收为"标准姿势"。

6.3 从 Agent 到 Harness

把上面的循环、工具、权限审批、会话日志、子 Agent 调度、上下文管理做成一个可复用、可扩展的运行时,就是 Agent Harness——模型是灵魂,Harness 让它在真实环境里持续工作。

公式:Agent = Model + Harness

09 篇讲 Harness 的完整概念谱系(评估 Harness → Agent Harness),10 篇直接读 DeepSeek Harness 源码。

6.3.1 Harness 的关键子系统

  • 权限层:哪些命令需人工确认(如 rm -rf、git push),哪些自动放行;
  • 沙箱:在隔离环境跑不可信命令,防破坏宿主机;
  • 会话日志(Append-only log):记录模型每次决策与环境反馈,可回放、可分叉(试错不留痕地探索);
  • 子 Agent 调度器:按拓扑派发/汇总,做上下文隔离;
  • 上下文管理器:负责 compaction、检索回填、窗口调度。

七、Agent 的失败模式与对策

落地 Agent 最痛的不是"不会做",而是"做着做着翻车"。常见失败模式:

7.1 循环调用(Loops / Non-termination)

  • 现象:模型反复调同一个工具、或 Thought→Action→Observation 打转,永远不 Final Answer。
  • 原因:工具结果没给出"终止信号";模型误以为还差一步;或 prompt 没强调"达成即收尾"。
  • 对策:
    • 设 max_steps(最大步数),超限强制终止并提示用户;
    • 工具返回里带明确"已完成/还需 X"的状态;
    • 用推理模型或加"反思:你是否已能给出最终答案"的引导。

7.2 上下文污染(Context Pollution)

  • 现象:长任务后期,模型被大量工具原始输出(超长日志、错误栈、冗余 JSON)淹没,开始答非所问、调用错误工具。
  • 原因:把所有 Observation 原样塞进上下文,噪声盖过信号。
  • 对策:
    • Observation 截断 + 摘要后再回填(如只留"前 2000 字符 + 错误行");
    • 结构化解析工具输出,只提取模型需要的字段;
    • 定期 compaction,把长历史压成摘要;
    • 子 Agent 只回传结论,细节留在子 Agent 上下文里。

7.3 工具误用 / 参数错误

  • 现象:调错工具、参数填错(如 city 填成数字)、漏填 required 字段。
  • 对策:
    • schema 的 description 写清楚"何时用、怎么填";
    • 解析失败时把"参数错误原因"回传模型重试;
    • 关键调用加 schema 校验(JSON Schema validate)再执行。

7.4 幻觉式行动(Hallucinated Action)

  • 现象:模型"编造"一个不存在的工具或文件,或声称已完成实则没执行。
  • 对策:
    • 严格白名单:只允许调用声明的工具;
    • 执行结果以"真实系统返回值"为准,不轻信模型自述;
    • 关键操作前让模型先"展示证据"(如先 grep 证明文件存在再编辑)。

7.5 成本与延迟失控

  • 现象:多 Agent + 长上下文 + 反复重试,账单爆炸、响应极慢。
  • 对策:
    • 分层模型(简单步用快廉价模型);
    • 限制并行/重试次数;
    • 优先用上下文工程减少 token,而非一味加窗口。

7.6 失败模式速查表

失败模式 信号 首选对策
循环调用 步数逼近上限还不收尾 max_steps + 终止信号
上下文污染 后期答非所问 Observation 摘要 + compaction
工具误用 报错"未知工具/参数错" schema 描述 + 校验 + 重试
幻觉行动 引用不存在的文件/工具 工具白名单 + 真实返回值
成本失控 账单/延迟异常 分层模型 + 限流

八、实践清单(可直接照抄)

  1. 最小可跑闭环:先实现"tool_calling + while 循环 + max_steps",跑通一个查天气 demo,再叠加记忆/多 Agent。
  2. 工具 schema 当产品写:每个函数的 name/description/参数描述都精心打磨,这是 Agent 可靠性的第一杠杆。
  3. Observation 必须被治理:永远不要原样塞大输出,截断/摘要/结构化解析三选一或组合。
  4. 危险操作人工确认:文件删除、git push、外发请求默认走审批。
  5. 错误回传模型:工具报错把信息(而非"失败了"三个字)还给模型,它多半能自我修正。
  6. 长任务上上下文工程:摘要压缩、长期记忆检索、AGENTS.md/todo 工作区记忆组合起来用。
  7. 可观测:保留 append-only 会话日志,出问题能回放定位,而不是"它又抽风了"。

九、参考

十、小结

Agent 的本质,是把 LLM 从"一次性问答函数"升级为"能自主循环、调用工具、管理记忆、从失败中学习"的系统。它的骨架是 ReAct(思考→行动→观察)思想,落地形态是 tool_calling 外套一个 while 循环;它的可靠性来自记忆系统(短期压缩 / 长期检索 / 工作区持久化)与上下文工程;它的上限来自规划(plan-and-execute、Reflexion、长思考模型)与多 Agent 分工。

落地时真正的难点不是"让模型调通一个工具",而是控制失败:循环调用、上下文污染、工具误用、幻觉行动、成本失控,每一步都有成熟对策(步数上限、Observation 治理、schema 打磨、白名单、分层模型)。

Coding Agent 是当前最成功的形态——目标可验证、反馈明确、工具天然。而其背后的 Harness(运行时)才是让模型在真实世界持续工作的真正引擎:Agent = Model + Harness。理解这条主线,就能在 Agent 框架快速更替的浪潮里,抓住不变的内核。

posted @ 2026-09-19 05:43  Stellan7  阅读(23)  评论(0)    收藏  举报