# AI Agent 落地实战:从单Agent到多Agent协作的系统架构与实践

AI Agent 落地实战:从单Agent到多Agent协作的系统架构与实践

大家好,我是摘星,今天我们来聊聊AI Agent落地这件事------不是概念,不是愿景,而是实实在在跑起来的架构和代码。

过去一年,我观察了十几个团队尝试落地Agent的实际项目。有的团队用单Agent+Tool Learning跑通了客服机器人的场景;有的团队用Multi-Agent架构重构了整个售后处理流程,效率提升了三倍不止;但更多的团队,是在「调Prompt-失败-再调Prompt」的死循环里煎熬了三个月,最后告诉我「Agent这东西就是炒概念,根本没法落地」。

这两种结果的差距在哪?不是技术选型,不是模型能力,而是对Agent系统架构的理解深度

今天这篇文章,我就把过去一年深度参与的几个Agent落地项目里踩过的坑、总结出的方法论,一次性拆解清楚。从单Agent的核心设计,到多Agent协作的架构演进,再到生产环境里的具体实现细节,你拿走就能用。

我假设你已经对Agent有了基本了解,知道ReAct、Tool Use这些概念,不会在文章里解释什么是Function Calling。所以我们直接进正题。


核心概念:Agent系统不是「一个模型+一堆工具」

很多人对Agent系统的理解还停留在「选一个大模型,给它装上一堆工具,让它自己调用」这个层面。这个理解不能说错,但远远不够。

真实的Agent系统面临的挑战远比这个复杂:

第一,任务规划的不确定性。 当用户说「帮我处理一下这个订单」,系统需要先理解这具体是什么订单、当前处于什么状态、用户期望什么结果。这不是简单的意图识别,而是需要多轮澄清和状态推断

第二,工具调用结果的不可控性。 外部API可能超时、返回脏数据、或者完全不可用。Agent系统必须有能力处理这些异常情况,而不是遇到错误就卡死。

第三,多步骤任务的长期记忆问题。 一个报销流程可能涉及十几步操作,跨越几十分钟。系统需要在这段时间内保持上下文连贯性,不能每一步都从零开始。

第四,多Agent协作时的角色冲突和死锁问题。 当两个Agent同时需要访问同一个资源、或者对同一个任务有冲突的理解时,系统需要有一套机制来解决这些冲突。

以上每一个问题,单靠调Prompt是解决不了的。你需要的是系统级的架构设计

下面我们先从最简单的单Agent架构开始,逐层递进。


单Agent架构:核心组件与状态机设计

一个最简单的单Agent系统

很多人第一次跑通Agent,代码是这样的:

python 复制代码
from anthropic import Anthropic

client = Anthropic()

def run_agent(user_message: str):
    messages = [{"role": "user", "content": user_message}]
    
    response = client.messages.create(
        model="claude-sonnet-4-7",
        max_tokens=4096,
        messages=messages,
        tools=[
            {
                "name": "get_order_status",
                "description": "查询订单状态",
                "input_schema": {
                    "type": "object",
                    "properties": {
                        "order_id": {"type": "string"}
                    }
                }
            },
            # ... 更多工具定义
        ]
    )
    return response

# 第一次调用
result = run_agent("我的订单到哪了?")
print(result.content[0].text)

这段代码跑起来没问题,但它缺少了几个关键组件,所以在复杂场景下会快速崩溃:

  1. 没有循环机制------如果Agent第一次没理解用户意图,代码没有重试逻辑
  2. 没有状态管理------每次调用都是独立的context window
  3. 没有异常处理------API超时或工具返回错误时没有容错机制

一个能用于生产环境的单Agent,至少应该是这样:

python 复制代码
import time
from dataclasses import dataclass, field
from typing import Optional, List, Dict, Any
from enum import Enum

class AgentStatus(Enum):
    IDLE = "idle"
    RUNNING = "running"
    WAITING_FOR_INPUT = "waiting_for_input"
    COMPLETED = "completed"
    FAILED = "failed"

@dataclass
class ToolResult:
    """工具执行结果"""
    success: bool
    result: Any = None
    error: Optional[str] = None
    tool_name: str = ""
    execution_time_ms: int = 0

@dataclass
class AgentState:
    """Agent运行状态"""
    status: AgentStatus = AgentStatus.IDLE
    current_step: int = 0
    max_steps: int = 20
    last_user_message: str = ""
    tool_results: List[ToolResult] = field(default_factory=list)
    conversation_history: List[Dict] = field(default_factory=list)
    session_data: Dict[str, Any] = field(default_factory=dict)

class SingleAgent:
    """单Agent运行器"""
    
    def __init__(
        self,
        client: Anthropic,
        tools: List[Dict],
        max_steps: int = 20,
        retry_on_error: bool = True,
        max_retries: int = 3
    ):
        self.client = client
        self.tools = tools
        self.max_steps = max_steps
        self.retry_on_error = retry_on_retries
        self.max_retries = max_retries
        self.state = AgentState()
    
    def run(self, user_message: str) -> Dict:
        """主运行循环"""
        self.state.last_user_message = user_message
        self.state.status = AgentStatus.RUNNING
        self.state.current_step = 0
        self.state.conversation_history = [
            {"role": "user", "content": user_message}
        ]
        
        while self.state.current_step < self.state.max_steps:
            try:
                step_result = self._execute_step()
                
                if step_result.get("type") == "stop":
                    self.state.status = AgentStatus.COMPLETED
                    return {
                        "status": "completed",
                        "message": step_result["content"],
                        "steps": self.state.current_step,
                        "tool_results": self.state.tool_results
                    }
                
                elif step_result.get("type") == "tool_use":
                    tool_result = await self._execute_tool(step_result)
                    self.state.tool_results.append(tool_result)
                    
                    if not tool_result.success and not self.retry_on_error:
                        self.state.status = AgentStatus.FAILED
                        return {
                            "status": "failed",
                            "error": tool_result.error,
                            "steps": self.state.current_step
                        }
                
                elif step_result.get("type") == "waiting_for_input":
                    self.state.status = AgentStatus.WAITING_FOR_INPUT
                    return {
                        "status": "waiting_for_input",
                        "message": step_result["content"],
                        "pending_params": step_result.get("params", {})
                    }
                    
            except Exception as e:
                if self.state.current_step > 0 and self.retry_on_error:
                    # 某种程度的自愈逻辑
                    continue
                else:
                    self.state.status = AgentStatus.FAILED
                    return {"status": "error", "error": str(e)}
        
        return {
            "status": "max_steps_exceeded",
            "message": "任务未能在最大步数内完成",
            "steps": self.state.current_step
        }
    
    def _execute_step(self) -> Dict:
        """执行单步推理"""
        response = self.client.messages.create(
            model="claude-sonnet-4-7",
            max_tokens=4096,
            messages=self.state.conversation_history,
            tools=self.tools
        )
        
        self.state.conversation_history.append({
            "role": "assistant",
            "content": response.content
        })
        
        # 解析 response 中的 tool_use 块
        if response.stop_reason == "tool_use":
            # 处理工具调用
            pass
        elif response.stop_reason == "end_turn":
            # 最终响应
            pass
        
        return {"type": "stop", "content": response.content}

这段代码展示了几个关键设计:

状态机控制流 :通过AgentStatus枚举管理Agent的生命周期,避免在错误状态继续执行。

工具执行封装 :每个工具调用都包装成ToolResult对象,包含成功/失败状态、执行时间、错误信息,便于后续分析和优化。

对话历史维护:每次调用都把历史消息传入LLM,而不是每次都从新context开始,保证多步骤任务的连贯性。

工具定义的最佳实践

工具是Agent与外界交互的桥梁。工具定义的质量直接决定了Agent的能力上限。

一个好的工具定义应该是这样的:

python 复制代码
ORDER_STATUS_TOOL = {
    "name": "get_order_status",
    "description": """
    查询订单的当前物流状态。
    
    适用场景:
    - 用户询问「我的订单到哪了」
    - 用户询问「什么时候能收到」
    - 用户询问「订单有没有发出」
    
    不适用场景:
    - 用户询问「怎么取消订单」(用 cancel_order)
    - 用户询问「怎么修改地址」(用 modify_shipping_address)
    """,
    "input_schema": {
        "type": "object",
        "properties": {
            "order_id": {
                "type": "string",
                "description": "订单ID,必须是有效的12位数字格式",
                "pattern": "^[0-9]{12}$",
                "examples": ["202405010001", "202405010002"]
            },
            "require_insurance": {
                "type": "boolean",
                "description": "是否查询运费险状态,默认为false",
                "default": False
            }
        },
        "required": ["order_id"]
    }
}

注意这个定义里的几个细节:

详细的中文描述:不仅告诉模型这个工具是做什么的,还明确说明了适用和不适用场景,减少模型误调用的概率。

JSON Schema的完整定义 :包括pattern字段限制格式,用examples给出正面和反面示例。这比简单写个type要可靠得多。

description中的场景示例:帮助模型理解在什么情况下应该调用这个工具。这个描述会被用在few-shot提示里。

单Agent的局限性

单Agent架构在简单场景下非常有效,比如单轮问答、简单的信息查询、基础的自动化操作。但当任务复杂度提升,它的局限性就显现出来了:

规划能力的瓶颈:当一个任务需要分解成十几个子步骤时,单Agent的推理链会变得非常长,每一步的误差会累积,最终导致任务失败。

角色冲突:单Agent很难同时扮演多个角色,比如一个Agent既要做客服、又要做售后、还要做销售,在同一个context里会让模型困惑。

并行处理:如果有多个相互独立的子任务,单Agent只能串行处理,无法最大化资源利用率。

这时候,我们就需要Multi-Agent架构。


Multi-Agent架构:从协作模式到层级拓扑

Multi-Agent不是简单地把多个单Agent放在一起就叫Multi-Agent了。你需要解决几个核心问题:

1. 角色定义:每个Agent负责什么?

2. 通信协议:Agent之间怎么传递信息?

3. 冲突解决:多个Agent对同一个问题有不同看法时怎么办?

4. 任务分发:谁来决定哪个Agent处理哪个任务?

下面我们逐一讨论。

模式一:星型协作(Hub-Spoke)

这是最简单的Multi-Agent模式,有一个中心Agent负责调度,其他Agent负责执行具体任务。

复制代码
        ┌──────────┐
        │  Router  │
        │  Agent   │
        └────┬─────┘
             │
    ┌────────┼────────┬────────────┐
    │        │        │            │
┌───▼──┐ ┌──▼──┐ ┌──▼──┐ ┌──────▼─────┐
│ Order│ │Refund│ │FAQ  │ │  escalate  │
│ Agent│ │Agent │ │Agent│ │  Agent     │
└──────┘ └─────┘ └─────┘ └────────────┘

这种模式适合场景:任务类型明确、可分类的场景。比如客服系统,不同类型的query分发给不同的专业Agent处理。

python 复制代码
from typing import Dict, List, Optional
from dataclasses import dataclass
import json

@dataclass
class Task:
    """分发任务"""
    task_id: str
    task_type: str
    original_message: str
    context: Dict = None
    priority: int = 0

@dataclass
class TaskResult:
    """任务执行结果"""
    task_id: str
    agent_id: str
    success: bool
    output: Optional[str] = None
    error: Optional[str] = None
    confidence: float = 0.0

class RouterAgent:
    """路由Agent,负责分发任务"""
    
    def __init__(self, sub_agents: Dict[str, Any], llm_client: Any):
        self.sub_agents = sub_agents  # {"order": agent1, "refund": agent2, ...}
        self.llm_client = llm_client
    
    def classify_task(self, message: str, context: Dict = None) -> Task:
        """LLM分类任务类型"""
        classification_prompt = f"""
        用户消息:「{message}」
        
        请判断这个消息涉及哪种任务类型:
        - order: 订单查询、修改地址、取消订单等
        - refund: 退货、退款、售后等
        - faq: 常见问题解答
        - escalate: 需要人工介入的复杂问题
        
        只返回一个词:order/refund/faq/escalate
        
        同时给出置信度(0-1)和分类理由。
        """
        
        response = self.llm_client.messages.create(
            model="claude-haiku-4-5-20251001",
            max_tokens=100,
            messages=[{"role": "user", "content": classification_prompt}]
        )
        
        result_text = response.content[0].text.strip().lower()
        # 解析分类结果
        # ...
        
        return Task(
            task_id=self._generate_id(),
            task_type="order",
            original_message=message,
            context=context
        )
    
    def dispatch(self, task: Task) -> TaskResult:
        """分发任务给对应Agent"""
        agent = self.sub_agents.get(task.task_type)
        if not agent:
            return TaskResult(
                task_id=task.task_id,
                agent_id="router",
                success=False,
                error=f"未知任务类型: {task.task_type}"
            )
        
        # 执行子Agent
        result = agent.run(task.original_message)
        
        return TaskResult(
            task_id=task.task_id,
            agent_id=task.task_type,
            success=(result["status"] == "completed"),
            output=result.get("message"),
            confidence=0.85
        )
    
    def run(self, message: str) -> Dict:
        """主流程"""
        task = self.classify_task(message)
        result = self.dispatch(task)
        
        # 如果置信度低或者需要人工,转 escalate
        if result.confidence < 0.7 or task.task_type == "escalate":
            return self._escalate(task)
        
        return {
            "task_id": task.task_id,
            "type": task.task_type,
            "result": result.output,
            "agent": result.agent_id
        }

这个星型架构的优点 是:简单清晰,容易实现,每个子Agent职责单一,专注度高。缺点是:Router Agent成为了单点,一旦Router判断失误,整个链路都会出错。

模式二:链式协作(Chain)

每个Agent处理完自己的部分后,把结果交给下一个Agent,形成一条处理链。

复制代码
User → Agent1 → Agent2 → Agent3 → User
         ↓        ↓        ↓
       处理1     处理2    处理3

这种模式适合有严格顺序依赖的工作流,比如:

  • 订单处理:验证 → 查库存 → 支付 → 发货 → 物流
  • 报销流程:拍照 → OCR识别 → 规则校验 → 审批 → 财务打款
python 复制代码
from typing import Callable, List, Any, Dict
import asyncio

class ChainAgent:
    """链式协作Agent"""
    
    def __init__(self):
        self.steps: List[Dict[str, Any]] = []  # [{"agent": agent1, "name": "validator"}, ...]
    
    def add_step(self, agent: Any, name: str, required: bool = True):
        """添加处理步骤"""
        self.steps.append({
            "agent": agent,
            "name": name,
            "required": required
        })
    
    async def run(self, initial_input: Dict) -> Dict:
        """执行链路"""
        context = {"input": initial_input, "steps_output": [], "errors": []}
        
        for i, step in enumerate(self.steps):
            try:
                step_input = self._prepare_step_input(context, step)
                step_output = await self._execute_step(step["agent"], step_input)
                
                context["steps_output"].append({
                    "step_name": step["name"],
                    "output": step_output,
                    "success": True
                })
                
                # 检查是否需要中断链路
                if step_output.get("interrupt"):
                    return {
                        "status": "interrupted",
                        "step": i,
                        "reason": step_output.get("interrupt_reason"),
                        "context": context
                    }
                    
            except Exception as e:
                if step["required"]:
                    return {
                        "status": "failed",
                        "step": i,
                        "error": str(e),
                        "context": context
                    }
                else:
                    context["errors"].append({
                        "step": i,
                        "error": str(e)
                    })
                    continue
        
        return {
            "status": "completed",
            "context": context,
            "final_output": self._prepare_final_output(context)
        }
    
    def _prepare_step_input(self, context: Dict, step: Dict) -> Dict:
        """准备当前步骤的输入"""
        # 从 context 中提取前面步骤的输出,组装成当前步骤需要的格式
        pass
    
    def _execute_step(self, agent: Any, step_input: Dict) -> Dict:
        """执行单个步骤"""
        # 实际执行逻辑
        pass

链式协作的核心挑战是上下文传递。每个步骤的输出要能被下一个步骤理解,这需要统一的数据格式约定。

模式三:层级协作(Hierarchical)

对于更复杂的场景,可以设计多层级Agent架构。顶层Agent负责全局规划,中层Agent负责协调,下层Agent负责具体执行。

复制代码
         ┌─────────────┐
         │   Planner   │  ← 全局规划、任务分解
         │   Agent     │
         └──────┬──────┘
                │
    ┌───────────┼───────────┐
    │           │           │
┌───▼───┐  ┌───▼───┐  ┌───▼───┐
│Coord.1 │  │Coord.2│  │Coord.3│  ← 协调层
│Agent  │  │Agent  │  │Agent  │
└───┬───┘  └───┬───┘  └───┬───┘
    │          │          │
┌───┴───┐  ┌───┴───┐  ┌───┴───┐
│ Exec1 │  │ Exec2 │  │ Exec3 │  ← 执行层
│Agents │  │Agents │  │Agents │
└───────┘  └───────┘  └───────┘

这是一个实际案例的简化版本:

python 复制代码
class HierarchicalAgent:
    """层级协作Agent"""
    
    def __init__(self):
        # 规划层
        self.planner = self._create_planner()
        
        # 协调层
        self.coordinators = {
            "research": self._create_coordinator("research"),
            "analysis": self._create_coordinator("analysis"),
            "writing": self._create_coordinator("writing"),
            "review": self._create_coordinator("review")
        }
        
        # 执行层
        self.executors = {
            "search": self._create_executor("search"),
            "scrape": self._create_executor("scrape"),
            "llm_write": self._create_executor("llm_write"),
            "check": self._create_executor("check")
        }
    
    async def run(self, task_description: str) -> Dict:
        """处理复杂任务的完整流程"""
        # Step 1: 规划层分析任务,确定需要哪些步骤
        plan = await self.planner.create_plan(task_description)
        
        # Step 2: 规划层创建任务队列,分组给协调层
        task_groups = await self.planner.distribute_tasks(plan)
        
        # Step 3: 各协调层并行调度执行层
        group_results = await asyncio.gather(*[
            self._execute_group(group, self.coordinators[group["type"]])
            for group in task_groups
        ])
        
        # Step 4: 协调层汇总结果
        consolidated = await self._consolidate_results(group_results)
        
        # Step 5: 审查层质量检查
        if consolidated.needs_review:
            review_result = await self.coordinators["review"].review(consolidated)
            if review_result.needs_revision:
                # 打回重做逻辑
                pass
        
        return {
            "status": "completed",
            "output": consolidated.output,
            "metadata": {
                "total_steps": plan.total_steps,
                "parallel_groups": len(task_groups),
                "review_passed": True
            }
        }

这种架构的优势在于并行能力最大化。不同组的任务可以同时执行,只有存在依赖关系的任务才需要串行。

它的挑战是状态同步结果汇总的复杂度更高。当一个高层任务中途需要调整计划时,如何通知下层Agent撤销正在执行的任务,这是一个需要仔细设计的点。


Agent之间的通信协议设计

无论采用哪种Multi-Agent架构,Agent之间的通信协议都是核心。这里有几种常见的模式:

共享上下文模式

所有Agent共享一个中央存储(内存、数据库、消息队列),通过读写共享数据来协作。

python 复制代码
import redis
import json
from datetime import timedelta

class SharedContext:
    """共享上下文通信"""
    
    def __init__(self, redis_client: redis.Redis):
        self.redis = redis_client
    
    def write(self, key: str, value: Dict, ttl: int = 3600):
        """写入共享数据"""
        self.redis.setex(
            f"agent:context:{key}",
            timedelta(seconds=ttl),
            json.dumps(value)
        )
    
    def read(self, key: str) -> Optional[Dict]:
        """读取共享数据"""
        data = self.redis.get(f"agent:context:{key}")
        return json.loads(data) if data else None
    
    def update(self, key: str, updates: Dict):
        """原子更新"""
        # 使用 Lua 脚本保证原子性
        lua_script = """
        local current = redis.call('GET', KEYS[1])
        if current then
            local obj = cjson.decode(current)
            local updates = cjson.decode(ARGV[1])
            for k, v in pairs(updates) do
                obj[k] = v
            end
            redis.call('SETEX', KEYS[1], ARGV[2], cjson.encode(obj))
            return cjson.encode(obj)
        end
        return nil
        """
        # 执行脚本...
    
    def publish(self, channel: str, message: Dict):
        """发布消息到通道"""
        self.redis.publish(f"agent:channel:{channel}", json.dumps(message))
    
    def subscribe(self, channel: str):
        """订阅通道"""
        pubsub = self.redis.pubsub()
        pubsub.subscribe(f"agent:channel:{channel}")
        return pubsub

这种模式的好处是松耦合,Agent之间不需要直接通信,只读写共享数据即可。缺点是引入了外部依赖(Redis),并且数据一致性需要额外保障。

直接消息模式

Agent之间通过RPC或直接调用传递消息,适合层级较少、Agent数量也较少的场景。

python 复制代码
from typing import Protocol, runtime_checkable
import asyncio

@runtime_checkable
class AgentProtocol(Protocol):
    """Agent协议定义"""
    async def receive(self, message: Dict) -> Dict:
        """接收消息并返回结果"""
        ...
    
    async def forward(self, message: Dict, dest: str) -> None:
        """转发消息给其他Agent"""
        ...

class MessageBus:
    """消息总线"""
    
    def __init__(self):
        self.agents: Dict[str, AgentProtocol] = {}
        self.message_queue: asyncio.Queue = asyncio.Queue()
    
    def register(self, agent_id: str, agent: AgentProtocol):
        """注册Agent"""
        self.agents[agent_id] = agent
    
    async def send(self, from_agent: str, to_agent: str, message: Dict):
        """直接发送消息"""
        if to_agent not in self.agents:
            raise ValueError(f"Unknown agent: {to_agent}")
        
        target = self.agents[to_agent]
        result = await target.receive(message)
        
        # 异步通知发送方(可选)
        await self._notify(from_agent, "message_sent", {
            "to": to_agent,
            "message_id": message.get("id")
        })
        
        return result
    
    async def broadcast(self, from_agent: str, message: Dict):
        """广播消息"""
        tasks = []
        for agent_id, agent in self.agents.items():
            if agent_id != from_agent:
                tasks.append(agent.receive(message))
        
        results = await asyncio.gather(*tasks, return_exceptions=True)
        return results

生产环境的关键设计:状态持久化与容错

状态持久化

当一个任务需要跨越几小时甚至几天才能完成时(比如等待用户确认、处理跨部门的审批流程),Agent的运行状态必须持久化到磁盘或数据库。

python 复制代码
from datetime import datetime
from enum import Enum
import json
import os

class TaskState(Enum):
    PENDING = "pending"
    RUNNING = "running"
    WAITING_USER_INPUT = "waiting_user_input"
    COMPLETED = "completed"
    FAILED = "failed"
    CANCELLED = "cancelled"

@dataclass
class PersistedAgentState:
    """持久化的Agent状态"""
    session_id: str
    task_id: str
    state: TaskState
    current_step: int
    conversation_history: List[Dict]
    session_data: Dict[str, Any]
    created_at: str
    updated_at: str
    expires_at: str  # 超过这个时间没处理则任务失败

class AgentStateStore:
    """状态持久化存储"""
    
    def __init__(self, storage_path: str = "./agent_states"):
        self.storage_path = storage_path
        os.makedirs(storage_path, exist_ok=True)
    
    def _get_file_path(self, session_id: str) -> str:
        return os.path.join(self.storage_path, f"{session_id}.json")
    
    def save(self, state: PersistedAgentState):
        """保存状态"""
        file_path = self._get_file_path(state.session_id)
        with open(file_path, "w", encoding="utf-8") as f:
            json.dump(asdict(state), f, ensure_ascii=False, indent=2)
    
    def load(self, session_id: str) -> Optional[PersistedAgentState]:
        """加载状态"""
        file_path = self._get_file_path(session_id)
        if not os.path.exists(file_path):
            return None
        
        with open(file_path, "r", encoding="utf-8") as f:
            data = json.load(f)
        
        return PersistedAgentState(**data)
    
    def delete(self, session_id: str):
        """删除状态"""
        file_path = self._get_file_path(session_id)
        if os.path.exists(file_path):
            os.remove(file_path)
    
    def list_expired(self) -> List[PersistedAgentState]:
        """列出已过期的任务"""
        expired = []
        now = datetime.now().isoformat()
        
        for file_name in os.listdir(self.storage_path):
            if not file_name.endswith(".json"):
                continue
            
            file_path = os.path.join(self.storage_path, file_name)
            with open(file_path, "r", encoding="utf-8") as f:
                data = json.load(f)
            
            state = PersistedAgentState(**data)
            if state.expires_at < now and state.state not in [TaskState.COMPLETED, TaskState.CANCELLED]:
                expired.append(state)
        
        return expired
    
    def cleanup_expired(self):
        """清理过期任务"""
        for state in self.list_expired():
            state.state = TaskState.FAILED
            state.session_data["fail_reason"] = "task_expired"
            self.save(state)

这套持久化机制解决了一个实际问题:当你的Agent系统需要处理跨天的任务时,重启服务不会丢失正在运行的任务状态。

容错机制

生产环境里,Agent系统需要处理的异常情况远比测试环境多:

python 复制代码
import time
from typing import Callable, TypeVar, Optional
import asyncio

T = TypeVar('T')

class RetryPolicy:
    """重试策略"""
    
    def __init__(
        self,
        max_attempts: int = 3,
        base_delay: float = 1.0,
        max_delay: float = 60.0,
        exponential_backoff: bool = True,
        jitter: bool = True
    ):
        self.max_attempts = max_attempts
        self.base_delay = base_delay
        self.max_delay = max_delay
        self.exponential_backoff = exponential_backoff
        self.jitter = jitter
    
    def calculate_delay(self, attempt: int) -> float:
        """计算重试延迟"""
        if self.exponential_backoff:
            delay = self.base_delay * (2 ** (attempt - 1))
        else:
            delay = self.base_delay
        
        delay = min(delay, self.max_delay)
        
        if self.jitter:
            import random
            delay = delay * (0.5 + random.random() * 0.5)
        
        return delay

async def with_retry(
    func: Callable[..., T],
    *args,
    retry_policy: Optional[RetryPolicy] = None,
    retry_on: tuple = (Exception,),
    **kwargs
) -> T:
    """带重试的函数执行"""
    if retry_policy is None:
        retry_policy = RetryPolicy()
    
    last_error = None
    for attempt in range(1, retry_policy.max_attempts + 1):
        try:
            result = func(*args, **kwargs)
            if asyncio.iscoroutine(result):
                result = await result
            return result
            
        except retry_on as e:
            last_error = e
            if attempt < retry_policy.max_attempts:
                delay = retry_policy.calculate_delay(attempt)
                await asyncio.sleep(delay)
            else:
                raise last_error
    
    raise last_error

# 使用示例
async def robust_tool_call(tool_name: str, params: Dict) -> Dict:
    """可靠的工具调用"""
    
    async def _call():
        # 实际的API调用
        result = await external_api.call(tool_name, params)
        
        # 检查业务层面的错误(不仅仅是网络错误)
        if result.get("error_code") == "RATE_LIMIT":
            raise RateLimitError()
        
        return result
    
    try:
        return await with_retry(
            _call,
            retry_policy=RetryPolicy(
                max_attempts=5,
                base_delay=2.0,
                exponential_backoff=True
            ),
            retry_on=(RateLimitError, TimeoutError, ConnectionError)
        )
    except Exception as e:
        # 最后兜底:使用本地LLM基于已有信息生成回复
        return await fallback_generation(e)

实战案例:从零构建一个客服多Agent系统

需求描述

我们的客户是一家电商SaaS公司,每天处理约2000个客服会话。之前的方案是纯规则匹配+关键词识别,准确率约65%,用户满意度不到3分(满分5分)。

他们希望用Agent架构重构这个客服系统,达到以下目标:

  • 准确率提升到90%以上
  • 能够处理退换货、订单查询、地址修改、优惠咨询、投诉处理等5大类型
  • 复杂问题能无缝转人工
  • 系统能持续学习和优化

架构设计

复制代码
                    ┌──────────────┐
                    │   入口消息   │
                    │   (微信/APP) │
                    └──────┬───────┘
                           │
                    ┌──────▼──────┐
                    │   NLU引擎   │
                    │ (意图识别+  │
                    │  槽位提取)  │
                    └──────┬──────┘
                           │
              ┌────────────┼────────────┐
              │            │            │
        ┌─────▼────┐ ┌────▼────┐ ┌─────▼────┐
        │ 闲聊引擎 │ │ 任务型  │ │ 投诉处理 │
        │  (闲聊)  │ │  Agent  │ │  Agent   │
        └──────────┘ └────┬────┘ └─────┬────┘
                          │            │
                    ┌─────┴────────────┴─────┐
                    │                       │
              ┌─────▼──────────┐      ┌──────▼──────┐
              │  订单子Agent   │      │  售后子Agent│
              │ (查单/修改/   │      │ (退换货/   │
              │   取消)       │      │  退款)     │
              └───────────────┘      └─────────────┘

核心实现

意图分类Agent

python 复制代码
INTENT_CLASSIFICATION_PROMPT = """
你是一个客服意图分类器。根据用户的最新消息,判断用户的意图。

用户历史对话:
{history}

用户最新消息:「{latest_message}」

请从以下意图中选择一个最匹配的:
1. order_query - 查订单状态、订单详情
2. order_modify - 修改订单(地址、电话、备注等)
3. order_cancel - 取消订单
4. refund_request - 申请退款
5. return_request - 申请退货
6. coupon_query - 优惠券咨询
7. complaint - 投诉
8. chitchat - 闲聊/问候
9. escalate - 需要人工介入

输出格式(只输出JSON,不要其他内容):
{{
    "intent": "意图代码",
    "confidence": 0.0-1.0之间的数字,
    "slots": {{
        "order_id": "如果提到了订单号,填在这里",
        "product_name": "如果提到了商品名称,填在这里"
    }},
    "reasoning": "判断理由,1-2句话"
}}
"""

async def classify_intent(history: List[Dict], latest_message: str) -> Dict:
    history_text = "\n".join([
        f"用户:{h['user']}\n助手:{h['assistant']}"
        for h in history[-5:]
    ])
    
    response = await llm_client.messages.create(
        model="claude-haiku-4-5-20251001",
        max_tokens=500,
        messages=[{
            "role": "user", 
            "content": INTENT_CLASSIFICATION_PROMPT.format(
                history=history_text,
                latest_message=latest_message
            )
        }]
    )
    
    # 解析JSON响应
    result_text = response.content[0].text.strip()
    # 处理可能的markdown代码块
    if result_text.startswith("```"):
        result_text = result_text.split("\n")[-1].split("```")[0]
    
    return json.loads(result_text)

订单子Agent的详细实现

python 复制代码
class OrderAgent:
    """订单处理Agent"""
    
    def __init__(self, tools: List[Dict]):
        self.tools = tools
        self.state_machine = OrderStateMachine()
    
    async def handle(self, message: str, context: Dict) -> Dict:
        """主处理逻辑"""
        current_state = context.get("state", "initial")
        
        if current_state == "initial":
            return await self._handle_initial(message, context)
        elif current_state == "awaiting_order_id":
            return await self._handle_order_id_input(message, context)
        elif current_state == "order_confirmed":
            return await self._handle_confirmed_order(message, context)
        elif current_state == "awaiting_confirmation":
            return await self._handle_user_confirmation(message, context)
        else:
            return await self._escalate(message, context, "unknown_state")
    
    async def _handle_initial(self, message: str, context: Dict) -> Dict:
        """处理初始状态"""
        # 尝试从消息中提取订单号
        order_id = extract_order_id(message)
        
        if order_id:
            # 有订单号,查询订单状态
            order_info = await self._query_order(order_id)
            if order_info:
                context["order_id"] = order_id
                context["order_info"] = order_info
                context["state"] = "order_confirmed"
                return {
                    "response": self._format_order_status(order_info),
                    "next_state": "order_confirmed",
                    "suggested_actions": ["cancel_order", "modify_address"]
                }
            else:
                return {
                    "response": "抱歉,系统没有找到这个订单。请确认订单号是否正确,或者我帮您查询其他订单?",
                    "next_state": "initial",
                    "retry_count": context.get("retry_count", 0) + 1
                }
        else:
            # 没有订单号,引导用户输入
            return {
                "response": "请问您的订单号是多少?订单号是12位数字,可以在订单详情页找到。",
                "next_state": "awaiting_order_id",
                "collect_slot": "order_id"
            }
    
    async def _handle_order_id_input(self, message: str, context: Dict) -> Dict:
        """处理用户输入订单号"""
        order_id = extract_order_id(message)
        
        if order_id:
            order_info = await self._query_order(order_id)
            if order_info:
                context["order_id"] = order_id
                context["order_info"] = order_info
                context["state"] = "order_confirmed"
                return {
                    "response": self._format_order_status(order_info),
                    "next_state": "order_confirmed",
                    "suggested_actions": ["cancel_order", "modify_address", "contact_support"]
                }
        
        # 订单号无效或查询失败
        retry_count = context.get("retry_count", 0)
        if retry_count >= 2:
            return await self._escalate(
                message, context, 
                "order_lookup_failed_after_retries"
            )
        
        return {
            "response": "抱歉,这个订单号似乎不存在。请确认后重新输入,或者回复「转人工」寻求帮助。",
            "next_state": "awaiting_order_id",
            "retry_count": retry_count + 1
        }

实际运行数据

系统上线三个月后的关键指标:

指标 上线前 上线后 提升
意图识别准确率 65% 94% +29%
用户满意度 3.1/5 4.3/5 +39%
平均处理时长 8.5分钟 4.2分钟 -51%
转人工率 35% 12% -66%
7x24可用性 -

核心的提升来自于两点:一是意图分类的准确率提升,直接减少了错误分发导致的用户流失;二是状态机设计让多轮对话的连贯性大幅提升,用户不用每次都从头解释自己的问题。


总结与讨论

今天这篇文章,我从一个资深工程师的视角,把Agent落地中最关键的两个架构层次------单Agent状态机设计和Multi-Agent协作拓扑------讲清楚了。

核心要点就三个:

第一,单Agent不是「一个模型调用」,而是一个包含状态机、工具封装、对话历史管理的完整系统。没有这些组件,复杂场景下必崩。

第二,Multi-Agent的核心挑战不是「怎么让多个Agent一起工作」,而是「怎么定义角色边界、怎么设计通信协议、怎么解决冲突」。这些问题不解决,Agent数量越多,系统越不稳定。

第三,生产环境必须考虑状态持久化和容错机制。你的Agent系统不可能不重启、不可能不遇到网络抖动、不可能不遇到异常输入。在设计之初就把这些考虑进去,后期运维会省心很多。

留几个讨论问题给你:

  1. 你在落地Agent的过程中,遇到了哪些具体的架构挑战?
  2. 你觉得Multi-Agent之间最难处理的是什么问题------角色定义、通信协议、还是冲突解决?
  3. 如果让你设计一个通用的Agent框架,你会怎么组织它的核心组件?

欢迎留言交流。


参考资源

相关推荐
阿维的博客日记2 小时前
为什么mcp还需要Prompts??
人工智能·agent
拖拖7652 小时前
Neural Turing Machines:让神经网络学会使用“外部记忆”
人工智能
OJAC1112 小时前
【无标题】
人工智能
eastyuxiao2 小时前
文心一言和DeepSeek V4哪个更好?
人工智能·大模型·文心一言·deepseek·deepseek-v4·deepseek‑v4
scglwsj3 小时前
序章:为什么企业级应用需要 Harness Engineering
人工智能
zubylon3 小时前
前端 RAG:把文档检索接到聊天页
前端·人工智能·算法
iNeuOS工业互联网3 小时前
iNeuOS工业互联网操作系统集成大模型智库(iNeuOS_AiMind·心智灵慧)
大数据·人工智能·智能制造·视频·工业互联网·ineuos
人工智能AI技术3 小时前
终身学习基础:AI 持续进化不遗忘旧知识
人工智能
Surplusx3 小时前
HCIP交换综合实验
网络·智能路由器