Projects/AI Chat Integrates Web Search (Bocha)
AI

AI Chat Integrates Web Search (Bocha)

Give self-hosted AI conversations real-time web access: Bocha Search + forced-RAG, usable with both account types.

What it is

Connected both self-hosted AI chat setups to "real-time web access": on the DSH agent side, added an mcp__bocha__search tool that can call the Bocha Search API on demand; on the Open WebUI web side, wired search to Bocha and enabled forced-RAG, so questions are retrieved before answering. Both the administrator's and family members' accounts can use it.

Why we did it

DSH's original web_search used free SearXNG, which is sufficient for daily use, but recall for long Chinese queries is average and summaries are short. Bocha provides long Chinese summaries, 0.3–0.5s per request, and is somewhat more accurate; the tradeoff is per-call billing (about ¥0.036/call). So the two are complementary, not replacements: the free one as the baseline, the paid one on demand.

Along the way, it solved two pitfalls: first, DSH's native provider plugin could not be installed while running (the module resolution table is fixed at startup); after changing it to an MCP configuration-based bundle, it takes effect live without a restart; second, "with a paid service, you feel uneasy if you don't check the balance," so the balance and consumption were both integrated into the .164 monitoring console.

How it is now

Running. DSH's daily search still uses SearXNG and incurs no cost; Bocha is only called manually when needed. The balance card samples every 15 minutes; below ¥5 turns yellow, below ¥2 turns red and sends a mobile notification. At 2026-10-17 16:00, the balance was 18.48 yuan; one web-enabled question in Open WebUI makes about 2 calls, about ¥0.07.

Features

  • DSH agent 通过 MCP 获得 `mcp__bocha__search` 联网搜索工具,安装即热生效、不用重启进程
  • Open WebUI 网页侧搜索接博查并开 forced-RAG,管理员与家人两条账号共用
  • 默认策略省钱优先:DSH 日常 web_search 仍走免费 SearXNG,博查按需手动调用
  • 在 .164 监控台加了「博查搜索余额」卡片:余额、今日消耗、7 天日均、14 天柱状图
  • 余额低于 ¥5 / ¥2 走 ntfy 手机推送,6 小时去重,恢复后补一条「已恢复」

Stack: BoCha API · MCP (stdio JSON-RPC) · Node.js · Python · systemd · Open WebUI · SearXNG

1. Prepare the Key

Apply for an API Key in the Bocha console, and place it in a location that only root can read:

install -m 600 /dev/null /root/.dsh/secrets/bocha.key
# 写入 Key 后核对权限
ls -l /root/.dsh/secrets/bocha.key   # 应为 -rw-------

In the configuration file, write only the path, not the Key itself.

2. Write the MCP server

/root/plugins-src/bocha-mcp/server.mjs: a zero-dependency Node script that uses stdio JSON-RPC and exposes one tool with three parameters — query, count (1–10, default 8), and freshness (noLimit / oneDay / oneWeek / oneMonth / oneYear). Place the bundle definition in the same directory (package.json, cordis.patch.yml, locale, icon).

3. Install the bundle

Insert a @deepseek-ai/dsh-mcp-client entry into the DSH configuration (a package bundled with DSH and natively resolvable), then attach the local bundle @local/bocha-mcp. Install it with plugin_manager; a normal result is application: applied with no warnings.

Why not use a native provider plugin: DSH's module resolution table is fixed at startup. Host plugins newly installed at runtime cannot import @deepseek-ai/*; the entire process must be restarted for them to take effect (which interrupts conversations and invalidates the old GUI token). The MCP route does not require a restart.

4. Verify

  • The protocol unit test (initialize + tools/list + tools/call) completes in about 505 ms in one run;
  • Tool.listTools shows mcp__bocha__search, and “MCP server: bocha” appears in the system prompt;
  • Ask directly “Shanghai weather today” and see whether it returns today's weather plus a daily forecast for the next week.

5. Connect to web chat

On the Open WebUI side, switch the search source to Bocha and enable forced-RAG (force retrieval before answering). Note that the API path has changed since v0.12.0; use POST /openai/config/update to write the configuration.

6. Balance monitoring

The collection script /opt/dashboard/bocha_balance.py is run by a systemd timer every 15 minutes and calls GET https://api.bochaai.com/v1/fund/remaining (header Authorization: Bearer <key>). Consumption is calculated from the balance difference: subtract the last data point of the day from the first data point of the day, then divide by ¥0.036/call to infer the number of calls. Thresholds of ¥5 / ¥2 trigger an ntfy notification.

Maintenance reminder

After changing server.mjs, disable and then re-enable @local/bocha-mcp in plugin_manager (or restart DSH); otherwise the old process will still be running.