从 Shippy 的构建过程看 Agent 工程:循环、工具、状态与安全边界

2026-07-16 38 预计阅读时间: 1 分钟
来源: huggingface.co AI 摘要 Original link

Disclaimer: This article is an AI-assisted summary. Read it together with the original source when precision matters. The summary may omit context, version differences, or edge cases and is not official documentation.

预计阅读时间:9 分钟

来源只提供了标题,没有披露 Shippy 的架构、模型、工具集或运行环境。因此,本文不把具体实现归因于原项目,而是围绕“构建一个 Agent 会教会我们什么”这一主题,给出一套可以落地验证的工程方法。

Agent 与普通聊天应用的区别,不在于提示词更长,而在于模型能够观察环境、选择动作、调用工具,并根据结果继续决策。一旦系统进入这个循环,工程重点就从“生成一段好文本”转向“控制一个可能失败、重复或越权的执行过程”。

Agent 的核心是受约束的执行循环

一个实用 Agent 通常包含四个动作:读取当前状态、决定下一步、执行工具、检查结果。循环结束条件必须由宿主程序掌握,不能完全交给模型。

最小状态至少应记录:

  • 用户目标与不可违反的约束
  • 已执行的动作及其结果
  • 剩余步数、时间或费用预算
  • 当前产物与完成条件
  • 最近一次错误以及重试次数

这解释了为什么只写一条“请自主完成任务”的提示词远远不够。没有步数上限,Agent 可能反复调用同一工具;没有完成条件,它可能过早宣布成功;没有结构化状态,长对话中的关键信息会逐渐丢失。

工具接口比工具数量更重要

工具是 Agent 与真实世界之间的边界。工具定义含糊时,模型必须猜测参数;返回值不稳定时,模型很难判断动作是否成功;工具权限过大时,一次错误决策就可能产生不可逆影响。

工具设计可以遵循几个约束:

  • 输入使用明确的结构化字段,并在执行前校验。
  • 输出同时包含机器可读状态和简短说明。
  • 查询与修改操作分离,避免一个工具承担多种副作用。
  • 写操作尽量支持预览、幂等键和回滚。
  • 日志记录参数摘要、耗时、结果与错误,但过滤密钥和隐私数据。

例如,不要提供一个接收任意 Shell 字符串的 run_command。更稳妥的做法是暴露 list_filesread_filerun_tests 等窄接口,并由宿主程序限制路径和参数。

可以这样实践:实现一个可运行的最小 Agent

下面的示例不依赖真实模型 API。它用一个确定性的“规划器”模拟模型决策,重点展示工具白名单、参数校验、状态记录和执行预算。保存为 agent.py 后,可直接运行 python agent.py

from dataclasses import dataclass, field
from pathlib import Path
from typing import Any, Callable

WORKSPACE = Path.cwd().resolve()


def list_files(path: str = ".") -> dict[str, Any]:
    target = (WORKSPACE / path).resolve()
    if target != WORKSPACE and WORKSPACE not in target.parents:
        return {"ok": False, "error": "path escapes workspace"}
    if not target.is_dir():
        return {"ok": False, "error": "directory does not exist"}
    return {
        "ok": True,
        "files": sorted(p.name for p in target.iterdir())[:20],
    }


TOOLS: dict[str, Callable[..., dict[str, Any]]] = {
    "list_files": list_files,
}


@dataclass
class AgentState:
    goal: str
    max_steps: int = 4
    history: list[dict[str, Any]] = field(default_factory=list)


def planner(state: AgentState) -> dict[str, Any]:
    # 在真实系统中,这里可以替换为要求返回 JSON 的模型调用。
    if not state.history:
        return {"type": "tool", "name": "list_files", "args": {"path": "."}}
    result = state.history[-1]["result"]
    if result.get("ok"):
        return {
            "type": "finish",
            "answer": f"工作区包含:{', '.join(result['files']) or '没有文件'}",
        }
    return {"type": "finish", "answer": f"任务失败:{result['error']}"}


def run_agent(goal: str) -> str:
    state = AgentState(goal=goal)

    for _ in range(state.max_steps):
        action = planner(state)
        if action.get("type") == "finish":
            return str(action.get("answer", ""))

        name = action.get("name")
        args = action.get("args", {})
        tool = TOOLS.get(name)
        if tool is None or not isinstance(args, dict):
            result = {"ok": False, "error": "invalid tool request"}
        else:
            try:
                result = tool(**args)
            except (TypeError, ValueError) as exc:
                result = {"ok": False, "error": str(exc)}

        state.history.append({"action": action, "result": result})

    return "任务因达到最大执行步数而终止"


if __name__ == "__main__":
    print(run_agent("列出当前工作区中的文件"))

接入真实模型时,可以保留 run_agent 和工具层,只替换 planner。模型输出应使用 JSON Schema 或 SDK 提供的结构化输出能力,并限定为两种结果:调用已注册工具,或者提交最终答案。不要直接执行模型生成的 Python、SQL 或 Shell 文本。

评估不能只看最终答案

Agent 可能给出正确答案,却走了一条昂贵或危险的路径。因此,评估应同时覆盖结果和过程:

  • 任务成功率:最终产物是否满足可自动检查的验收条件。
  • 路径效率:调用了多少工具,是否存在重复或无效步骤。
  • 恢复能力:工具超时、返回空数据或参数错误后能否调整。
  • 权限合规:是否尝试调用未授权工具或访问工作区之外的资源。
  • 成本与延迟:每个任务消耗的 token、模型调用次数和总耗时。

测试集不应只有理想输入。还要加入缺失参数、矛盾要求、不可达资源、工具返回脏数据和提示注入等场景。对于会发送消息、修改数据或部署代码的 Agent,测试环境必须使用隔离账号和可清理资源。

上线时逐级扩大自主权

Agent 的自主程度不是越高越好。一个稳妥的采用顺序是:先让它只生成计划,再允许调用只读工具,随后开放可预览的写操作,最后才考虑无需人工确认的执行。

上线前可以检查以下项目:

  • 每个循环都有步数、时间和费用上限。
  • 每个工具都采用最小权限,并校验输入与资源范围。
  • 高风险动作需要人工批准,或者至少提供预览和撤销机制。
  • 日志能够重建决策过程,同时不会泄露凭据。
  • 完成条件可以由程序验证,而不是只相信模型的自我判断。
  • 关键任务准备了确定性的降级路径。

构建 Agent 的难点通常不在于让模型“开始行动”,而在于让它知道何时停止、失败后如何恢复,以及哪些事情无论如何都不能做。把循环、状态、工具和评估设计成明确的工程边界,Agent 才能从演示进入可靠的生产流程。


相关推荐