前言

今天使用的时候刚好遇到了服务端 400 的报错,如下图。
tool_search报错
经过排查和 ToolSearch 有关系,Kimi 官方建议使用 Claude Code 的时候不能开启 ToolSearch
Kimi官方建议

什么是 ToolSearch?为什么不能开启?随着我慢慢排查,捋清楚了 ToolSearch 的流程。


我的排查过程

前提条件

为了 LLM 的可观测性,可 debug,我部署了 newAPI,我本地的 agentprovider 都走自己的 newAPI 网关,保存 LLM 调用的 requestresponse,把 log 都保存到了我的磁盘上。

对比前一条请求

我之前写了一个 skilldebug,主要是为了 diff 两个 request,看前缀 prompt 是否稳定。用这个 skill 去查询我的本地 log,找到报错的请求和前一条成功的请求后,给出的 diff 结果是这样的
diff结果
这里 messages 追加了 3 条,另外 tools 中增加了一个 TaskCreate,难道是因为这个增加的 tool?具体新增的这个 tool 是这样定义的:

1
2
3
4
5
6
{
"defer_loading": true,
"description": "...",
"input_schema": {...},
"name": "TaskCreate"
}

这个TaskCreate标记为延迟加载,能从字面上理解是后续加载的工具,不是一开始就加载的。

再看具体的messages

新增的三条 message 是这样的:

  • assistanttool_use 决定使用 ToolSearch 工具去查询 TaskCreate 这个工具(这条消息是LLM生成的)
  • usertool_result 返回了 typetool_reference 的工具 TaskCreate
  • 运行时新增一条 system prompt,简单提示 Task 的一套工具如何使用

    猜测是 Anthropic 的实践下来的经验,也算是一种 prompt engineering 了,在 LLM 首次使用一套工具时,插入一条 prompt 让 LLM 理解这一套工具的用法。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
{
"role": "assistant",
"content": [
{
"text": "我先用 TaskCreate 跟踪本阶段工作,然后安装依赖并实施 ESLint 规则。",
"type": "text"
},
{
"id": "tool_vqnWwTAWKDB1midZawWC2gbd",
"input": {
"max_results": 1,
"query": "select:TaskCreate"
},
"name": "ToolSearch",
"type": "tool_use"
}
]
},
{
"role": "user",
"content": [
{
"content": [
{
"tool_name": "TaskCreate",
"type": "tool_reference"
}
],
"tool_use_id": "tool_vqnWwTAWKDB1midZawWC2gbd",
"type": "tool_result"
},
{
"cache_control": {
"type": "ephemeral"
},
"text": "Tool loaded.",
"type": "text"
}
]
},
{
"role": "system",
"content": "The task tools haven't been used recently. If you're working on tasks that would benefit from tracking progress, consider using TaskCreate to add new tasks and TaskUpdate to update task status (set to in_progress when starting, completed when done). Also consider cleaning up the task list if it has become stale. Only use these if relevant to the current work. This is just a gentle reminder - ignore if not applicable.\n"
}

也就是说之前的 TaskCreate 工具是 LLM 用 ToolSearch 工具搜索出来的,搜索出来后,加入 tools 列表。

缓存是否有问题?

这里其实就明白大概是 ToolSearch 的问题了,但是又引出了疑问:

按照我之前的理解,提示词缓存的顺序是tools + system prompt + messages,如果延迟工具添加以后,难道不会破坏缓存吗?

官方文档的prompt caching章节还真有解释:

  • 延迟工具:不会影响缓存
  • 在一些情况下,是会影响缓存的!其中就有 custom gateway,也就是接入第三方模型的时候。
    缓存影响说明

为什么不能开启 ToolSearch

这个问题的答案也就清楚了:

  • Claude Code 的 ToolSearch 允许延迟加载工具到上下文中,但是会加入一种特殊的标记 tool_reference,要求服务端识别,并且不把延迟加载的工具放在前缀中,不然影响缓存。
  • 第三方服务端如果不适配,那么 tools 前缀就会影响缓存。Kimi的选择是不适配这个功能,建议不开启ToolSearch,服务内部不认识tool_referrence,因此 Kimi 的服务端则直接返回了 400 的错误。

完整的 ToolSearch 流程

ToolSearch在系统提示词中的描述

为了节省上下文空间,有一些工具的使用方式不会被Claude Code加载到上下文中,在系统提示词中告诉 LLM 可以用 ToolSearch 这个工具去搜索,这些工具就是 deferred tools,以下就是这段系统提示词:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
The following deferred tools are now available via ToolSearch. Their schemas are NOT loaded — calling them directly will fail with InputValidationError. Use ToolSearch with query "select:<name>[,<name>...]" to load tool schemas before calling them:
CronCreate
CronDelete
CronList
EnterPlanMode
EnterWorktree
ExitPlanMode
ExitWorktree
NotebookEdit
ScheduleWakeup
TaskCreate
TaskGet
TaskList
TaskOutput
TaskStop
TaskUpdate
WebFetch
WebSearch

The following skills are available for use with the Skill tool:
...
...

ToolSearch调用过程

CC 客户端和 Claude 服务端交互的 JSON 大致如下:

1
2
3
4
5
6
{
"system": [...],
"messages": [...],
"tools": [...],
...
}
  • Claude 服务端的行为:
    1. LLM推理的时候,认为自己要用TaskCreate这个工具了,但是不会用,具体的description和input_schema并没有在tools列表中。想起系统提示词中需要用ToolSearch
    2. 因此,推理中,就在后续messages中添加一条消息,给CC客户端返回一个tool_use,决定使用ToolSearch工具去查询TaskCreate的具体信息。
  • CC 客户端做了三件事情:
    1. 在messages中构造第一条消息,返回tool_result,表示找到了TaskCreate这个工具,类型是tool_reference。
    2. 构造第二条消息,是一条system prompt,简单提示TaskXXX这一套工具如何使用
    3. tools 添加 TaskCreate 的具体说明,并标注是延迟工具。
  • Claude 服务端:
    会处理 tools 列表,将新增的 deferred tooltools 中拿出来,防止影响 prompt cache,在 messages 追加 deferred tool 的用法。

参考资料