兰 亭 墨 苑
期货 · 量化 · AI · 终身学习
首页
归档
编辑文章
标题 *
URL 别名 *
内容 *
(支持 Markdown 格式)
# 给 weclaw 接上 DeepSeek 联网搜索——一次从"听说"到上线的实战 ## 起因 "听说 DeepSeek 的 response 接口支持搜索,帮我验证一下。" 就这么一句话。weclaw(我们那个把微信、OpenAI 兼容 API 桥接到各家大模型的 agent 中台)一直在用 DeepSeek 的 `chat/completions`,但从没碰过所谓"response 接口"。于是先验证,再决定接不接。 ## 第一步:它真的能搜,而且搜得很实 官方文档确认 DeepSeek 有 Responses API(`POST /v1/responses`,OpenAI 风格),`deepseek-v4-flash` 支持,内置 `web_search` 工具,服务端执行。 光看文档不算数,拿现有的 key 直接打一发: ```json POST https://api.deepseek.com/v1/responses { "model": "deepseek-v4-flash", "input": "OpenAI 2026 年最近发布的模型是哪个?给来源。", "tools": [{"type": "web_search"}], "stream": false } ``` 返回的 `output[]` 里清清楚楚:`reasoning`(思考)→ `web_search_call`(`action.type=search`,真发了查询词)→ `web_search_call`(`action.type=open_page`,真抓了 `openai.com`、`help.openai.com` 等页面)→ `message`(`phase=final_answer`,带可核实的来源链接)。不是幻觉,是真去网上抓了页子的。 顺便摸清几个关键约束: - **`chat/completions` 会拒绝** `web_search` 工具——报错 `unknown variant 'web_search', expected 'function'`。搜索是 Responses 独占。 - **搜索结果是黑盒**:标题、摘要、正文一律不返回,服务端直接塞进 LLM 上下文。`annotations` 恒为空;URL 只在 `open_page` 动作里出现,还没标题。 - **只在 `deepseek-v4-flash` + `api.deepseek.com`** 上能用(v4-pro 官方说 8 月初上,暂未)。 结论:能搜,值得接。 ## 第二步:一个 helper,喂两条路径 weclaw 里 DeepSeek 有两条互相独立的调用路径: - **admin/API 反代**(`api/server.go`):网页后台聊天,**非流式**,裸字节透传上游。 - **微信**(`agent/http_agent.go`):**非流式**,固定 `{model, messages}`。 两条路径都要能搜索,又不能各写一遍。于是抽一个共享 helper: ```go func CallResponsesSearch(ctx, endpoint, apiKey, headers, model, msgs, systemPrompt) (answer string, sources []string, err error) ``` 它干三件事:把 endpoint 从 `/chat/completions` 切到 `/responses`、注入 `web_search` 工具、解析 `output[]` 取最终答案 + 来源 URL(去重、剥掉 DeepSeek 给 URL 追加的 `#ws_call_id=...` 追踪尾巴)。 触发方式选了**最省事的 agent 级配置**:`AgentConfig` 加一个 `web_search bool`,某个 agent 标成搜索 agent,就永远走 Responses。新增一个独立的 `deepseek-search`(别名 `dss`),不动原来日常用的 deepseek——避免把每次对话都降级到 v4-flash。 ### 来源怎么展示?白嫖现成的渲染器 搜索结果只有 URL、没标题。本来打算写新前端,翻代码发现 `renderMarkdown` **早就自带"📎 参考链接"渲染**:文本末尾 `---` 后跟 `N. [标题](url)` 列表,它自动渲染成带圆圈编号的可点链接框。 于是 helper 只要把来源拼成这种脚注块追加到答案末尾: ``` 答案是…… --- 1. [](https://help.openai.com/...) 2. [](https://www.theverge.com/...) ``` 前端零改动,自动出"📎 参考链接"框。这是这次最爽的一处复用。 ### admin 非流式?打包成 chat.completion admin 后台是 `await resp.json()` 读 `choices[0].message.content`,完全不走流式。那就在后端把 Responses 的结果重新打包成标准 `chat.completion` JSON 吐回去——前端解析逻辑一行不用改。搜索期间把按钮文案从 "Thinking" 换成 "🔍 搜索中…" 就行。 ## 第三步:上线,和一个隐蔽的坑 部署后先测 admin 路径:问"OpenAI 2026 最近发布的模型"——20 多秒后返回标准 `chat.completion`,答案是真实的 GPT-5.6 / 2026-07-09,末尾带来源脚注。✅ 然后微信发 `dss 今天的新闻`——**没搜索**。模型一本正经地回:"我知识截止到 2025 年,建议你打开联网搜索功能。" 翻日志,两个细节露了马脚: 1. 创建 agent 的日志是 `[agent] created HTTP agent`,不是 `[handler] created`——说明走的是**另一条建 agent 的代码路径**。 2. 回复只花了 **5.2 秒**——真搜索一次要 20~30 秒,5 秒说明根本没搜,走的是普通 chat。 根因:`HTTPAgent` 有**两个创建点**——`cmd/start.go` 的 `createAgentByName`(启动建默认 agent + 微信按需启动都用它)和 `messaging/handler.go` 的 reload 工厂(配置热重载时才装)。我第一次只改了后者,而微信 `/dss` 走的是前者,所以那个实例 `webSearch=false`,自然不搜。 补上 `cmd/start.go` 那处,重新部署,再测——成功。微信问"今天几号",老老实实去查了日历网站,回 "2026-08-05"(嗯,差一天,那是搜索内容本身的事,不是代码 bug),末尾带来源链接。 ## 复盘:几个值得记的点 - **"听说 X 支持 Y"先验证再信。** 这次验证直接推翻了"加个参数就行"的直觉——chat 接口根本不收 `web_search`,必须换 API 风格、改请求体结构。 - **黑盒能力要摸清边界。** 搜索结果不返回正文,意味着想做 Perplexity 式引用块还得外接搜索 API;但只做"答案 + 来源链接",现成就够。清楚边界才知道哪些能省、哪些省不了。 - **多个工厂/创建点是最容易漏的坑。** 加字段时 `grep` 一遍所有构造调用,比"我改了那处"靠谱得多。这次 `[agent]` vs `[handler]` 的日志前缀、和"5 秒 vs 30 秒"的时间差,是定位的两个关键线索。 - **复用现成渲染器省巨多事。** `renderMarkdown` 那套脚注解析本来是给别的 agent 准备的,这次白嫖,来源展示零前端代码。 ## 结果 weclaw 现在多了一个 `deepseek-search` agent: - **网页后台**:选中它,问时间敏感的问题,答案末尾自动出"📎 参考链接"。 - **微信**:发 `dss 你的问题`,机器人真的去搜网,回复带来源。 一次搜索大约 2 分钱(flash 定价),token 比普通 chat 重一个量级,所以做成按需的 agent 而不是默认开。 从"听说支持搜索"到两条路径都能搜,一天搞定。最有成就感的不是代码量(其实没多少),而是那个"5 秒没搜"的坑被日志里的细节精准钓出来——这是调试手感最好的那种 bug。 --- *weclaw · DeepSeek Responses API · 2026-08-06*
配图 (可多选)
选择新图片文件或拖拽到此处
标签
更新文章
删除文章