refactor: 统一自建 Tool Search — 移除 tool_reference/defer_loading 依赖,全 provider 通用

- 重命名 ExecuteTool → ExecuteExtraTool,作为一等工具始终可用
- ToolSearchTool 输出改为纯文本(区分 core/deferred),移除 tool_reference blocks
- 移除 modelSupportsToolReference() 及相关 GrowthBook 配置
- 移除 API 侧 defer_loading 字段和 tool search beta header 注入
- 简化 system prompt(工具使用指南从 ~120 行压缩到 ~10 行)
- extractDiscoveredToolNames 支持文本格式解析(向后兼容旧 session 的 tool_reference)
- 更新 promptEngineeringAudit 测试以匹配简化后的 prompt 结构

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-05-09 14:19:31 +08:00
parent 4fc95bd5a7
commit 8c157f0767
17 changed files with 280 additions and 401 deletions

View File

@@ -157,7 +157,6 @@ import {
import { getAgentContext } from 'src/utils/agentContext.js'
import { isClaudeAISubscriber } from 'src/utils/auth.js'
import {
getToolSearchBetaHeader,
modelSupportsStructuredOutputs,
shouldIncludeFirstPartyOnlyBetas,
shouldUseGlobalCacheScope,
@@ -1191,6 +1190,11 @@ async function* queryModel(
// ToolSearchTool returns tool_reference blocks which unsupported models can't handle
let filteredTools: Tools
// canDefer is true when the model supports defer_loading.
// Deferred tools that haven't been discovered are filtered out from the API
// request — their schemas are only included after ToolSearch discovers them.
// With defer_loading, we only include discovered tools to save prompt tokens.
if (useToolSearch) {
// Dynamic tool loading: Only include deferred tools that have been discovered
// via tool_reference blocks in the message history. This eliminates the need
@@ -1211,15 +1215,9 @@ async function* queryModel(
)
}
// Add tool search beta header if enabled - required for defer_loading to be accepted
// Header differs by provider: 1P/Foundry use advanced-tool-use, Vertex/Bedrock use tool-search-tool
// For Bedrock, this header must go in extraBodyParams, not the betas array
const toolSearchHeader = useToolSearch ? getToolSearchBetaHeader() : null
if (toolSearchHeader && getAPIProvider() !== 'bedrock') {
if (!betas.includes(toolSearchHeader)) {
betas.push(toolSearchHeader)
}
}
// Tool search beta header and defer_loading removed — unified self-built
// tool search via ToolSearchTool + ExecuteExtraTool for all providers.
// No longer relies on API-side tool_reference or defer_loading features.
// Determine if cached microcompact is enabled for this model.
// Computed once here (in async context) and captured by paramsFromContext.
@@ -1250,13 +1248,9 @@ async function* queryModel(
}
const useGlobalCacheFeature = shouldUseGlobalCacheScope()
const willDefer = (t: Tool) =>
useToolSearch && (deferredToolNames.has(t.name) || shouldDeferLspTool(t))
// MCP tools are per-user → dynamic tool section → can't globally cache.
// Only gate when an MCP tool will actually render (not defer_loading).
const needsToolBasedCacheMarker =
useGlobalCacheFeature &&
filteredTools.some(t => t.isMcp === true && !willDefer(t))
useGlobalCacheFeature && filteredTools.some(t => t.isMcp === true)
// Ensure prompt_caching_scope beta header is present when global cache is enabled.
if (
@@ -1273,7 +1267,7 @@ async function* queryModel(
: 'system_prompt'
: 'none'
// Build tool schemas, adding defer_loading for MCP tools when tool search is enabled
// Build tool schemas — no defer_loading since we use self-built tool search
// Note: We pass the full `tools` list (not filteredTools) to toolToAPISchema so that
// ToolSearchTool's prompt can list ALL available MCP tools. The filtering only affects
// which tools are actually sent to the API, not what the model sees in tool descriptions.
@@ -1285,7 +1279,6 @@ async function* queryModel(
agents: options.agents,
allowedAgentTypes: options.allowedAgentTypes,
model: options.model,
deferLoading: willDefer(tool),
}),
),
)
@@ -1653,13 +1646,10 @@ async function* queryModel(
betasParams.push(CONTEXT_1M_BETA_HEADER)
}
// For Bedrock, include both model-based betas and dynamically-added tool search header
// For Bedrock, include model-based betas (no tool search header — self-built search)
const bedrockBetas =
getAPIProvider() === 'bedrock'
? [
...getBedrockExtraBodyParamsBetas(retryContext.model),
...(toolSearchHeader ? [toolSearchHeader] : []),
]
? [...getBedrockExtraBodyParamsBetas(retryContext.model)]
: []
const extraBodyParams = getExtraBodyParams(bedrockBetas)