Skip to content

决策不是执行:手写有限工具循环 ​

用户问“杭州 20 摄氏度相当于多少华氏度”,模型可以提出调用 convert,但它既没有执行 Python,也没有证明结果。执行器收到工具名、参数和调用 ID 后,校验、运行并回填结果;下一次模型请求才看得到这个观察。Chatbot 只需生成文字,Agent 还要在观察后决定是否继续。

第一个可运行循环 ​

保存为 lab.py,用 Python 3.12 运行,无第三方依赖或 API key。decide 是固定输出的决策替身,目的是把协议与执行边界暴露出来;真实模型接入见模型 API 课程,不能用本例评价模型能力。

python
def decide(messages):
    if messages[-1]["role"] == "tool":
        return {"type": "final", "text": str(messages[-1]["content"])}
    return {"type": "call", "id": "c1", "name": "convert", "args": {"c": 20}}

def execute(call):
    if call["name"] != "convert":
        raise ValueError("unknown tool")
    args = call["args"]
    if set(args) != {"c"} or type(args["c"]) not in (int, float):
        raise ValueError("invalid arguments")
    if not -273.15 <= args["c"] <= 1000:
        raise ValueError("out of range")
    return {"fahrenheit": args["c"] * 9 / 5 + 32}

def run(policy, limit=3):
    history = [{"role": "user", "content": "转换 20 摄氏度"}]
    for _ in range(limit):
        action = policy(history)
        if action["type"] == "final":
            return {"status": "completed", "history": history, "answer": action["text"]}
        history.append({"role": "assistant", "call": action})
        try:
            result = {"ok": True, "data": execute(action)}
        except ValueError as error:
            result = {"ok": False, "error": str(error)}
        history.append({"role": "tool", "call_id": action["id"], "content": result})
    return {"status": "budget_exhausted", "history": history}

result = run(decide)
assert result["status"] == "completed"
assert result["history"][-1]["content"]["data"]["fahrenheit"] == 68
print(result["status"], len(result["history"]))

输出 completed 3。第一轮只有用户消息,产生调用意图;第二条消息保存意图,第三条按 call_id 保存观察;第二轮才能结束。若省略 assistant 调用而只加入工具结果,很多真实模型协议无法把结果对应回请求。多个工具结果按 ID 配对,不能按返回先后配对。

错误也是观察,但不是成功 ​

校验错误回填 ok=False,让模型可以修正参数;认证失败则通常要结束或请求重新登录,不应让模型不断猜凭证。模型无工具调用可能是拒答或信息不足,不等于业务完成,所以产品还应将模型结束与业务验收分开。本例仅证明循环协议。

固定 pipeline 适合“读取→转换→返回”这种已知步骤,根本不必调用模型;开放任务才需要动态决策。工具调用次数限制保证最多执行三次,不限制单次工具阻塞时间,生产还需超时、总 deadline 和取消。写工具需要服务端权限与幂等,不能直接复用这个只读示例。

练习 ​

追加一个始终请求 convert 的决策函数,验证 limit=2 时停在 budget_exhausted,历史中恰好两次工具观察;再给 c 传入 True,确认不会作为数值通过。

核对本章答案

下一章

用心记录每一步成长