引言

随着大语言模型(LLM)能力的爆发,AI Agent 成为当前最热门的技术方向之一。从 AutoGPT 到各类智能助手,Agent 赋予了 LLM 像人一样使用工具、规划任务、记忆上下文的能力,使其从“对话机器”进化为“行动者”。

但如何设计一个稳定、可扩展的 Agent?它背后的核心模式是什么?本文将从设计模式的角度出发,深入浅出地解析 AI Agent 的架构思想,并以 ReAct(Reasoning + Acting) 模式为主线,通过一个完整的 Python 实战项目,带你从零构建一个可运行的智能代理。

核心概念:AI Agent 的设计模式

一个典型的 AI Agent 由四个关键模块组成:

  • 感知(Perception):接收用户输入与环境反馈。
  • 大脑(Brain):基于 LLM 进行推理、规划和决策。
  • 行动(Action):执行具体操作,如调用 API、查询数据库、控制设备。
  • 记忆(Memory):存储短期/长期信息,维持上下文连贯性。

这四个模块形成一个循环:感知 → 推理 → 行动 → 观察结果 → 更新记忆 → 再次推理,直到完成任务。这一循环正是 Agent 设计的核心骨架。

主流的 Agent 设计模式

在工程实践中,演化出了几种经典的设计模式:

  1. ReAct 模式
    由 Google 提出,将 推理(Reasoning)行动(Acting) 交织在一起。模型输出“思考过程”和“行动指令”,然后执行行动并将结果反馈给下一步推理。这种模式极大提高了可解释性和任务完成率。

  2. Plan-and-Execute 模式
    先由 LLM 生成一个完整的计划步骤,再逐步执行。更适合复杂、长链条的任务,但灵活性稍弱,计划可能出现偏差。

  3. Reflection 模式
    允许 Agent 在执行过程中自我评估、反思错误并纠正,提升决策质量。

  4. Multi-Agent 协作模式
    多个 Agent 分工合作,通过消息传递协同完成复杂目标,如 ChatDev 中的模拟软件开发团队。

本文重点讲解最通用、最易落地的 ReAct 模式,并给出一个可直接运行的实现。

ReAct 设计思想

ReAct 的核心在于提示词(Prompt)设计。我们告诉 LLM:

  • 你可以使用以下工具(Tool),每个工具有名称、描述和参数。
  • 你需要按固定格式输出:Thought:(你的推理)、Action:(工具名)、Action Input:(输入参数)。
  • 当可以给出最终答案时,输出 Final Answer:

系统会不断解析模型输出,调用真实工具,将结果作为 Observation 反馈给模型,循环直到产生最终答案。这种显式的思维链+行动交织,让模型学会像人类一样“边想边做”。

实战示例:构建一个 ReAct Agent

下面我们用 Python 实现一个能搜索互联网执行数学计算的 Agent。为了简化,我们使用一个模拟的搜索函数和真实的 Python eval() 做计算(生产环境需安全处理)。

完整可运行代码

import openai
import json
import re
import os

# 配置 OpenAI API(请替换为你的 key,或设置环境变量)
openai.api_key = os.getenv("OPENAI_API_KEY", "your-api-key-here")

# ---------- 工具定义 ----------
def search(query: str) -> str:
    """模拟搜索引擎,实际可接 SerpAPI 或 Bing API"""
    # 返回一些虚构的结果,仅作演示
    fake_db = {
        "2024年诺贝尔物理学奖": "2024年诺贝尔物理学奖授予了研究人工神经网络的科学家。",
        "Python最新版本": "截至2024年10月,Python最新稳定版为3.12。"
    }
    return fake_db.get(query, f"未找到与'{query}'相关的结果。")

def calculator(expression: str) -> str:
    """安全的计算器工具"""
    try:
        # 仅允许数字和基本运算符,防止注入
        if not re.match(r'^[\d\.\+\-\*\/\(\) ]+$', expression):
            return "错误:表达式包含非法字符。"
        result = eval(expression)
        return str(result)
    except Exception as e:
        return f"计算错误:{str(e)}"

# 工具列表(名称、描述、参数)
TOOLS = [
    {
        "name": "search",
        "description": "当需要查找实时信息或常识时使用。输入一个查询字符串。",
        "parameters": {"query": "string"}
    },
    {
        "name": "calculator",
        "description": "执行数学计算。输入一个合法的数学表达式。",
        "parameters": {"expression": "string"}
    }
]

# ---------- 构造 Prompt ----------
def build_prompt(user_query: str, tools_desc: str) -> str:
    return f"""你是一个智能助手,可以利用工具来回答问题。请严格按照以下格式回复:

可用工具:
{tools_desc}

格式要求:
- 每次只输出一个 Thought/Action/Action Input 组合,或最终答案。
- 如果你想使用工具,必须按以下格式输出:
  Thought: [你的思考]
  Action: [工具名]
  Action Input: [输入参数]
- 工具执行后,你会收到一个 Observation,请根据它继续思考。
- 当你已经得到足够信息可以回答用户时,输出:
  Thought: 我已经知道答案
  Final Answer: [最终回答]
- 禁止输出多余内容。

开始!
用户问题:{user_query}
"""

# ---------- 工具描述字符串 ----------
def tools_to_string(tools: list) -> str:
    lines = []
    for t in tools:
        params = ', '.join(f"{k}: {v}" for k, v in t['parameters'].items())
        lines.append(f"- {t['name']}: {t['description']} 参数: ({params})")
    return '\n'.join(lines)

# ---------- 调用 LLM ----------
def call_llm(messages: list) -> str:
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",  # 或 gpt-4
        messages=messages,
        temperature=0,
        stop=["Observation:"]
    )
    return response.choices[0].message.content.strip()

# ---------- 解析 Action ----------
def parse_action(llm_text: str):
    """从模型输出中提取 Action 和 Action Input"""
    thought_match = re.search(r"Thought:\s*(.*)", llm_text, re.IGNORECASE)
    action_match = re.search(r"Action:\s*(.*)", llm_text, re.IGNORECASE)
    input_match = re.search(r"Action Input:\s*(.*)", llm_text, re.IGNORECASE)
    final_match = re.search(r"Final Answer:\s*(.*)", llm_text, re.IGNORECASE)

    if final_match:
        return {"type": "final", "answer": final_match.group(1).strip()}
    elif action_match and input_match:
        return {
            "type": "action",
            "thought": thought_match.group(1).strip() if thought_match else "",
            "action": action_match.group(1).strip(),
            "action_input": input_match.group(1).strip()
        }
    else:
        return {"type": "unknown", "text": llm_text}

# ---------- 执行工具 ----------
def execute_tool(action_name: str, action_input: str) -> str:
    if action_name == "search":
        return search(action_input)
    elif action_name == "calculator":
        return calculator(action_input)
    else:
        return f"错误:未知工具 '{action_name}'"

# ---------- 主循环 ----------
def run_agent(user_query: str, max_steps: int = 5) -> str:
    tools_desc = tools_to_string(TOOLS)
    system_prompt = build_prompt(user_query, tools_desc)
    messages = [{"role": "user", "content": system_prompt}]

    for _ in range(max_steps):
        llm_output = call_llm(messages)
        print(f"🤖 LLM 输出:\n{llm_output}\n{'-'*40}")

        parsed = parse_action(llm_output)

        if parsed["type"] == "final":
            return parsed["answer"]
        elif parsed["type"] == "action":
            action_name = parsed["action"]
            action_input = parsed["action_input"]

            # 执行工具
            observation = execute_tool(action_name, action_input)
            print(f"🔧 工具执行: {action_name}({action_input}) -> {observation}\n")

            # 将本轮输出和观察结果加入对话
            messages.append({"role": "assistant", "content": llm_output})
            messages.append({"role": "user", "content": f"Observation: {observation}"})
        else:
            # 解析失败,原样返回错误提示
            messages.append({"role": "assistant", "content": llm_output})
            messages.append({"role": "user", "content": "请按指定格式回复,必须包含 Thought/Action/Action Input 或 Final Answer。"})

    return "抱歉,我无法在限定步骤内完成任务。"

# ---------- 测试运行 ----------
if __name__ == "__main__":
    query = "2024年诺贝尔物理学奖颁给了谁?"
    result = run_agent(query)
    print("✅ 最终答案:", result)

运行说明

  1. 安装 openai 库:pip install openai
  2. 设置环境变量 OPENAI_API_KEY,或直接替换代码中的 key
  3. 运行脚本,即可看到 Agent 一步步思考、调用搜索工具、返回结果的过程。

输出示例:

🤖 LLM 输出:
Thought: 我需要搜索2024年诺贝尔物理学奖的获奖者
Action: search
Action Input: 2024年诺贝尔物理学奖
----------------------------------------
🔧 工具执行: search(2024年诺贝尔物理学奖) -> 2024年诺贝尔物理学奖授予了研究人工神经网络的科学家。

🤖 LLM 输出:
Thought: 我已经知道答案
Final Answer: 2024年诺贝尔物理学奖授予了研究人工神经网络的科学家。
----------------------------------------
✅ 最终答案: 2024年诺贝尔物理学奖授予了研究人工神经网络的科学家。

常见问题与注意事项

1. 模型输出格式不稳定

ReAct 严重依赖模型响应格式。在实践中,可能会遇到不输出 Action: 或格式混乱的情况。可以通过以下方式提升稳定性:
- 使用能力更强的模型(如 GPT-4)
- 在 prompt 中加入 few-shot 示例
- 利用 OpenAI 的函数调用(Function Calling)特性,绕过文本解析,直接获得结构化输出

2. 工具安全与错误处理

工具可能抛出异常或返回超长内容。务必:
- 捕获所有异常,返回友好的错误信息给模型
- 限制工具返回的长度,避免超出 token 限制
- 绝对不要在生产环境中直接使用 eval(),示例中我们用正则限制了输入字符,但仍建议用更安全的表达式解析库(如 numexpr

3. Token 消耗控制

每一轮对话都会将完整历史发送给 LLM,步骤一多 token 会迅速增长。优化手段:
- 对 Observation 进行摘要压缩
- 采用滑动窗口只保留最近 N 轮
- 使用长期记忆模块(如向量数据库)存储关键信息,需要时检索

4. 死循环问题

Agent 可能反复调用同一个工具却得不到有用信息。可设置最大步数上限,并在连续多次相同 Action 时强制终止或引导其改变策略。

总结

本文从 AI Agent 的核心设计模式出发,重点剖析了 ReAct 模式,并给出了一个完整的 Python 实现。通过这个最小化代理,你可以清晰地看到 LLM 如何将推理与行动结合,从而蜕变为真正能“做事”的智能体。

未来,随着模型能力的提升和工程框架的成熟,Agent 设计模式会进一步演化,但 感知-推理-行动-记忆 这一基本循环将始终是构建智能代理的基石。掌握这些模式思想,你将能更从容地设计出自己的 AutoGPT、数据分析 Agent,甚至自动驾驶决策系统。

希望本篇内容能帮助你打开 AI Agent 开发的大门。如有疑问,欢迎在评论区交流探讨!

Logo

这里是“一人公司”的成长家园。我们提供从产品曝光、技术变现到法律财税的全栈内容,并连接云服务、办公空间等稀缺资源,助你专注创造,无忧运营。

更多推荐