Skip to content
dsh.fish
Bundle

dsh-web-search-doubao

Doubao feedcoop global_search provider for the DeepSeek Harness web capability seam (ctx.web)

Source
JayLi52
License
MIT
Updated
Updated yesterday

Readme

# dsh-web-search-doubao

中文 | [English](#english)

[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)的**豆包联网搜索插件**:把字节跳动 feedcoop `global_search` API 接入 `ctx.web` 能力接缝,替换内置搜索提供方。

这是一个**树外 bundle 插件**(out-of-tree bundle):向 `ctx.web` 注册一个 `WebSearchProvider`(id: `doubao`),自带 `cordis.patch.yml` 层——装入 profile 后自动挂载**并自动把搜索选择切到 `doubao`**。**零运行时依赖**,不修改任何 harness 源码。

## 安装

在 harness 仓库根目录(源码运行用 `pnpm dsh ...`,安装版用 `dsh ...`):

```sh
dsh plugin --profile web add github:JayLi52/dsh-web-search-doubao
```

安装会:把包链接进 profile、把它的 bundle 层加入 `dsh.profile.bundles`、挂载提供方、切换 `searchProvider: doubao`。装完即用,无需手改配置。

## 配置 token

只需提供 feedcoop Bearer token,推荐环境变量方式——在 `$DSH_HOME/.env`(Windows 默认 `C:\Users\<你>\.dsh\.env`)写一行:

```sh
DOUBAO_SEARCH_API_KEY=<你的-token>
```

然后**重启一次** `dsh web`(环境变量在启动时加载)。之后改 token 以外的配置都走 profile patch 热重载,不用再重启。

也可以在 profile 的 `cordis.patch.yml` 里内联(覆盖环境变量):

```yaml
- id: web-search-doubao
  name: dsh-web-search-doubao
  config:
    apiKey: <你的-token>
```

验证组合结果:

```sh
dsh --profile web --dump-config | grep -A3 web-search-doubao
```

在 Web UI 新会话里让模型联网搜索(`web_search` 工具)即走豆包。想切回内置搜索,在 profile 的 `cordis.patch.yml` 加:

```yaml
- id: web
  name: '@deepseek-ai/dsh-web'
  config:
    searchProvider: deepseek-official
```

(profile patch 晚于 bundle 层应用,后写者赢。)

## 配置项

| 字段 | 默认值 | 说明 |
|---|---|---|
| `apiKey` | `''` | Bearer token;为空时回退读取 `$<apiKeyEnv>`。两者皆空 → 提供方不可用 |
| `apiKeyEnv` | `DOUBAO_SEARCH_API_KEY` | 存放 token 的环境变量名 |
| `baseURL` | `https://open.feedcoopapi.com` | API 基址 |
| `docCount` | `10` | 请求未带 `maxResults` 时的默认 `DocCount` |
| `maxSnippetLength` | `300` | 每条结果摘要长度上限 |

## 映射规则

feedcoop `Result.Documents[]` → 接缝 `WebSearchSource`:`Url`→`url`(缺失丢弃)、`Title`→`title`、`Snippet[]` 中 `Type==='text'` 的 `Text` 换行拼接→`snippet`、`DocumentInfo.PublishTime`→`publishedAt`。`maxResults` 截断由接缝负责;图片类 Snippet 忽略。feedcoop 的错误信封(HTTP 200 + `ResponseMetadata.Error`,如 `invalid_api_key`)会被抛成显式错误,不会静默成"无结果"。

## 排障

| 症状 | 原因与处理 |
|---|---|
| 工具调用报 `unknown tool ""`,模型只剩 thinking 死循环 | **不是本插件的问题**:harness 的 `llm-deepseek` 适配器对"续包带空串 id/name"的流(DashScope 兼容模式等 OpenAI 兼容端点)有解析 bug,会把工具名覆盖成空串。修复见 deepseek-harness `packages/llm/llm-deepseek/src/translate.ts`(仅当非空才覆盖)。用 DeepSeek 官方端点不受影响 |
| `Doubao API error: invalid_api_key` | token 错或未设置;检查 `$DSH_HOME/.env` 或 patch 里的 `apiKey`,改 env 需重启 |
| `WEB_PROVIDER_CONFIGURED_UNAVAILABLE` | 选了 `doubao` 但 `available()` 为 false——即没配 token |
| `WEB_PROVIDER_AMBIGUOUS` | 多个可用提供方且没配 `searchProvider`;显式指定 id |
| 改了 `cordis.patch.yml` 没生效 | patch 是热重载的;若整层解析失败会保留旧树并打日志,检查 YAML 是否为顶层数组 |

## 自证路由(A/B)

怀疑搜索没走豆包时,用 `--patch` 覆盖层投毒 key 跑一次性任务:错 key 必报 `invalid_api_key`(或空结果),真 key 正常——两次唯一变量是 doubao 的 key,即可证明路由。

```yaml
# wrong-key-test.yml
- id: web-search-doubao
  name: dsh-web-search-doubao
  config:
    apiKey: sk-invalid-for-ab-test-0000000000
```

```sh
dsh --profile headless --patch ./wrong-key-test.yml "用 web_search 搜 海淀必去景点"
```

## 分享给别人

**profile 目录本身不可移植**(`package.json` 里是本机 `link:` 绝对路径;key 与模型配置按设计不在 profile 内)。分享"配方":把本仓库链接发给对方,执行:

```sh
# 1. 装插件(自动挂载 + 自动切换搜索),并在 $DSH_HOME/.env 自备 token
dsh plugin --profile web add github:JayLi52/dsh-web-search-doubao

# 2.(可选)开 Claude Code 委托:先让包可解析
#    npm 安装版:dsh plugin --profile web add @deepseek-ai/dsh-subagent-claude-code
#    源码运行版:harness 仓库 apps/cli/package.json 加
#      "@deepseek-ai/dsh-subagent-claude-code": "workspace:^" 后 pnpm install
#    再在 ~/.dsh/profiles/web/cordis.patch.yml 追加:
#      - insert:
#          - id: subagent-claude-code
#            name: '@deepseek-ai/dsh-subagent-claude-code'

# 3.(可选)跳过 ~/.agents 共享技能注入:启动环境设 DSH_AGENTS_HOME=<空目录>
```

模型 key(`DEEPSEEK_API_KEY` 或 Models 页配置)由对方自备。

---

<a id="english"></a>

# English

A [DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness) web
capability seam (`ctx.web`) search provider backed by the Doubao / ByteDance
feedcoop `global_search` API. Out-of-tree bundle plugin: registers a
`WebSearchProvider` (id `doubao`) and ships a `cordis.patch.yml` layer that
mounts it **and switches the seam's search selection to `doubao`** on install.
Zero runtime dependencies; no harness source changes.

## Install

```sh
dsh plugin --profile web add github:JayLi52/dsh-web-search-doubao
```

## Token

Set `DOUBAO_SEARCH_API_KEY=<token>` in `$DSH_HOME/.env` and restart once, or
inline it via the profile patch (`config.apiKey` on the `web-search-doubao`
row). Verify with `dsh --profile web --dump-config`. To switch back to the
shipped provider, override `searchProvider: deepseek-official` on the `web`
row in the profile patch (later layers win).

## Config fields

| Field | Default | Meaning |
|---|---|---|
| `apiKey` | `''` | Bearer token; falls back to `$<apiKeyEnv>`. Empty → unavailable. |
| `apiKeyEnv` | `DOUBAO_SEARCH_API_KEY` | Env var holding the token. |
| `baseURL` | `https://open.feedcoopapi.com` | Endpoint base. |
| `docCount` | `10` | Default `DocCount` without `maxResults`. |
| `maxSnippetLength` | `300` | `MaxSnippetLength` per request. |

## Mapping & errors

`Documents[]` maps to `WebSearchSource` (`url`/`title`/`snippet`/`publishedAt`);
text-type snippet parts joined with newlines; image parts ignored; the seam
owns `maxResults` truncation. feedcoop error envelopes (HTTP 200 +
`ResponseMetadata.Error`, e.g. `invalid_api_key`) are thrown as explicit
errors, never silently mapped to "no results".

## Troubleshooting

- `unknown tool ""` with endless thinking: **not this plugin** — the harness
  `llm-deepseek` adapter overwrites parsed tool-call name/id with the empty
  strings that OpenAI-compatible endpoints (e.g. DashScope) send on
  continuation chunks. Fix: only overwrite when non-empty, in
  `packages/llm/llm-deepseek/src/translate.ts`. The official DeepSeek endpoint
  is unaffected.
- `invalid_api_key`: wrong/missing token; env changes need a restart.
- `WEB_PROVIDER_CONFIGURED_UNAVAILABLE`: `doubao` selected but no token set.
- `WEB_PROVIDER_AMBIGUOUS`: several usable providers, no `searchProvider` set.

## License

[MIT](LICENSE)

Install

dsh plugin --profile web add github:JayLi52/dsh-web-search-doubao

Profile: web

  • This source has no pinned commit, so a later push upstream changes what installs. Prefer pinning a commit.
Source