Skip to content

设计模式

1. 从任务形态选择模式

1.1 设计模式解决的问题

Agent 设计模式解决的是架构选型问题。同样是让模型调用工具,系统可以做成固定工作流、单 Agent 循环、入口路由、Supervisor-Worker、多 Agent 交接,也可以加入评估器不断优化结果。模式选择会影响工具权限、上下文组织、失败恢复、成本和可观测性。

设计前先判断任务形态。路径稳定、输入输出明确的任务,优先使用工作流。下一步高度依赖工具观察的任务,适合引入 Agent 循环。任务入口类型很多时,需要路由。任务可以拆给多个专业能力时,可以使用 Supervisor-Worker 或 Handoff。输出可以被测试、规则或审查标准验证时,可以加入 Evaluator-Optimizer。

本文用两个场景贯穿说明。场景一是代码迁移:需要搜索仓库、修改文件、运行测试、修复失败。场景二是企业助手:需要识别用户意图、查询知识库、调用业务系统、必要时移交人工或专业 Agent。这两个场景能覆盖主要模式的取舍。

1.2 模式选择矩阵

模式适用任务控制权优点主要风险
固定工作流步骤稳定、输入输出明确代码可预测、易测试面对开放任务时分支膨胀
单 Agent路径动态、工具数量少模型和 Runtime实现简单、上下文集中工具过多后选择混乱
Routing多类任务入口路由器降低单 Agent 负担类别定义不清导致误分
Supervisor-Worker可拆分复杂任务Supervisor职责清晰、可并行状态同步和聚合复杂
Handoff多个专业 Agent 顺序接力当前 Agent用户体验自然交接摘要质量决定成败
Evaluator-Optimizer输出可验证评估器和生成器能逐步提高质量容易无效迭代

这个矩阵只提供选型起点。真实系统常把多种模式组合起来。例如代码迁移可以由固定工作流管理阶段:分析、修改、测试、总结;在分析和修复阶段使用单 Agent;在大型仓库中再引入 worker 分工处理前端、后端和配置。企业助手可以先用 routing 判断任务类型,再把复杂任务 handoff 给专业 Agent。

1.3 从简单结构开始

Agent 架构最常见的问题是过早复杂化。一个任务若能用稳定工作流解决,直接上多 Agent 会增加状态同步、权限管理、上下文传递和调试成本。更稳妥的路径是先做可测试工作流,再在路径动态的节点引入单 Agent,最后根据职责和权限拆分多个 Agent。

这条路径也便于评估。每次引入新模式,都应比较任务成功率、平均工具调用次数、失败类型、人工接管率和成本。若复杂模式没有改善结果,就应回到更简单的结构。

1.4 固定工作流:可测试的基线

固定工作流把控制流写在代码里,模型只在局部节点执行任务。以企业助手的工单分类为例,流程可以固定为:清洗用户输入、分类、抽取字段、查询知识库、生成答复、规则校验、返回结果。每一步都有明确输入输出,失败时也容易定位。

工作流适合三类任务。第一类是业务规则明确的任务,例如表单抽取、合同条款检查、客服意图分类。第二类是有强格式要求的任务,例如生成 JSON、SQL、配置文件。第三类是需要可审计的流程,例如审批、发布、数据写入。代码控制路径能让团队明确知道每一步何时发生、失败如何处理、哪些节点需要人工确认。

代码迁移也可以先用工作流搭外层阶段:创建迁移计划、定位引用、生成补丁、运行测试、修复失败、输出总结。这个外层流程给任务提供边界,后续只在“定位引用”和“修复失败”这类动态节点引入 Agent。

2. ReAct 与单 Agent 循环

2.1 单 Agent:小范围动态决策

单 Agent 模式由一个 Agent 维护统一状态和工具集合。代码迁移的初版可以这样设计:Agent 看到目标和仓库状态,拥有 search_textread_fileapply_patchrun_tests 四类工具。它先搜索相关符号,读取文件,生成补丁,运行测试,根据错误继续修复,最后总结变更。

单 Agent 的关键是让模型在 Runtime 管控下做小范围动态决策。工具数量应控制在当前任务所需范围内。搜索阶段只给只读工具,编辑阶段再给写入工具,发布阶段才给部署工具。状态中要记录已读文件、已改文件、测试结果和失败原因。这样模型每轮能看到任务进度,也能避免重复读取同一材料。

单 Agent 的失败信号很明显。若提示词越来越长,工具列表越来越多,模型频繁选错工具,或同一上下文中要处理法律、代码、客服和数据分析,职责已经过于拥挤。此时应拆分工具集合、拆分任务阶段,或引入路由和多 Agent 协作。

2.2 从推理文本到环境交互

历史背景

ReAct 来自论文 Synergizing Reasoning and Acting in Language Models。论文把 reasoning 与 acting 放在同一条轨迹中:模型先根据目标和历史观察决定下一步行动,外部环境返回观察结果,模型再继续修正后续动作。早期 Chain-of-Thought 主要提升模型在文本空间里的分步推理能力,ReAct 进一步把外部搜索、知识库、API、网页环境和任务状态接入循环,使模型能够边查、边做、边修正。

这个思想适合解释很多 Agent 产品的基础形态。用户要求“修复项目里的登录错误”时,系统不能只生成一段建议;它要搜索报错、读取文件、定位调用链、修改代码、运行测试,再根据测试结果继续调整。ReAct 提供的核心价值在于把一次回答拆成多轮“动作与观察”,让模型的下一步建立在真实环境反馈上。

问题边界

ReAct 解决的是路径依赖中间结果的任务。若输入已经包含所有信息,普通问答足够;若步骤完全固定,工作流更容易测试;若每一步都要根据搜索结果、测试结果或外部 API 返回继续选择,ReAct 循环就有空间。

任务形态典型行为ReAct 的收益主要风险
文档调研搜索、读取、归纳、补查根据证据缺口调整查询循环搜索、证据过长
代码修复搜索报错、读文件、改代码、跑测试用测试观察驱动下一步工具误用、修改范围扩大
网页操作观察页面、点击、填写、校验结果页面状态可持续反馈页面变化导致动作失效
数据分析查询数据、运行代码、解释结果中间计算影响后续分析结果未校验就下结论

重点在于控制权分配。模型提出候选动作,Runtime 执行校验、调用工具、写入状态、判断终止。模型不能直接拥有系统权限,工具调用必须经过 Runtime。

2.3 ReAct 循环的底层结构

状态机视角

工程实现中,ReAct 更像一个受限状态机。每一轮只允许模型输出一种结构化动作:调用工具、请求澄清、给出最终答案或停止。Runtime 把动作解析为可执行请求,执行后把观察结果压缩回状态。

这里的状态至少包含目标、可用工具、已执行动作、观察结果、证据、错误、预算、终止条件。状态不能只保存聊天记录,因为聊天记录很难判断“是否读过这个文件”“这个搜索词是否已经失败”“证据是否足够覆盖结论”。

结构化动作

论文和很多教程会用 Thought -> Action -> Observation 展示轨迹。生产系统通常不把内部推理逐字暴露给用户,也不依赖自由文本解析动作。更稳妥的做法是让模型返回 JSON 或 Function Calling 结构。

json
{
  "type": "tool_call",
  "name": "search_text",
  "args": {
    "query": "login redirect error",
    "path": "src"
  },
  "intent": "定位登录跳转错误的实现位置"
}

Runtime 对这个动作做四层处理:确认工具存在,按 schema 校验参数,检查路径与权限,记录这次调用的 trace id。工具返回后,Runtime 生成面向下一轮模型的观察,而非把原始输出全部塞回上下文。

json
{
  "ok": true,
  "tool": "search_text",
  "items": [
    {"path": "src/auth/router.ts", "line": 42, "snippet": "redirectAfterLogin"}
  ],
  "truncated": false,
  "elapsed_ms": 31
}

最小代码示例

下面的 Python 示例用内存数据模拟代码修复 Agent。它省略真实模型 SDK,把重点放在循环、工具执行、状态更新和停止条件。

python
files = {
    "src/auth/router.ts": "function redirectAfterLogin(next) { return next || '/home' }",
    "tests/auth.test.ts": "expect(redirectAfterLogin('/dashboard')).toBe('/dashboard')",
}


def search_text(query):
    # 工具:返回命中的文件和片段,避免把完整文件塞入上下文。
    return [
        {"path": path, "snippet": text}
        for path, text in files.items()
        if query in text
    ]


def read_file(path):
    # 工具:真实系统还要校验路径是否在工作区内。
    return files.get(path, "")


def fake_model(state):
    if not state["matches"]:
        return {"type": "tool_call", "name": "search_text", "args": {"query": "redirectAfterLogin"}}
    if not state["read"]:
        return {"type": "tool_call", "name": "read_file", "args": {"path": state["matches"][0]["path"]}}
    return {"type": "final", "answer": "登录跳转逻辑位于 src/auth/router.ts,需要结合测试继续确认。"}


def run_react(goal, max_steps=4):
    state = {"goal": goal, "matches": [], "read": {}, "steps": []}
    tools = {"search_text": search_text, "read_file": read_file}

    for _ in range(max_steps):
        action = fake_model(state)
        if action["type"] == "final":
            return {"answer": action["answer"], "trace": state["steps"]}

        tool = tools[action["name"]]
        observation = tool(**action["args"])
        state["steps"].append({"action": action, "observation": observation})

        if action["name"] == "search_text":
            state["matches"] = observation
        if action["name"] == "read_file":
            state["read"][action["args"]["path"]] = observation

    return {"answer": "达到最大轮次,任务停止。", "trace": state["steps"]}

这段代码刻意保留了三个关键点:动作由模型选择,执行由 Runtime 完成,终止由 Runtime 控制。真实系统会继续加入 schema 校验、沙箱、工具超时、错误分类、证据评分和人工确认。

2.4 工程落地中的边界

失败模式

失败类型表现处理方式
循环搜索多轮更换关键词却没有新增证据记录已搜索查询,连续无增量后停止
工具跳跃还没有读文件就尝试修改分阶段暴露工具,先读后写
观察污染工具返回内容包含诱导模型越权的文本把工具输出标记为不可信数据,只提取结构字段
证据不足早早给出结论Runtime 检查引用来源和覆盖范围
成本失控小任务调用过多模型和工具设置轮次、token、工具调用预算

ReAct 的可控性来自状态和工具治理。提示词可以影响模型策略,但不能替代路径限制、权限校验、预算控制和 trace 审计。

与其他范式的关系

范式核心结构适用场景与 ReAct 的关系
Plan-and-Execute先生成计划,再逐步执行长任务、步骤较多可把每个执行步骤内部做成 ReAct
Reflection执行后评估与修正输出质量要求高可在 ReAct 结束或阶段结束后加入反思
Supervisor-Worker上级分派、下级执行多能力团队协作Worker 内部常使用 ReAct
Workflow固定路径执行稳定业务流程可把少量不确定节点交给 ReAct

在工程选型里,ReAct 适合作为最小可运行 Agent 的第一种结构。任务跨度扩大后,再引入计划、反思、协作和评估机制。

2.5 代码迁移中的组合方式

假设目标是把项目中的旧请求库替换成新请求库。外层工作流可以固定阶段:创建迁移计划、定位引用、生成补丁、运行测试、修复失败、输出总结。定位引用和修复失败这两个阶段路径不稳定,适合交给单 Agent。

定位阶段只开放 search_textfind_filesread_file。Agent 先搜索旧 API 名称、配置项和测试引用,再把候选文件写入状态。修改阶段再开放 apply_patch。验证阶段开放 run_tests,并把失败测试名称、退出码和错误摘要回填给模型。

这个组合能避免一开始把全部权限交给模型。Runtime 按阶段暴露工具,模型按观察结果选择下一步。若仓库规模较小,单 Agent 足够;若模块很多、文件冲突频繁,再考虑 Supervisor-Worker。

3. 计划、拆分与执行

3.1 长任务中的计划问题

背景

ReAct 让模型根据每一轮观察选择下一步动作,但它在长任务里容易出现局部最优。代码迁移、复杂调研、复杂数据分析这类任务往往跨越十几步甚至几十步。如果模型每轮只看下一步,可能反复搜索同类资料,遗漏阶段目标,也可能在没有完成前置准备时进入写作或修改阶段。

Plan-and-Execute 把任务拆成两个层次:Planner 先生成可执行计划,Executor 按步骤执行并把结果写回状态。计划可以被修订,但系统始终有一个全局路线。LangChain 早期的 Plan-and-Execute Agent、Anthropic 对 workflow 与 agent 的区分、以及许多代码 Agent 的任务列表机制,都体现了这种分层思想。

适用边界

任务特征使用计划的收益潜在代价
多阶段依赖明确先后顺序,降低遗漏初始计划可能过粗
多文件修改控制修改范围和验证顺序计划维护需要额外状态
调研写作先定主题结构,再收集证据新证据可能推翻原结构
企业流程便于审计每个阶段动态异常需要重规划

计划适合处理“任务太长导致局部动作失焦”的问题。若任务只有两三步,直接 ReAct 循环更轻;若流程完全固定,普通工作流更稳定。

3.2 任务如何从目标变成步骤

背景

Agent 面对的用户目标经常是开放的:“整理这批材料”“修复这个 bug”“分析这次指标波动”。这些目标无法直接映射到一个工具调用。系统需要先把目标拆成可执行步骤,再逐步取得证据和产出物。经典规划问题强调状态、动作、目标和代价;LLM Agent 的规划继承了这个视角,只是动作空间变成工具调用、文件读写、模型生成和人工确认。

任务拆分的价值在于降低每一步的不确定性。一个好的拆分结果应当让 Runtime 知道当前阶段需要哪些输入、允许哪些工具、完成后产出什么、失败时如何回退。

拆分粒度

粒度示例优点风险
目标级修复登录问题简洁无法直接执行
阶段级定位、修改、测试、总结便于跟踪阶段内部仍需动作选择
动作级搜索关键字、读取文件、运行测试可执行计划过长,维护成本高
工具级调用 search_text易校验过早绑定实现细节

实际系统通常采用阶段级计划,再让每个阶段内部通过 ReAct 执行动作。这样既能保持全局方向,又能应对观察结果带来的变化。

3.3 规划数据结构

从自然语言到结构化计划

json
{
  "goal": "整理向量数据库选型笔记",
  "steps": [
    {
      "id": "collect",
      "description": "搜索并读取与向量数据库、pgvector、HNSW 相关的笔记",
      "inputs": ["notes_root"],
      "outputs": ["evidence_notes"],
      "allowed_tools": ["search_notes", "read_note"],
      "done_when": "至少读取三条相关笔记,并记录来源"
    },
    {
      "id": "compare",
      "description": "按成本、运维、召回、延迟整理对比表",
      "inputs": ["evidence_notes"],
      "outputs": ["comparison_table"],
      "allowed_tools": ["read_note"],
      "done_when": "每个结论都有来源"
    }
  ]
}

结构化计划能被 Runtime 使用。inputsoutputs 让系统知道步骤之间的依赖;allowed_tools 限制执行面;done_when 提供完成判断。自然语言计划若没有这些字段,只能作为提示,难以进入工程控制。

规划流程图

拆分模板可以来自工程经验,例如代码修复常见模板是“复现失败 -> 定位 -> 修改 -> 验证 -> 总结”;调研写作常见模板是“收集资料 -> 建立结构 -> 补证据 -> 成文 -> 校验引用”。

3.4 Planner 与 Executor 的运行机制

双层状态

Plan-and-Execute 的状态一般分为任务状态和计划状态。任务状态记录目标、上下文、工具结果、证据和错误;计划状态记录步骤、依赖、当前进度、完成条件和重规划原因。

Planner 不应输出空泛目标,例如“分析代码”“修复问题”。更好的计划要包含产出物和完成判断,例如“读取认证路由文件,确认跳转参数来源”“修改 redirectAfterLogin 的默认逻辑,并运行 auth 测试”。

计划结构

计划可以用 JSON 表示,便于 Runtime 跟踪和校验。

json
{
  "goal": "修复登录后跳转错误",
  "steps": [
    {
      "id": "s1",
      "task": "定位登录跳转相关代码",
      "allowed_tools": ["search_text", "read_file"],
      "done_when": "找到负责 redirect 的函数和测试文件"
    },
    {
      "id": "s2",
      "task": "修改跳转逻辑并补充测试",
      "allowed_tools": ["read_file", "apply_patch"],
      "done_when": "代码改动只影响认证路由和对应测试"
    },
    {
      "id": "s3",
      "task": "运行认证测试并整理结果",
      "allowed_tools": ["run_tests"],
      "done_when": "测试通过或失败原因被定位"
    }
  ]
}

这里的 allowed_tools 可以降低工具误用概率。执行搜索阶段时,Runtime 不暴露写入工具;进入修改阶段后,才允许 apply_patch。计划既影响模型上下文,也影响工具权限。

执行循环伪代码

python
def execute_plan(goal, planner, executor, tools, max_replans=2):
    state = {"goal": goal, "evidence": [], "errors": [], "step_results": {}}
    plan = planner.create(goal)
    replan_count = 0

    while not plan.done():
        step = plan.current_step()
        action = executor.decide(step=step, state=state, tools=step["allowed_tools"])

        # Runtime 只允许当前步骤声明过的工具。
        if action["tool"] not in step["allowed_tools"]:
            state["errors"].append({"type": "tool_not_allowed", "step": step["id"]})
            continue

        result = tools.run(action["tool"], action["args"])
        state["step_results"].setdefault(step["id"], []).append(result)

        if executor.step_done(step, state):
            plan.mark_done(step["id"])
        elif executor.need_replan(step, state) and replan_count < max_replans:
            plan = planner.revise(plan, state)
            replan_count += 1
        elif executor.need_replan(step, state):
            return {"ok": False, "reason": "replan budget exhausted", "state": state}

    return {"ok": True, "answer": executor.finalize(plan, state), "state": state}

这段伪代码的关键在于把计划做成 Runtime 可读取的执行结构。它决定当前步骤、可用工具、完成判断和重规划预算。

3.5 规划算法与模型能力

常见方法对比

方法机制适合场景局限
Prompt 直接拆分让模型输出步骤原型、低风险任务稳定性依赖提示词
模板化规划按任务类型套用阶段模板业务流程明确覆盖不了长尾情况
搜索式规划生成多条候选路径并评分高价值复杂任务成本高,评估器要求高
人工确认计划执行前让用户审阅关键步骤有写入、副作用或合规风险交互成本增加

Tree of Thoughts 和 Graph of Thoughts 等研究提供了多路径搜索和评估思路,但生产系统常从更轻的结构化计划开始。先保证计划可执行、可校验、可恢复,再考虑复杂搜索。

最小规划器伪代码

python
def plan_for_task(goal, task_type):
    templates = {
        "code_fix": ["reproduce", "locate", "patch", "test", "summarize"],
        "research": ["collect", "compare", "draft", "verify"],
    }
    stages = templates.get(task_type, ["collect", "execute", "verify"])

    return [
        {
            "id": stage,
            "allowed_tools": tools_for_stage(stage),
            "done_when": done_condition(stage),
        }
        for stage in stages
    ]


def validate_plan(plan):
    # 校验每个阶段都有工具范围和完成条件。
    for step in plan:
        if not step["allowed_tools"] or not step["done_when"]:
            return False
    return True

这个示例强调工程里的起点:先把任务类型和阶段模板稳定下来,再让模型填充具体查询词、文件路径或步骤说明。

3.6 重规划与验证

重规划触发

重规划不应频繁发生,否则系统会变成没有方向的循环。常见触发条件包括:核心文件不存在,测试结果与假设冲突,外部系统权限不足,用户补充了新约束,当前步骤连续失败。

重规划必须带上失败原因和证据。若只让模型“重新计划”,它可能重复原路径。Runtime 应记录计划版本,最终回答里保留关键计划变更,方便复盘。

与 ReAct 的取舍

维度ReActPlan-and-Execute
控制粒度每轮一个动作先阶段计划,再执行动作
上下文压力长任务中轨迹增长快计划摘要可压缩上下文
灵活性每步都可根据观察调整调整通过重规划发生
测试难度重点测工具调用轨迹还要测计划质量和步骤完成
适合任务信息查找、调试、网页操作迁移、调研、跨系统流程

实践中两者经常组合使用。Planner 负责全局阶段,Executor 在单个步骤内使用 ReAct。这样既保留全局结构,又能利用环境反馈。

3.7 工程落地要点

计划质量控制

问题表现控制方式
计划过粗每步仍然像一个大任务要求每步有产出物和完成条件
计划过细调用成本和上下文膨胀合并纯机械步骤,保留关键分支
步骤依赖不清后续步骤使用不存在的结果在计划结构里记录输入和依赖
重规划失控每次失败都推翻全部计划限制重规划次数和影响范围
验收模糊Executor 自称完成Runtime 结合工具结果校验

计划越长,越需要状态压缩。可以只把当前步骤、已完成摘要、关键证据和失败原因交给模型,完整 trace 留在外部存储。

3.8 失败与治理

规划常见问题

问题表现处理方式
目标误解计划偏离用户真实意图在计划前做意图确认或约束抽取
依赖缺失后续步骤需要不存在的产物计划校验时检查输入输出链
过度拆分小任务被拆成十几步设置最大阶段数,合并机械步骤
无法验收步骤完成条件无法判断每步绑定可观察产物
重规划频繁执行轨迹反复推翻计划记录重规划原因和次数

规划的工程价值来自可执行性。只要计划能限制工具、驱动状态、支持校验和复盘,它就能成为 Agent Runtime 的控制骨架。

4. Reflection 与 Reflexion 的评估修正

4.1 Evaluator-Optimizer:可验证输出的迭代

Evaluator-Optimizer 模式由生成器和评估器组成。生成器产出代码、报告、SQL 或配置,评估器使用测试、规则、静态分析或另一个模型给出反馈,生成器再修正。代码迁移天然适合这个模式:生成补丁后运行测试,失败输出作为反馈;测试通过后,再运行 lint 或 review 规则。

这个模式有效的前提是评价标准具体。好的标准包括“构建通过”“所有引用来自给定文件”“JSON schema 校验通过”“没有修改无关文件”。模糊标准会导致模型反复润色,却难以收敛。迭代次数必须有上限,连续多轮没有新增改进时应停止并说明限制。

评估器可以是模型,也可以是程序。能用程序验证的部分优先用程序,例如单元测试、类型检查、JSON schema、链接检查。模型评估适合语义质量、遗漏风险和可读性审查。两者结合时,程序结果优先级更高。

4.2 从一次生成到自我修正

背景

Reflection 泛指模型在生成结果后进行评估、发现问题并修正输出的机制。Reflexion 则来自论文 Reflexion: Language Agents with Verbal Reinforcement Learning,它让 Agent 在失败后生成语言形式的反思记忆,并在后续尝试中使用这些记忆改进行为。两者常被混用,但工程实现时需要区分:Reflection 更像一个评估修正环节,Reflexion 更强调失败经验沉淀和下次调用。

这类机制出现的原因很直接:Agent 常常能完成大部分步骤,却在最后答案、工具参数、测试修复或证据归纳上出现细小错误。只靠一次模型调用,错误会直接暴露给用户;加一层评估与修正,可以把部分问题挡在发布前。

问题边界

场景适合的反思方式风险
文档写作检查证据覆盖、结构、遗漏反思模型可能空泛批评
代码修复根据测试失败生成下一步修正可能扩大修改范围
工具调用检查参数和工具选择是否合理评估需要完整 trace
长期学习保存失败经验供下次任务检索错误经验可能污染记忆

Reflection 不会自动提升事实正确性。它必须依赖可检查的证据、测试、规则或外部反馈。没有观察结果的自我评价,容易变成语气更自信的二次生成。

4.3 运行机制

Evaluator-Optimizer 循环

Anthropic 把 evaluator-optimizer 归入常见工作流:一个模型生成答案,另一个评估器给出反馈,生成器据此修改。Agent 场景中,评估器可以是代码测试、规则评分器、LLM Judge 或人工审核。

关键点在于反馈要可执行。比如“内容不够好”没有帮助;“第 3 段提到 pgvector 成本低,但没有引用来源,请回到笔记或资料中补证据”可以直接驱动下一轮行动。

Reflexion 的记忆写入

Reflexion 的特殊之处在于把失败经验写成语言记忆。下一次类似任务开始时,Agent 检索这些经验,改变初始策略。它不更新模型参数,因此实现成本低,但记忆质量决定效果。

python
def reflect_after_trial(task, trace, outcome):
    if outcome["passed"]:
        return None

    # 反思内容必须绑定失败证据,避免保存空泛经验。
    return {
        "task_type": task["type"],
        "failure": outcome["reason"],
        "lesson": "先运行最小测试定位失败,再扩大修改范围。",
        "evidence": trace[-3:],
        "expires_after_days": 30,
    }


def start_next_trial(task, memory_store):
    memories = memory_store.search(namespace=task["type"], query=task["goal"], limit=3)
    return {
        "goal": task["goal"],
        "reflections": [m["lesson"] for m in memories],
    }

这段代码强调两点:反思记忆只在失败后写入,并且要携带证据。没有证据的经验容易沉淀成噪声,后续任务会被错误建议误导。

4.4 与 ReAct、计划的组合

三种嵌入位置

嵌入位置机制适合场景
每轮动作后检查工具选择、参数、观察结果高风险工具调用
阶段完成后检查计划步骤是否满足完成条件Plan-and-Execute
最终输出前检查答案、证据、格式、风险写作、客服、代码修复

在代码 Agent 中,Reflection 通常跟测试结果绑定。模型修改代码后运行测试,失败日志就是评估依据;模型再根据失败日志提出补丁。这里的反思来自外部反馈,而非纯文本自评。

时序示例

评估反馈不要覆盖原始证据。最终调试时需要看到模型候选补丁、测试失败日志、评估器反馈和后续修订之间的关系。

4.5 工程风险

常见失败

失败类型表现处理方式
自我肯定评估器重复生成器观点使用测试、规则、证据字段约束评估
反馈空泛只说“需要改进”要求反馈绑定段落、文件、工具结果
无限修正多轮修改仍不达标设置修正预算和人工接管条件
记忆污染保存了错误经验写入前检查证据,定期过期和降权
成本升高每次任务多次模型调用只在高风险阶段启用评估

Reflection 的效果来自可验证反馈。若任务没有测试、规则、证据或人工样本,优先补评估依据,再考虑反思循环。

4.6 判断一个模式是否有效

模式选择最终要靠数据验证。单 Agent 引入后,应比较任务成功率、平均轮次、工具调用数和人工接管率。多 Agent 引入后,应额外比较重复工作、冲突次数、交接失败和总成本。Evaluator 引入后,应比较质量提升和迭代成本。

若一个模式让成本上升明显,成功率没有提升,就应回退到更简单结构。一个小而稳定的单 Agent,通常比缺少治理的多 Agent 系统更适合生产环境。复杂模式的价值来自明确分工和可验证收益,而非角色数量本身。

5. 多 Agent 协作与框架选型

5.1 Routing:把入口任务分流

Routing 模式用于多类任务入口。企业助手经常同时面对“查知识库”“查订单”“修改资料”“报故障”“闲聊”几类请求。入口 Agent 或分类模型先判断任务类型,再把请求交给对应处理链。路由可以使用规则、轻量模型或大模型。稳定业务场景里,规则和模型混合更可靠:明确关键词和权限先走规则,模糊请求再交给模型判断。

路由设计的重点是类别要可执行。类别名称要对应后续能力,例如 knowledge_qabilling_queryticket_createhuman_escalation。每个类别都要定义输入、工具、权限和失败兜底。低置信度路由可以请求澄清,或进入工具更少的通用处理链。

对代码助手来说,Routing 也有价值。用户请求可能是解释代码、修复 bug、添加功能、写测试、检查性能或整理文档。不同任务需要不同工具和上下文。解释代码通常只读,修复 bug 需要编辑和测试,性能分析可能需要运行基准。入口路由能减少模型看到的工具数量,也能降低误操作风险。

5.2 Supervisor-Worker:复杂任务拆分

当任务可以拆成多个相对独立的子任务时,可以使用 Supervisor-Worker。Supervisor 负责理解目标、拆分任务、分派 worker、收集产物、处理冲突和输出最终结果。Worker 只负责具体领域,例如资料检索、代码修改、测试修复、安全审查。

Supervisor-Worker 的收益来自职责和权限分离。Search Worker 只读仓库,Patch Worker 能写文件但不能部署,Test Worker 能运行受控测试,Review Worker 读取补丁和日志。权限分离能减少高风险工具暴露面。Worker 输出也要结构化,至少包含任务 id、输入摘要、执行步骤、证据、产物、失败和建议。

这个模式的主要成本是状态同步。多个 worker 可能读取同一文件、重复搜索、给出冲突建议,甚至同时修改相邻代码。共享状态必须记录文件锁、已完成任务、冲突点和产物位置。Supervisor 还要判断是否继续分派,避免多 Agent 系统在反复讨论中消耗预算。

5.3 Handoff:面向用户的专业接力

Handoff 表示当前 Agent 把控制权交给另一个 Agent。企业助手中常见:入口 Agent 判断用户在问账单问题,于是交给 Billing Agent;Billing Agent 发现需要技术排查,再交给 Support Agent;Support Agent 完成诊断后交回入口 Agent 生成用户可读答复。

Handoff 的关键产物是交接摘要。摘要要包含用户原始目标、身份和权限状态、已完成动作、关键证据、未解决问题、下一步建议。摘要过短会丢信息,过长会把无关历史带给下游 Agent。工程上可以把摘要分成固定字段,降低自由文本带来的遗漏。

Handoff 与 Supervisor-Worker 的差异在控制方式。Supervisor-Worker 中 supervisor 始终管理全局任务;handoff 中当前 Agent 会把后续对话交给另一个 Agent。面向用户的多专业助手适合 handoff,因为体验接近真实服务转接;后台复杂任务适合 Supervisor-Worker,因为集中控制更容易做审计和预算管理。

5.4 企业助手中的组合方式

企业助手的入口通常是 Routing。用户可能询问制度、查询订单、申请权限、反馈故障或要求生成报告。入口 Agent 先做意图识别和权限检查。知识类问题进入 RAG 工作流;订单类问题进入业务 API 工具链;故障类问题进入工单或技术支持 Agent;高风险操作进入人工确认。

如果用户的问题从一个领域转到另一个领域,可以使用 Handoff。例如用户先问“这笔费用扣款原因”,Billing Agent 查询账单后发现疑似系统故障,再移交 Support Agent。交接摘要必须包含账单号、已查询记录、异常现象、用户权限和待处理问题。Support Agent 不需要看到完整聊天历史,只需要完成排查所需信息。

5.5 模式组合的状态设计

多模式组合时,状态要分层。全局状态由工作流或 Supervisor 管理,记录用户目标、阶段、预算、权限和最终产物。局部 Agent 状态记录本阶段工具轨迹和中间证据。Worker 状态记录子任务输入、输出、失败和建议。评估器状态记录检查项、通过项和失败项。

分层状态能避免所有消息堆在一个上下文里。代码迁移中,全局状态只需要知道模块迁移进度和最终验证结果;Patch Worker 的局部状态可以包含具体文件 diff;Test Worker 的局部状态可以包含失败测试日志。Supervisor 汇总结构化结果,而无需读取每个 worker 的完整对话。

状态还要支持暂停和恢复。长任务可能持续数分钟甚至数小时,用户可能中途修改目标。Runtime 应保存阶段、工具结果、失败原因和已生成产物。恢复时,Agent 读取压缩后的状态,无需重放全部历史消息。

5.6 框架落地方式

框架选择应服务架构设计。OpenAI Agents SDK 适合需要工具、handoff、guardrail 和 tracing 的应用。LangGraph 适合需要图结构、持久状态、人机协作和复杂编排的系统。CrewAI 适合角色分工明确的内容生产、研究和运营任务。AutoGen AgentChat 适合对话式多 Agent 协作和研究原型。

选择框架前,应先画出自己的控制流:任务如何进入、工具在哪里调用、状态如何保存、失败如何恢复、哪些动作需要确认。工具 schema、状态模型和评估集最好保持框架无关,未来迁移时成本更低。框架可以加速开发,长期稳定性来自清晰边界和可验证指标。

5.7 实施顺序

实际项目可以按以下顺序推进。第一阶段,用固定工作流和少量只读工具跑通最常见任务。第二阶段,在路径动态的节点引入单 Agent,并设置严格工具和预算。第三阶段,为任务建立 trace 和评估集。第四阶段,当单 Agent 指令拥挤或权限边界不清时,再拆分专业 Agent。第五阶段,引入多 Agent 后先关闭并行,让系统按顺序运行,确认状态和产物正确后再开启并行。

这个顺序看起来保守,但能减少调试成本。Agent 架构真正困难的地方在于让每个角色的输入、输出、权限和失败都可控。模式越复杂,越要用小步演进保持可理解性。

5.8 先看系统需求

背景

Agent 框架很多,名称也容易让人分心。实际选型应从系统需求出发:任务是固定流程还是动态决策,是否需要多 Agent,是否需要持久状态,是否要接入 MCP,是否要评测和可观测,是否能接受框架对运行时的约束。

框架的价值在于减少重复建设,但它也会带来抽象成本。原型阶段可以用轻量 Runtime;任务复杂后,再引入状态图、多 Agent、工具协议和评测平台。

选型维度

维度需要回答的问题
状态是否需要 checkpoint、恢复、长期任务
工具是否要 Function Calling、MCP、自定义工具
流程固定工作流、ReAct、计划、图结构哪种为主
协作是否需要多 Agent 和 handoff
观测是否能记录 trace、span、成本和错误
评测是否能接入自动化 eval 和 replay
部署是否支持现有语言、云环境和权限体系

如果这些问题没有答案,直接比较框架功能会导致选择偏差。

5.9 常见框架对比

工程视角

框架/平台侧重点适合场景注意点
OpenAI Agents SDKAgent、tool、handoff、guardrail、trace使用 OpenAI 生态快速搭建 Agent与 OpenAI 模型和 SDK 集成较深
LangGraph状态图、checkpoint、可恢复执行复杂流程、长期状态、多节点图需要设计图和状态结构
AutoGen多 Agent 对话和协作研究原型、多角色协作生产治理要额外补齐
CrewAI角色和任务编排团队式任务分派适合角色抽象清晰的场景
自研 Runtime完全贴合业务强权限、强审计、特殊工具建设成本高

这张表只提供工程取舍。实际选型还要结合团队语言栈、模型供应商、部署环境、合规要求和现有观测系统。

架构位置

框架位于业务应用和底层模型、工具、状态之间。选型时要看它是否能接入你的工具系统和观测系统,而不只是看示例代码是否简洁。

5.10 选型流程

分阶段路线

阶段推荐方式目标
原型手写 Runtime 或 SDK 示例跑通目标、工具和状态
小规模试点引入框架的状态和工具能力形成可复现 trace
生产灰度接入权限、评测、观测和回放控制风险
平台化框架能力与内部网关、MCP、AgentOps 集成多团队复用

不要在原型阶段一次性引入所有平台能力。先用最小 Runtime 验证任务价值,再围绕真实失败补框架能力。

评估脚本

python
def score_framework(framework):
    weights = {
        "state": 0.2,
        "tooling": 0.2,
        "observability": 0.2,
        "eval": 0.15,
        "deployment": 0.15,
        "team_fit": 0.1,
    }
    return sum(framework[k] * w for k, w in weights.items())

这个评分只适合内部讨论。最终要用同一组真实任务跑 POC,比较成功率、成本、延迟、trace 完整度和开发复杂度。

5.11 风险

常见问题

问题表现处理方式
被示例误导demo 很快,生产缺权限和评测用真实任务 POC
过度抽象简单流程被图结构复杂化从最小链路开始
锁定供应商模型和工具难切换抽象模型适配层
trace 缺失失败无法复盘选型时检查观测能力
状态不可恢复长任务中断丢失上下文要求 checkpoint

框架选型会随着系统阶段调整。可以先把业务接口、工具 schema、状态模型和评测数据集设计好,再根据阶段选择框架或自研实现。

参考资料