上周做了个实验,结果挺震惊。
让大模型搜两只股票的市盈率然后做对比。它搜到了正确的网页,然后......直接编了个数字。跟真实数据差了 30%。
不是搜索能力的问题,是"搜完之后怎么处理"的问题。大模型做精确数值计算,天生不靠谱。它会数错、估错,甚至直接跳过一些数据项。
这就是我开始折腾 Dynamic Filtering 的原因。
什么是 Dynamic Filtering
搜索/抓取拿到原始结果后,模型自己判断要不要写 Python 代码来处理数据。不靠推理"估",靠代码"算"。
Anthropic 2026 年 2 月推出的增强搜索能力。官方基准测试:
- BrowseComp 准确率平均提升 11%
- Token 效率提升 24%
- Sonnet 从 33.3% 升到 46.6%
但 Web Search 和 Web Fetch 是 Server-Managed Tools(服务端托管工具),通过亚马逊云科技的 Amazon Bedrock 暂时不能直接用。
解决方案:自建 Proxy 中间层。
踩坑实录
坑 1:工具格式不兼容
Bedrock 要求工具有 name、description、input_schema 三个字段。Anthropic 的 server-managed tool 是 type: "web_search_20250305" 这种格式,直接发给 Bedrock 会报验证错误。
Proxy 做的第一件事就是拦截替换:
json
// 客户端发的
{"type": "web_search_20250305", "name": "web_search", "max_uses": 5}
// Proxy 替换后发给 Bedrock
{
"name": "web_search",
"description": "Search the web for current information...",
"input_schema": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"]
}
}
好消息:Claude 本身理解这些工具语义,标准定义就能正确触发调用。
max_uses、allowed_domains 这些配置参数不传给 Bedrock,Proxy 自己做约束。
坑 2:Agentic Loop 的 Token 累计
Proxy 要自己跑多轮推理循环:调模型 → 模型返回 tool_use → 调搜索服务 → 注入结果 → 继续推理。
一开始忘了累加每轮的 Token 消耗,最终 usage 字段老对不上官方 API。
正确做法:每次迭代的 input_tokens 和 output_tokens 都累加到 total。迭代上限设 25 次防止死循环。
坑 3:引用标记还原
官方 API 的回答带结构化 citations 数组。Proxy 用三步还原:
- system prompt 注入引用指令("用 [N] 格式标注来源编号")
- 搜索结果加
[Result N]前缀,注册到 registry,编号累积递增 - 后处理用正则把文本里的
[1]、[3]拆出来,附上对应的 citation 对象
输出格式跟官方一致:
json
{
"type": "text",
"text": "Python 3.13 was released in October 2024",
"citations": [{
"type": "web_search_result_location",
"url": "https://docs.python.org/3/whatsnew/3.13.html",
"title": "What's New In Python 3.13",
"encrypted_index": "MQ=="
}]
}
大部分情况跑得好。极少数情况模型会忘记标 [N]------提示工程方案的局限。
坑 4:Fargate 没有 Docker daemon
Dynamic Filtering 需要代码沙箱。模型写的 Python 代码在 Docker 容器里执行。
一开始想用 Amazon Fargate 图省事。结果 Fargate 压根没有 Docker daemon 访问权限。
折腾半天换成 EC2 launch type 才搞定。
沙箱安全配置:网络隔离(network_disabled=True)、权限限制(cap_drop=["ALL"])、内存上限 256MB。
增强版(web_search_20260209 / web_fetch_20260209)才需要 Docker。标准版不需要。
坑 5:搜索提供商差异
Tavily(AI 专用搜索)和 Brave(独立索引)两个选择。通过环境变量 WEB_SEARCH_PROVIDER 切换。
Tavily 内容清洗过,原生支持域名过滤参数。Brave 不直接支持,得靠查询注入 site: 前缀:
python
if allowed_domains:
site_filter = " OR ".join(f"site:{d}" for d in allowed_domains)
search_query = f"({site_filter}) {query}"
不管哪个提供商,Proxy 都做二次 DomainFilter 过滤兜底。
坑 6:跨模型引用遵从率
Proxy 的搜索能力在中间层实现,不绑定特定模型。Qwen3-Coder、Kimi K2.5、MiniMax M2.1、GLM-4.7、DeepSeek 3.2 都跑通了。
但引用标记 [N] 的遵从率,Claude 远高于其他模型。用非 Claude 模型要有心理准备------引用可能不完整。
实测数据
Proxy vs 官方 API
测试 Prompt:"Compare the current stock prices and P/E ratios of AAPL and GOOGL."
| 指标 | 官方 API | Proxy | 差异 |
|---|---|---|---|
| input_tokens | 18,521 | 18,426 | -0.5% |
| output_tokens | 1,373 | 1,420 | +3.4% |
| web_search 次数 | 2 | 2 | 一致 |
| AAPL P/E | 33.42 | 33.42 | ✅ |
| GOOGL P/E | 28.10 | 28.10 | ✅ |
Token 差异 4% 以内。计算结果完全一致。响应格式 100% 兼容。
三方案对比
同一任务------抓取页面,找频率前 3 的词:
| 方案 | Token | 准确性 | 工具定义开销 |
|---|---|---|---|
| Dynamic Filtering | 6,731 | 100% ✅ | ~185 tokens(2 个极简定义) |
| 纯推理 | 6,745 | 全部错误 ❌ | --- |
| Agent + MCP + CodeInterpreter | 24,669 | 100% ✅ | ~2,500 tokens/轮 |
纯推理直接错了。Agent + CodeInterpreter 对了,但 Token 是 Dynamic Filtering 的 3.7 倍。
原因:MCP 暴露 5 个工具的完整定义,每轮都带着。3 轮迭代光工具定义就多吃约 7,000 tokens。Dynamic Filtering 把逻辑藏在 Proxy 层,模型只看到极简定义。
部署一把梭
bash
ENABLE_WEB_SEARCH=true \
WEB_SEARCH_PROVIDER=tavily \
WEB_SEARCH_API_KEY=tvly-your-api-key \
./scripts/deploy.sh -e prod -r us-west-2 -p arm64 -l ec2
客户端只改一行 base_url,其他代码一行不动:
python
client = anthropic.Anthropic(
base_url="https://your-proxy-endpoint.com",
api_key="your-proxy-api-key",
)
总结
三个关键收获:
- 精确数值别让模型"脑算"------有代码执行的方案都 100% 正确,纯推理全错
- Token 差距明显------Dynamic Filtering 比 Agent + MCP 省了 3.7 倍
- 模型不绑定------Proxy 层实现,Qwen3、Kimi、DeepSeek 都能用
精确计算场景用增强版,摘要理解任务标准版就够了,还不用 Docker。