一、AI Agent架构演进:从Rule-based到LLM-based

过去3年,我参与了12个AI Agent项目的落地,踩过无数坑,也积累了一些实战经验。今天把这些经验整理出来,希望能帮你少走弯路。

先说说Agent架构的演进路线。

第一代:规则引擎+决策树

这是最早期的方案,用if-else堆出来的"智能"。优点是行为完全可预测,出了问题能精准定位。缺点是僵硬到让人崩溃——用户换个说法,系统就懵了。

# 第一代Agent的典型实现
def process_query(query):
    if "查询" in query and "余额" in query:
        return check_balance()
    elif "转账" in query:
        return transfer_money()
    else:
        return "抱歉,我无法理解您的需求"

第二代:单Agent+ReAct模式

LLM的加入让Agent具备了理解自然语言的能力。ReAct(Reasoning + Acting)模式让模型能自主推理并调用工具。灵活性大幅提升,但问题是不可控——模型有时候会"抽风",调用错误的工具,或者陷入死循环。

第三代:多Agent协同

多个Agent各司其职,协同完成复杂任务。比如一个Agent负责理解用户意图,一个负责查询数据,一个负责生成回复。这种方式强大但复杂,调试难度直线上升。

选型建议

不要盲目追求复杂的架构。如果你的业务场景简单,规则引擎可能就够用了;如果是中等复杂度的对话系统,单Agent+工具调用是性价比最高的选择;只有当你需要处理跨领域、多步骤的复杂任务时,才考虑多Agent协同。

二、单Agent开发的5个常见坑

坑1:Prompt设计不当导致循环调用

这是最常见的问题。Agent反复调用同一个工具,或者在两个工具之间来回切换,陷入死循环。

# 错误的Prompt设计
prompt = """你是一个助手,可以调用工具完成任务。"""

# 正确的Prompt设计
prompt = """你是一个智能助手,具备以下能力:
1. 查询用户信息(调用search_user工具)
2. 处理订单(调用process_order工具)

执行规则:
- 每个任务最多调用3次工具
- 如果工具返回错误,直接向用户说明情况
- 禁止重复调用同一个工具
- 任务完成后立即输出结果,不要继续调用工具"""

关键是在Prompt中明确限制调用次数和退出条件。我通常会在系统层面加一个max_iterations参数,默认设置为5次。

class Agent:
    def __init__(self, max_iterations=5):
        self.max_iterations = max_iterations
        self.call_history = []
    
    def run(self, task):
        for i in range(self.max_iterations):
            action = self.decide(task, self.call_history)
            if action == "FINISH":
                break
            result = self.execute(action)
            self.call_history.append({"action": action, "result": result})
        else:
            return "任务执行超过最大迭代次数,已终止"

坑2:工具调用参数幻觉

模型会"瞎编"参数。比如你的工具要求传入user_id(整数),模型给你传了个"user_12345"(字符串)。更离谱的是,有时候模型会传入根本不存在的参数。

# 工具定义时,参数描述必须详细
tools = [
    {
        "name": "search_user",
        "description": "根据用户ID查询用户信息",
        "parameters": {
            "user_id": {
                "type": "integer",
                "description": "用户ID,必须是纯数字,例如:10086",
                "required": True
            }
        }
    }
]

# 在工具执行前加参数校验
def validate_params(tool_name, params):
    tool_config = get_tool_config(tool_name)
    for key, value in params.items():
        if key not in tool_config["parameters"]:
            raise ValueError(f"参数{key}不存在")
        expected_type = tool_config["parameters"][key]["type"]
        if expected_type == "integer" and not isinstance(value, int):
            raise ValueError(f"参数{key}必须是整数,当前值:{value}")

坑3:长任务执行超时与状态丢失

有些任务执行时间较长,比如批量处理数据、调用外部API等。如果在这个过程中出现超时或者异常,前面的工作就白费了。

import time
from functools import wraps

def timeout_handler(seconds):
    def decorator(func):
        @wraps(func)
        def wrapper(*args, **kwargs):
            start_time = time.time()
            result = func(*args, **kwargs)
            elapsed = time.time() - start_time
            if elapsed > seconds:
                raise TimeoutError(f"工具执行超时:{elapsed:.2f}秒")
            return result
        return wrapper
    return decorator

# 使用示例
@timeout_handler(30)
def query_external_api(params):
    # 外部API调用
    response = requests.post(url, json=params)
    return response.json()

对于长任务,建议采用分段执行+状态持久化的方式。

class LongTaskExecutor:
    def __init__(self, task_id):
        self.task_id = task_id
        self.state_file = f"task_state_{task_id}.json"
    
    def save_state(self, step, data):
        state = {"step": step, "data": data, "timestamp": time.time()}
        with open(self.state_file, "w") as f:
            json.dump(state, f)
    
    def load_state(self):
        if os.path.exists(self.state_file):
            with open(self.state_file, "r") as f:
                return json.load(f)
        return None

坑4:错误处理不完善导致崩溃

很多开发者只关注正常流程,忽略了异常情况。工具调用失败、网络超时、返回数据格式异常……这些都会导致Agent崩溃。

def safe_execute_tool(tool_name, params):
    try:
        result = execute_tool(tool_name, params)
        if result.get("status") == "error":
            return {
                "success": False,
                "error": result.get("message", "工具执行失败"),
                "suggestion": "请检查参数是否正确"
            }
        return {"success": True, "data": result}
    except TimeoutError as e:
        return {"success": False, "error": "执行超时", "suggestion": "请稍后重试"}
    except Exception as e:
        return {"success": False, "error": str(e), "suggestion": "请联系管理员"}

坑5:日志缺失导致调试困难

线上出了问题,没有日志就是瞎子摸象。我见过太多项目因为日志不完善,排查一个问题要花半天时间。

import logging

class AgentLogger:
    def __init__(self, agent_id):
        self.logger = logging.getLogger(f"agent_{agent_id}")
        self.logger.setLevel(logging.DEBUG)
        handler = logging.FileHandler(f"agent_{agent_id}.log")
        formatter = logging.Formatter(
            "%(asctime)s - %(levelname)s - %(message)s"
        )
        handler.setFormatter(formatter)
        self.logger.addHandler(handler)
    
    def log_tool_call(self, tool_name, params, result, duration):
        self.logger.info(
            f"TOOL_CALL | {tool_name} | params={params} | "
            f"result={result} | duration={duration:.2f}s"
        )
    
    def log_llm_call(self, prompt_tokens, completion_tokens, duration):
        self.logger.info(
            f"LLM_CALL | prompt_tokens={prompt_tokens} | "
            f"completion_tokens={completion_tokens} | duration={duration:.2f}s"
        )

三、工具函数设计:Agent能力的核心

工具函数是Agent的"手脚",设计不好,Agent就是个废物。

命名规范

采用动词+名词的格式,一眼就能看出工具的功能。比如search_document、create_order、update_status。避免使用get_data、process这种模糊的名字。

参数描述

模型完全依赖参数描述来理解如何使用工具。描述必须详细到"小白级别"。

# 差的参数描述
"parameters": {
    "date": {"type": "string"}
}

# 好的参数描述
"parameters": {
    "date": {
        "type": "string",
        "description": "查询日期,格式为YYYY-MM-DD,例如:2024-01-15。必须是过去的日期,不能是未来日期"
    }
}

返回值设计

统一返回结构化JSON,包含状态码、数据和提示信息。

def search_user(user_id):
    try:
        user = db.query(f"SELECT * FROM users WHERE id={user_id}")
        if not user:
            return {
                "status": 404,
                "data": None,
                "message": f"未找到ID为{user_id}的用户"
            }
        return {
            "status": 200,
            "data": {
                "user_id": user.id,
                "name": user.name,
                "email": user.email
            },
            "message": "查询成功"
        }
    except Exception as e:
        return {
            "status": 500,
            "data": None,
            "message": f"查询失败:{str(e)}"
        }

实战案例:6个工具函数

在一个客服Agent项目中,我设计了6个核心工具函数:

# 工具1:查询用户信息
def search_user(user_id: int) -> dict:
    """根据用户ID查询用户基本信息"""
    pass

# 工具2:查询订单列表
def search_orders(user_id: int, status: str = None) -> dict:
    """查询用户的订单列表,可按状态筛选"""
    pass

# 工具3:查询订单详情
def get_order_detail(order_id: int) -> dict:
    """根据订单ID查询订单详细信息"""
    pass

# 工具4:提交退款申请
def submit_refund(order_id: int, reason: str, amount: float) -> dict:
    """为指定订单提交退款申请"""
    pass

# 工具5:查询知识库
def search_knowledge(query: str, top_k: int = 3) -> dict:
    """从知识库中检索相关文档"""
    pass

# 工具6:转人工客服
def transfer_to_human(user_id: int, reason: str) -> dict:
    """将对话转接到人工客服"""
    pass

四、多Agent协同的3种模式

当单Agent无法满足需求时,就需要引入多Agent协同。我总结了三常用的模式:

模式1:主从模式(Master-Worker)

一个Master Agent负责任务分解和调度,多个Worker Agent负责执行具体任务。这种模式适合任务可以清晰分解的场景。

class MasterAgent:
    def __init__(self, workers):
        self.workers = workers  # Worker Agent列表
    
    def process(self, task):
        # 任务分解
        subtasks = self.decompose(task)
        results = []
        for subtask in subtasks:
            # 分配给合适的Worker
            worker = self.select_worker(subtask)
            result = worker.execute(subtask)
            results.append(result)
        # 汇总结果
        return self.aggregate(results)

模式2:流水线模式(Pipeline)

Agent按顺序执行,前一个Agent的输出是后一个Agent的输入。适合有明确处理流程的场景,比如"理解意图→查询数据→生成回复"。

class PipelineAgent:
    def __init__(self):
        self.agents = [
            IntentAgent(),      # 意图识别
            QueryAgent(),       # 数据查询
            ResponseAgent()     # 回复生成
        ]
    
    def process(self, user_input):
        data = user_input
        for agent in self.agents:
            data = agent.execute(data)
        return data

模式3:协作模式(Collaboration)

多个Agent平等协作,通过讨论和协商达成共识。适合需要多角度思考的复杂问题。

选型建议

主从模式适合任务边界清晰的场景,比如数据分析、报告生成;流水线模式适合流程固定的场景,比如客服对话、内容审核;协作模式适合需要创意和讨论的场景,比如方案设计、决策支持。

五、多Agent通信:消息传递与状态同步

多Agent协同,通信是关键。

消息格式设计

统一的消息协议能降低沟通成本。我通常采用以下格式:

message = {
    "from": "agent_intent",
    "to": "agent_query",
    "type": "request",  # request/response/notification
    "task_id": "task_20240115_001",
    "payload": {
        "intent": "query_order",
        "user_id": 10086,
        "order_id": None
    },
    "timestamp": 1705305600
}

状态管理

两种方案:共享状态和消息传递。

共享状态适合紧耦合的场景,所有Agent访问同一个状态存储。优点是实现简单,缺点是容易出现并发问题。

class SharedState:
    def __init__(self):
        self.state = {}
        self.lock = threading.Lock()
    
    def update(self, key, value):
        with self.lock:
            self.state[key] = value
    
    def get(self, key):
        with self.lock:
            return self.state.get(key)

消息传递适合松耦合的场景,Agent之间通过消息同步状态。优点是可扩展性好,缺点是消息丢失和延迟问题。

死锁预防

多Agent系统最怕死锁。两个Agent互相等待对方释放资源,整个系统就卡死了。

解决方案是设置超时机制和重试策略:

def execute_with_timeout(agent, task, timeout=30):
    try:
        result = agent.execute_with_timeout(task, timeout)
        return result
    except TimeoutError:
        # 超时后释放资源,通知其他Agent
        agent.release_resources()
        agent.notify_peers("任务超时,已释放资源")
        return {"status": "timeout", "message": "执行超时"}

框架对比

目前主流的多Agent框架有AutoGen和CrewAI。AutoGen更灵活,支持复杂的对话模式,适合研究和实验;CrewAI更工程化,提供了角色定义、任务编排等高级功能,适合生产环境。选择哪个取决于你的具体需求。

六、安全与可控:Agent开发的红线

Agent能调用工具、访问数据,一旦出问题就是大事。安全不是可选项,是必须项。

权限控制

工具调用必须有白名单机制。Agent只能调用被授权的工具,不能越权操作。

class ToolPermission:
    def __init__(self):
        self.permissions = {
            "agent_customer_service": [
                "search_user", "search_orders", "search_knowledge"
            ],
            "agent_finance": [
                "search_user", "query_balance", "submit_refund"
            ]
        }
    
    def check_permission(self, agent_id, tool_name):
        allowed_tools = self.permissions.get(agent_id, [])
        if tool_name not in allowed_tools:
            raise PermissionError(
                f"Agent {agent_id} 无权调用工具 {tool_name}"
            )

输入校验

Prompt注入攻击是Agent面临的主要安全威胁。恶意用户可能通过精心构造的输入,让Agent执行非预期操作。

def validate_user_input(user_input):
    # 检查是否包含可疑的指令
    suspicious_patterns = [
        "忽略之前的指令",
        "ignore previous instructions",
        "你现在是",
        "system:",
        "admin:"
    ]
    for pattern in suspicious_patterns:
        if pattern.lower() in user_input.lower():
            raise ValueError("检测到可疑输入,已拦截")
    
    # 限制输入长度
    if len(user_input) > 500:
        raise ValueError("输入过长,请简化问题")
    
    return user_input

输出审核

Agent的输出也要审核,防止泄露敏感信息或生成不当内容。

def filter_output(output):
    # 敏感信息过滤
    sensitive_patterns = [
        r"\d{18}",  # 身份证号
        r"1[3-9]\d{9}",  # 手机号
        r"\d{16,19}"  # 银行卡号
    ]
    for pattern in sensitive_patterns:
        output = re.sub(pattern, "***", output)
    return output

审计日志

完整记录Agent的每一次操作,包括输入、输出、工具调用、执行时间等。出了问题能快速定位原因。

def audit_log(agent_id, action, input_data, output_data, timestamp):
    log_entry = {
        "agent_id": agent_id,
        "action": action,
        "input": input_data,
        "output": output_data,
        "timestamp": timestamp,
        "status": "success"  # or "error"
    }
    # 写入审计日志
    audit_logger.info(json.dumps(log_entry, ensure_ascii=False))

七、性能优化:让Agent更快更稳

性能问题在生产环境中会被放大。日均2000次调用和日均20万次调用,完全是两个世界。

工具调用优化

批量调用和并行执行能显著提升性能。

import asyncio

async def batch_execute_tools(tool_calls):
    tasks = []
    for call in tool_calls:
        task = asyncio.create_task(
            execute_tool_async(call["tool_name"], call["params"])
        )
        tasks.append(task)
    
    results = await asyncio.gather(*tasks, return_exceptions=True)
    return results

LLM调用优化

缓存能减少重复调用,流式输出能提升用户体验。

from functools import lru_cache

@lru_cache(maxsize=1000)
def llm_call_cached(prompt):
    """带缓存的LLM调用"""
    return call_llm(prompt)

def stream_response(prompt):
    """流式输出响应"""
    for chunk in call_llm_stream(prompt):
        yield chunk
        time.sleep(0.01)  # 控制输出速度

错误恢复

断点续传和状态回滚能让Agent从失败中恢复,而不是从头开始。

class CheckpointManager:
    def __init__(self, task_id):
        self.task_id = task_id
        self.checkpoint_file = f"checkpoint_{task_id}.json"
    
    def save_checkpoint(self, step, context):
        checkpoint = {
            "step": step,
            "context": context,
            "timestamp": time.time()
        }
        with open(self.checkpoint_file, "w") as f:
            json.dump(checkpoint, f)
    
    def restore_checkpoint(self):
        if os.path.exists(self.checkpoint_file):
            with open(self.checkpoint_file, "r") as f:
                return json.load(f)
        return None

八、落地案例:某金融企业智能客服Agent项目

分享一个真实案例。

项目背景

某金融企业的客服团队每天要处理2000+次咨询,其中60%是重复性问题:查询余额、查询交易记录、修改密码等。人工客服成本高,响应速度慢,客户满意度低。

技术方案

采用单Agent架构,配置8个工具函数:

tools = [
    search_user,           # 查询用户信息
    search_orders,         # 查询订单列表
    get_order_detail,      # 查询订单详情
    check_balance,         # 查询余额
    search_transactions,   # 查询交易记录
    reset_password,        # 重置密码
    search_knowledge,      # 知识库检索
    transfer_to_human      # 转人工客服
]

结合知识库RAG(检索增强生成),将企业的产品文档、FAQ、业务规则等结构化知识注入Agent。

实施效果

上线3个月后,关键指标:

  • 问题解决率:85%(无需转人工)
  • 平均响应时间:2.3秒
  • 客户满意度:4.2/5.0
  • 人工客服工作量下降:62%

关键经验

  1. Prompt设计要反复打磨。我们迭代了17个版本才达到理想效果。
  2. 工具函数要足够"傻瓜"。参数描述越详细,模型调用越准确。
  3. 错误处理要完善。线上环境什么情况都可能发生。
  4. 日志和监控不能省。出了问题能快速定位才是真本事。

九、文末互动

你在开发Agent时遇到过最棘手的问题是什么?是Prompt调不好,还是工具调用总出错?欢迎在评论区聊聊,我们一起探讨解决方案。


作者简介

作者所在的黑箭科技专注于企业级AI应用开发,已为多家企业提供AI Agent、智能客服、自动化流程等解决方案。如果你的企业正在考虑落地AI Agent,或者在开发过程中遇到技术难题,欢迎联系黑箭科技,我们可以提供专业的咨询和定制开发服务。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐