Agent 经典范式

为了更好地组织智能体的“思考”与“行动”过程,业界涌现出了多种经典的架构范式。在本章中,我们将聚焦于其中最具代表性的三种,并一步步从零实现它们:

  • ReAct (Reasoning and Acting): 一种将“思考”和“行动”紧密结合的范式,让智能体边想边做,动态调整。
  • Plan-and-Solve: 一种“三思而后行”的范式,智能体首先生成一个完整的行动计划,然后严格执行。
  • Reflection: 一种赋予智能体“反思”能力的范式,通过自我批判和修正来优化结果。

ReAct

基本模式

思考与行动是相辅相成的,思考指导行动,而行动的结果又反过来修正思考。

具体执行分成3步:

  • Thought (思考): 这是智能体的“内心独白”。它会分析当前情况、分解任务、制定下一步计划,或者反思上一步的结果。
  • Action (行动): 这是智能体决定采取的具体动作,通常是调用一个外部工具,例如 Search['华为最新款手机']
  • Observation (观察): 这是执行Action后从外部工具返回的结果,例如搜索结果的摘要或API的返回值。

智能体将不断重复这个 Thought -> Action -> Observation 的循环,将新的观察结果追加到历史记录中,形成一个不断增长的上下文,直到它在Thought中认为已经找到了最终答案,然后输出结果。这个过程形成了一个强大的协同效应:推理使得行动更具目的性,而行动则为推理提供了事实依据。

代码架构

核心是[工具,工具描述]

  • 角色定义: “你是一个有能力调用外部工具的智能助手”,设定了LLM的角色。
  • **工具清单 ({tools})**: 告知LLM它有哪些可用的“手脚”。
  • **格式规约 (Thought/Action)**: 这是最重要的部分,它强制LLM的输出具有结构性,使我们能通过代码精确解析其意图。
  • **动态上下文 ({question}/{history})**: 将用户的原始问题和不断累积的交互历史注入,让LLM基于完整的上下文进行决策。每次的observation也会扔到上下文里。

优缺点、优化技巧☆

优点:

  1. 高可解释性:步骤透明。通过 Thought 链,能让我们清楚看到每一步AI的理解、操作。
  2. 动态规划和纠错能力:每一次都会把observation放到上下文,里面包含了外界新信息,因此可以动态调整&修正搜索词。
  3. 工具协同能力:结构清晰,工具非常好写。

缺点:

  1. 非常依赖LLM能力:因为决策由LLM的Thought环节来决定,导致错误会产生错误的规划和错误的 Tool Function 的输入值。
  2. 执行效率:循序渐进,串行思考,导致每次都会多次调用LLM,消耗比较大。
  3. 脆弱性:提示词模板(工具的解释?)的改动会非常影响输出结果,导致变化会大。
  4. 陷入局部最优:步进式的决策模式,容易因为眼前的observation来决策,导致缺乏长远规划。

优化技巧:

  1. 检查完整提示词
  2. 分析原始输出
  3. 验证工具输入输出
  4. 调整提示词的示例
  5. 尝试不同模型或者参数

Plan and solve

  1. 规划阶段 (Planning Phase): 首先,智能体会接收用户的完整问题。它的第一个任务不是直接去解决问题或调用工具,而是将问题分解,并制定出一个清晰、分步骤的行动计划。这个计划本身就是一次大语言模型的调用产物。
  2. 执行阶段 (Solving Phase): 在获得完整的计划后,智能体进入执行阶段。它会严格按照计划中的步骤,逐一执行。每一步的执行都可能是一次独立的 LLM 调用,或者是对上一步结果的加工处理,直到计划中的所有步骤都完成,最终得出答案。

规划器

使用prompt来控制LLM输出一个plan,里面包含了所有执行步骤。

执行器&状态管理

执行器的目标不是分解问题,而是在已有上下文的基础上,专注解决当前这一个步骤。因此,prompt 需要包含以下关键信息:

  • 原始问题: 确保模型始终了解最终目标。
  • 完整计划: 让模型了解当前步骤在整个任务中的位置。
  • 历史步骤与结果: 提供至今为止已经完成的工作,作为当前步骤的直接输入。
  • 当前步骤: 明确指示模型现在需要解决哪一个具体任务。

状态管理:指的是维护一个历史记录(状态),也就是上下文,慢慢添加。

1
2
# 更新历史记录,为下一步做准备,在for循环里
history += f"步骤 {i+1}: {step}\n结果: {response_text}\n\n"

Reflection

为智能体引入一种 事后(post-hoc)的自我校正循环,使其能够审视自己的工作,发现不足,并进行自我迭代优化。核心工作流程:执行 -> 反思 -> 优化

  1. **执行 (Execution)**:首先,智能体使用我们熟悉的方法(如 ReAct 或 Plan-and-Solve)尝试完成任务,生成一个初步的解决方案或行动轨迹。这可以看作是“初稿”。
  2. 反思 (Reflection):接着,智能体进入反思阶段。它会调用一个独立的、或者带有特殊提示词的大语言模型实例,来扮演一个“评审员”的角色。这个“评审员”会审视第一步生成的“初稿”,并从多个维度进行评估,例如:
    • 事实性错误:是否存在与常识或已知事实相悖的内容?
    • 逻辑漏洞:推理过程是否存在不连贯或矛盾之处?
    • 效率问题:是否有更直接、更简洁的路径来完成任务?
    • 遗漏信息:是否忽略了问题的某些关键约束或方面? 根据评估,它会生成一段结构化的**反馈 (Feedback)**,指出具体的问题所在和改进建议。
  3. **优化 (Refinement)**:最后,智能体将“初稿”和“反馈”作为新的上下文,再次调用大语言模型,要求它根据反馈内容对初稿进行修正,生成一个更完善的“修订稿”。

实现

  1. 初始执行提示词 (Execution Prompt) :这是智能体首次尝试解决问题的提示词,内容相对直接,只要求模型完成指定任务。
  2. 反思提示词 (Reflection Prompt) :这个提示词是 Reflection 机制的灵魂。它指示模型扮演“代码评审员”的角色,对上一轮生成的代码进行批判性分析,并提供具体的、可操作的反馈。
  3. 优化提示词 (Refinement Prompt) :当收到反馈后,这个提示词将引导模型根据反馈内容,对原有代码进行修正和优化。

优缺点

优点:

  1. 解决方案质量提升:迭代,提升结果质量
  2. 鲁棒性和可靠性增强:因为有内部自我纠错循环

缺点:

  1. 开销增大:模型多次迭代,意味着需要花费更多的 token调用
  2. 任务延迟提高:因为reflection是串行运行的,多迭代多花时间
  3. 提示工程复杂度上升:需要对 “执行、反思、优化” 有不同的高效提示词

三者比较

  1. ReAct: 通过“思考-行动-观察”的动态循环,它成功地利用搜索引擎回答了自身知识库无法覆盖的实时性问题。其核心优势在于环境适应性动态纠错能力
  2. Plan-and-Solve: 它将复杂的任务分解为清晰的步骤,然后逐一执行。其核心优势在于结构性稳定性,特别适合处理逻辑路径确定、内部推理密集的任务。
  3. Reflection (自我反思与迭代): 其核心价值在于能显著提升解决方案的质量,适用于对结果的准确性和可靠性有极高要求的场景。

Agent框架

使用场景总结

  • 深度协作和创造性思维:CAMEL 对于创作特别有用处,代码少提示多,重设计。
  • 严格流程控制:对于需要精确步骤控制的任务,LangGraph 的图结构更合适。
  • 大规模并发:AgentScope 的消息驱动架构在高并发场景下更有优势。
  • 复杂决策树:AutoGen 的群聊模式在多方决策场景下更加灵活。

AutoGen

思路&架构

多个Agent扮演专家,自我对话式完成任务。

有不同角色

  • AssistantAgent (助理智能体): 这是任务的主要解决者,其核心是封装了一个大型语言模型(LLM)。它的职责是根据对话历史生成富有逻辑和知识的回复,例如提出计划、撰写文章或编写代码。通过不同的系统消息(System Message),我们可以为其赋予不同的“专家”角色。
  • UserProxyAgent (用户代理智能体): 这是 AutoGen 中功能独特的组件。它扮演着双重角色:既是人类用户的“代言人”,负责发起任务和传达意图;又是一个可靠的“执行器”,可以配置为执行代码或调用工具,并将结果反馈给其他智能体。这种设计清晰地区分了“思考”(由 AssistantAgent 完成)与“行动”。

执行流程

  • 轮询群聊 (RoundRobinGroupChat): 这是一种明确的、顺序化的对话协调机制。它会让参与的智能体按照预定义的顺序依次发言。这种模式非常适用于流程固定的任务,例如一个典型的软件开发流程:产品经理先提出需求,然后工程师编写代码,最后由代码审查员进行检查。
  • 工作流:
    1. 首先,创建一个 RoundRobinGroupChat 实例,并将所有参与协作的智能体(如产品经理、工程师等)加入其中。
    2. 当一个任务开始时,群聊会按照预设的顺序,依次激活相应的智能体。
    3. 被选中的智能体根据当前的对话上下文进行响应。
    4. 群聊将新的回复加入对话历史,并激活下一个智能体。
    5. 这个过程会持续进行,直到达到最大对话轮次或满足预设的终止条件。

优缺点

  1. 易于设计,职责匹配的方式贴近人类社会

  2. 对于流程化任务,有清晰的执行步骤

  3. LLM不确定性,可能陷入错误分支或者陷入循环。

  4. 整体串行,调试困难

AgentScope

思路&架构

AgentScope 就是一个完整的”智能体操作系统”,为开发者提供了从开发、测试到部署的全生命周期支持。与许多框架采用的继承式设计不同,AgentScope 选择了组合式架构消息驱动模式

消息驱动

AgentScope 的核心创新在于其消息驱动架构。在这个架构中,所有的智能体交互都被抽象为消息的发送和接收,而不是传统的函数调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
from agentscope.message import Msg

# 消息的标准结构
message = Msg(
name="Alice", # 发送者名称
content="Hello, Bob!", # 消息内容
role="user", # 角色类型
metadata={ # 元数据信息
"timestamp": "2024-01-15T10:30:00Z",
"message_type": "text",
"priority": "normal"
}
)

好处:

  • 异步解耦: 消息的发送方和接收方在时间上解耦,无需相互等待,天然支持高并发场景。
  • 位置透明: 智能体无需关心另一个智能体是在本地进程还是在远程服务器上,消息系统会自动处理路由。
  • 可观测性: 每一条消息都可以被记录、追踪和分析,极大地简化了复杂系统的调试与监控。
  • 可靠性: 消息可以被持久化存储和重试,即使系统出现故障,也能保证交互的最终一致性,提升了系统的容错能力。

优缺点

  1. 消息机制,天然支持并行发布任务给所有Agent

  2. 消息机制,可视化好,便于留记录

  3. 通信开销,分布式的消息传递机制导致会有网络延迟

  4. 消耗多,每次对话都有详细提示词、多次通信,token消耗快

CAMEL

与 AutoGen 和 AgentScope 这样功能全面的框架不同,CAMEL最初的核心目标是探索如何在最少的人类干预下,让两个智能体通过“角色扮演”自主协作解决复杂任务。

CAMEL 实现自主协作的基石是两大核心概念:角色扮演 (Role-Playing) 和 **引导性提示 (Inception Prompting)**。

引导性提示:

  • 明确自身角色
  • 告知协作者角色
  • 定义共同目标
  • 设定行为约束和沟通协议:这是最关键的一环。例如,指令会要求 AI 用户“一次只提出一个清晰、具体的步骤”,并要求 AI 助理“在完成上一步之前不要追问更多细节”,同时规定双方需在回复的末尾使用特定标志(如 <SOLUTION>)来标识任务的完成。

优缺点

  1. 轻架构、重提示(代码设计少)

  2. 多模态工具支持

  3. 高度依赖提示工程

  4. 不善于搭建大规模多智能体工程

LangGraph

思路&架构

LangGraph 将智能体的执行流程建模为一种状态机(State Machine),并将其表示为有向图(Directed Graph)。在这种范式中,图的节点(Nodes)代表一个具体的计算步骤(如调用 LLM、执行工具),而 边(Edges)则定义了从一个节点到另一个节点的跳转逻辑。

状态节点:

1
2
3
4
5
6
# 定义全局状态的数据结构
class AgentState(TypedDict):
messages: List[str] # 对话历史
current_task: str # 当前任务
final_answer: str # 最终答案
# ... 任何其他需要追踪的状态

有无条件边,这边举例一个有条件边

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# 添加条件边,实现动态路由
workflow.add_conditional_edges(
# 起始节点
"executor",
# 判断函数
should_continue,
# 路由映射:将判断函数的返回值映射到目标节点
{
"continue_to_planner": "planner", # 如果返回"continue_to_planner",则跳回planner节点
"end_workflow": END # 如果返回"end_workflow",则结束流程
}
)

# 判断函数
def should_continue(state: AgentState) -> str:
"""条件函数:根据状态决定下一步路由。"""
# 假设如果消息少于3条,则需要继续规划
if len(state["messages"]) < 3:
# 返回的字符串需要与添加条件边时定义的键匹配
return "continue_to_planner"
else:
state["final_answer"] = state["messages"][-1]
return "end_workflow"

优缺点

  1. 代码架构,高度可控。

  2. 使用状态机图,可预测、可设置路径,对于需要严格按照串行实现的工作(比如审计)会非常适合。

  3. 对于**循环(Cycles)**有原生支持,比如可以通过条件边来构建“反思-修正”循环。

  4. 需要事先写好结点,可控但缺少涌现式的交互,就是不太柔软。

  5. 某个结点数据出错误时会造成全部出错。

Hello Agent框架

架构

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
hello-agents/
├── hello_agents/
│ │
│ ├── core/ # 核心框架层
│ │ ├── agent.py # Agent基类
│ │ ├── llm.py # HelloAgentsLLM统一接口
│ │ ├── message.py # 消息系统
│ │ ├── config.py # 配置管理
│ │ └── exceptions.py # 异常体系
│ │
│ ├── agents/ # Agent实现层
│ │ ├── simple_agent.py # SimpleAgent实现
│ │ ├── react_agent.py # ReActAgent实现
│ │ ├── reflection_agent.py # ReflectionAgent实现
│ │ └── plan_solve_agent.py # PlanAndSolveAgent实现
│ │
│ ├── tools/ # 工具系统层
│ │ ├── base.py # 工具基类
│ │ ├── registry.py # 工具注册机制
│ │ ├── chain.py # 工具链管理系统
│ │ ├── async_executor.py # 异步工具执行器
│ │ └── builtin/ # 内置工具集
│ │ ├── calculator.py # 计算工具
│ │ └── search.py # 搜索工具
└──

自动检测供应商

  1. 最优先:框架会依次检查 MODELSCOPE_API_KEY, OPENAI_API_KEY, ZHIPU_API_KEY 等环境变量是否存在。一旦发现任何一个,就会立即确定对应的服务商。
  2. 次优先:如果用户没有设置特定服务商的密钥,但设置了通用的 LLM_BASE_URL,框架会转而解析这个 URL。
  3. 辅助判断:如果上述两种方式都无法确定,框架会尝试分析通用环境变量 LLM_API_KEY 的格式。

框架接口

Message类

类似于阿里的AgentScope框架,也做了Msg来进行通讯,其实就是封装下rolecontent,然后加了个stamp时间戳。

核心还是要理解OpenAI的API标准是:{ role:str, content:str }

Config类

把全局静态变量(比如环境变量)那些全部扔到里面统一管理。

Agent抽象基类

  1. 构建函数里把核心都带进来:名称、LLM 实例、系统提示词和配置
  2. 强制要求实现run方法
  3. 历史记录history的初始化,以及一些基础方法:clear、append、copy

自定义的Agent类

继承抽象Agent基类后,在run方法里实现

  1. 历史消息添加
  2. while循环,带着工具列表和上下文,多次调用LLM
    1. 在循环里,工具调用之后的结果也会加入上下文内容
    2. 如果已经没有工具调用了,或者 超过最大迭代次数,就代表回答结束。
  3. 保存上面的长串message到历史记录

工具管理类

有注册Tool功能等,本质就是个字典 + 一堆方法。

使用

可以直接用这个基础框架,优化一下prompt、实现ReAct等不同范式。