康小丁
27.19M · 2026-03-27
上周做了个实验,结果挺震惊。
让大模型搜两只股票的市盈率然后做对比。它搜到了正确的网页,然后……直接编了个数字。跟真实数据差了 30%。
不是搜索能力的问题,是"搜完之后怎么处理"的问题。大模型做精确数值计算,天生不靠谱。它会数错、估错,甚至直接跳过一些数据项。
这就是我开始折腾 Dynamic Filtering 的原因。
搜索/抓取拿到原始结果后,模型自己判断要不要写 Python 代码来处理数据。不靠推理"估",靠代码"算"。
Anthropic 2026 年 2 月推出的增强搜索能力。官方基准测试:
但 Web Search 和 Web Fetch 是 Server-Managed Tools(服务端托管工具),通过亚马逊云科技的 Amazon Bedrock 暂时不能直接用。
解决方案:自建 Proxy 中间层。
Bedrock 要求工具有 name、description、input_schema 三个字段。Anthropic 的 server-managed tool 是 type: "web_search_20250305" 这种格式,直接发给 Bedrock 会报验证错误。
Proxy 做的第一件事就是拦截替换:
// 客户端发的
{"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 自己做约束。
Proxy 要自己跑多轮推理循环:调模型 → 模型返回 tool_use → 调搜索服务 → 注入结果 → 继续推理。
一开始忘了累加每轮的 Token 消耗,最终 usage 字段老对不上官方 API。
正确做法:每次迭代的 input_tokens 和 output_tokens 都累加到 total。迭代上限设 25 次防止死循环。
官方 API 的回答带结构化 citations 数组。Proxy 用三步还原:
[Result N] 前缀,注册到 registry,编号累积递增[1]、[3] 拆出来,附上对应的 citation 对象输出格式跟官方一致:
{
"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]——提示工程方案的局限。
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。标准版不需要。
Tavily(AI 专用搜索)和 Brave(独立索引)两个选择。通过环境变量 WEB_SEARCH_PROVIDER 切换。
Tavily 内容清洗过,原生支持域名过滤参数。Brave 不直接支持,得靠查询注入 site: 前缀:
if allowed_domains:
site_filter = " OR ".join(f"site:{d}" for d in allowed_domains)
search_query = f"({site_filter}) {query}"
不管哪个提供商,Proxy 都做二次 DomainFilter 过滤兜底。
Proxy 的搜索能力在中间层实现,不绑定特定模型。Qwen3-Coder、Kimi K2.5、MiniMax M2.1、GLM-4.7、DeepSeek 3.2 都跑通了。
但引用标记 [N] 的遵从率,Claude 远高于其他模型。用非 Claude 模型要有心理准备——引用可能不完整。
测试 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 层,模型只看到极简定义。
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,其他代码一行不动:
client = anthropic.Anthropic(
base_url="https://your-proxy-endpoint.com",
api_key="your-proxy-api-key",
)
三个关键收获:
精确计算场景用增强版,摘要理解任务标准版就够了,还不用 Docker。