AI Agent 设计模式实战:ReAct 架构详解
随着人工智能技术的不断进步,AI Agent 设计模式逐渐成为研究和应用的热点。ReAct 模式作为 AI Agent 设计模式的起点,以其模拟人类思考和行动过程的特点,为各种智能应用提供了一种有效的实现途径。
AI Agent ReAct 模式结合了推理与行动,通过思考、行动、观察的循环解决复杂任务。文章详细阐述了 ReAct 的概念来源、TAO 循环机制,并与 Reasoning-Only 和 Action-Only 模式进行了对比。实现部分涵盖了 Prompt 模板设计、Agent 类构建、工具定义、执行器循环逻辑及实际测试案例。同时分析了 ReAct 在稳定性、成本和响应时间方面的局限性,并提出了缓存、动态工具选择等优化策略。该模式广泛应用于智能客服、知识助手等场景,是理解 AI Agent 架构的重要基础。

随着人工智能技术的不断进步,AI Agent 设计模式逐渐成为研究和应用的热点。ReAct 模式作为 AI Agent 设计模式的起点,以其模拟人类思考和行动过程的特点,为各种智能应用提供了一种有效的实现途径。

微信公众号「极客日志」,在微信中扫描左侧二维码关注。展示文案:极客日志 zeeklog
使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online
生成新的随机RSA私钥和公钥pem证书。 在线工具,RSA密钥对生成器在线工具,online
基于 Mermaid.js 实时预览流程图、时序图等图表,支持源码编辑与即时渲染。 在线工具,Mermaid 预览与可视化编辑在线工具,online
将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

ReAct 模式最早出现的 Agent 设计模式,目前也是应用最广泛的。从 ReAct 出发,有两条发展路线:
一条更偏重 Agent 的规划能力,包括 REWOO、Plan & Execute、LLM Compiler。
另一条更偏重反思能力,包括 Basic Reflection、Reflexion、Self Discover、LATS。

在后续文章中,将沿着上图的脉络,结合产品流程和源代码,详细介绍这八种 AI Agent 设计模式。选择结合源代码的原因在于,在 AI 大模型时代,很多的概念和方法都太新了。只有结合源代码,产品经理才能真正理解背后的原理和逻辑,才能知道什么能做,什么不能做,AI 的边界在哪里,以及该如何与人类经验配合。
下面,我们先从 ReAct 模式开始。
ReAct 的概念来自论文《ReAct: Synergizing Reasoning and Acting in Language Models》,这篇论文提出了一种新的方法,通过结合语言模型中的推理(reasoning)和行动(acting)来解决多样化的语言推理和决策任务。ReAct 提供了一种更易于人类理解、诊断和控制的决策和推理过程。
它的典型流程如下图所示,可以用一个有趣的循环来描述:思考(Thought)→ 行动(Action)→ 观察(Observation),简称 TAO 循环。

和 ReAct 相对应的是 Reasoning-Only 和 Action-Only。在 Reasoning-Only 的模式下,大模型会基于任务进行逐步思考,并且不管有没有获得结果,都会把思考的每一步都执行一遍。在 Action-Only 的模式下,大模型就会处于完全没有规划的状态下,先进行行动再进行观察,基于观察再调整行动,导致最终结果不可控。

假设我们正在构建一个智能助手,用于管理我们的日程安排。
在 reasoning-only 模式中,智能助手专注于分析和推理,但不直接采取行动。
在 action-only 模式中,智能助手专注于执行任务,但不做深入的推理或分析。
在 ReAct 模式中,智能助手结合推理和行动,形成一个循环的感知 - 动作循环。不仅分析了你的需求(推理),还实际修改了日程安排(行动)。
下面通过实际的源码,详细介绍 ReAct 模式的实现方法。建议在 PC 端打开阅读源代码以获得更好的体验。
在实现 ReAct 模式的时候,首先需要设计一个清晰的 Prompt 模板,主要包含以下几个元素:
Prompt 模板示例:
TOOL_DESC = """{name_for_model}: Call this tool to interact with the {name_for_human} API. What is the {name_for_human} API useful for? {description_for_model} Parameters: {parameters} Format the arguments as a JSON object."""
REACT_PROMPT = """Answer the following questions as best you can. You have access to the following tools:
{tool_descs}
Use the following format:
Question: the input question you must answer
Thought: you should always think about what to do
Action: the action to take, should be one of [{tool_names}]
Action Input: the input to the action
Observation: the result of the action
... (this Thought/Action/Action Input/Observation can be repeated zero or more times)
Thought: I now know the final answer
Final Answer: the final answer to the original input question
Begin!
Question: {query}"""
一个 ReAct Agent 需要定义好以下元素:
llm:背后使用的 LLM 大模型。tools:后续会用到的 Tools 集合。stop:什么情况下 ReAct Agent 停止循环。class LLMSingleActionAgent {
llm: AzureLLM
tools: StructuredTool[]
stop: string[]
private _prompt: string = '{input}'
constructor({ llm, tools = [], stop = [] }: LLMSingleActionAgentParams) {
this.llm = llm
this.tools = tools
if (stop.length > 4)
throw new Error('up to 4 stop sequences')
this.stop = stop
}
}
Tools 有两个最重要的参数,name 和 description。
Name 就是函数名,description 是工具的自然语言描述,LLM 根据 description 来决定是否需要使用该工具。工具的描述应该非常明确,说明工具的功能、使用的时机以及不适用的情况。
export abstract class StructuredTool {
name: string
description: string
constructor(name: string, description: string) {
this.name = name
this.description = description
}
abstract call(arg: string, config?: Record<string, any>): Promise<string>
getSchema(): string {
return `${this.declaration} | ${this.name} | ${this.description}`
}
abstract get declaration(): string
}
我们先简单地将两个描述信息拼接一下,为 Agent 提供 4 个算数工具:
一个很有意思的事情是,这几个算数工具函数并不需要实际的代码,大模型可以仅靠自身的推理能力就完成实际的算数运算。当然,对于更复杂的工具函数,还是需要进行详细的代码构建。
执行器 executor 是在 Agent 的运行时,协调各个组件并指导操作。还记得 ReAct 模式的流程吗?Thought、Action、Observation、循环,Executor 的作用就是执行这个循环。
class AgentExecutor {
agent: LLMSingleActionAgent
tools: StructuredTool[] = []
maxIterations: number = 15
constructor(agent: LLMSingleActionAgent) {
this.agent = agent
}
addTool(tools: StructuredTool | StructuredTool[]) {
const _tools = Array.isArray(tools) ? tools : [tools]
this.tools.push(..._tools)
}
}
executor 会始终进行如下事件循环直到目标被解决了或者思考迭代次数超过了最大次数:
async call(input: promptInputs): Promise<AgentFinish> {
const toolsByName = Object.fromEntries(
this.tools.map(t => [t.name, t]),
)
const steps: AgentStep[] = []
let iterations = 0
while (this.shouldContinue(iterations)) {
const output = await this.agent.plan(steps, input)
console.log(iterations, output)
// Check if the agent has finished
if ('returnValues' in output)
return output
const actions = Array.isArray(output)
? output as AgentAction[]
: [output as AgentAction]
const newSteps = await Promise.all(
actions.map(async (action) => {
const tool = toolsByName[action.tool]
if (!tool)
throw new Error(`${action.tool} is not a valid tool, try another one.`)
const observation = await tool.call(action.toolInput)
return { action, observation: observation ?? '' }
}),
)
steps.push(...newSteps)
iterations++
}
return {
returnValues: { output: 'Agent stopped due to max iterations.' },
log: '',
}
}
我们提出一个问题,看看 Agent 怎么通过 ReAct 方式进行解决。'一种减速机的价格是 750 元,一家企业需要购买 12 台。每台减速机运行一小时的电费是 0.5 元,企业每天运行这些减速机 8 小时。请计算企业购买及一周运行这些减速机的总花费。'
describe('agent', () => {
const llm = new AzureLLM({
apiKey: Config.apiKey,
model: Config.model,
})
const agent = new LLMSingleActionAgent({ llm })
agent.setPrompt(REACT_PROMPT)
agent.addStop(agent.observationPrefix)
agent.addTool([new AdditionTool(), new SubtractionTool(), new DivisionTool(), new MultiplicationTool()])
const executor = new AgentExecutor(agent)
executor.addTool([new AdditionTool(), new SubtractionTool(), new DivisionTool(), new MultiplicationTool()])
it('test', async () => {
const res = await executor.call({ input: '一种减速机的价格是 750 元,一家企业需要购买 12 台。每台减速机运行一小时的电费是 0.5 元,企业每天运行这些减速机 8 小时。请计算企业购买及一周运行这些减速机的总花费。' })
expect(res).toMatchInlineSnapshot(`
{
"log": "Final Answer: The total cost of purchasing and operating the gearboxes for a week is 9336 yuan.",
"returnValues": {
"output": "The total cost of purchasing and operating the gearboxes for a week is 9336 yuan.",
},
}
`)
}, { timeout: 50000 })
})
我们来看一下 Agent 的输出,以及 Agent 在这个过程,是如何思考和行动的。可以看到,通过 Thought、Action、Observation 的循环,AI Agent 很好地一步步完成最终答案的输出。
Question: 一种减速机的价格是 750 元,一家企业需要购买 12 台。每台减速机运行一小时的电费是 0.5 元,企业每天运行这些减速机 8 小时。请计算企业购买及一周运行这些减速机的总花费
Thought: I need to calculate the total cost of purchasing and operating the gearboxes for a week.
Action: Multiplication Tool
Action Input: [750, 12]
Observation: 9000
Thought: Now I need to calculate the cost of operating the gearboxes for a day.
Action: Multiplication Tool
Action Input: [0.5, 8, 12]
Observation: 48
Thought: Now I need to calculate the cost of operating the gearboxes for a week.
Action: Multiplication Tool
Action Input: [48, 7]
Observation: 336
Thought: Now I need to calculate the total cost of purchasing and operating the gearboxes for a week.
Action: Addition Tool
Action Input: [9000, 336]
Observation: 9336
在 AI Agent 的多种实现模式中,ReAct 模式是最早出现、也是目前使用最广泛的模式。ReAct 的核心思想就是模拟人思考和行动的过程,通过 Thought、Action、Observation 的循环,一步步解决目标问题。
尽管 ReAct 框架提出了一种非常好的思路,让现有的应用得到一次智能化的进化机会,但它也存在很多的不足:
max_iterations 限制,并监控 Token 使用情况。针对上述局限性,常见的优化策略包括:
现在有很多场景已经有了非常成熟的 ReAct Agent 应用,比如智能客服、知识助手、个性化营销、智能销售助理等等。通过深入理解其底层实现,开发者可以更好地驾驭 AI 技术,构建高效可靠的智能系统。
ReAct 模式通过 Thought、Action、Observation 的循环,实现了推理与行动的协同。本文详细介绍了其概念、实现步骤、代码结构以及潜在的限制。在实际开发中,建议结合具体业务场景,合理配置 Prompt 和工具,并关注性能与成本的平衡。