Skip to content

Agent 架构通论

Agent(智能体)不是一个模型或一个库,而是一种软件架构:让 LLM 当「决策器」,在工具与环境之间反复行动,直到目标达成。RAG 与 Agent 讲了 Agent 的最小循环(ReAct)与框架速查;本文在此基础上展开完整架构:形态谱系、五要素、控制循环类型学、记忆与上下文工程、工具层设计、多智能体编排,给出从「能跑」到「可控、可解释、可上线」的设计地图。控制循环的理论源头见 ReAct 论文精读。

一、先搞清楚对象:从 Workflow 到 Agent 的谱系

  • Workflow:步骤与分支在代码里写死,确定性执行,结果可预测——适合规则明确、可穷举流程的任务;
  • Agent:步骤由模型在运行时决定,「先查什么、调哪个工具、要不要再问一次」都是推理产物——适合任务开放、路径不可预写、需要边做边调整的场景。

两者之间没有硬边界,是一条光谱:

固定脚本 → 参数化脚本 → 规则分支流程 → LLM 单步决策(工具调用)
→ ReAct 循环(单层 Agent) → 多阶段/分层 Agent → 多 Agent 协作
         确定性越来越低、弹性越来越高、成本与不可控性同步上升
  • 判断要不要 Agent,问四个问题:
问题答案偏「不需要 Agent」答案偏「需要 Agent」
任务路径能预写穷举吗?能,分支有限不能,依赖上下文即兴组合
失败能重试一次就够吗?能,重试语义简单需要观察结果后再调整策略
需要动真实工具/外部状态吗?只读知识即可(考虑 RAG)要调用工具并处理副作用
对延迟/成本敏感吗?敏感,每步都贵可接受多轮往返换取正确性
  • 忠告:Agent 是最后一个选项不是第一个。能固定成 Workflow 就别让模型做决定;能用一次长上下文读文档就别开循环;能 微调 定型行为就不要靠 agent 绕。谱系上「往左移一步」往往省 90% 的稳定性成本。

二、五要素:一个 Agent 系统由什么组成

                ┌─────────────────────────── 环境 ───────────────────────────┐
                │  外部世界:数据库 / 文件系统 / 网络 API / 浏览器 / 其他 Agent │
                └────────────────────────────────────────────────────────────┘
                                    ▲工具调用        ▼工具结果
   ┌────────┐   ┌───────────┐   ┌──────────────────┐   ┌────────────────────┐
   │ 决策器  │──▶│ 规划器     │──▶│ 工具执行层(含 MCP) │──▶│ 记忆(工作+长期)     │
   │ (LLM)  │   │ (循环控制) │   │                  │   │                    │
   └────────┘   └───────────┘   └──────────────────┘   └────────────────────┘
        ▲                                                  │
        └──────────── 状态(上下文) 回到决策器 ──────────────┘
  1. 决策器(大脑):承载推理的 LLM,决定「下一步做什么」。可分级路由:复杂规划用强模型、机械执行用快模型;
  2. 规划器(循环):控制循环的运行时——何时问 LLM、何时执行工具、何时终止,见第三节类型学;
  3. 工具层:模型能触碰外部世界的一切接口(API / 代码执行 / 检索 / 写文件),标准接入见 MCP;
  4. 记忆:短期工作记忆(上下文窗口内的消息)与长期记忆(窗口外的外部存储),见第四节;
  5. 状态与环境:当前目标、已做步骤、中间产物的载体;真实环境必须有沙箱/权限墙,否则 Agent 的每一次幻觉都可能变成一次越权(安全红线见 AI 工程化)。
  • 设计顺序建议:先手工写死一个循环把任务跑通,再逐个引入规划/记忆/框架——每一步都要有「多花钱多值」的理由。

三、控制循环类型学:Agent 的「怎么转」

ReAct 是所有循环的地基(最小实现见 RAG 与 Agent 第六节),在此之上按「规划复杂度」分出四类主流模式:

3.1 反应式循环(Reactive):走一步看一步

  • 流程:思考 → 行动(调工具)→ 观察(工具结果)→ 再思考,不预写步骤;
  • 适合:步骤依赖上一步结果、无法预分解的开放任务(查资料、调试、问答决策);
  • 弱点:长任务容易「迷失目标」——上下文越长,模型越容易忘掉最初要干什么,步数越多越贵。

3.2 规划-执行(Plan-and-Execute):先列计划再干

  • 流程分两阶段:
    1. 规划:模型把目标分解成有序步骤清单(步骤可含依赖关系),先输出再动手;
    2. 执行:逐条执行步骤,每步可带子任务;执行中遇到偏差时,触发再规划(re-plan)而不是硬着头皮走完;
  • 适合:任务可预分解、步骤数多、希望中间可控(能看到计划、能人工插改)的场景——大多数生产任务;
  • 变体:计划写入外部文件/状态,避免步骤间「上下文漂移」;执行完每步做验证(见 3.3),验证不过就回到规划器。

3.3 反思/验证(Reflection):生成 + 评价 + 修订

  • 流程:生成器产出结果(或执行完一步)→ 评价器对照任务目标与约束打回 → 生成器依据反馈修订;
  • 适合:答案/方案质量比步数重要、且「评价比生成容易」的任务(写代码后跑测试、写方案后自检);
  • 注意:反思循环的成本是生成成本的 2 倍起,只在关键节点反思而非每步都反思;评价器与生成器尽量用不同提示,防止「自己夸自己」。

3.4 分层编排(Hierarchical):主管拆活、工人干活

  • 流程:Manager(主管)把大任务拆成子任务派给多个 Worker,各 Worker 独立小循环,结果回报汇总;
  • 适合:任务横跨多个领域/工具集、需要隔离上下文(让主循环不被子任务细节淹没)、子任务可并行;
  • 它也是「多智能体」的最小形态——详见第六节。

3.5 模式选型速查

你的任务特征首选循环理由
未知路径,边做边定反应式 ReAct不预设步骤,灵活
步骤可分解、要求可控可插改规划-执行计划可见,中途可干预
结果质量>效率,评价比生成本低反思(关键节点)自检提升质量
跨领域、子任务多、要并行分层编排拆开干,隔离上下文
以上混合规划-执行为主 + 关键步骤反思兼顾可控与质量

3.6 终止条件:循环必须「有终点」

没有显式终止,一切循环都是死循环。四类终止信号缺一不可:

  1. 目标达成:模型显式输出完成标记 + 产物通过验证(最好有程序化验证,见 Agent 评测与可观测);
  2. 预算耗尽:最大步数 / 最大 token / 最大耗时,三者都要设;
  3. 异常退出:工具连续报错超过阈值、重试耗尽、检测到上下文溢出;
  4. 人工接管:不确定或高风险动作必须停下等人(人工审批也是终止)。

四、记忆与上下文工程:Agent 的「记性」

Agent 记性差是头号失败源。按记忆用途分四层:

记忆类型载体内容典型实现
工作记忆上下文窗口本轮对话/当前任务状态消息历史
情景记忆长期存储过去任务的经验与结果向量库/记忆服务(见下)
语义记忆长期存储事实、偏好、领域知识向量库 / 知识图谱
程序性记忆代码/缓存「这类任务怎么干」的套路复用策略模板、技能库
  • 上下文不是无限的(成本 + 中间丢失,原理见 LLM 原理),工程上永远假设「窗口会满」,提前做预算:
策略做法代价/风险
截断丢掉最旧消息丢失早期关键信息
滚动摘要定期把旧消息总结成摘要摘要本身会失真
关键信息抽取只保留与目标相关的字段需维护「状态结构」
外部检索需要的记忆从长期存储按需取回引入检索错误(方法见 RAG)
进度落盘任务状态写入文件/外部存储,跨轮恢复需约定序列化结构
  • 工具输出治理(最容易被忽略):把工具全量输出直接塞回上下文,几轮后上下文就全是噪声。铁律:工具结果进上下文前先加工——截断到关键字段、由模型/代码总结成一句话结论、只保留与目标相关的部分;
  • 长任务不要靠模型「记得」:中间结论、待办清单、已排除方案,写进外部笔记/结构化状态,而不是指望上下文;
  • 长期记忆的写入时机:任务完成或阶段切换时总结入长期存储,并定期做合并去重——记忆库不治理会变成垃圾场(数据治理可借鉴 RAG 的版本对齐,见 rag-agent)。

五、工具层设计:模型的手,也是模型的嘴

工具是 Agent 能力的上限,也是风险的入口。工具描述是写给模型看的接口文档:

  • 一个工具的四个要素:
jsonc
{
  "name": "search_package",          // 短、动词开头、无歧义
  "description": "按名称查 npm 包信息。查文档、版本、依赖时才用;查代码语义不要用。参数 name 传包名,不带 version 号", // 写给模型:何时用、何时别用、参数怎么填
  "parameters": {                     // JSON Schema
    "type": "object",
    "properties": { "name": { "type": "string", "description": "npm 包名" } },
    "required": ["name"]
  }
}
  • 工具描述与 schema 的质量,决定模型「会不会用、敢不敢用、用不错」——工具调不对,九成是描述没写清而不是模型笨;
  • 执行语义要显式:超时、重试次数、幂等性(重复调用是否安全)、并发上限、返回结构(成功/失败要机器可判,别把错误埋在文本里);
  • 工具即攻击面:Agent 的工具调用 = 让模型决定执行任意代码/访问任意数据(详见 MCP 安全红线与 AI 工程化 权限墙)——最小权限 + 敏感动作人工审批 + 全量审计;
  • 工具越多,模型选择越难:控制工具集规模,按任务动态装载相关工具(工具检索/分组),而不是一次给几百个。

六、多智能体:真需要,还是营销词

  • 先问三个问题,全部为「是」再考虑多智能体:
判据说明
任务能可靠拆成子任务吗?拆不开 = 拆了只会让信息在 agent 间丢失
子任务之间低耦合吗?高耦合场景通信成本 > 拆分工的收益
异构能力/隔离上下文有价值吗?如「规划专家」不该被代码细节灌爆上下文
  • 三种主流拓扑:
拓扑结构适合代价
编排者-工人(orchestrator-worker)主管拆活、工人执行、结果回报任务可拆、需要并行主管是瓶颈与单点
流水线(pipeline)每个 Agent 处理一个阶段,接力阶段清晰、产物可交接错误在阶段间累积
协商(peer/debate)多个 Agent 各自解题再投票/互评关键决策想降低单点偏差token 成倍,未必更准
  • 成本真相:多智能体的 token 开销通常是单 Agent 的数倍,「三个臭皮匠」在没有独立验证机制时并不会更可靠。演进路线:单 Agent → 规划-执行 → 真的瓶颈出现再拆多 Agent,而不是开场就上团队;
  • 多 Agent 之间推荐显式任务单/交接协议(目标、输入、验收标准、负责人),而不是自由对话——对话式协作 = 上下文爆炸与责任模糊。

七、从架构到落地:怎么选实现

  • 落地形态分层,建议从下往上挑够用的一层:
代表何时选
手写循环自己写的几十行循环学习/实验,先理解再谈框架
图式编排LangGraph 类(显式状态机与循环)生产级复杂流程,要可控与可恢复
Agent 框架OpenAI Agents SDK / Claude Agent SDK 类官方 SDK 内完成度够的场景
多智能体框架CrewAI / AutoGen 类确实需要角色分工与协作
协议层MCP(工具接入)/ A2A(Agent 互通)让工具与 Agent 标准化互操作

框架对比表的「适合场景」逐项见 RAG 与 Agent 第六节;工具接入标准见 MCP;与 A2A 的边界辨析见 mcp 第七节。

  • 选型一句话:先确定循环与状态模型,再选能表达的框架——你要的若只是「步进式循环 + 显式状态」,一个状态机库或手写循环就够了,不必引整个多智能体框架;
  • 无论哪个框架,要求它暴露:步数/预算限制、人工中断点、轨迹审计、状态持久化。做不到这三项的生产级框架要警惕。

八、失败模式与反模式

症状根因对策
死循环 / 重复调同一工具缺终止条件,或工具结果没改变状态设步数与重复调用上限;工具结果进入上下文前先加工
越走越偏,忘掉原始目标上下文漂移目标声明置顶 + 关键信息落盘 + 阶段再规划
上下文被工具输出灌满工具结果不治理截断/摘要/只取关键字段(见第四节铁律)
错误级联放大一步失败后继续带错执行每步验证;连续失败即止损返回人工
幻想的工具/参数schema 缺枚举或描述不清收紧参数 schema、工具描述写给模型
过早提交「看起来完成了」无验证环节程序化验证 / 反思循环把关
越权或误改真实数据工具权限过大最小权限 + 沙箱 + 敏感操作审批(见第五节)
多 Agent 信息丢失子任务结果没结构化回报显式任务单/交接协议(见第六节)

九、最小实现骨架:规划-执行循环(可运行版)

与框架无关、可直接运行的骨架(requests 直连 DeepSeek 的 OpenAI 兼容接口),对应上文全部循环要点。2026-09-04 已实测:目标 (12+23)*4-7(真值 133),deepseek-chat 先规划出 4 步,再连续真实调用 add→mul→sub 三个工具,notes 逐条落盘、验证通过,共 3027 tokens。运行依赖 python3 + requests,执行前 export DEEPSEEK_API_KEY=sk-...(密钥不写进代码)。

python
# -*- coding: utf-8 -*-
"""规划-执行(plan-and-execute)最小循环 —— 真实可跑版(DeepSeek deepseek-chat)
依赖: python3 + requests; 环境变量 DEEPSEEK_API_KEY
用法: export DEEPSEEK_API_KEY=sk-... && python3 本文件
五个工程钩子全部为真实逻辑: 工具子集装载 / 结果加工(condense) /
程序化验证(validate) / 预算与终止 / notes 落盘(防漂移)
"""
import os, re, json, requests

API = "https://api.deepseek.com/chat/completions"
MODEL = os.environ.get("DEEPSEEK_MODEL", "deepseek-chat")

# ---- 工具层: 描述写给模型(何时用/参数怎么填), 执行器写给机器 ----
TOOLS_SPEC = [
    {"type": "function", "function": {
        "name": "add", "description": "整数加法, 返回 a+b。需要把两个数合并时才用。",
        "parameters": {"type": "object", "properties": {"a": {"type": "integer", "description": "加数"}, "b": {"type": "integer", "description": "加数"}}, "required": ["a", "b"]}}},
    {"type": "function", "function": {
        "name": "mul", "description": "整数乘法, 返回 a*b。需要把两个数相乘时才用。",
        "parameters": {"type": "object", "properties": {"a": {"type": "integer", "description": "乘数"}, "b": {"type": "integer", "description": "乘数"}}, "required": ["a", "b"]}}},
    {"type": "function", "function": {
        "name": "sub", "description": "整数减法, 返回 a-b。需要从 a 里减去 b 时才用。",
        "parameters": {"type": "object", "properties": {"a": {"type": "integer", "description": "被减数"}, "b": {"type": "integer", "description": "减数"}}, "required": ["a", "b"]}}},
]

def execute_tool(name, args):
    """确定性执行; 失败返回结构化 ok=False, 绝不抛异常让循环崩掉"""
    try:
        a, b = int(args["a"]), int(args["b"])
        v = {"add": a + b, "mul": a * b, "sub": a - b}.get(name)
        return {"ok": v is not None, "value": v} if v is not None else {"ok": False, "error": "unknown tool " + name}
    except Exception as e:
        return {"ok": False, "error": str(e)}

def chat(messages, tools=None, json_mode=False):
    payload = {"model": MODEL, "messages": messages, "stream": False}
    if tools:
        payload["tools"], payload["tool_choice"] = tools, "auto"
    if json_mode:
        payload["response_format"] = {"type": "json_object"}
    r = requests.post(API, headers={"Authorization": "Bearer " + os.environ["DEEPSEEK_API_KEY"]},
                      json=payload, timeout=60)
    r.raise_for_status()
    body = r.json()
    return body["choices"][0]["message"], body["usage"]["total_tokens"]

def condense(tool_name, args, result):
    """铁律: 工具结果加工成一句话再进上下文, 防噪声灌满"""
    if result["ok"]:
        return f"{tool_name}({args['a']},{args['b']}) -> {result['value']}"
    return f"{tool_name}({args['a']},{args['b']}) FAILED: {result['error']}"

def ask_planner(goal, extra=""):
    """阶段一规划: 先拆成有序步骤(JSON)再动手; 拆失败就退化为单步, 不中断"""
    content = ("把下面的目标分解成 2~4 个有序执行步骤, 只输出 JSON: "
               '{"steps": ["第1步...", "第2步..."]}\n目标:' + goal + "\n" + extra)
    msg, _ = chat([{"role": "user", "content": content}], json_mode=True)
    try:
        return json.loads(msg["content"])["steps"]
    except Exception:
        return [goal]

def run_agent(goal, max_steps=15):
    state = {"goal": goal, "plan": [], "notes": [], "done": False, "reason": "", "tokens": 0}
    state["plan"] = ask_planner(goal)
    history = [{"role": "user",
                "content": f"目标: {goal}\n计划: {json.dumps(state['plan'], ensure_ascii=False)}"
                           "\n严格按计划推进: 每轮用工具算一步或输出最终答案。"}]

    step = 0
    while not state["done"] and step < max_steps:          # 预算与终止(3.6)
        step += 1
        recent = (state["notes"] or ["(尚无进展)"])[-5:]   # 目标置顶 + 最近落盘回放, 防漂移
        msg, toks = chat(history + [{"role": "user", "content": f"[步骤{step}] 进度: {'; '.join(recent)}\n继续。"}],
                         tools=TOOLS_SPEC)                 # 工具子集装载: 此处只挂演示所需三个
        state["tokens"] += toks
        history.append(msg)

        if msg.get("tool_calls"):                          # think -> act
            for tc in msg["tool_calls"]:
                name, args = tc["function"]["name"], json.loads(tc["function"]["arguments"])
                result = execute_tool(name, args)
                state["notes"].append(condense(name, args, result))                    # -> observe, 落盘加工结果
                history.append({"role": "tool", "tool_call_id": tc["id"],
                                "content": json.dumps(result, ensure_ascii=False)})    # 结果回灌
                if not result["ok"]:
                    state["notes"].append("该路径失败, 需换方案")                       # 触发再规划信号
        else:                                              # 模型声称完成: 程序化验证, 别只听它说
            answer = msg["content"]
            nums = re.findall(r"-?\d+", answer.replace(",", ""))
            state["done"] = bool(nums) and float(nums[-1]) == 133.0
            state["reason"] = "validated(最终答案=133)" if state["done"] else "product_failed_validation"
            state["notes"].append("最终答复: " + answer.strip()[:60])
            if not state["done"]:                          # 验证不过 -> 回规划器换路径
                state["plan"] = ask_planner(goal, "上次验证失败, 换一条路径重新规划。")

    if not state["done"]:
        state["reason"] = "step_budget_exhausted"
    return state

if __name__ == "__main__":
    assert os.environ.get("DEEPSEEK_API_KEY"), "请先 export DEEPSEEK_API_KEY=sk-..."
    # 真值 133, 必须连续 add -> mul -> sub 三次真实工具调用才能算对
    s = run_agent("计算 (12+23)*4-7, 只回答最终数字")
    print("plan  :", s["plan"])
    print("notes :")
    for n in s["notes"]:
        print("  -", n)
    print("done  :", s["done"], "| reason:", s["reason"], "| tokens:", s["tokens"])

# 实测输出(2026-09-04, deepseek-chat):
#   plan  : ['先计算括号内的加法:12+23=35', '再计算乘法:35*4=140',
#            '最后计算减法:140-7=133', '输出最终数字133']
#   notes : ['add(12,23) -> 35', 'mul(35,4) -> 140', 'sub(140,7) -> 133', '最终答复: 133']
#   done  : True | reason: validated(最终答案=133) | tokens: 3027
  • 骨架拆解出的五个工程钩子,对应本页各节:工具子集装载(第五节)、结果加工(第四节)、程序化验证与再规划(第三、八节)、预算与终止(3.6)、notes 落盘(第四节);
  • 把骨架换成 LangGraph 类状态机时,state 就是图的共享状态,notesplan 成为显式节点,循环/分支变成图边——先理解这个骨架,再看任何框架都不晕。

十、踩坑清单

  1. 把 Agent 当银弹:能写死流程的任务上循环,又慢又不可控——先往 Workflow 移;
  2. 循环无终止:没有步数/token/时间预算,或终止全靠模型自觉;
  3. 不设程序化验证:模型说「完成」就信,结果验证不过的「假完成」上线;
  4. 上下文不管饱:工具全量输出直接回灌,几轮后模型在噪声里迷失;
  5. 中间状态不落盘:长任务一断(超时/重启/换线程)就全忘;
  6. 目标会丢:任务声明不置顶,模型越走越偏没人拉回来;
  7. 工具描述敷衍:schema 里只有字段没有「何时用/何时别用」,模型乱调或不敢调;
  8. 工具权限过大:给了 Agent 的生产权限没有沙箱与审批,一次注入翻全墙;
  9. 直接上多智能体:单 Agent 的瓶颈还没找到就拆团队,成本翻倍问题依旧;
  10. 没有可观测与评测就上线:Agent 的失败是概率性的,看不见轨迹就无法迭代(见 Agent 评测与可观测);
  11. 迷信框架:框架解决的是「状态与循环的工程表达」,不解决「目标会不会丢」——架构设计仍然要自己做。

关联阅读:ReAct 循环的最小实现与工具调用示例在 RAG 与 Agent;工具接入标准见 MCP;评测、可观测与门禁是 Agent 上线的另一半,见 Agent 评测与可观测;把「一步调用」跑稳跑便宜的工程手段见 AI 工程化

基于 VitePress 构建 · 内容以知识共享方式沉淀