解决DeepSeek Harness接入第三方API后的网页搜索问题

DeepSeek Harness在接入第三方API后的使用过程中会出现dsh联网搜索服务不可用的提示,在此记录一下对应的解决方案。

问题描述

DeepSeek Harness在接入第三方API后的使用过程中出现dsh联网搜索服务不可用的提示,尝试通过CC Swtich将Chat/Completions格式的第三方API转化为兼容Anthropic Messages协议,并在DSH的网页搜索插件中配置它,但仍然收到DeepSeek returned no web_search_tool_result blocks的错误信息。

问题原因

造成以上问题的根本原因是在DeepSeek Harness中大语言模型API与检索API是相互独立的,模型负责判断+读资料+写答案,搜索API 只负责把资料取回来。DSH的网页搜索需要接入一个兼容 Anthropic Messages 协议、且支持原生 web_search 的端点,而当前第三方API端点不是原生联网搜索的端点,即使通过CC Swtich转译也无法支持WebSearch(原生搜索)功能。

解决方法

一、接入官方API

  • 打开 DSH 的设置页。
  • 依次进入插件(Plugins)—官方(Official),进入网页搜索。
  • 在输入框里填入DeepSeek API Key并保存。

使用此方法配置后DSH将使用配置的API进行web search,请随时注意deepseek api余额情况。

二、接入Tavily

Tavily 是一个专为 AI Agent 和 LLM 优化的搜索引擎 API。与 Google、Bing 等传统搜索 API 不同,Tavily 不仅仅是返回一串 URL 和摘要——它在一次 API 调用中完成搜索、抓取、过滤、提取和排序,直接把最相关的结构化内容喂给你的模型。在这里,一个简单的方式就是利用CC Switch的MCP 面板通过MCP搜索服务器将搜索与对话模型解耦。步骤如下。

  • 在app.tavily.com进行注册,获取API
  • 在CC Switch → MCP 面板 → 添加服务器
  • 传输类型选择HTTP / Streamable HTTP(远程)
  • URL填写为https://mcp.tavily.com/mcp/?tavilyApiKey=tvly-你的key
  • 把该 MCP server 同步到 Claude Code
  • 配置完成后在 Claude Code 里输入/mcp,能看到tavily-search / tavily-extract / tavily-crawl 等工具即配置成功。

经过以上操作,在Claude Code 里就能用搜索工具,但DSH仍然无法使用,原因是DSH只接受Anthropic兼容Messages API,Tavily这类纯搜索API不能直接接入,需要加一层转换成Messages协议的代理。
把下面代码存成tavily-adapter.mjs:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
import http from 'node:http';

const PORT = process.env.PORT || 8787;
const TAVILY_KEY = process.env.TAVILY_API_KEY || 'tvly-你的key';

function extractQuery(messages = []) {
for (let i = messages.length - 1; i >= 0; i--) {
const m = messages[i];
if (m.role !== 'user') continue;
const blocks = Array.isArray(m.content) ? m.content : [{ type: 'text', text: String(m.content) }];
for (const b of blocks) if (b?.type === 'text' && b.text?.trim()) return b.text.trim();
}
return '';
}

const server = http.createServer(async (req, res) => {
if (req.method !== 'POST' || !req.url.startsWith('/v1/messages')) {
res.writeHead(404, { 'content-type': 'application/json' });
return res.end(JSON.stringify({ type: 'error', error: { type: 'not_found_error', message: 'not found' } }));
}
let raw = '';
for await (const c of req) raw += c;
let parsed; try { parsed = JSON.parse(raw); } catch {
res.writeHead(400, { 'content-type': 'application/json' });
return res.end(JSON.stringify({ type: 'error', error: { type: 'invalid_request_error', message: 'bad json' } }));
}

const query = extractQuery(parsed.messages);

// 调用 Tavily
const r = await fetch('https://api.tavily.com/search', {
method: 'POST',
headers: { 'content-type': 'application/json', authorization: `Bearer ${TAVILY_KEY}` },
body: JSON.stringify({ query, max_results: 5, include_answer: false })
});
const tav = await r.json();
if (!r.ok) {
res.writeHead(502, { 'content-type': 'application/json' });
return res.end(JSON.stringify({ type: 'error', error: { type: 'api_error', message: JSON.stringify(tav) } }));
}

const results = (tav.results || []).map(it => ({
type: 'web_search_result',
url: it.url,
title: it.title || it.url,
...(it.published_date ? { page_age: it.published_date } : {})
}));

const message = {
id: 'msg_tavily_' + Date.now(),
type: 'message',
role: 'assistant',
model: parsed.model || 'tavily-adapter',
content: [
{ type: 'web_search_tool_result', tool_use_id: 'toolu_tavily_' + Date.now(), content: results },
{ type: 'text', text: '', citations: (tav.results || []).filter(x => x.content).map(x => ({ type: 'web_search_result_location', url: x.url, cited_text: x.content })) }
],
stop_reason: 'end_turn',
stop_sequence: null,
usage: { input_tokens: 0, output_tokens: 0 }
};

res.writeHead(200, { 'content-type': 'application/json' });
res.end(JSON.stringify(message));
});

server.listen(PORT, () => console.log(`tavily-adapter: http://127.0.0.1:${PORT}/v1/messages`));

在powershell中输入

1
2
$env:TAVILY_API_KEY="tvly-你的key"
node tavily-adapter.mjs

保持进程常驻的情况下将DSH的网页搜索的接口地址改成上面的地址http://127.0.0.1:8787即可。

--- 本文结束  The End ---