Skip to content

Agent:规划、工具调用与记忆 ​

Agent = LLM + 工具 + 循环。

它和普通对话的区别只有一个:模型可以采取行动,并根据结果继续决策。

一、最小 Agent 就是一个 while 循环 ​

模型决策说话,还是调工具?
执行工具按参数调用,拿到结果
结果回填把结果放回消息列表
再决策基于新信息继续或收尾

循环直到任务完成或达到步数上限

python
def run_agent(question, tools, max_steps=8):
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": question},
    ]
    for step in range(max_steps):
        resp = call_llm(messages, tools=tools)          # 模型决定:说话 or 调工具
        msg = resp.choices[0].message
        messages.append(msg)

        if not msg.tool_calls:                          # 没有工具调用 = 结束
            return msg.content

        for call in msg.tool_calls:
            result = execute(call.function.name, call.function.arguments)
            messages.append({
                "role": "tool",
                "tool_call_id": call.id,
                "content": json.dumps(result, ensure_ascii=False),
            })
    return "已达到最大步数,未能完成任务。"

记住这个循环。 所有框架(LangGraph、AutoGen、CrewAI)都是它的工程化封装。

二、工具设计:Agent 成败的 80% ​

一句最重要的经验

Agent 不可靠,几乎总是因为工具设计得差,而不是模型不够强。

原则反例正例
名字自解释querysearch_orders_by_phone
描述写清何时用「查询数据」「按手机号查订单。用户提供手机号后用此工具;不要传姓名」
参数少而明确params: objectphone: string, 11 位手机号
返回精简返回 200 条原始记录返回 ≤10 条关键字段 + total
错误可读Error 500未找到该手机号的订单,请确认号码或改用订单号查询
幂等安全每次调用都创建订单查询类默认安全;写操作需确认
python
TOOLS = [{
    "type": "function",
    "function": {
        "name": "search_orders_by_phone",
        "description": "按手机号查询该用户的历史订单。当用户询问自己的订单状态、物流、退款进度时使用。",
        "parameters": {
            "type": "object",
            "properties": {
                "phone": {"type": "string", "description": "11 位中国手机号,不含空格"},
                "limit": {"type": "integer", "description": "返回条数,默认 5,最大 10"},
            },
            "required": ["phone"],
        },
    },
}]

工具设计的五条硬规则 ​

  1. 一个工具只做一件事(不要 handle_everything)
  2. 描述里写「什么时候用」比「是什么」更重要
  3. 返回结果要控制在几百 token 内 —— 否则撑爆上下文且干扰判断
  4. 写操作必须可确认/可撤销,或强制人工审批
  5. 给「查不到」明确的返回值,而不是抛异常让模型猜

三、四种 Agent 形态 ​

形态说明适用风险
单次工具调用判断要不要调、调哪个、答80% 场景极低
ReAct 循环推理→行动→观察,多步多步查询、需要组合中
规划-执行先出计划,再逐步执行复杂任务、步骤长中高
多智能体多个角色分工协作极复杂、可并行高(调试困难)

强烈建议:从最简单的开始

能用「意图路由 + 单次工具调用」解决的,不要上 Agent。 能用「固定的三步流水线」解决的,不要上自主循环。 自主性每增加一级,可靠性和可调试性就下降一档。

四、规划的实用做法 ​

python
PLAN_PROMPT = """你是任务规划器。把用户需求拆成可执行的步骤。

可用工具:
{tool_list}

要求:
1. 每步只做一件事,可独立验证
2. 最多 6 步
3. 只输出 JSON 数组:[{"step":1,"action":"工具名或think","args":{...},"why":"目的"}]
4. 若无需工具即可回答,返回 []

用户需求:{question}"""

关键实践:计划产物必须是结构化 JSON,而不是自然语言。这样才能逐步校验、部分重试、插入人工确认点。

循环控制(防失控) ​

机制做法
最大步数8–12 步封顶
重复检测相同工具+参数连续两次 → 中断并提示
预算控制token/金额上限
超时总时长上限(如 60s)
死循环识别无新信息产出即终止
人工检查点关键写操作前暂停确认

五、记忆:三种,别混用 ​

类型存什么实现
短期(会话)最近 N 轮对话消息数组,超长则摘要
长期(事实)用户偏好、历史结论向量库/结构化存储,检索后注入
工作记忆当前任务的中间结果显式变量(如 state["found_orders"])
python
def compress_history(messages, keep_last=6, max_chars=4000):
    """长会话压缩:保留首轮(含系统约束)+ 最近若干轮 + 早期摘要。"""
    if sum(len(m.get("content") or "") for m in messages) < max_chars:
        return messages
    early, recent = messages[1:-keep_last], messages[-keep_last:]
    summary = call_llm("用要点总结以下对话的关键事实与结论:\n" +
                       "\n".join(f"{m['role']}: {m.get('content','')}" for m in early))
    return [messages[0], {"role": "system", "content": f"早前对话摘要:{summary}"}] + recent

记忆不是越多越好

塞入无关历史会让模型跑偏,还会显著增加成本。记忆的价值在于「相关」,不在于「全」。

六、可靠的 Agent 工程要求 ​

要求做法
可观测每步记录:输入、决策、工具、结果、耗时、token
可重放保存完整 trace,能复现失败案例
可中断长任务支持取消、断点续跑
幂等写操作带 request_id,重试不重复下单
降级工具失败时能退回「只回答不操作」
限权工具集最小化,敏感操作走审批
沙箱代码执行类工具必须隔离环境

七、评测 Agent ​

指标定义
任务成功率端到端完成的比例(最重要)
平均步数越少越好(成本/延迟)
工具调用准确率选对工具+参数的比例
无效调用率重复/多余调用
平均成本/延迟每任务
危险行为率越权、循环、误写

评测集要包含:正常多步任务、需要澄清的模糊任务、工具会失败的任务、诱导越权的任务。

八、常见失败模式与对应 ​

失败原因处理
反复调同一个工具返回没给有效信息改进返回内容/加「已查询过」提示
不调用工具就编答案描述不清晰 / 系统提示没强制明确「涉及个人数据必须先查」
参数格式错参数定义含糊加示例与枚举,服务端做校验并返回可读错误
步数爆炸任务拆得太细合并工具、给更强的规划约束
越权操作工具权限过大最小权限 + 人工确认
上下文被工具结果撑爆返回太啰嗦服务端截断 + 摘要字段

通过标准 ​

下一步 ​

→ 评测与可观测

AI 学习路线 · 从入门到专家