DeepSeek Harness web_search 与 web_fetch 异常:一个查询失败整体失败与表格竖线丢失
DeepSeek Harness(DSH)的 web_search 与 web_fetch 有两个容易被误判成「网络抖动」的缺陷,共同点是结果在写入侧就已经不对了**:搜索那条是一个子查询失败就把整批结果作废——扇出用的是共享取消信号,任何一个查询失败都会取消同批的其它查询,而随后的收集逻辑仍把第一个错误抛出去,导致「已成功、且在按次计费的服务上已经付费」的结果被一起丢掉;抓取那条是表格单元格里连续的竖线被折叠成一个——Markdown 表格要求竖线逐字符转义,而实现用的是「一段连续竖线整体替换」,于是代码表达式语义被悄悄改写,且无法从 Markdown 结果侧还原(三种数量的竖线渲染出来是同一个形状)(#8794、#9630)。
DSH插件 一个查询失败就整批作废:扇出被共享取消信号牵连
一句话结论:并行子查询共享同一个取消信号,任一失败即中止其余查询;虽然收集用的是「全部等待」形式,但随后仍会抛出第一个错误,于是结果合并那一步永远不会执行**——工具只回一句错误,成功结果全丢。** 症状是「多查询搜索只报一个错误、没有任何结果」(#8794)。
DSH插件 判据:两条,一条看形态、一条看账单
- 看返回值形态:携带多个子查询的一次调用只返回一条错误、且没有任何结果条目——即为本条;如果返回了部分结果并标注某个子查询失败,那是另一种(部分成功)形态(#8794)。
- 看计费记录(很直观):在按次计费的搜索服务上,每个子查询都是一次独立计费轮次。如果你的后台显示扣费次数等于你提交的子查询数、而工具只回错误,说明结果被丢弃了——你付了钱但没拿到东西(#8794)。
DSH插件 三步处置与修法方向
DSH插件 表格竖线被折叠:Markdown 转义写成了「整段替换」
一句话结论:渲染单元格时用「一段连续竖线整体替换」的正则把 || 压成 |,而 GFM 要求竖线逐字符转义——结果代码表达式 first||last 被写成 first|last,语义变了;更糟的是这种损坏无法从 Markdown 结果侧还原**(|、||、||| 渲染后同形)。** 症状是抓取成功、没有截断,但内容被悄悄改写(#9630)。
DSH插件 三步确认与处置
- 确认判据:抓一个含代码表格的页面(表格单元格里出现
||这类表达式),检查转换后的 Markdown 里竖线是否变少;同时确认请求是成功的、结果没有被截断——「成功但被改写」是本条的指纹(#9630)。 - 临时规避一:抓取原始内容而不是渲染后的表格(例如源码页、接口返回),避开这条转换路径(#9630)。
- 临时规避二:不要让模型基于该表格里的代码做判断,必要时回原页面核对(#9630)。
- 修法:把「整段替换」改成每字符转义——报告者提供了一份可独立摘取的最小补丁(#9630)。
- 验证:重抓同一页面,确认 Markdown 里的竖线数量与原页面一致(
||仍是\|\|两个字符而不是一个)(#9630)。
DSH插件 排查注意事项
这两条的排查要点都是「先分清是抓取/搜索本身失败,还是结果在转换环节被改了」。 七条要点:
- 搜索那条看形态 + 看账单,两条一起用最准(#8794)。
- 「只回一条错误、零结果」≠「真的没搜到」:多子查询场景下优先怀疑整批作废(#8794)。
- 扇出的正确语义是「各子查询独立成败」,而不是「全成功或全失败」(#8794)。
- 成本影响要写进结论:按次计费时整批作废是真金白银(#8794)。
- 抓取那条「成功但被改写」比报错更危险:它不会让你警觉(#9630)。
- 损坏不可逆:不要指望在下游解析阶段修复竖线——信息在转换时就丢了(#9630)。
- 修复状态如实:两条在讨论所指版本(
0.2.1-alpha.2前后)里都未修;搜索那条的修法(去共享中止 + 部分成功语义)与抓取那条的补丁都未合入,当下只有「拆单查询」「抓原始内容」这类用户侧规避。
搜索与抓取类问题排查时,容易被「网络」这个方向带偏。若你想把「环境」这一层先确认下来——比如某个搜索/抓取类插件是否装过、装了几个版本——与其在各处手敲命令,不如用 DSH Plugin Hub 这类可视化插件市场:已安装列表、装卸历史与系统日志集中在一个面板里。

常见问题
DeepSeek Harness 的 web_search 会把整批结果作废,是因为扇出实现用的是共享取消信号:任一查询失败就中止同一批里的其它查询,即便收集逻辑用的是「全部等待」的形式,随后仍然会把第一个错误抛出去,于是结果合并那一步永远不会执行。表现就是工具只回一句错误,而实际上已经成功(且在按次计费的服务上已经付费)的结果被丢掉。修法方向是去掉共享中止、逐条返回成功的查询并按名字标注失败项,只有全部失败才整体失败。
DeepSeek Harness 里判断整批作废还是部分成功,可以先看返回值形态:如果工具只返回一条错误、而没有任何结果条目,同时你那条查询里确实有多个子查询,那更可能是整批作废;如果返回了结果但标注某个子查询失败,那才是部分成功。另一个辅助判据是费用——按次计费的搜索服务商会记录每一次子查询,如果后台显示扣费次数等于你提交的子查询数、而工具只回了错误,就说明结果被丢弃了。
DeepSeek Harness 里双竖线变单竖线,是因为渲染器把单元格里连续的竖线整段替换成了一个转义竖线。Markdown 表格约定里竖线需要按字符逐个转义,而当前实现用的是「一段连续竖线一起替换」的写法,于是双竖线被压成一个。这会改变代码表达式语义,而且从 Markdown 结果侧无法还原——三种不同数量的竖线渲染出来是同一个形状,信息在转换时就已丢失。
DeepSeek Harness 里绕开 web_fetch 竖线折叠有两条办法:一是改抓取目标本身(例如抓原始源码或 API 返回,而不是渲染后的表格);二是抓完之后不要依赖 Markdown 里的表格做代码判断,必要时回原页面核对。根治要在渲染器里修,因为损坏发生在写入侧,后续任何解析都救不回来。
DeepSeek Harness 里搜索那条会影响计费——在按次计费的搜索服务上,每个子查询都是一次独立计费轮次,整批作废意味着你付了钱但拿不到结果。抓取那条不影响计费,但会影响判断质量:代码里的或运算被显示成单竖线后,模型可能据此得出错误结论。
相关术语
- 扇出(fan-out)
- 扇出指把一次工具调用拆成多个并行子请求的执行方式,在搜索场景下表现为一次 web_search 携带多个查询词。扇出的语义期望是「各子查询独立成败」,但若共享取消信号,一个失败会牵连全部。— https://github.com/deepseek-ai/deepseek-harness/discussions/8794
- allSettled 语义
- allSettled 语义指等待所有子任务结束、无论成功失败都收集结果,而不是遇到第一个失败就中止。搜索扇出应当采用这种语义,才能做到部分成功也能返回。— https://github.com/deepseek-ai/deepseek-harness/discussions/8794
- GFM 表格中的竖线转义
- GFM 表格用竖线分隔单元格,因此单元格内容里的竖线需要转义为反斜杠加竖线。转义要求逐字符处理:连续两个竖线是「两个字符」,不是「一段」。— https://github.com/deepseek-ai/deepseek-harness/discussions/9630
- web_fetch
- web_fetch 是 DeepSeek Harness 用来抓取网页并转换成文本的工具,转换结果通常是 Markdown。HTML 表格会被渲染成 GFM 表格,因此表格内容的转义正确性直接决定抓取结果能否忠实还原页面。— https://github.com/deepseek-ai/deepseek-harness/discussions/9630
来源
- #8794 — web_search: one failed query aborts the other queries and discards their (billed) results· deepseek-ai(GitHub Discussions)
- #9630 — web_fetch collapses consecutive pipes in HTML table cells· deepseek-ai(GitHub Discussions)