web-search-proxy:给内网里的Claude Code探索世界的能力

web-search-proxy: A lightweight search & fetch proxy

以下内容包含AIGC

我工作的地方有一批外包同事,开发环境是内网虚拟机,完全隔离外网。出于数据安全考虑(防止代码库和文档外发),这个设定本身没什么问题,但带来了一个很现实的摩擦:他们没法上网。

查个库的最新版本,翻翻 changelog,确认一个 API 的用法——这些事情需要在外网电脑查完,再手动粘贴到虚拟机里。每天重复,很烦。

之前我在内网搭了一套 new-api(LLM Gateway) 负载均衡了 2 个 MiniMax 的 max订阅,帮他们接通了 Claude Code,模型接入的问题算是解决了。但 Claude Code 依然没有联网能力,遇到事实类问题还是一样抓瞎。它能写代码,但它不知道:某个库半年前发布了 breaking change、某个场景有哪些正在快速崛起的开源解决方案。

所以我花了半天时间,用 Claude Code 写了给 Claude Code 用的联网工具。


web-search-proxy 是什么

一个轻量的 HTTP 代理服务,部署在有外网访问权限的机器上,暴露两个接口:

  • POST /search:通过 DuckDuckGo 搜索,返回标题、链接、摘要
  • POST /fetch:抓取任意 URL 的页面内容,支持纯文本或 Markdown 输出

Claude Code 通过 Skill 机制调用这两个接口,就能在内网环境里完成搜索和页面读取,不需要浏览器,不需要复杂配置。

架构很简单:内网虚拟机里的 Claude Code → 内网的 web-search-proxy 实例 → 外网。代理服务本身部署在能访问外网的节点上就行。

从信息安全的角度看,这个设计中,信息的流向本身就是单向的:服务只接收内网发来的 POST 请求,触发向外网发出 GET 请求并把响应转发回来。请求入参有长度上限,Agent 没有办法把大段内网内容夹带出去。数据流向始终是“外网 → 内网”,不存在反向泄露的通道。这样既符合数据安全的管控要求,又解决了内网 Agent 获取不到互联网信息的痛点。


半天能干什么

开发过程基本是这样:我用小作文描述需求,Sonnet 做计划、写代码,我看结果、发现问题、调整方向,循环。

用的是从 AWS Kiro 逆向出来的 Sonnet 中转(我穷,不想原价给反华急先锋 Anthropic 送美金,更不能接受被 A÷ 随意封号的风险),我认为 Sonnet 模型在普通编程任务上已经足够胜任了,我无法想象把 Fable 5 当成豆包聊生活琐事的人是多么有钱。整个项目 TypeScript + Express,两个核心接口加上域名黑名单、可选的 /logs 查询审计日志接口、GitHub Actions 自动构建推送 DockerHub,半天跑通。

/fetch 默认返回的是原始 HTML,我增加了 sanitizemarkdown 两个参数——前者把 HTML 剥成纯文本,后者转成 Markdown —— 更适合人类和Agent阅读的技术文档。还有 BLOCKED_DOMAINS 的设计,支持通配符和 [bare](拦截所有裸主机名,防止 proxy 服务被滥用去 fetch redis192.168.x.x 这类内网服务),Sonnet 改了一版就对了。

说这些不是要炫耀什么开发速度。我想说的是:AI 多轮 Rush 的过程中,人需要在环路里。Sonnet 可以很快地给你一个能跑的版本,但“能跑”和“对”之间,还差一个有判断力的人。这个判断力不是编码能力,是你知道自己要什么、知道哪里不对、知道怎么描述清楚。

AGI 还没来。在它到来之前,Agent 完成长程任务仍然依赖人的观察和反馈才能有效闭环。人的价值不在于能不能比 AI 写得更快,而在于品位、判断力,还有知道什么时候该拉一把方向盘。


怎么用

最快的方式,一行 Docker:

1
docker run -d -p 3030:3030 --restart unless-stopped icheerme/web-search-proxy:latest

或者 docker-compose:

1
2
3
4
5
6
7
8
services:
web-search-proxy:
image: icheerme/web-search-proxy:latest
ports:
- "3030:3030"
restart: unless-stopped
environment:
- TZ=Asia/Shanghai

然后装 Skill:把仓库里 skill/SKILL.mdYOUR_PROXY_HOST 替换成你的服务地址,在 Claude Code 里 /add-skill 加载。之后 Claude 会自动识别什么时候需要搜索,自己调用,不用你手动指定。

几个有用的环境变量:

变量 说明
BLOCKED_DOMAINS fetch 黑名单,支持精确匹配、*.internal 通配符、[bare] 拦截裸主机名
ADMIN_PASSWORD 设置后启用 GET /logs?password=xxx,查最近 50 条记录

技术栈:Node.js 20 · TypeScript · Express · Turndown。


后续计划

目前搜索后端是 DuckDuckGo,免费但深度有限。接下来打算加一个 POST /tavily 接口,接入 Tavily Search API,支持配置多个 API Key 轮询——这样就能把这个服务变成一个中心化的 Tavily 搜索节点,内网所有 Agent 共用,做“最大化搜索”时不用担心单 Key 的速率限制。


项目在这里:github.com/icheer/web-search-proxy

如果你也在做类似的内网 AI 基础设施,希望能有点参考价值。

Buy me a coffee ☕