目录

Claude API基础专题(七):Agent架构与智能体设计

Claude API 基础专题(七):Agent 架构与智能体设计

预计阅读时间:45 分钟 | 难度:⭐⭐⭐⭐


目标读者:希望构建复杂AI应用系统的架构师与高级开发者 前置知识:已完成第一篇《API基础》、第二篇《提示词工程》、第三篇《工具调用》、第四篇《RAG系统》、第五篇《MCP协议》、第六篇《Claude Code与Computer Use》 说明:本文是 Claude API 系列的收官篇,梳理 Agent 架构的核心概念与设计模式


学习目标

读完本文后,你应当能够:

  1. 说清 Agent 与单次工具调用的边界,并指出五个必备要素中缺一项会导致什么后果
  2. 写出一个状态外部化、终止条件前置的最小 Agent 主循环
  3. 根据任务特征在星型、链式、网状三种多 Agent 架构中做出取舍,并说明各自的失败模式
  4. 针对上下文溢出、限流、工具失败三类常见错误,给出对应的重试与回退策略
  5. 列出生产环境 Agent 系统上线前必须配置的权限、沙箱与监控项

章节导航

小节主题重要程度
7.1从工具调用到 Agent:跨越的关键一步⭐⭐⭐⭐⭐
7.2单 Agent 系统架构⭐⭐⭐⭐⭐
7.3多 Agent 协作系统⭐⭐⭐⭐⭐
7.4执行模式:并行与链式⭐⭐⭐⭐⭐
7.5状态管理与上下文⭐⭐⭐⭐⭐
7.6错误处理与容错机制⭐⭐⭐⭐⭐
7.7安全与权限管理⭐⭐⭐⭐
7.8生产环境推荐做法⭐⭐⭐⭐⭐

7.1 从工具调用到Agent:跨越的关键一步

为什么需要Agent?

工具调用本身已经能解决不少问题,但遇到多步骤、有分支、需要保留中间状态的任务时就会卡住。先看工具调用的典型工作模式:

# 工具调用的工作模式
result = await client.messages.create(
    model="claude-opus-4-20250514",
    messages=[{
        "role": "user",
        "content": "帮我查一下北京天气"
    }],
    tools=[{"name": "get_weather", ...}]
)

这个模式里,LLM 是被动响应者:用户提问 → LLM 调用工具 → 工具返回结果 → LLM 回答。LLM 不持有状态,不主动决策,只根据当前输入决定调用哪个工具。

现实任务往往比这复杂。下表列出工具调用在几类任务上的局限,以及 Agent 能补上的能力:

任务特征工具调用的局限Agent 能补上的能力
多步骤决策每次决策独立保持目标状态
条件分支无法根据结果跳转动态规划路径
长期任务上下文会丢失持久化状态
多工具协同缺乏编排能力编排执行流程
错误恢复失败即终止重试和回退

Agent的本质定义

Agent 可以拆成五个要素:

Agent = LLM + 状态 + 工具 + 执行循环 + 终止条件

┌─────────────────────────────────────────────────────────────┐
│                        Agent 系统                           │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│   ┌───────────┐                                            │
│   │    LLM    │ ← 大脑:理解、推理、决策                     │
│   └─────┬─────┘                                            │
│         │                                                  │
│   ┌─────▼─────┐                                            │
│   │   状态    │ ← 记忆:目标、上下文、中间结果                 │
│   └─────┬─────┘                                            │
│         │                                                  │
│   ┌─────▼─────┐                                            │
│   │   工具    │ ← 能力:搜索、代码、文件、API                 │
│   └─────┬─────┘                                            │
│         │                                                  │
│   ┌─────▼─────┐                                            │
│   │  执行循环  │ ← 引擎:观察→决策→执行                      │
│   └─────┬─────┘                                            │
│         │                                                  │
│   ┌─────▼─────┐                                            │
│   │  终止条件  │ ← 边界:完成、出错、超时                     │
│   └───────────┘                                            │
│                                                             │
└─────────────────────────────────────────────────────────────┘

缺了任何一个都跑不起来:

  • LLM:没有 LLM 就没有智能决策
  • 状态:没有状态就无法处理多步骤任务
  • 工具:没有工具就无法影响外部世界
  • 执行循环:没有循环就无法持续工作
  • 终止条件:没有终止条件就会无限循环

这五项也是后面几节展开的线索:7.2 讲单 Agent 如何把这五项组装起来,7.3 讲多 Agent 之间如何分工,7.5 讲状态怎么管,7.6 讲出错时怎么办。

Agent vs 传统软件

维度传统软件Agent
决策方式确定性的if-else基于LLM的概率推理
流程定义预先设计运行时动态规划
错误处理显式try-catch自我纠错能力
状态管理显式变量隐式上下文
扩展方式模块化添加工具/提示词
可预测性中(有一定随机性)

7.2 单 Agent 系统架构

最小可运行 Agent

从一个最简单的 Agent 入手,逐步理解其架构:

from anthropic import Anthropic
from dataclasses import dataclass, field
from typing import Optional, Any
import asyncio

@dataclass
class AgentState:
    """
    Agent状态容器

    为什么需要专门的状态类?
    1. 封装所有状态,边界清晰
    2. 便于序列化和持久化
    3. 类型提示让代码更健壮
    """
    goal: str                           # 当前目标
    messages: list[dict] = field(default_factory=list)  # 对话历史
    context: dict[str, Any] = field(default_factory=dict) # 共享上下文
    results: list[dict] = field(default_factory=list)    # 执行结果
    iterations: int = 0                 # 迭代计数器
    max_iterations: int = 10           # 最大迭代次数

class SimpleAgent:
    """
    最小可运行Agent

    设计原则:
    - 简单性:先让它跑起来
    - 可观测性:每个步骤都记录
    - 可停止:明确的终止条件
    """

    def __init__(self, api_key: str, tools: list[dict]):
        self.client = Anthropic(api_key=api_key)
        self.tools = tools

    async def run(self, goal: str) -> dict[str, Any]:
        """
        Agent主循环

        流程:
        1. 初始化状态
        2. 进入执行循环
        3. 每次迭代:LLM推理 → 调用工具 → 更新状态
        4. 达到终止条件时退出
        """
        state = AgentState(goal=goal)
        # 把用户目标作为第一条消息塞进上下文
        state.messages.append({
            "role": "user",
            "content": f"目标:{goal}\n\n请决定下一步行动。"
        })

        while not self._should_terminate(state):
            state.iterations += 1

            # 步骤1:LLM推理,传入完整对话历史
            response = await self._think(state)

            # 步骤2:检查是否需要调用工具
            tool_use_blocks = [b for b in response.content if b.type == "tool_use"]
            if tool_use_blocks:
                # 把 assistant 的响应加入历史
                state.messages.append({"role": "assistant", "content": response.content})
                # 执行所有工具调用,按 Anthropic API 规范构造 tool_result
                tool_results = []
                for tool_use in tool_use_blocks:
                    tool_result = await self._execute_tool(tool_use)
                    state.results.append(tool_result)
                    tool_results.append({
                        "type": "tool_result",
                        "tool_use_id": tool_use.id,
                        "content": str(tool_result),
                    })
                # tool_result 必须以 user 角色回传
                state.messages.append({"role": "user", "content": tool_results})
            else:
                # LLM直接回答,任务完成
                text_blocks = [b for b in response.content if b.type == "text"]
                return {
                    "status": "completed",
                    "goal": goal,
                    "result": text_blocks[0].text if text_blocks else "",
                    "iterations": state.iterations
                }

        return {
            "status": "terminated",
            "goal": goal,
            "reason": "max_iterations_reached",
            "iterations": state.iterations
        }

    def _should_terminate(self, state: AgentState) -> bool:
        """判断是否应该终止"""
        # 达到最大迭代次数
        if state.iterations >= state.max_iterations:
            return True
        # 其他终止条件可以在这里添加
        return False

    async def _think(self, state: AgentState) -> Any:
        """LLM推理:传入完整对话历史,让模型能看到之前的工具调用与结果"""
        response = self.client.messages.create(
            model="claude-opus-4-20250514",
            max_tokens=4096,
            messages=state.messages,
            tools=self.tools
        )
        return response

    async def _execute_tool(self, tool_use: Any) -> dict:
        """执行工具调用,返回原始结果(由调用方按 API 规范包装)"""
        tool_name = tool_use.name
        tool_args = tool_use.input

        # 这里应该调用实际的工具
        # 为了简化,省略具体实现
        return {
            "tool": tool_name,
            "args": tool_args,
            "output": f"Tool {tool_name} executed"
        }

上面这段代码有两处与 Anthropic Messages API 规范强相关的细节,容易踩坑:

  1. tool_result 必须以 role: "user" 回传,且 contenttool_result 类型的块数组,每块带 tool_use_id 指向对应的工具调用。如果直接把工具返回值塞进 role: "assistant",API 会报 400。
  2. _think 必须传入完整的 state.messages,否则 LLM 看不到上一轮的工具调用和结果,会重复发起相同的调用。原始版本每次只发一条新消息,等于丢掉了上下文。

参考来源:Anthropic Messages API - Tool use

为什么要这样设计?

1. 状态外部化(AgentState 类)

# 好:状态外部化
state = AgentState(goal=goal)
while not self._should_terminate(state):
    ...

# 不好:状态散落在各处
while self.iterations < self.max_iterations:
    self.messages.append(...)
    self.context.update(...)

把状态收进 dataclass,是为了能序列化保存(断点续跑)、热切换(修改状态不影响逻辑)、并行运行多个 Agent 实例。如果状态散落在 self 的各个属性里,这三件事都做不了。

2. 终止条件前置判断

# 好:先检查再执行
while not self._should_terminate(state):
    response = await self._think(state)
    ...

# 不好:先执行再检查
while True:
    response = await self._think(state)
    if self._should_terminate(state):
        break

先检查再执行,避免在达到终止条件后还多跑一次 LLM 推理——这一次推理既浪费 token,又可能触发不必要的工具调用。

3. 结果记录完整

主循环里每次工具调用后,assistant 响应和 tool_result 都要追加到 state.messages

state.messages.append({"role": "assistant", "content": response.content})
state.messages.append({"role": "user", "content": tool_results})

完整的对话历史让 LLM 在下一轮推理时能看到之前的工具调用和返回值,否则会重复发起相同调用。这也是 7.2 节开头强调的 API 规范要求。


7.3 多Agent协作系统

为什么需要多Agent?

单个 Agent 处理复杂任务时会遇到几个具体问题:要同时精通多个领域、上下文越来越长导致响应变慢、一个环节出错可能污染整个对话。以旅行规划为例:

任务:帮用户规划一次旅行

单个Agent的问题:
- 需要同时是旅行专家 + 酒店专家 + 天气专家 + 预算专家
- 知识过于分散,难以精通所有领域
- 单一Agent处理所有任务,响应会变慢

多Agent方案:
- 旅行规划Agent(主Agent)负责任务分解和协调
- 酒店Agent负责搜索和推荐酒店
- 天气Agent负责查询天气预报
- 预算Agent负责计算和控制预算

拆分后每个 Agent 的上下文更短、职责更窄,主 Agent 的工作收敛到分解任务和汇总结果,不必自己精通每个领域。

多Agent架构模式

模式一:星型架构(主从模式)

                    ┌─────────────┐
                    │  主Agent    │
                    │ (协调者)    │
                    └──────┬──────┘
          ┌────────────────┼────────────────┐
          │                │                │
    ┌─────▼─────┐   ┌─────▼─────┐   ┌─────▼─────┐
    │ 子Agent1  │   │ 子Agent2  │   │ 子Agent3  │
    │ (酒店)    │   │ (天气)    │   │ (预算)    │
    └───────────┘   └───────────┘   └───────────┘
class MasterAgent:
    """
    星型架构主Agent

    特点:
    - 主Agent负责任务分解和结果汇总
    - 子Agent只做特定领域的任务
    - 主Agent和子Agent之间通过消息传递
    """

    def __init__(self):
        self.sub_agents = {
            "hotel": HotelAgent(),
            "weather": WeatherAgent(),
            "budget": BudgetAgent()
        }

    async def run(self, task: str) -> dict:
        # 1. 分解任务
        subtasks = await self._decompose_task(task)

        # 2. 并行派发子任务
        results = await asyncio.gather(*[
            self._run_subagent(name, subtask)
            for name, subtask in subtasks.items()
        ])

        # 3. 汇总结果
        return await self._aggregate_results(results)

模式二:链式架构(流水线模式)

┌───────────┐    ┌───────────┐    ┌───────────┐    ┌───────────┐
│  Agent1   │ →  │  Agent2   │ →  │  Agent3   │ →  │  Agent4   │
│ (预处理)   │    │ (核心处理) │    │ (验证)    │    │ (输出)    │
└───────────┘    └───────────┘    └───────────┘    └───────────┘
class ChainAgent:
    """
    链式架构Agent

    特点:
    - 每个Agent有明确的职责(预处理/核心/验证/输出)
    - 输出层层传递,形成处理流水线
    - 适合有明确处理步骤的任务
    """

    def __init__(self):
        self.chain = [
            PreprocessAgent(),
            CoreAgent(),
            ValidationAgent(),
            OutputAgent()
        ]

    async def run(self, input_data: Any) -> Any:
        current = input_data

        for agent in self.chain:
            current = await agent.process(current)

            if not self._is_valid(current):
                # 验证失败,触发错误处理
                return await self._handle_error(current, agent)

        return current

模式三:网状架构(对等模式)

┌───────────┐────────────┐
│  Agent1   │──────────││
└───┬───────┘          ││
    │                  ││
    │    ┌─────────────▼┐
    └───►│  Agent2      │
         └──────┬───────┘
         ┌──────▼───────┐
         │  Agent3       │
         └───────────────┘
class PeerNetwork:
    """
    网状架构(对等模式)

    特点:
    - 没有中心协调者
    - Agent之间可以直接通信
    - 适合需要横向协作的场景
    - 复杂度高,需要好的协调机制
    """

    async def run(self, task: str) -> dict:
        # 使用消息队列进行Agent间通信
        queue = MessageQueue()

        # 启动所有Agent
        agents = [Agent(i) for i in range(3)]
        tasks = [agent.run(queue) for agent in agents]

        # 发送初始任务
        await queue.publish("task", task)

        # 等待所有Agent完成
        results = await asyncio.gather(*tasks)

        return self._merge_results(results)

如何选择架构模式?

场景推荐架构原因
任务明确、子任务独立星型便于并行、易于管理
处理流程固定链式清晰、易于调试
横向协作、无固定流程网状灵活、适应性强
需要高可靠性星型+链式混合主备冗余

7.4 执行模式:并行与链式

并行执行

当多个子任务彼此独立时,并行执行能把总耗时从 N×T 压到接近 T:

async def parallel_execution(tasks: list[dict]) -> list[dict]:
    """
    并行执行多个任务

    为什么并行能提升效率?
    假设每个任务需要3秒:
    - 串行:3 * N 秒
    - 并行:约3秒(理想情况)

    注意:
    - 并行任务之间不能有依赖
    - 需要处理好并发竞争问题
    """
    async def execute_single(task: dict) -> dict:
        agent = create_agent(task["type"])
        result = await agent.run(task["input"])
        return {"task_id": task["id"], "result": result}

    # 使用asyncio.gather实现真正并行
    results = await asyncio.gather(*[
        execute_single(task) for task in tasks
    ])

    return results

判断一个任务能不能并行,看的是它是否依赖其他任务的输出:

# 适合并行的任务
parallel_tasks = [
    {"id": 1, "type": "search", "input": "北京天气"},
    {"id": 2, "type": "search", "input": "上海天气"},  # 独立
    {"id": 3, "type": "search", "input": "广州天气"},  # 独立
]

# 不适合并行的任务(存在依赖)
sequential_tasks = [
    {"id": 1, "type": "search", "input": "用户ID"},
    {"id": 2, "type": "fetch", "input": "依赖任务1的结果"},  # 依赖
    {"id": 3, "type": "save", "input": "依赖任务2的结果"},   # 依赖
]

链式执行

链式执行用于有依赖关系的任务:

async def chain_execution(tasks: list[dict]) -> dict:
    """
    链式执行(串行依赖)

    执行流程:
    Task1 → Task2 → Task3 → ... → TaskN

    每个任务接收前一个任务的输出作为输入
    """
    context = {}

    for i, task in enumerate(tasks):
        # 构建当前任务的输入
        task_input = await self._prepare_input(task, context)

        # 执行当前任务
        agent = create_agent(task["type"])
        result = await agent.run(task_input)

        # 保存结果到上下文,供后续任务使用
        context[task["id"]] = result

        # 检查是否需要错误恢复
        if not self._is_success(result):
            if task.get("retry"):
                result = await self._retry(task, context)
            else:
                raise ExecutionError(f"Task {task['id']} failed")

    return context  # 返回完整的上下文(包含所有任务的结果)

并行与链式的混合模式

class HybridExecutor:
    """
    混合执行器:并行 + 链式的结合

    适用场景:
    - 部分任务可以并行
    - 部分任务有依赖关系

    示例:旅行规划
    1. 并行:同时查询 酒店、景点、天气
    2. 链式:基于查询结果生成行程 → 审核行程 → 输出最终方案
    """

    async def run(self, workflow: dict) -> dict:
        # 阶段1:并行执行独立任务
        parallel_results = await self._parallel_phase(workflow["parallel_tasks"])

        # 阶段2:基于并行结果执行链式任务
        context = {"parallel": parallel_results}
        chain_result = await self._chain_phase(
            workflow["chain_tasks"], 
            context
        )

        return {
            "parallel_results": parallel_results,
            "chain_result": chain_result
        }

    async def _parallel_phase(self, tasks: list[dict]) -> dict:
        """并行阶段"""
        results = await asyncio.gather(*[
            self._execute(task) for task in tasks
        ])
        return {task["id"]: result for task, result in zip(tasks, results)}

    async def _chain_phase(self, tasks: list[dict], context: dict) -> dict:
        """链式阶段"""
        for task in tasks:
            context[task["id"]] = await self._execute(task, context)
        return context

7.5 状态管理与上下文

状态持久化的重要性

Agent 跑得越久,对话历史越长,迟早会撞上上下文窗口上限:

# 问题:LLM的上下文是有限的
MAX_TOKENS = 200000  # Claude Opus的最大上下文

# 如果对话历史越来越长...
messages = [
    {"role": "user", "content": "第一轮对话"},      # 100 tokens
    {"role": "assistant", "content": "第一轮回答"},  # 200 tokens
    {"role": "user", "content": "第二轮对话"},      # 150 tokens
    {"role": "assistant", "content": "第二轮回答"},  # 300 tokens
    # ... 100轮后
    # tokens总量超过限制
]

撞上上限后有两种走法:要么把旧历史压缩成摘要,要么按相关性挑出一部分留下。下面分别给出实现。

方案一:状态压缩与摘要

class StateManager:
    """
    状态管理器

    职责:
    1. 压缩对话历史
    2. 提取关键信息
    3. 维护工作上下文
    """

    def __init__(self, max_history: int = 10):
        self.max_history = max_history  # 保留最近N轮对话
        self.summaries = []            # 压缩后的摘要
        self.working_memory = {}       # 当前工作内存

    def add_interaction(self, user_msg: str, assistant_msg: str):
        """添加一轮交互"""
        # 1. 加入历史
        self.summaries.append({
            "user": user_msg,
            "assistant": assistant_msg,
            "timestamp": now()
        })

        # 2. 检查是否需要压缩
        if len(self.summaries) > self.max_history:
            self._compress()

    def _compress(self):
        """
        压缩历史

        压缩策略:
        1. 保留最近N轮完整对话
        2. 更早的对话压缩成摘要
        3. 提取关键实体和决策
        """
        recent = self.summaries[-self.max_history:]
        older = self.summaries[:-self.max_history]

        # 用LLM生成摘要
        summary_prompt = f"""
        请总结以下对话的关键信息:
        {self._format_conversation(older)}

        提取:
        1. 用户的主要目标
        2. 已完成的关键步骤
        3. 当前状态
        4. 重要的中间结果
        """

        summary = self.client.messages.create(
            model="claude-opus-4-20250514",
            messages=[{"role": "user", "content": summary_prompt}]
        )

        # 保存摘要,清空旧历史
        self.working_memory["conversation_summary"] = summary.content[0].text
        self.summaries = recent

    def get_context_for_llm(self) -> str:
        """构建发送给LLM的上下文"""
        parts = []

        # 1. 添加摘要(如果有)
        if "conversation_summary" in self.working_memory:
            parts.append(f"对话摘要:{self.working_memory['conversation_summary']}")

        # 2. 添加最近N轮对话
        for item in self.summaries[-self.max_history:]:
            parts.append(f"用户:{item['user']}")
            parts.append(f"助手:{item['assistant']}")

        return "\n".join(parts)

方案二:按相关性挑选上下文

class SmartContextManager:
    """
    智能上下文管理器

    思路:
    - 按相关性给每条历史打分,而不是按时间顺序硬截断
    - 保留与当前任务最相关的上下文
    """

    def __init__(self, max_tokens: int = 150000):
        self.max_tokens = max_tokens
        self.priority_levels = {
            "critical": ["目标", "关键约束", "核心决策"],
            "important": ["中间结果", "用户偏好", "当前状态"],
            "normal": ["一般对话", "解释说明"],
            "discardable": ["问候", "重复确认"]
        }

    def build_context(self, all_items: list[dict], current_task: str) -> str:
        """
        构建当前任务所需的上下文

        算法:
        1. 为每个上下文项计算相关性分数
        2. 按分数排序
        3. 从最高分开始选取,直到达到token限制
        """
        scored_items = []

        for item in all_items:
            relevance = self._calculate_relevance(item, current_task)
            priority = self._get_priority(item)

            # 综合分数 = 相关性 * 优先级权重
            weight = {"critical": 1.0, "important": 0.7, "normal": 0.4, "discardable": 0.0}
            score = relevance * weight.get(priority, 0.5)

            scored_items.append((score, item))

        # 按分数降序排序
        scored_items.sort(key=lambda x: x[0], reverse=True)

        # 选取项直到达到token限制
        selected = []
        total_tokens = 0

        for score, item in scored_items:
            item_tokens = self._estimate_tokens(item)
            if total_tokens + item_tokens <= self.max_tokens:
                selected.append(item)
                total_tokens += item_tokens

        return self._format_selected(selected)

7.6 错误处理与容错机制

错误分类与处理策略

class ErrorType(Enum):
    """Agent可能遇到的错误类型"""

    # LLM相关错误
    LLM_TIMEOUT = "llm_timeout"           # LLM响应超时
    LLM_RATE_LIMIT = "llm_rate_limit"     # API限流
    LLM_INVALID_RESPONSE = "llm_invalid"  # LLM返回无效响应

    # 工具相关错误
    TOOL_NOT_FOUND = "tool_not_found"     # 工具不存在
    TOOL_EXECUTION_FAILED = "tool_failed" # 工具执行失败
    TOOL_TIMEOUT = "tool_timeout"        # 工具执行超时

    # 状态相关错误
    STATE_CORRUPTED = "state_corrupted"  # 状态损坏
    CONTEXT_OVERFLOW = "context_overflow" # 上下文溢出

    # 业务相关错误
    MAX_ITERATIONS = "max_iterations"    # 达到最大迭代
    USER_CANCELLED = "user_cancelled"    # 用户取消

class ErrorAction(Enum):
    """错误处理动作:决定错误发生后Agent的下一步行为"""

    RETRY = "retry"                       # 重试当前操作
    RETRY_WITH_FALLBACK = "retry_fallback"  # 使用备用方案重试
    FAIL = "fail"                         # 终止任务并上报错误
    RETURN_BEST_RESULT = "return_best"    # 返回当前最佳结果
    ESCALATE = "escalate"                 # 上报给上层处理者

class ErrorHandler:
    """
    错误处理器

    设计原则:
    1. 不同错误类型采用不同的处理策略
    2. 记录错误日志便于调试
    3. 在适当时候回退或重试
    """

    def __init__(self):
        self.error_counts = {}

    async def handle(self, error: Exception, state: AgentState) -> ErrorAction:
        """
        处理错误,返回处理动作
        """
        error_type = self._classify(error)

        # 记录错误
        self._log_error(error_type, error, state)

        # 根据错误类型决定动作
        handlers = {
            ErrorType.LLM_TIMEOUT: self._handle_timeout,
            ErrorType.LLM_RATE_LIMIT: self._handle_rate_limit,
            ErrorType.TOOL_EXECUTION_FAILED: self._handle_tool_failure,
            ErrorType.CONTEXT_OVERFLOW: self._handle_context_overflow,
            ErrorType.MAX_ITERATIONS: self._handle_max_iterations,
        }

        handler = handlers.get(error_type, self._handle_unknown)
        return await handler(error, state)

    async def _handle_timeout(self, error, state):
        """超时处理:等待后重试"""
        state.context["retry_count"] = state.context.get("retry_count", 0) + 1

        if state.context["retry_count"] < 3:
            await asyncio.sleep(2 ** state.context["retry_count"])  # 指数退避
            return ErrorAction.RETRY
        else:
            return ErrorAction.FAIL

    async def _handle_rate_limit(self, error, state):
        """限流处理:等待指定时间"""
        retry_after = getattr(error, "retry_after", 60)
        await asyncio.sleep(retry_after)
        return ErrorAction.RETRY

    async def _handle_tool_failure(self, error, state):
        """工具失败处理:尝试备用方案"""
        tool_name = getattr(error, "tool_name", None)

        # 检查是否有备用工具
        if self._has_fallback(tool_name):
            state.context["using_fallback"] = True
            return ErrorAction.RETRY_WITH_FALLBACK

        return ErrorAction.FAIL

    async def _handle_context_overflow(self, error, state):
        """上下文溢出处理:压缩历史"""
        # 调用状态管理器压缩历史
        state_manager = state.context.get("state_manager")
        if state_manager:
            state_manager._compress()
            return ErrorAction.RETRY
        else:
            return ErrorAction.FAIL

    async def _handle_max_iterations(self, error, state):
        """达到最大迭代:返回当前最佳结果"""
        return ErrorAction.RETURN_BEST_RESULT

重试与回退机制

class RetryPolicy:
    """
    重试策略

    为什么需要重试策略?
    1. 瞬时故障(网络抖动)可能自行恢复
    2. 限流错误等待后通常可以继续
    3. 合理重试能提高系统稳定性
    """

    def __init__(
        self,
        max_retries: int = 3,
        base_delay: float = 1.0,
        exponential_base: float = 2.0,
        max_delay: float = 60.0,
        jitter: bool = True
    ):
        self.max_retries = max_retries
        self.base_delay = base_delay
        self.exponential_base = exponential_base
        self.max_delay = max_delay
        self.jitter = jitter

    def get_delay(self, attempt: int) -> float:
        """计算重试延迟"""
        # 指数退避
        delay = self.base_delay * (self.exponential_base ** attempt)
        delay = min(delay, self.max_delay)

        # 添加随机抖动,避免惊群效应
        if self.jitter:
            import random
            delay = delay * (0.5 + random.random())

        return delay

class FallbackManager:
    """
    备用方案管理器

    思路:
    - 每个主工具可以有多个备用工具
    - 主工具失败时,自动尝试备用工具
    - 记录使用情况,便于优化
    """

    def __init__(self):
        self.fallback_map = {
            "primary_search": ["fallback_search_1", "fallback_search_2"],
            "primary_translate": ["fallback_translate"],
            "primary_code_exec": ["fallback_sandbox"]
        }
        self.usage_stats = {}

    async def execute_with_fallback(self, primary_tool: str, args: dict) -> Any:
        """执行主工具,失败时尝试备用方案"""
        tools_to_try = [primary_tool] + self.fallback_map.get(primary_tool, [])

        last_error = None
        for tool in tools_to_try:
            try:
                result = await self._execute_tool(tool, args)
                self._record_success(tool)
                return result
            except Exception as e:
                last_error = e
                self._record_failure(tool, e)
                continue

        # 所有工具都失败
        raise AllToolsFailedError(tools_to_try, last_error)

7.7 安全与权限管理

权限模型设计

class PermissionScope(Enum):
    """权限范围"""
    NONE = "none"           # 无任何权限
    READ = "read"           # 只读
    WRITE = "write"         # 读写
    EXECUTE = "execute"     # 执行
    ADMIN = "admin"         # 管理

class Permission:
    """
    权限定义

    为什么需要权限系统?
    1. 防止Agent执行危险操作
    2. 限制资源访问范围
    3. 满足合规审计要求
    """

    def __init__(
        self,
        scopes: list[PermissionScope] = None,  # 权限范围列表
        file_paths: list[str] = [],      # 允许访问的文件路径
        allowed_tools: list[str] = [],   # 允许使用的工具
        allowed_domains: list[str] = [], # 允许访问的网络域名
        max_execution_time: int = 300,   # 最大执行时间(秒)
        max_api_calls: int = 100         # 最大API调用次数
    ):
        self.scopes = scopes if scopes is not None else [PermissionScope.READ]
        self.file_paths = file_paths
        self.allowed_tools = allowed_tools
        self.allowed_domains = allowed_domains
        self.max_execution_time = max_execution_time
        self.max_api_calls = max_api_calls

class SecurityManager:
    """
    安全管理器

    职责:
    1. 验证操作权限
    2. 拦截危险操作
    3. 记录所有操作日志
    """

    def __init__(self, permission: Permission):
        self.permission = permission
        self.audit_log = []

    def check_file_access(self, path: str, mode: str) -> bool:
        """检查文件访问权限"""
        # 如果是只读权限但要求写操作
        if mode == "write" and PermissionScope.WRITE not in self.permission.scopes:
            return False

        # 检查路径是否在允许范围内
        import os
        real_path = os.path.realpath(path)

        for allowed_path in self.permission.file_paths:
            if real_path.startswith(os.path.realpath(allowed_path)):
                return True

        return False

    def check_tool_usage(self, tool_name: str) -> bool:
        """检查工具使用权限"""
        return tool_name in self.permission.allowed_tools

    def check_network_access(self, domain: str) -> bool:
        """检查网络访问权限"""
        for allowed in self.permission.allowed_domains:
            if domain.endswith(allowed) or domain == allowed:
                return True
        return False

    def audit(self, operation: str, details: dict):
        """记录审计日志"""
        self.audit_log.append({
            "timestamp": now(),
            "operation": operation,
            "details": details
        })

沙箱隔离

class SandboxConfig:
    """
    沙箱配置

    为什么需要沙箱?
    即使Agent出错,也只会影响沙箱内的模拟环境
    不会影响真实系统
    """

    def __init__(
        self,
        use_sandbox: bool = True,
        network_isolation: bool = True,
        filesystem_boundary: str = "/workspace/sandbox",
        memory_limit: str = "2GB"
    ):
        self.use_sandbox = use_sandbox
        self.network_isolation = network_isolation
        self.filesystem_boundary = filesystem_boundary
        self.memory_limit = memory_limit

class SandboxExecutor:
    """沙箱执行器"""

    def __init__(self, config: SandboxConfig):
        self.config = config

    async def execute(self, code: str, language: str) -> Any:
        """在沙箱中执行代码"""
        if self.config.use_sandbox:
            return await self._execute_in_sandbox(code, language)
        else:
            return await self._execute_direct(code, language)

    async def _execute_in_sandbox(self, code: str, language: str) -> Any:
        """在沙箱中执行"""
        # 1. 准备沙箱环境
        sandbox = await self._prepare_sandbox()

        # 2. 设置资源限制
        sandbox.set_memory_limit(self.config.memory_limit)
        sandbox.set_network_isolation(self.config.network_isolation)
        sandbox.set_filesystem_boundary(self.config.filesystem_boundary)

        # 3. 执行代码
        try:
            result = await sandbox.run(code, language)
            return result
        finally:
            # 4. 清理沙箱
            await sandbox.cleanup()

7.8 生产环境推荐做法

架构设计原则

生产环境 Agent 系统的架构原则可以归纳为五条:

  1. 分离关注点(Separation of Concerns)

    • Agent 核心逻辑与工具实现分离
    • 状态管理与执行逻辑分离
    • 安全检查与业务逻辑分离
  2. 失败设计(Design for Failure)

    • 每个组件都可能失败
    • 优雅降级,而非整体崩溃
    • 快速失败,便于诊断
  3. 可观测性(Observability)

    • 日志:记录每个关键步骤
    • 指标:QPS、延迟、错误率
    • 追踪:请求全链路追踪
  4. 资源管理(Resource Management)

    • 限制并发请求数
    • 控制内存使用
    • 防止资源泄漏
  5. 安全第一(Security First)

    • 最小权限原则
    • 纵深防御
    • 审计追踪

监控与告警

class AgentMonitor:
    """
    Agent监控指标
    """

    metrics = {
        "requests_total": "总请求数",
        "requests_success": "成功请求数",
        "requests_failed": "失败请求数",
        "average_latency": "平均延迟",
        "p99_latency": "P99延迟",
        "active_agents": "活跃Agent数",
        "tools_usage": "工具使用统计",
        "error_distribution": "错误分布"
    }

class AlertManager:
    """
    告警管理
    """

    alert_rules = {
        "high_error_rate": {
            "condition": "error_rate > 0.05",  # 错误率超过5%
            "severity": "critical",
            "action": "notify_oncall"
        },
        "high_latency": {
            "condition": "p99_latency > 30s",
            "severity": "warning",
            "action": "notify_team"
        },
        "agent_timeout": {
            "condition": "timeout_count > 10/min",
            "severity": "warning",
            "action": "investigate"
        }
    }

部署架构建议

┌─────────────────────────────────────────────────────────────────┐
│                        负载均衡层                               │
│                    (Nginx / 云负载均衡)                        │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────▼───────────────────────────────────┐
│                        API网关层                                │
│              (认证、限流、日志、路由)                           │
└─────────────────────────────┬───────────────────────────────────┘
┌─────────────────────────────▼───────────────────────────────────┐
│                      Agent服务集群                               │
│  ┌────────────┐  ┌────────────┐  ┌────────────┐  ┌────────────┐│
│  │  Agent-1   │  │  Agent-2   │  │  Agent-3   │  │  Agent-N   ││
│  │  (实例1)    │  │  (实例2)    │  │  (实例3)    │  │  (实例N)    ││
│  └────────────┘  └────────────┘  └────────────┘  └────────────┘│
└─────────────────────────────┬───────────────────────────────────┘
          ┌───────────────────┼───────────────────┐
          │                   │                   │
┌─────────▼─────┐   ┌────────▼────────┐   ┌─────▼─────────┐
│   工具服务1    │   │    工具服务2     │   │    工具服务3    │
│  (搜索服务)    │   │   (代码执行)     │   │   (API服务)    │
└───────────────┘   └─────────────────┘   └───────────────┘

性能优化建议

优化项方法效果
缓存 LLM 响应对相同输入缓存响应减少 API 调用
并行工具调用不依赖结果的工具并行执行降低延迟
状态压缩对话历史压缩摘要减少 token 消耗
预热机制定期预加载模型降低冷启动延迟
连接池复用复用 HTTP/数据库连接提高吞吐

其中"缓存 LLM 响应"要小心:如果工具结果会随时间变化(比如查天气、查库存),缓存命中反而会返回过期数据。建议只对纯函数式工具(如固定文档检索、数学计算)开启缓存,并给缓存条目设 TTL。

常见问题排查

上线后遇到问题时,按下面的顺序定位:

现象可能原因排查步骤
Agent 反复调用同一个工具_think 没传完整 messages,LLM 看不到上一轮结果检查 messages=state.messages 是否传入;打印每轮 messages 长度
API 报 400 tool_use_id not foundtool_result 的 tool_use_id 与 assistant 的 tool_use 块对不上检查 tool_use.id 是否正确透传到 tool_result
Agent 跑几轮后上下文溢出没有压缩历史,messages 无限增长接入 7.5 的 StateManager,或在每轮结束后估算 token 数
工具偶尔超时但 Agent 直接挂掉没有重试策略,单次失败即终止接入 7.6 的 RetryPolicy,对超时类错误做指数退避
Agent 在沙箱外执行了危险操作权限检查没覆盖到该工具或路径检查 allowed_toolsfile_paths 白名单是否完整;查审计日志
并行子 Agent 结果丢失asyncio.gather 中某个协程抛异常未被捕获return_exceptions=True 或给每个子任务包 try/except

最小可运行示例

下面是一个能直接跑起来的最小 Agent,用 get_weather 工具演示完整的"推理 → 调用工具 → 回传结果 → 再推理"闭环:

import os
from anthropic import Anthropic

client = Anthropic(api_key=os.environ["ANTHROPIC_API_KEY"])

tools = [{
    "name": "get_weather",
    "description": "查询指定城市的天气",
    "input_schema": {
        "type": "object",
        "properties": {
            "city": {"type": "string", "description": "城市名"}
        },
        "required": ["city"]
    }
}]

def execute_tool(tool_use):
    """实际工具实现:这里用假数据,生产环境替换成真实 API"""
    if tool_use.name == "get_weather":
        city = tool_use.input["city"]
        return f"{city} 今天晴,25°C"
    raise ValueError(f"未知工具: {tool_use.name}")

def run_agent(goal: str, max_iterations: int = 10):
    messages = [{"role": "user", "content": goal}]

    for i in range(max_iterations):
        response = client.messages.create(
            model="claude-opus-4-20250514",
            max_tokens=1024,
            tools=tools,
            messages=messages,
        )

        tool_use_blocks = [b for b in response.content if b.type == "tool_use"]
        if not tool_use_blocks:
            # 没有工具调用,说明 LLM 给出了最终回答
            text = "".join(b.text for b in response.content if b.type == "text")
            print(f"[最终回答] {text}")
            return text

        # 把 assistant 响应加入历史
        messages.append({"role": "assistant", "content": response.content})

        # 执行所有工具调用,按 API 规范回传 tool_result
        tool_results = []
        for tool_use in tool_use_blocks:
            result = execute_tool(tool_use)
            print(f"[工具调用] {tool_use.name}({tool_use.input}) → {result}")
            tool_results.append({
                "type": "tool_result",
                "tool_use_id": tool_use.id,
                "content": result,
            })
        messages.append({"role": "user", "content": tool_results})

    print("达到最大迭代次数,Agent 终止")

if __name__ == "__main__":
    run_agent("帮我查一下北京和上海的天气,然后总结哪个更适合出行")

运行前确保已安装 anthropic 并设置 ANTHROPIC_API_KEY 环境变量。这段代码演示了 7.2 节主循环的最小形态:状态就是 messages 列表,终止条件是"没有 tool_use 块"或"达到 max_iterations"。


本章总结

核心知识点

知识点掌握程度关键点
Agent定义⭐⭐⭐⭐⭐LLM+状态+工具+执行循环+终止条件
单Agent架构⭐⭐⭐⭐⭐状态外部化、终止条件前置
多Agent协作⭐⭐⭐⭐⭐星型/链式/网状架构
执行模式⭐⭐⭐⭐⭐并行与链式混合
状态管理⭐⭐⭐⭐⭐压缩、摘要、智能选择
错误处理⭐⭐⭐⭐⭐分类处理、重试回退
安全机制⭐⭐⭐⭐权限模型、沙箱隔离
生产实践⭐⭐⭐⭐⭐监控、告警、优化

Claude API 七篇完整系列

Claude API 全系列七篇至此完成:

主题核心要点
API基础认证、请求、会话、结构化输出
提示词工程Few-shot、CoT、Temperature
工具调用Function Calling、MCP
RAG系统分块、嵌入、搜索、重排序
MCP协议架构、服务器开发、客户端
Computer Use观察-决策-执行、安全机制
Agent架构多Agent、状态管理、生产实践

自测题

下面 5 道题用来检验掌握程度,答案附在题后。

Q1:Agent 的五个必备要素中,如果去掉"终止条件",系统会出现什么现象?

Q2:下面这段代码会导致什么问题?如何修复?

response = client.messages.create(
    model="claude-opus-4-20250514",
    messages=[{"role": "user", "content": "继续"}],
    tools=tools,
)

Q3:星型架构和链式架构分别适合什么场景?如果一个任务既有可并行的子任务、又有严格顺序依赖的子任务,应该怎么组合?

Q4asyncio.gather 中某个子 Agent 抛了异常,默认情况下整个 gather 会怎样?如何避免单个子任务失败拖垮全部并行任务?

Q5:为什么 tool_result 必须以 role: "user" 回传,而不是 role: "assistant"?从 API 的消息交替规则角度解释。

参考答案

A1:Agent 会进入无限循环,不断调用 LLM 和工具,直到撞上 API 限流或上下文溢出。终止条件(最大迭代次数、目标完成检测、超时)是防止 Agent 失控的最后一道闸。

A2:每次只发一条 "继续" 消息,LLM 看不到之前的工具调用和结果,会重复发起相同调用或丢失任务上下文。修复方法是把完整的 state.messages 传给 messages 参数,让 LLM 能看到整个对话历史。

A3:星型适合子任务彼此独立的场景(如同时查酒店、天气、景点);链式适合有严格顺序依赖的场景(如"查用户 → 查订单 → 生成报告")。既有独立又有依赖的任务,用 7.4 节的混合模式:先并行跑独立子任务,再按依赖顺序跑链式任务。

A4:默认情况下 asyncio.gather 会在第一个异常抛出时立即返回,其他未完成的协程会被取消。要避免单点失败拖垮全部,用 return_exceptions=True 参数,这样异常会作为返回值而不是抛出,调用方可以逐个检查结果。

A5:Anthropic Messages API 要求 user 和 assistant 角色严格交替。assistant 发起 tool_use 后,工具结果属于"用户侧提供的事实反馈",必须以 user 角色回传,才能维持交替顺序。如果用 assistant 角色,会破坏交替规则,API 返回 400。

进阶路径

掌握本文内容后,可以按以下方向继续深入:

  1. MCP 协议与 Agent 结合:把 7.3 节的子 Agent 替换为 MCP 服务器,让 Agent 能动态发现和调用外部工具。参考第五篇《MCP协议》和 MCP 官方规范
  2. 长时任务与断点续跑:把 AgentState 序列化到数据库(如 Redis、PostgreSQL),实现 Agent 崩溃后能从上次断点恢复。重点解决状态版本兼容和工具幂等性。
  3. 多 Agent 评估与调优:搭建离线评估管线,用固定测试集衡量 Agent 的任务完成率、平均迭代次数、token 消耗。Anthropic 的 Agent 评估指南 提供了评估框架。
  4. 成本控制:监控每个 Agent 实例的 token 消耗,对高消耗任务做模型降级(如简单子任务用 Haiku,复杂推理用 Opus)。参考 Anthropic 模型选择指南
  5. 安全加固:在 7.7 节的权限模型基础上,加入人工审批环节(human-in-the-loop),对高危操作(如删除文件、转账)强制要求人工确认后再执行。

参考资料


文档元信息 难度:⭐⭐⭐⭐ | 类型:专家设计 | 更新日期:2026-03-25 | 预计阅读时间:60 分钟 | 字数:约 10000 字