Agent 开发实战 · 第 1 篇

Agent 的运行循环与最小架构

拆清运行器、模型、工具、状态和策略,建立可控制的最小闭环。

28 分钟开发入门

学完能做什么

你会把 Agent 从“会聊天的模型”拆成可控制的运行循环,明确模型、工具、状态、策略和日志各自负责什么,并画出一个可以开始编码的最小架构。

Agent 不是一个更长的提示词

模型的一次调用只有输入和输出。Agent 则是在程序控制下重复执行“观察、判断、行动、校验”,直到任务完成、失败或触发停止条件。

text
接收任务 → 整理上下文 → 调用模型 → 校验决定
    ↑                              ↓
记录状态 ← 返回结果或继续 ← 执行允许的工具

模型负责理解和提出下一步,程序负责权限、执行和停止。不要把数据库写入、文件删除或消息发送直接交给模型。

最小架构的五个部件

部件主要职责不应该负责
运行器推进循环、超时、重试、停止猜测业务规则
模型适配层统一请求与响应格式直接执行工具
工具注册表声明、查找和调用工具接受未校验参数
状态存储保存步骤、结果和任务状态无限保存全部对话
策略层权限、预算、确认和风险边界生成自然语言答案

第一版可以把它们写在一个文件里,但接口要分开。之后更换模型、工具或存储时,核心循环不用重写。

用状态机看清一次运行

建议至少定义以下状态:

text
CREATED      已创建任务
PLANNING     模型正在判断下一步
VALIDATING   程序正在校验模型输出
WAITING      等待用户确认或补充材料
EXECUTING    正在执行工具
RESPONDING   正在生成最终回答
SUCCEEDED    成功结束
FAILED       明确失败
CANCELLED    被用户或系统取消

状态转换必须由程序决定。模型可以建议“任务已完成”,但运行器仍要检查完成条件。

一个不依赖具体 SDK 的循环

python
MAX_STEPS = 8


def run(task, context, model, tools, policy):
    state = {
        "status": "CREATED",
        "step": 0,
        "messages": build_initial_messages(task, context),
        "events": [],
    }

    while state["step"] < MAX_STEPS:
        state["step"] += 1
        state["status"] = "PLANNING"
        decision = model.decide(state["messages"], tools.schemas())

        state["status"] = "VALIDATING"
        checked = policy.validate(decision, state)

        if checked.kind == "final":
            state["status"] = "SUCCEEDED"
            return checked.answer, state

        if checked.kind == "need_confirmation":
            state["status"] = "WAITING"
            return checked.prompt, state

        state["status"] = "EXECUTING"
        result = tools.call(checked.tool_name, checked.arguments)
        state["messages"].append(as_tool_result(checked.call_id, result))
        state["events"].append(safe_event(checked, result))

    state["status"] = "FAILED"
    raise RuntimeError("达到最大步骤数,任务未完成")

这段伪代码最重要的不是语法,而是三个边界:每一步都校验、循环有上限、有副作用的动作可以暂停确认。

停止条件必须写在代码里

至少设置:

  • 最大步骤数,避免模型反复调用同一工具;
  • 单次和整次任务超时;
  • 请求与工具调用预算;
  • 连续重复动作检测;
  • 用户取消信号;
  • 已满足的业务完成条件;
  • 无法恢复的错误类型。

“让模型自己判断什么时候停”不够可靠。模型输出只是证据之一。

事件日志比完整思维过程更有用

记录可审计事件,不要求模型展示隐藏推理:

json
{
  "task_id": "task_123",
  "step": 3,
  "event": "tool_completed",
  "tool": "search_documents",
  "argument_summary": {"query_length": 18},
  "result_summary": {"documents": 4},
  "duration_ms": 426,
  "status": "ok"
}

日志中不要保存密钥、完整个人资料和不必要的原文。为输入、提示词、工具定义和结果保留版本号,复现问题时才知道当时运行的是什么。

先从只读 Agent 开始

第一版工具优先选择搜索、读取、计算和格式转换。写入、发送、删除和支付类工具需要更严格的身份、授权、参数预览、幂等和人工确认。

推荐演进顺序:

  1. 单轮模型调用;
  2. 一个只读工具;
  3. 多轮工具循环;
  4. 可暂停与恢复;
  5. 有条件的写入工具;
  6. 评测、监控和发布。

架构练习

为“根据内部材料生成每周项目摘要”画一张架构图,并回答:

  1. 哪些材料允许读取,哪些字段必须脱敏?
  2. “完成”的判断条件是什么?
  3. 最多允许多少次检索和模型调用?
  4. 哪些错误可重试,哪些应立即停止?
  5. 摘要只生成草稿,还是允许直接发送?
  6. 需要记录哪些事件才能复现一次运行?

完成检查清单

  • 模型只提出决定,程序掌握执行权。
  • 状态、完成条件和失败状态都有明确定义。
  • 步骤、时间、预算和重复动作都有上限。
  • 高风险动作可以暂停并向用户展示参数。
  • 日志能复现过程,但不泄露密钥和敏感数据。

下一步

下一篇将完成第一次模型 API 请求,把密钥、超时、错误处理和最小日志都落到可运行代码里。