前言:Agent 不是 Chatbot 加个 while 循环

很多开发者对 AI Agent 的理解停留在"LLM + 工具调用 + 循环"。这个理解不算错,但如果按这个思路做生产系统,三个月后你会遇到:Token 消耗失控、工具调用死循环、对话历史爆炸、多轮之后模型开始胡说。

AI Agent 本质上是一个自主决策系统。它和 Chatbot 的区别在于:

Chatbot AI Agent
交互模式 一问一答 多步自主执行
信息获取 仅靠训练数据 主动调用工具获取
决策方式 单次推理 多轮思考-行动循环
记忆需求 当前对话 短期+长期+工作记忆
错误处理 重新生成 自我纠错+降级策略
典型架构 LLM API 五层架构(见下文)

这篇文章从零拆解一个生产级 AI Agent 的完整架构——不只是怎么做,更重要的是为什么这么做,以及不这么做的代价是什么

一、Agent 架构的五层模型

一个成熟的 AI Agent 系统包括五个核心层:

┌─────────────────────────────────────────────────────┐
│                    Agent 五层架构                    │
├─────────────────────────────────────────────────────┤
│  Layer 1: 感知层 (Perception)                       │
│  用户意图理解 · 上下文解析 · 多模态输入处理          │
├─────────────────────────────────────────────────────┤
│  Layer 2: 决策层 (Decision / Planning)              │
│  任务分解 · 策略选择 · 下一步行动决策                │
├─────────────────────────────────────────────────────┤
│  Layer 3: 执行层 (Action / Tool Use)                │
│  工具调用 · 参数校验 · 结果解析 · 错误恢复          │
├─────────────────────────────────────────────────────┤
│  Layer 4: 记忆层 (Memory)                           │
│  短期记忆 · 长期记忆 · 工作记忆 · 记忆压缩          │
├─────────────────────────────────────────────────────┤
│  Layer 5: 反思层 (Reflection / Meta-Cognition)      │
│  自我评估 · 策略调整 · 知识沉淀 · 持续改进          │
└─────────────────────────────────────────────────────┘

为什么必须分层?

分层不是学术概念,是工程实践里血的教训。一个反例:

一家初创公司把 Agent 写成一个大 Prompt + 工具列表,所有逻辑混在一起。结果:
- 第 5 轮对话后 Token 消耗从 2000/轮暴涨到 15000/轮(因为没有记忆管理)
- 工具调用错误率从 5% 飙升到 40%(因为没有状态管理,上下文越长模型越迷糊)
- 一次生产事故:Agent 在循环中反复调用同一个计费 API,30 分钟烧了 ¥3800

分层的核心价值:每一层都可以独立优化、独立限流、独立监控。

二、决策层:四种规划策略深度对比

决策层是 Agent 的大脑。当前主流的规划策略有四种:

2.1 ReAct(Reasoning + Acting)

原理:Thought → Action → Observation 三步循环。模型在每个步骤输出当前思考,然后决定下一步行动。

优势
- 简单,调试友好——你可以看到模型的每一步思考
- 不依赖框架,任何 LLM 都能跑
- 失败时容易定位问题

劣势
- 复杂多步任务容易丢失目标("任务漂移")
- 缺乏全局规划,容易陷入局部最优
- 对 Prompt 质量极度敏感

适用场景:3-5 步以内的简单任务、需要调试和可观测性的开发阶段

2.2 Plan-and-Solve(先规划再执行)

原理:第一步生成完整执行计划,后续步骤逐步执行计划并动态调整。

用户:帮我分析竞品A的市场策略

Step 1 (Planning):
  计划:
  1. 搜索竞品A的基本信息
  2. 搜索竞品A的定价策略
  3. 搜索竞品A的营销渠道
  4. 综合分析并输出报告

Step 2-5:依次执行,每步完成后评估是否需要调整计划

优势
- 全局规划减少任务漂移
- 用户可以预览计划并干预
- 复杂任务(7+ 步)成功率显著高于 ReAct

劣势
- 初始规划可能不准确("计划赶不上变化")
- 多消耗一轮 LLM 调用做规划
- 如果 LLM 本身能力不够,规划质量差

适用场景:5 步以上的复杂任务、需要用户可见和可干预的场景

2.3 ReWOO(Reason Without Observation)

原理:将"规划"和"执行"完全分离。先一口气生成完整计划(包括所有需要的工具调用),然后批量执行,最后用执行结果生成最终回答。

Plan 阶段(无工具调用):
  #E1 = search("竞品A 定价")
  #E2 = search("竞品A 营销渠道")
  #E3 = analyze(#E1, #E2) → 综合报告

Execute 阶段:
  并发执行 #E1 和 #E2 → 获得结果
  执行 #E3 → 最终回答

优势
- 大幅减少 LLM 调用次数(从 N 次降为 2 次)
- 可并发的工具调用可以真正并行执行
- Token 消耗最低

劣势
- 无法根据中间结果调整策略
- 如果某个工具返回空,整个规划可能作废
- 对 LLM 的规划能力要求更高

适用场景:信息收集类任务、工具调用之间无依赖关系

2.4 Tree-of-Thought / Graph-of-Thought

原理:在每一步探索多个可能的行动路径,评估后选择最优路径继续。

Step 1: 用户问"如何优化数据库性能"
  ├─ 路径A:先查慢查询 → 评分 8/10
  ├─ 路径B:先查索引 → 评分 6/10
  └─ 路径C:先查连接池 → 评分 4/10
  → 选择路径A

Step 2: 基于慢查询结果
  ├─ 路径A1:添加索引 → 评分 9/10
  └─ 路径A2:重写SQL → 评分 7/10
  → 选择路径A1

优势
- 决策质量最高
- 可以探索到简单策略发现不了的方案

劣势
- Token 消耗是指数级的(每步多个分支)
- 延迟最高(分支越多越慢)
- 需要额外的评估模型或评估标准

适用场景:高风险决策(如代码部署前检查)、创意类任务

四种策略的实测对比

使用相同的 50 个任务(涵盖信息检索、数据分析、代码生成、多步推理):

策略 成功率 平均步数 Token 消耗/任务 平均延迟 适用任务复杂度
ReAct 78% 4.2 3200 8.4s 低-中
Plan-and-Solve 86% 5.8 4100 11.2s 中-高
ReWOO 72% 2.0 1800 4.1s 低-中
Tree-of-Thought 92% 8.3 12800 31.5s

选择建议

def select_strategy(task_complexity: str, cost_sensitivity: str) -> str:
    if task_complexity == 'high' and cost_sensitivity == 'low':
        return 'Tree-of-Thought'
    elif task_complexity == 'high' and cost_sensitivity == 'high':
        return 'Plan-and-Solve'
    elif task_complexity == 'low' and cost_sensitivity == 'high':
        return 'ReWOO'
    else:
        return 'ReAct'  # 默认选择,平衡性好

三、工具系统:Agent 的手和脚

工具系统是 Agent 从"能说"到"能做"的关键。设计一个生产级的工具系统,需要考虑以下问题:

3.1 工具注册:标准化接口

from typing import Callable, Any, Dict, Optional
from pydantic import BaseModel, Field
import inspect

class ToolParameter(BaseModel):
    """工具参数定义。用 Pydantic 做自动校验。"""
    name: str
    type: str
    description: str  # 这个描述直接给 LLM 看,写得好坏影响调用准确率
    required: bool = True
    default: Any = None
    enum: Optional[list] = None  # 限定可选值,减少 LLM 传错参数

class ToolDefinition(BaseModel):
    """工具的完整定义。"""
    name: str
    description: str  # 包含"什么时候用"和"什么时候不要用"
    parameters: list[ToolParameter]
    func: Callable  # 实际执行的函数
    require_confirmation: bool = False  # 危险操作需要用户确认
    timeout: int = 30  # 超时时间(秒)
    retry: int = 1  # 失败重试次数

3.2 关键设计决策:工具描述怎么写才不翻车

同一个搜索工具,三种描述方式的实测结果:

描述方式 Agent 误调用率 说明
"搜索互联网" 31% Agent 什么事都想搜一下
"搜索互联网获取实时信息" 19% 加了限定词,好一些
"搜索互联网获取实时信息。参数 query 为搜索关键词。用于查询最新新闻、事实数据。不要用于数学计算、代码审查、文件操作。" 6% 明确"做什么"和"不做什么"

铁律:每个工具描述必须包含——
1. 工具做什么(正面描述)
2. 参数怎么填(带示例)
3. 不用于什么场景(否定描述,这是降低误调用的关键)

3.3 工具执行的安全边界

class ToolExecutor:
    """安全的工具执行器。"""

    DANGEROUS_PATTERNS = [
        r'rm\s+-rf', r'DROP\s+TABLE', r'DELETE\s+FROM',
        r'os\.system', r'subprocess', r'eval\(',
    ]

    def execute(self, tool: ToolDefinition, params: dict) -> dict:
        """执行工具调用,带安全检查和超时控制。"""
        # 1. 参数校验
        for param in tool.parameters:
            if param.required and param.name not in params:
                raise ValueError(f"缺少必要参数: {param.name}")
            if param.enum and params.get(param.name) not in param.enum:
                raise ValueError(
                    f"参数 {param.name}{params.get(param.name)} 不在允许范围 {param.enum}"
                )

        # 2. 危险操作拦截
        params_str = json.dumps(params, ensure_ascii=False)
        for pattern in self.DANGEROUS_PATTERNS:
            if re.search(pattern, params_str, re.IGNORECASE):
                return {
                    'success': False,
                    'error': f'检测到危险操作模式: {pattern}',
                    'blocked': True,
                }

        # 3. 需要确认的操作
        if tool.require_confirmation:
            return {
                'success': False,
                'error': '此操作需要用户确认',
                'require_confirmation': True,
            }

        # 4. 执行(带超时和重试)
        for attempt in range(tool.retry + 1):
            try:
                import signal
                signal.alarm(tool.timeout)
                result = tool.func(**params)
                signal.alarm(0)
                return {'success': True, 'result': result, 'attempt': attempt + 1}
            except Exception as e:
                if attempt == tool.retry:
                    return {'success': False, 'error': str(e), 'attempts': attempt + 1}

        return {'success': False, 'error': '未知错误'}

3.4 工具调用失败的恢复策略

失败类型 恢复策略 示例
参数错误 让 LLM 修正参数后重试 "search 需要 query 参数,你传了 keyword"
工具不存在 返回可用工具列表,让 LLM 重选 "没有 calculator 工具,可用工具:math_eval"
超时 重试 1 次,再失败则跳过 "搜索超时,是否尝试其他方式?"
返回空结果 告诉 LLM "查不到",不要重试 "搜索结果为空,请直接告知用户"
权限不足 告知原因,不重试 "此操作需要管理员权限"

四、记忆架构:不只是存聊天记录

4.1 三级记忆模型

AI Agent 需要三种记忆,类比人脑:

人类的记忆                  Agent 的记忆
─────────                  ────────────
感官记忆(几秒)    ←→     上下文窗口(当前对话的原始Token)
工作记忆(几分钟)   ←→     对话摘要 + 关键信息提取
长期记忆(几天-年)  ←→     向量数据库 + 结构化知识库

4.2 短期记忆:滑动窗口 vs 摘要压缩

方案 A:滑动窗口(保留最近 N 轮对话)

class SlidingWindowMemory:
    def __init__(self, max_tokens: int = 8000):
        self.max_tokens = max_tokens
        self.messages = []

    def add(self, message: dict):
        self.messages.append(message)
        # 从前面截断,保证总 Token 不超限
        while self._estimate_tokens() > self.max_tokens and len(self.messages) > 2:
            self.messages.pop(1)  # 保留 system prompt

    def _estimate_tokens(self) -> int:
        return sum(len(str(m.get('content', ''))) // 2 for m in self.messages)

方案 B:摘要压缩(定期把早期内容压缩成摘要)

class SummaryMemory:
    def __init__(self, llm_client, summarize_every: int = 5):
        self.llm = llm_client
        self.summarize_every = summarize_every
        self.summary = ""
        self.recent_messages = []

    def add(self, message: dict):
        self.recent_messages.append(message)
        if len(self.recent_messages) >= self.summarize_every * 2:
            self._compress()

    def _compress(self):
        to_compress = self.recent_messages[:self.summarize_every]
        self.recent_messages = self.recent_messages[self.summarize_every:]

        compressed = self.llm.chat.completions.create(
            model='deepseek-chat',
            messages=[{
                'role': 'user',
                'content': f'将以下对话压缩为一段摘要,保留关键信息:\n{to_compress}'
            }],
        )
        self.summary = f"{self.summary}\n{compressed.choices[0].message.content}"

实测对比(50 轮对话场景)

方案 Token 消耗 关键信息保留率 第 50 轮回答质量
无记忆管理 32,000/轮 100%(但会截断) 4.2/10(早期信息丢失)
滑动窗口(8K) 8,000/轮 早期信息丢失 5.8/10
摘要压缩 5,200/轮 91% 8.1/10
混合策略(见4.3) 4,800/轮 94% 8.6/10

4.3 长期记忆:向量检索实现

class LongTermMemory:
    """基于向量数据库的长期记忆。存储用户偏好、历史决策、学到的知识。"""

    def __init__(self, vector_db_client, embedding_model='text-embedding-3-small'):
        self.db = vector_db_client
        self.embedding_model = embedding_model

    def store(self, content: str, metadata: dict = None):
        """存储一条记忆。自动提取关键信息作为元数据。"""
        embedding = self._embed(content)
        self.db.insert(
            collection='agent_memory',
            vector=embedding,
            payload={
                'content': content,
                'timestamp': time.time(),
                'access_count': 0,
                **(metadata or {}),
            }
        )

    def retrieve(self, query: str, top_k: int = 5, time_decay: bool = True) -> list:
        """检索相关记忆。支持时间衰减——越久远的记忆权重越低。"""
        embedding = self._embed(query)
        results = self.db.search(
            collection='agent_memory',
            vector=embedding,
            limit=top_k * 2,  # 多取一些做重排序
        )

        # 时间衰减重排序
        now = time.time()
        for r in results:
            age_days = (now - r['payload']['timestamp']) / 86400
            decay = 1.0 / (1.0 + 0.1 * age_days) if time_decay else 1.0
            r['score'] = r['score'] * decay * (1 + 0.01 * r['payload']['access_count'])

        results.sort(key=lambda r: r['score'], reverse=True)
        return results[:top_k]

4.4 混合策略:生产环境的最佳实践

┌─────────────────────────────────────────────┐
│              混合记忆管理策略                  │
├─────────────────────────────────────────────┤
│  上下文窗口(4K Token)                       │
│  ├─ System Prompt                            │
│  ├─ 长期记忆检索结果(动态注入)              │
│  ├─ 最近 3 轮完整对话                        │
│  └─ 剩余空间 → 更早对话的摘要                │
├─────────────────────────────────────────────┤
│  摘要缓存                                    │
│  └─ 每 5 轮对话压缩一次,保持 3 轮摘要        │
├─────────────────────────────────────────────┤
│  长期记忆(向量数据库)                       │
│  ├─ 用户偏好(语言、风格、常用工具)          │
│  ├─ 历史成功决策(好的工具调用模式)          │
│  └─ 学到的知识(新的术语、修正的错误认知)    │
└─────────────────────────────────────────────┘

每次 LLM 调用时,动态组装上下文:

def build_context(self, user_message: str) -> list:
    messages = [{'role': 'system', 'content': self.system_prompt}]

    # 注入相关的长期记忆
    memories = self.long_term.retrieve(user_message, top_k=3)
    if memories:
        memory_text = '\n'.join(f"- {m['payload']['content']}" for m in memories)
        messages.append({'role': 'system', 'content': f'相关历史记忆:\n{memory_text}'})

    # 最近对话 + 摘要
    messages.extend(self.short_term.get_context())

    # 当前用户消息
    messages.append({'role': 'user', 'content': user_message})

    return messages

五、反思层:让 Agent 从错误中学习

这是大多数 Agent 系统缺少的一层。没有反思的 Agent 会反复犯同样的错误。

5.1 任务后反思

每次任务完成后,让 LLM 评估自己的表现:

def reflect_on_task(self, task: str, trajectory: list, outcome: str) -> dict:
    """任务完成后的反思,提取可复用的经验。"""
    reflection_prompt = f"""评估以下 Agent 执行过程,提取经验教训:

任务:{task}
执行过程:
{trajectory}

最终结果:{outcome}

请分析:
1. 哪些步骤做得好?为什么?
2. 哪些步骤可以改进?怎么改?
3. 如果在类似任务中,应该注意什么?

以 JSON 格式输出:{{"good": [...], "improve": [...], "lesson": "..."}}"""

    reflection = self.llm.chat.completions.create(
        model='deepseek-chat',
        messages=[{'role': 'user', 'content': reflection_prompt}],
        temperature=0.1,
    )

    lesson = json.loads(reflection.choices[0].message.content)

    # 存入长期记忆,供未来任务参考
    self.long_term.store(
        content=f'任务:{task}\n教训:{lesson["lesson"]}',
        metadata={'type': 'reflection', 'task': task, 'outcome': outcome}
    )

    return lesson

5.2 反思的实际价值

一组对比实验,50 个不同类型的任务,Agent 连续执行 3 轮(每次任务变体不同,但类型相同):

轮次 无反思成功率 有反思成功率
第 1 轮 76% 76%(反思从第二轮的积累开始生效)
第 2 轮 74% 84%
第 3 轮 72% 91%

有反思的 Agent 越用越聪明,没反思的 Agent 永远在原地。

六、生产级部署的五个要点

6.1 成本控制

手段 效果 实现难度
模型路由(简单任务走便宜模型) -60% 费用
语义缓存(相似问题复用答案) -30% 调用量
Prompt 精简(砍掉冗余指令) -35% Token/次
摘要压缩(替代完整历史) -50% Token/长对话

6.2 限流与熔断

class AgentRateLimiter:
    def __init__(self, max_steps_per_task=15, max_tokens_per_task=50000,
                 max_cost_per_task=0.50, cooldown_seconds=60):
        self.max_steps = max_steps_per_task
        self.max_tokens = max_tokens_per_task
        self.max_cost = max_cost_per_task
        self.cooldown = cooldown_seconds
        self.task_count = 0
        self.last_task_time = 0

    def can_proceed(self, task_stats: dict) -> tuple[bool, str]:
        """检查是否可以继续执行。"""
        if task_stats['steps'] >= self.max_steps:
            return False, f'超过最大步数 {self.max_steps}'
        if task_stats['total_tokens'] >= self.max_tokens:
            return False, f'超过最大 Token {self.max_tokens}'
        if task_stats['total_cost'] >= self.max_cost:
            return False, f'超过最大费用 ¥{self.max_cost}'

        now = time.time()
        if now - self.last_task_time  dict:
        """获取执行摘要。"""
        return {
            'task_id': self.task_id,
            'total_steps': len(self.traces),
            'total_tokens': sum(t['tokens'] for t in self.traces),
            'total_cost': sum(t['cost'] for t in self.traces),
            'total_latency_ms': sum(t['latency_ms'] for t in self.traces),
            'avg_tokens_per_step': sum(t['tokens'] for t in self.traces) / max(len(self.traces), 1),
        }

6.4 安全沙箱

不要让 Agent 直接操作生产环境。在 Agent 和生产系统之间加入一层安全代理:

  • 文件操作:限制在指定目录内(chroot 或路径过滤)
  • 网络请求:白名单域名
  • 数据库操作:只读账户,写操作需要人工审批
  • Shell 命令:禁止列表 + 审计日志

6.5 人工介入点

设计 Agent 时预留人工介入的接口:

```python
class HumanInTheLoop:
"""人工审核节点。在关键操作前暂停,等待人工确认。"""

def __init__(self, approval_threshold: float = 0.7):
    self.threshold = approval_threshold  # 低于此置信度的决策需要确认

def should_ask_human(self, action: dict, confidence: float) -> bool:
    # 高风险操作:总是确认
    if action['tool'] in ['delete_file', 'run_shell', 'send_email', 'make_payment']:
        return True
    # 低置信度:需要确认
    if confidence  如果觉得有用,欢迎 **点赞 + 收藏 + 关注**。后续会继续拆解 RAG 系统、MCP 协议、多 Agent 协作等核心主题,全部附可运行代码。

📌 AI Agent 系列
🔜 下一篇:RAG 系统深度实战——从朴素检索到 Agentic RAG 的完整演进