模型点三道菜,回喂被拒 400

「手写中文版 claude code」教学系列:codeAgent 是我从零手写的 claude code 复刻------不套壳不翻译,从界面到 Agent 主循环一行行实现,注释全是中文大白话,适合当 Agent 开发教材从头读到尾。 上一课治好了工具的"钱袋子"(按套餐发说明书省钱);这一课钻进工具真正跑起来之后的环节------模型一次点好几道菜,结果怎么端回去才不被 API 拒。 仓库:github.com/Harvil1/cod...

翻车现场

模型一口气发起三个工具调用(读文件 A、读文件 B、跑个命令),我执行完按 OpenAI 的习惯逐条回喂:

scss 复制代码
assistant (tool_calls: [A, B, C])
tool (result A)
tool (result B)
tool (result C)

切到 Anthropic 格式的端点,直接 400。原因:Anthropic 协议规定所有 tool_result 必须装在同一条 user 消息里,而且不允许两条 user 消息相邻。OpenAI 的"一菜一盘"在它家是非法摆盘。

修复:翻译层统一合并

agent/llm_client.py 里的转换函数,docstring 就是踩坑记录:

python 复制代码
def _convert_messages_to_anthropic(self, messages: list) -> list:
    """把 OpenAI 格式的整段对话历史翻译成 Anthropic 格式。

    最关键的一步:把连续多条工具结果合并成一条 user 消息。
    因为 Anthropic 协议规定所有工具结果必须装在同一个 user 消息里,
    不允许两条 user 消息挨着;而 OpenAI 那边每个工具结果各自成条。
    (另外 system 消息在这里被抽走------它要走顶层参数,见上层拼装。)

    参数:
        messages:OpenAI 格式的消息列表

    返回:Anthropic 格式的消息列表。
    """
    conversation = []
    i = 0
    while i < len(messages):
        m = messages[i]
        role = m.get("role")

        if role == "system":
            i += 1
            continue  # system 走顶层参数,不进消息列表
        ...

核心手法是双指针扫描:遇到连续的 tool 消息块,一口气收进同一个 user 消息再往下走;顺带把 system 抽走(OpenAI 的 system 是消息列表的一员,Anthropic 的 system 是顶层参数------又一个差异点)。

并行回喂还有三个坑,一并说了

1. id 配对不能错。 三个结果必须各回各的 tool_call_id,张冠李戴模型会拿着 A 的结果回答 C 的问题------不报错但全错,比 400 更难查。

2. 顺序无所谓,配对才要命。 协议不要求结果按发起顺序回,但 id 必须一一对应。

3. 换端点不换代码。 这层翻译存在的意义:主循环只说 OpenAI 方言,Client 子类负责翻译------加新服务商只写一个翻译器,主循环零改动。

小结

  1. 并行 tool_calls 的结果回喂,Anthropic 要求合并成单条 user、禁连续 user
  2. 转换层双指针扫描:合并 tool 块 + 抽离 system,一次遍历搞定
  3. id 严格配对;协议差异全部闷在 Client 层,主循环保持方言纯净

下一篇:模型点了菜厨房没做------发送前的孤儿 tool_call 对账兜底(附 _fix_tool_call_pairs 源码)。


仓库在这,注释全中文,欢迎 Star ⭐:github.com/Harvil1/cod...

标签 :Agent Function Calling 大模型API Python

相关推荐
松就是我902981 小时前
如何设计多Agent
后端
高频因子挖掘机1 小时前
盘中筛选要同时看价格和盘口?按需分层比“一次全拿”更好维护
后端·github·api
asong1 小时前
从写代码到部署上线,Cloudflare 给 AI 配了一把新钥匙
前端·javascript·后端
RobinDevNotes1 小时前
PagedAttention原理,KV缓存也能分页
人工智能·后端
卷无止境1 小时前
当AI写代码遇见Rust为什么会卡壳
后端·python
Percy_kk1 小时前
Django的CRUD映射
后端·django
用户EasyAdminBlazor1 小时前
Blazor Server 性能优化实战:Circuit、数据库、Redis 与并发
后端
王中阳Go1 小时前
Agent 第一句就 500,我们查了三轮:入口日志少打了一个参数
后端·agent·ai编程
羑悻1 小时前
穿越Docker内核迷雾:揭秘镜像分层存储的叠加态与卷挂载的多维空间穿梭技术
后端·docker·容器