AI Agent 系统设计:从单轮对话到多步推理的架构演进

cover

一、大模型的"健忘"与"短视":Agent 系统的工程痛点

大语言模型在单轮问答中展现了令人印象深刻的能力,但一旦面对需要多步推理、工具调用、状态维护的复杂任务,其局限性便暴露无遗。模型没有持久记忆,每次对话都是无状态的;模型无法主动获取实时信息,知识截止于训练数据;模型缺乏行动能力,只能生成文本而不能执行操作。

这些局限催生了 AI Agent 系统的诞生。Agent 的核心思想是:将大模型从被动的文本生成器,升级为能够感知环境、制定计划、调用工具、反思结果的自主智能体。然而,从概念到工程落地的距离远比想象中遥远。

Agent 系统面临的核心工程痛点包括:第一,规划失败——多步任务中,模型生成的计划可能包含不可执行的步骤或逻辑矛盾;第二,工具调用错误——模型可能生成格式错误的 API 调用,或选择了不恰当的工具;第三,状态丢失——长链路推理中,早期关键信息被后续对话淹没,导致后续步骤偏离目标;第四,死循环——Agent 在错误路径上反复重试,消耗大量 Token 却无法收敛到正确结果。

二、ReAct 范式与记忆架构:Agent 系统的底层运行机制

现代 Agent 系统的运行机制,建立在 ReAct(Reasoning + Acting)范式之上。其核心循环是:观察环境状态 → 推理下一步行动 → 执行行动 → 观察执行结果 → 继续推理,直到任务完成或达到最大步数。

sequenceDiagram
    participant U as 用户
    participant A as Agent 核心
    participant M as 记忆模块
    participant P as 规划器
    participant T as 工具集

    U->>A: 提交任务指令
    A->>M: 检索相关历史记忆
    M-->>A: 返回上下文记忆
    A->>P: 生成任务分解计划

    loop ReAct 循环
        P-->>A: 返回下一步行动
        A->>A: 推理(Thought):分析当前状态
        alt 需要调用工具
            A->>T: 执行工具调用(Action)
            T-->>A: 返回工具结果(Observation)
        else 直接生成回答
            A->>A: 生成最终回答
        end
        A->>M: 存储本轮交互记忆
        A->>P: 更新计划状态
    end

    A->>M: 存储完整任务轨迹
    A-->>U: 返回最终结果

记忆架构是 Agent 系统的关键基础设施。根据信息的生命周期与访问模式,记忆被划分为三个层次:

工作记忆(Working Memory):即当前对话上下文窗口中的信息,包括用户指令、历史推理步骤、工具返回结果。受限于上下文窗口长度,工作记忆是最稀缺的资源,需要通过摘要与淘汰策略管理。

短期记忆(Short-term Memory):跨对话轮次的任务状态,如当前执行到计划中的第几步、已获取的中间结果。通常以结构化 JSON 存储在会话状态中,供 Agent 在每轮推理时读取。

长期记忆(Long-term Memory):跨会话的知识与经验,如用户偏好、历史任务的成功模式。通常通过向量数据库实现语义检索,将相关记忆注入工作记忆中。

规划器(Planner)负责将复杂任务分解为可执行的子任务序列。常见的规划策略包括:一次性规划(生成完整计划后逐步执行)、自适应规划(每步执行后根据结果重新规划)、层次化规划(将任务分解为子目标树,逐层展开)。

三、生产级 Agent 框架:带反思与工具调用的完整实现

以下代码实现了一个基于 ReAct 范式的 Agent 框架,包含规划、工具调用、反思与记忆管理:

import json
import re
from typing import Callable, Dict, List, Optional
from dataclasses import dataclass, field

@dataclass
class Tool:
    """工具定义:名称、描述、参数 Schema 与执行函数"""
    name: str
    description: str
    parameters: Dict
    execute: Callable


@dataclass
class AgentState:
    """Agent 运行状态:维护推理链与任务进度"""
    task: str
    plan: List[str] = field(default_factory=list)
    current_step: int = 0
    thought_history: List[str] = field(default_factory=list)
    action_history: List[str] = field(default_factory=list)
    observation_history: List[str] = field(default_factory=list)
    max_steps: int = 10
    is_completed: bool = False


class ReActAgent:
    """基于 ReAct 范式的 Agent:推理-行动-观察循环"""

    def __init__(self, llm_client, tools: Dict[str, Tool], memory_store=None):
        self.llm = llm_client
        self.tools = tools
        self.memory = memory_store

    def _build_prompt(self, state: AgentState) -> str:
        """构建包含历史推理链的提示词"""
        prompt = f"任务: {state.task}\n\n"

        # 注入相关长期记忆
        if self.memory:
            relevant = self.memory.retrieve(state.task, top_k=3)
            if relevant:
                prompt += "相关历史经验:\n"
                for mem in relevant:
                    prompt += f"- {mem}\n"
                prompt += "\n"

        # 注入可用工具描述
        prompt += "可用工具:\n"
        for tool in self.tools.values():
            prompt += f"- {tool.name}: {tool.description}\n"
            prompt += f"  参数: {json.dumps(tool.parameters, ensure_ascii=False)}\n"
        prompt += "\n"

        # 注入历史推理链
        for i, (thought, action, obs) in enumerate(zip(
            state.thought_history, state.action_history, state.observation_history
        )):
            prompt += f"第{i+1}轮:\n"
            prompt += f"思考: {thought}\n"
            prompt += f"行动: {action}\n"
            prompt += f"观察: {obs}\n\n"

        prompt += "请输出下一步的思考和行动。格式:\n"
        prompt += "思考: <你的推理过程>\n"
        prompt += "行动: <工具名称>(<参数JSON>) 或 回答(<最终答案>)\n"
        return prompt

    def _parse_action(self, action_str: str) -> tuple:
        """解析行动字符串为工具名与参数"""
        # 匹配 "工具名(参数)" 或 "回答(答案)"
        match = re.match(r'(\w+)\((.*)\)', action_str.strip(), re.DOTALL)
        if not match:
            return "answer", action_str

        tool_name = match.group(1)
        params_str = match.group(2).strip()

        if tool_name == "回答":
            return "answer", params_str

        try:
            params = json.loads(params_str)
        except json.JSONDecodeError:
            params = {"raw_input": params_str}

        return tool_name, params

    def _reflect(self, state: AgentState) -> Optional[str]:
        """反思机制:检测是否陷入循环或偏离目标"""
        if len(state.observation_history) < 2:
            return None

        # 检测连续相同观察(死循环信号)
        last_two = state.observation_history[-2:]
        if last_two[0] == last_two[1]:
            return "检测到重复结果,需要换一种策略。"

        # 检测步数过多但无进展
        if state.current_step >= state.max_steps * 0.8 and not state.is_completed:
            return f"已执行{state.current_step}步仍未完成,考虑简化方案或直接回答。"

        return None

    def run(self, task: str) -> str:
        """执行完整 Agent 循环"""
        state = AgentState(task=task)

        # 初始规划
        plan_prompt = f"将以下任务分解为具体步骤:\n{task}\n输出JSON数组。"
        plan_response = self.llm.generate(plan_prompt)
        try:
            state.plan = json.loads(plan_response)
        except json.JSONDecodeError:
            state.plan = [task]

        for step in range(state.max_steps):
            state.current_step = step + 1

            # 反思检查
            reflection = self._reflect(state)
            if reflection:
                state.thought_history.append(reflection)

            # 生成推理与行动
            prompt = self._build_prompt(state)
            response = self.llm.generate(prompt)

            # 解析思考与行动
            thought_match = re.search(r'思考:\s*(.+?)(?=\n行动:)', response, re.DOTALL)
            action_match = re.search(r'行动:\s*(.+)', response, re.DOTALL)

            thought = thought_match.group(1).strip() if thought_match else response
            action_str = action_match.group(1).strip() if action_match else ""

            state.thought_history.append(thought)
            state.action_history.append(action_str)

            # 执行行动
            tool_name, params = self._parse_action(action_str)

            if tool_name == "answer":
                state.is_completed = True
                # 存储任务轨迹到长期记忆
                if self.memory:
                    self.memory.store(task, {
                        "plan": state.plan,
                        "success": True,
                        "steps": state.current_step,
                    })
                return params

            if tool_name in self.tools:
                try:
                    result = self.tools[tool_name].execute(**params)
                    observation = str(result)
                except Exception as e:
                    observation = f"工具执行失败: {type(e).__name__}: {e}"
            else:
                observation = f"未知工具: {tool_name},可用工具: {list(self.tools.keys())}"

            state.observation_history.append(observation)

        return "达到最大步数限制,任务未完成。"

关键设计要点:反思机制在每步执行后检测死循环与进度停滞,主动引导 Agent 调整策略;工具调用包含完整的异常处理,将错误信息作为观察反馈给 Agent,使其能够根据错误信息修正行动;记忆模块在任务完成后存储成功轨迹,为后续相似任务提供经验参考。

四、Agent 系统的可靠性瓶颈:架构权衡与工程妥协

Token 消耗与推理深度的矛盾:每轮 ReAct 循环都需要将完整历史注入提示词,随着步数增加,Token 消耗呈线性增长。在 10 步以上的长链路任务中,Token 成本可能超过单次推理的 10 倍。解决方案包括:对历史推理链进行摘要压缩、使用滑动窗口保留最近 N 步、将中间结果结构化存储而非全文保留。

规划质量的不确定性:大模型生成的计划可能包含不可行步骤或逻辑跳跃。一次性规划在简单任务上效率高,但复杂任务中往往需要多次修正。自适应规划更鲁棒,但每步重新规划增加了延迟与 Token 消耗。实践中,推荐"粗粒度一次性规划 + 细粒度逐步执行"的混合策略。

工具调用的可靠性:模型可能生成格式错误的参数、调用不存在的工具、或选择不恰当的工具。严格的参数 Schema 校验与错误反馈机制是必要的,但过多的校验逻辑会增加系统复杂度。在工具数量较多时,工具选择的准确率会显著下降,需要通过工具描述优化与 few-shot 示例提升选择精度。

安全性与可控性:Agent 具备了执行行动的能力,也带来了安全风险。未经验证的工具调用可能产生不可逆的副作用(如删除数据、发送消息)。生产环境中必须实施工具调用的权限控制与审批机制,在自主性与安全性之间取得平衡。

五、总结

AI Agent 系统将大模型从被动生成器升级为自主智能体,ReAct 范式提供了推理-行动-观察的核心循环框架,记忆架构解决了状态持久化问题,反思机制增强了系统的自我纠错能力。然而,Agent 系统的可靠性仍受限于大模型的推理质量、工具调用的准确率与 Token 消耗的工程约束。

落地路线建议:从单工具 Agent 起步,验证 ReAct 循环的稳定性;逐步增加工具数量,同时优化工具描述与选择策略;引入反思与记忆机制提升长链路任务的成功率;在生产环境中实施工具调用的权限控制与审批流程。Agent 系统的构建是渐进式的,每一步都需要充分的测试与监控。

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐