模型点三道菜,回喂被拒 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

相关推荐
喵了几个咪14 小时前
GoWind CMS 拆解:在 Go 里给 TipTap 搭一套 Yjs 协同后端
开发语言·后端·golang
Flynt14 小时前
我把微信运动的排行榜和定点推送复刻了一遍:100万次步数更新只要4秒,坑在别处
redis·后端·性能优化
_孤傲_16 小时前
快速掌握 MySQL 语法
数据库·后端·mysql
IT_陈寒18 小时前
Java静态变量初始化坑得我头皮发麻
前端·人工智能·后端
步行cgn18 小时前
Thymeleaf 编辑表单详解:从字段回填到模拟 PUT 请求
后端
Song_RsHt19 小时前
Redis 与微服务,什么场景下无可替代
java·redis·后端·学习·微服务
学长毕设20 小时前
基于SpringBoot的建筑材料管理系统-附源码
java·spring boot·后端
程序员cxuan20 小时前
掀桌子了!Kimi K3.1 Pro 这效果也太离谱了。
人工智能·后端·程序员
摸爬滚打的小李20 小时前
关于 Arthas
java·后端
武子康20 小时前
黑图、彩色噪声、NaN、偏色怎么排查
人工智能·后端·agent