Contents

智能体架构设计:从简单到复杂

1. 引言:为什么需要不同的Agent架构

在第1章中,我们建立了对AI智能体(AI Agent)的基本认知:智能体是一个能够感知环境、自主决策、执行行动并持续循环直到完成目标的系统。如果把大语言模型(LLM)比作"大脑",那么架构就是大脑与手脚之间的"神经系统"——它决定了智能体如何思考、何时行动、如何反馈。

💡 生活比喻:架构之于智能体,就像"工作流程"之于一家公司。同样是接到客户订单,路边小吃店老板一个人就能从接单、做饭到收钱全包;而大型连锁餐厅则需要前台、后厨、配送、财务多个岗位配合。规模不同,“流程"就不同。智能体架构也是如此——任务简单就用简单流程,任务复杂才需要复杂流程

现实世界中的任务复杂度千差万别。一个"把今天的天气告诉我"的请求,与"帮我调研三家竞品并输出对比报告"的任务,所需的推理深度、工具调用次数、错误恢复能力完全不在一个量级。如果对所有任务都套用最复杂的架构,不仅浪费token、增加延迟,还会因链条过长而放大幻觉风险;反之,若对复杂任务只用单次LLM调用,又必然力不从心。

⚠️ 常见误区:很多初学者一上来就想搭"多Agent协作系统”,结果发现简单的翻译任务也被拆成七八次调用,既慢又贵。架构不是越复杂越好,而是越匹配越好。

因此,架构选型的本质是在"能力上限"与"成本/复杂度"之间寻找平衡点。本章将带你从最简单的单次调用出发,逐级演进到ReAct、Plan-and-Execute、Reflection,直至多Agent协作,让你在面对具体需求时能够做出合理的工程决策。

2. 架构演进路线图(从简单到复杂)

我们可以把智能体架构按"自主性"和"复杂度"划分为五个层级:

层级 架构名称 核心特征 典型适用场景
Level 1 单次LLM调用 一次提示,一次回答 翻译、摘要、简单问答
Level 2 ReAct 思考-行动-观察循环 工具调用、信息检索
Level 3 Plan-and-Execute 先规划再分步执行 多步骤复杂任务
Level 4 Reflection 自我反思与迭代改进 代码生成、写作优化
Level 5 多Agent协作 角色分工协同 大型项目、研究任务

💡 一图记五层:可以把它想象成"做事的人"在升级——Level 1 是"问一句答一句的客服";Level 2 是"边查资料边回答的助理";Level 3 是"先列清单再逐项做的项目经理";Level 4 是"做完自己检查改错的工匠";Level 5 是"分工合作的项目团队"。

演进的内在逻辑是:每解决上一层的瓶颈,就诞生下一层架构。单次调用无法使用工具,于是有了ReAct;ReAct缺乏全局规划容易"走偏",于是有了Plan-and-Execute;后者产出质量依赖单次生成,于是加入Reflection做迭代打磨;当任务规模超出单Agent承载能力时,便走向多Agent协作。

⚠️ 注意:这五层不是"替换"关系,而是"叠加"关系。高级架构内部往往嵌套着低级架构,例如 Plan-and-Execute 的执行器内部就常常使用 ReAct。理解了这一点,你才能灵活组合,而不是死记硬背。

3. Level 1:单次LLM调用(最简单的Agent)

3.1 生活比喻

Level 1 就像自动售货机:你投币(输入提示),它出货(给出回答),一次完成,没有后续动作。它不会"再去仓库找货",也不会"问你要不要加购"。简单、直接、可预测。

严格来说,单次LLM调用算不上完整的"智能体"——它没有循环、没有工具、没有状态。但它是所有更复杂架构的原子单元,理解它能帮你认清"何时真的需要升级架构"。

3.2 这段代码做了什么

下面这段代码实现一个"中英互译"小程序:先告诉LLM"你是翻译官,只输出译文",再把待翻译文本作为用户消息发给它,最后取出回复内容返回。整个过程只调用一次API,没有任何循环或工具。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# file: level1_single_call.py
# 单次LLM调用示例:中英互译
import os
from openai import OpenAI

# 初始化客户端,API Key从环境变量读取(避免硬编码泄露密钥)
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

def translate(text: str, direction: str = "zh2en") -> str:
    """根据方向完成中英互译"""
    # 根据方向决定源语言和目标语言
    src, dst = ("中文", "英文") if direction == "zh2en" else ("英文", "中文")
    # 构造系统提示,限定LLM的角色与输出格式
    system_prompt = f"你是一个专业翻译,负责将{src}翻译成{dst},只输出译文。"

    # 单次调用:一次system + 一次user,得到一次response
    response = client.chat.completions.create(
        model="gpt-5.4",  # 模型名可按你账号可用的模型替换
        messages=[
            {"role": "system", "content": system_prompt},  # 设定角色
            {"role": "user", "content": text},             # 用户输入
        ],
        temperature=0.3,  # 低温度让翻译更稳定、少发挥
    )
    # choices[0].message.content 即模型生成的文本
    return response.choices[0].message.content.strip()

if __name__ == "__main__":
    print(translate("人工智能正在改变软件开发的范式。", "zh2en"))
    # 输出示例:Artificial intelligence is changing the paradigm of software development.

💡 小贴士temperature 控制"随机性"。翻译这类要求稳定的任务用 00.3;写诗、起标题等需要创意的任务可调到 0.71.0。

⚠️ 注意事项:务必把 API Key 放在环境变量里,不要直接写进代码再提交到 GitHub,否则会被爬虫盗用导致账单爆炸。

3.3 为什么选这个架构(对比分析)

维度 Level 1 单次调用 Level 2+ 更复杂架构
实现难度 极低,十几行代码 中到高
延迟 一次API往返,最快 多次调用,明显变慢
成本 最省token 成倍增加
能力 不能用工具、不能多步推理 可查资料、可分步、可反思

适用场景:输入输出明确、无需外部信息、单步即可完成。

局限性:无法调用工具(如搜索、计算器)、无法处理需要多轮推理的任务、无法根据中间结果调整策略。一旦你的任务需要"先查再答"或"分步走",就该升级到Level 2了。

4. Level 2:ReAct架构(思考-行动-观察循环)

4.1 生活比喻

ReAct 就像你查字典做作业:遇到一道题(用户问题),你不是立刻写答案,而是先想"我需要先查这个词的意思"(Thought → Action),查完看到解释(Observation),再想"现在我能答题了吗?"——不能就再查一次,能了就写下最终答案(Final Answer)。“先想、再做、再看结果"不断循环,这就是 ReAct。

4.2 原理图解

ReAct(Reasoning + Acting)由Yao等人于2022年提出,核心思想是让LLM交替输出"思考"和"行动”,并在行动后获取"观察"结果反馈给LLM,形成闭环:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
用户输入
┌─────────────────────────────┐
│  LLM 推理:Thought → Action │  ← 循环开始
└─────────────────────────────┘
   │ Action(如 search("天气"))
┌─────────────────────────────┐
│      工具执行 / 环境反馈       │
└─────────────────────────────┘
   │ Observation(如 "北京 25℃ 晴")
┌─────────────────────────────┐
│  LLM 再推理:是否已能回答?    │
└─────────────────────────────┘
   ├── 否 → 回到循环顶部
   └── 是 → 输出 Final Answer

关键点在于:LLM本身不直接执行工具,它只生成"行动指令";执行由外部代码完成,结果再注入回对话。这种"思考-行动-观察"的文本协议让LLM在无需微调的情况下获得工具使用能力。

⚠️ 注意:这里有个容易混淆的点——LLM 输出的是"文本形式的指令"(比如字符串 Action: search),真正去调用 search() 函数的是我们写的 Python 代码。LLM 只负责"决定用什么工具、传什么参数",不负责"动手"。

4.3 这段代码做了什么

下面用纯Python(不依赖LangChain)实现一个完整ReAct智能体。它注册了两个工具:计算器和搜索;然后用一段提示词告诉LLM"请按 Thought/Action/Action Input 的格式输出";主循环里每次拿到LLM输出就用正则解析出动作,执行对应工具,再把观察结果塞回对话让LLM继续推理,直到它给出 Final Answer 或达到步数上限。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# file: level2_react_agent.py
# ReAct架构智能体:纯Python实现,不依赖LangChain
import os
import re
from openai import OpenAI

# 初始化客户端,密钥从环境变量读取
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

# ---------- 工具定义 ----------
def calculator(expression: str) -> str:
    """简易计算器,仅支持四则运算"""
    try:
        # 用正则限制字符集,防止恶意注入(如导入os)
        if not re.match(r"^[\d\s\+\-\*/\.\(\)]+$", expression):
            return "错误:表达式包含非法字符"
        # 字符集已限定,此处eval相对安全
        return str(eval(expression))
    except Exception as e:
        return f"计算出错:{e}"

def search(query: str) -> str:
    """模拟搜索工具(真实场景可接入搜索API)"""
    # 这里用一个字典假装是搜索引擎的返回
    fake_db = {
        "北京天气": "北京今日 25℃,晴,微风",
        "上海天气": "上海今日 28℃,多云",
        "Python版本": "Python最新稳定版为 3.13",
    }
    return fake_db.get(query.strip(), f"未找到关于'{query}'的信息")

# 工具注册表:名称 -> (函数, 描述)
# 描述会拼进提示词,让LLM知道有哪些工具可用
TOOLS = {
    "calculator": (calculator, "四则运算计算器,输入数学表达式,如 3*8+2"),
    "search": (search, "信息搜索工具,输入关键词查询天气、常识等"),
}

# ---------- ReAct 提示模板 ----------
REACT_PROMPT = """你是一个使用ReAct模式工作的智能体。
可用工具:
{tool_descriptions}

请严格按以下格式输出,每一步用关键词分隔:

Thought: 你的思考过程
Action: 工具名称
Action Input: 工具输入参数(字符串)

当你已得到足够信息可以回答用户问题时,输出:

Thought: 我已经可以回答了
Final Answer: 最终答案

问题:{question}
"""

def build_tool_description() -> str:
    """构造工具说明文本,拼成提示词的一部分"""
    lines = []
    for name, (_, desc) in TOOLS.items():
        lines.append(f"- {name}: {desc}")
    return "\n".join(lines)

def parse_action(text: str):
    """从LLM输出中解析出 Action 和 Action Input,或 Final Answer"""
    # 用正则匹配各关键字后的内容
    action_match = re.search(r"Action:\s*(.+)", text)
    input_match = re.search(r"Action Input:\s*(.+)", text)
    final_match = re.search(r"Final Answer:\s*([\s\S]+)", text)
    # 优先判断是否已给出最终答案
    if final_match:
        return "final", final_match.group(1).strip()
    if action_match and input_match:
        return action_match.group(1).strip(), input_match.group(1).strip()
    return None, None  # 格式不符时返回None,由主循环兜底处理

def react_agent(question: str, max_steps: int = 5) -> str:
    """ReAct主循环:思考-行动-观察,直到得出答案或步数耗尽"""
    messages = [
        {"role": "system", "content": REACT_PROMPT.format(
            tool_descriptions=build_tool_description(),  # 注入工具清单
            question=question,                            # 注入用户问题
        )},
    ]

    for step in range(max_steps):
        # 1. LLM推理:生成 Thought + Action
        resp = client.chat.completions.create(
            model="gpt-5.4",
            messages=messages,
            temperature=0.2,  # 低温度让格式更稳定
        )
        output = resp.choices[0].message.content
        print(f"\n===== Step {step + 1} =====")
        print(output)

        # 2. 解析动作(要么是工具调用,要么是final)
        action, action_input = parse_action(output)

        # 3. 若给出最终答案,结束循环
        if action == "final":
            return action_input

        # 4. 执行工具
        if action in TOOLS:
            tool_fn, _ = TOOLS[action]            # 取出对应函数
            observation = tool_fn(action_input)   # 真正"动手"执行
            print(f"Observation: {observation}")
            # 把LLM的输出和观察结果都塞回对话,供下一轮推理
            messages.append({"role": "assistant", "content": output})
            messages.append({"role": "user", "content": f"Observation: {observation}"})
        else:
            # 工具名不存在时提示LLM重新选择
            messages.append({"role": "assistant", "content": output})
            messages.append({"role": "user", "content": f"Observation: 工具'{action}'不存在,请从可用工具中选择。"})

    return "达到最大步数,未能得出答案。"

if __name__ == "__main__":
    # 测试:计算并搜索的组合任务
    answer = react_agent("北京今天多少度?如果把这个温度乘以2再加10是多少?")
    print(f"\n最终答案:{answer}")

运行后你将看到智能体先调用 search 查询北京天气,再用 calculator 计算 25*2+10=60,最终给出答案。整个过程无需微调,完全靠提示工程与文本协议驱动。

💡 小贴士max_steps 是必备的"安全气囊"。没有它,一旦LLM陷入"查了又查"的死循环,你的API账单会一路飙升。生产环境建议设 5~10 步。

⚠️ 注意事项:示例中的 calculator 用了 eval。虽然前面用正则限制了字符集,但 eval 始终有风险。生产环境请改用 ast.literal_eval 或专门的数学解析库(如 numexprsympy)。

4.4 适用场景与局限性

适用场景:需要调用工具、信息检索、简单多步推理。

局限性

  • 缺乏全局规划:每一步只看上一步结果,容易在长链任务中"走偏"或陷入循环;
  • 错误传播:某步观察错误会污染后续推理;
  • 步数失控:需要外部 max_steps 兜底,否则可能无限循环。

4.5 调试与排错小贴士

ReAct 智能体最常见的故障是"格式不对"——LLM 没有按 Thought/Action/Action Input 的关键字输出,导致 parse_action 解析失败。排查时可关注三点:第一,把每一步的原始输出打印出来,肉眼检查格式;第二,提示词里加一句"严格按格式输出,不要加多余解释"并给出示例;第三,若模型支持 Function Calling,可改用结构化调用代替文本协议,稳定性会高很多。

另一个高频问题是"工具名拼错"。LLM 可能输出 Calculator(大写)或 compute(同义词),而注册表里只有小写 calculator。解决方法是在 parse_action 里做大小写归一化和别名映射,或当工具不存在时把可用清单再提示一遍,让模型自我纠正。

💡 小贴士:调试时把 temperature 临时调到 0,能减少随机性,更容易复现问题。问题定位后再调回原值。

5. Level 3:Plan-and-Execute架构

5.1 生活比喻

Plan-and-Execute 就像出差前的行程规划:你不会到了机场才想"先飞哪、住哪、见谁",而是出发前先列一张清单——1.订机票 2.订酒店 3.联系客户 4.准备资料——然后逐项执行。中途如果航班取消,你就修订后续计划(Replan)。“先想清楚要做哪些事,再一件件做”,这就是它和 ReAct 的根本区别。

5.2 Planner + Executor分离

Plan-and-Execute的核心改进是先规划、再执行。它把"想清楚要做哪些子任务"和"逐个完成子任务"解耦:

  • Planner:一次性生成有序的子任务列表(计划);
  • Executor:依次执行每个子任务,可使用ReAct或单次调用;
  • Replanner(可选):执行中如遇意外,可动态修订剩余计划。

这种"谋定而后动"的方式显著提升了长任务的稳定性。

5.3 这段代码做了什么

下面实现一个简化版 Plan-and-Execute。plan() 让LLM把任务拆成JSON数组形式的子任务清单;execute() 逐个执行子任务并把已完成的上下文带进去;最后再让LLM把所有子任务结果汇总成最终答案。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
# file: level3_plan_execute.py
# Plan-and-Execute架构智能体
import os
import json
from openai import OpenAI

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

PLANNER_PROMPT = """你是一个任务规划器。请将用户任务拆分为有序的子任务列表。
每个子任务应可独立执行,粒度适中(不多于5个子任务)。
仅输出JSON数组,不要其他文字,格式如:
["子任务1", "子任务2"]

用户任务:{task}
"""

EXECUTOR_PROMPT = """你是一个任务执行器。请完成以下子任务。
你可以使用如下已知能力:搜索、计算、翻译、写作。
若需要外部信息,可基于常识合理假设并在结果中标注"假设"。
仅输出执行结果,不要解释过程。

子任务:{subtask}
上下文(已完成结果):{context}
"""

def plan(task: str) -> list:
    """生成子任务计划:让LLM输出JSON数组"""
    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": PLANNER_PROMPT.format(task=task)}],
        temperature=0.2,  # 规划阶段低温度,让拆解更稳定
    )
    raw = resp.choices[0].message.content.strip()
    try:
        return json.loads(raw)  # 把JSON文本解析成Python列表
    except json.JSONDecodeError:
        # 解析失败则退化为单任务,保证不崩
        return [task]

def execute(subtask: str, context: str) -> str:
    """执行单个子任务,带上已完成结果作为上下文"""
    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": EXECUTOR_PROMPT.format(
            subtask=subtask, context=context
        )}],
        temperature=0.3,
    )
    return resp.choices[0].message.content.strip()

def plan_execute_agent(task: str) -> str:
    """Plan-and-Execute主流程:规划→执行→汇总"""
    # 1. 规划:拆出子任务清单
    subtasks = plan(task)
    print(f"计划:{subtasks}")

    # 2. 依次执行,并累积上下文(后面的子任务能看到前面的结果)
    context_parts = []
    for i, st in enumerate(subtasks, 1):
        result = execute(st, "\n".join(context_parts))
        context_parts.append(f"[{i}] {st} -> {result}")  # 拼进上下文
        print(f"执行[{i}]:{st}\n结果:{result}\n")

    # 3. 汇总:让LLM把所有子任务结果整理成最终答案
    summary_prompt = f"请根据以下子任务结果,回答原始任务:\n原始任务:{task}\n子任务结果:\n" + "\n".join(context_parts)
    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": summary_prompt}],
        temperature=0.3,
    )
    return resp.choices[0].message.content.strip()

if __name__ == "__main__":
    print(plan_execute_agent("帮我调研Python和Rust在Web开发中的优劣,并给出学习建议。"))

💡 小贴士:让LLM输出JSON时,加一句"仅输出JSON,不要其他文字"能大幅提高解析成功率。如果模型支持 response_format={"type": "json_object"},用它会更加稳。

⚠️ 注意事项:Planner 拆出的子任务可能粒度不均——有时太粗(一个子任务啥都包了),有时太碎(拆成20条)。可以通过提示词约束"3~5个、每个可独立完成"来缓解。

5.4 与ReAct对比

维度 ReAct Plan-and-Execute
规划方式 边走边看,逐步决策 先全局规划,再分步执行
上下文窗口压力 全程累积,易超长 子任务隔离,可裁剪上下文
错误恢复 较弱,依赖单步反思 较强,可Replan修订计划
LLM调用次数 与步数1:1 规划1次 + 执行N次 + 汇总1次
适用任务长度 短至中等(2-6步) 中至长(5-15步)
实现复杂度 中高

简单经验:步数≤5且任务动态性强 → ReAct;步数>5且可预先拆解 → Plan-and-Execute

💡 混合玩法:实际工程中,Plan-and-Execute 的 Executor 往往内部就是一个 ReAct 智能体——先用规划拿到清单,再让 ReAct 逐项执行并查资料。这种"外层规划、内层反应"的组合,兼顾了全局视野和局部灵活性,是生产环境最常见的搭配之一。

6. Level 4:Reflection/Self-Critique架构

6.1 生活比喻

Reflection 就像写作文先打草稿再自己改:第一稿写完不直接交,而是回头读一遍,发现"这段没说清、那个例子不贴切",改完再读,直到满意为止。在智能体里,“写作文"的角色叫 Generator,“挑毛病"的角色叫 Critic,两者循环直到通过审查。

6.2 自我反思与迭代改进

Reflection架构让智能体在产出结果后自我审视,发现不足并迭代改进,直到满足质量标准或达到上限。它通常由两个角色构成:

  • Generator(生成器):产出初始结果;
  • Critic(评审器):对结果打分并提出改进建议;
  • 循环:Generator根据Critic反馈重新生成。

这在代码生成、技术写作等"质量可判定"的场景中效果尤为显著。

⚠️ 注意:Critic 必须有明确的"合格标准”(比如输出 PASS),否则会无限挑刺、永远停不下来。同时 Critic 自己也是LLM,可能误判——所以最好限制迭代次数(如3轮)。

6.3 这段代码做了什么

下面以"生成Python函数"为例:generate() 负责写代码(或根据反馈改代码),critique() 负责审查并给出意见(合格就回 PASS)。主循环里先生成、再审查,若审查通过就停,否则带着反馈重新生成,最多迭代3轮。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# file: level4_reflection.py
# Reflection架构智能体:代码生成与自我改进
import os
from openai import OpenAI

client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))

GENERATOR_PROMPT = """你是一个Python工程师。请根据需求生成代码。
若收到改进建议,请据此修订代码并输出完整版本。

需求:{requirement}
上一版代码:
{previous_code}
改进建议:
{feedback}

只输出Python代码,不要解释。
"""

CRITIC_PROMPT = """你是一个严格的代码审查员。请审查以下代码:
需求:{requirement}
代码:
{code}

请从以下维度评审:正确性、健壮性、可读性、边界处理。
若代码已合格,输出:PASS
否则输出具体改进建议(不超过3条)。
"""

def generate(requirement: str, previous_code: str, feedback: str) -> str:
    """生成器:产出或修订代码"""
    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": GENERATOR_PROMPT.format(
            requirement=requirement,
            previous_code=previous_code or "(无)",  # 首轮没有上一版
            feedback=feedback or "(无)",            # 首轮没有反馈
        )}],
        temperature=0.4,  # 略高温度,鼓励多样化尝试
    )
    return resp.choices[0].message.content.strip()

def critique(requirement: str, code: str) -> str:
    """评审器:检查并给出反馈(合格返回PASS)"""
    resp = client.chat.completions.create(
        model="gpt-5.4",
        messages=[{"role": "user", "content": CRITIC_PROMPT.format(
            requirement=requirement, code=code
        )}],
        temperature=0.2,  # 审查要严格稳定,用低温度
    )
    return resp.choices[0].message.content.strip()

def reflection_agent(requirement: str, max_iterations: int = 3) -> str:
    """Reflection主循环:生成→评审→改进,直到通过或达上限"""
    code = ""        # 上一版代码,首轮为空
    feedback = ""    # 评审反馈,首轮为空
    for i in range(max_iterations):
        # 1. 生成/修订代码
        code = generate(requirement, code, feedback)
        print(f"\n--- 第{i+1}轮代码 ---\n{code}\n")

        # 2. 评审
        feedback = critique(requirement, code)
        print(f"评审反馈:{feedback}\n")

        # 3. 通过则退出循环
        if "PASS" in feedback:
            print("代码通过审查。")
            break
    return code

if __name__ == "__main__":
    req = "写一个函数,输入整数列表,返回其中第二大的数。要求处理空列表、单元素列表等边界情况。"
    final_code = reflection_agent(req)
    print(f"\n最终代码:\n{final_code}")

💡 小贴士:Generator 和 Critic 最好用不同的提示词甚至不同的模型/温度,避免"自己审自己"时互相迁就、挑不出毛病。让 Critic 角色"严格、挑剔"是关键。

适用场景:代码生成、文档撰写、翻译润色等有明确质量标准的任务。

局限性:评审器本身可能误判;迭代会成倍增加token消耗;对"开放性创意"任务效果不稳定(创意没有唯一正确答案,Critic 难以判定"合格”)。

6.4 适用边界再强调

Reflection 不是万能药。对于"写一首关于秋天的诗"这类开放性任务,Critic 很难给出客观的合格标准,迭代往往变成"个人品味拉锯战",反而越改越平庸。它的甜区是有可验证标准的任务:代码能跑通且测试通过、翻译忠实原文、文档覆盖指定要点。在这些场景下,Critic 的反馈是具体且可执行的,迭代才有意义。

⚠️ 注意事项:别让 Reflection 成本失控。每多一轮迭代,token 消耗约翻倍。建议先用2轮验证收益,再决定是否加大迭代上限。

7. Level 5:多Agent协作架构(概述)

7.1 生活比喻

多Agent协作就像公司里不同岗位的人配合做事:一个项目来了,项目经理拆活、派给前端、后端、测试,大家各干各的又互相交接,最后由项目经理汇总交付。没有谁全包,但通过分工和沟通把大事做成。

当任务规模进一步扩大——例如"完成一份行业研究报告"涉及资料搜集、数据分析、图表绘制、报告撰写等多个专业环节——单Agent的上下文与角色定位都会捉襟见肘。这时引入多Agent协作架构:每个Agent专注一个角色,通过结构化的消息传递协同完成整体任务。

7.2 常见协作模式

常见协作模式包括:

  • 串行流水线:Agent A → B → C,前者输出作为后者输入;就像工厂流水线,上一道工序的成品是下一道的原料。
  • 层级委派:一个Manager Agent拆解任务并分发给Worker Agents,再汇总结果;就像经理派活给组员再收作业。
  • 辩论/投票:多个Agent对同一问题各自作答,通过比较择优;就像请多位专家各写一版方案再投票。
  • 对等协作:Agent间双向通信,动态协商分工(如AutoGen的GroupChat);就像圆桌会议上大家自由讨论、自发认领任务。

💡 小贴士:多Agent不是"越多越好"。每多一个Agent,通信开销、协调难度、调试成本都会上升。能用一个Agent搞定的,就别拆成三个。

⚠️ 注意事项:多Agent系统最怕"死循环"——A等B回复、B等A确认,谁都不动。务必设计好"谁听谁的"、设置超时和最大轮数兜底。

多Agent协作的工程细节(角色设计、通信协议、状态管理、死锁避免)将在第9章深入展开,本章仅作概念性铺垫。

8. 架构选型决策树

面对一个新任务,如何选择合适的架构?可参考如下决策路径:

步骤 判断条件 推荐架构
1 任务可一步完成、无需工具? 是 → Level 1 单次调用
2 需要调用工具,但步数≤5? 是 → Level 2 ReAct
3 步数>5或可预先拆解为子任务? 是 → Level 3 Plan-and-Execute
4 产出有明确质量标准、可迭代改进? 是 → Level 4 Reflection
5 任务涉及多个专业领域、需角色分工? 是 → Level 5 多Agent协作

💡 一句话口诀:能一步就一步,要工具用ReAct,步骤多先规划,要质量加反思,跨领域就分工。

实践中的几点经验:

  1. 从简单架构起步:先用Level 1/2跑通,再按需升级,避免过度设计;
  2. 混合使用:Plan-and-Execute的Executor子步骤可内部采用ReAct;Reflection可叠加在任何架构之上;
  3. 成本敏感时优先降级:Reflection和多Agent会成倍增加调用次数,线上服务务必评估ROI;
  4. 可观测性是底线:无论哪种架构,务必记录每一步的输入输出与工具调用,便于调试与回归。

⚠️ 注意事项:架构选型不是一次性决定。任务会演化、模型会升级、预算会变。每隔一段时间回头评估"现在的架构还合适吗",是工程上的好习惯。

小结

本章沿着"从简单到复杂"的主线,系统梳理了智能体架构的五个层级:

  • Level 1 单次调用是原子单元,像自动售货机,适合无工具的简单任务;
  • Level 2 ReAct通过"思考-行动-观察"循环赋予LLM工具使用能力,像查字典做作业;
  • Level 3 Plan-and-Execute先规划后执行,像出差前列行程单,提升长任务稳定性;
  • Level 4 Reflection引入自我审视,像写完作文自己改,实现质量迭代;
  • Level 5 多Agent协作通过角色分工应对大规模任务,像团队协作完成项目。

每种架构都有其适用边界,没有"最好"的架构,只有"最合适"的架构。工程上的关键能力不是会写复杂代码,而是能根据任务特征做出恰当的取舍。记住口诀:能简不繁、按需升级、混合搭配、成本可控

下一章预告

本章所有架构都以LLM作为推理核心,但我们一直把它当作"黑盒"调用。第3章:LLM核心引擎将打开这个黑盒,深入探讨:

  • LLM的提示工程进阶技巧(few-shot、CoT、结构化输出);
  • 函数调用(Function Calling)与ReAct的关系与取舍;
  • 流式输出、上下文窗口管理、token成本优化;
  • 如何为不同架构选择合适的模型与参数。

理解了引擎本身,你才能在后续章节中更从容地驾驭各类智能体架构。我们第3章见。