From 25dadb396cb325bf9eb60db3a04dcb4770f160de Mon Sep 17 00:00:00 2001 From: gzzwp <38277434+gzzwp@users.noreply.github.com> Date: Mon, 19 Oct 2026 17:03:16 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=20DeepSeek=20=E8=81=94?= =?UTF-8?q?=E7=BD=91=E6=90=9C=E7=B4=A2=E5=B7=A5=E5=85=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 基于 DeepSeek 官方 Search API(https://api.deepseek.com/v1/tools/search) 封装的联网搜索工具,为 MaxKB 智能体提供实时网页检索能力。 - 支持 site:/lang:/after:/before:/-关键词 等搜索语法 - 可选返回网页正文(need_content) - 同时返回结构化 results 与带引用的 markdown - 分级错误处理(400/401/402/429/5xx)并给出排查提示 - api_key 作为启动参数(密码框),与输入参数分离 --- ...\347\275\221\346\220\234\347\264\242.tool" | Bin 0 -> 9020 bytes tools/tool_deepseek_search/README.md | 161 ++++++++++++++ tools/tool_deepseek_search/data.yaml | 5 + tools/tool_deepseek_search/deepseek_search.py | 203 ++++++++++++++++++ tools/tool_deepseek_search/logo.png | Bin 0 -> 260 bytes 5 files changed, 369 insertions(+) create mode 100644 "tools/tool_deepseek_search/1.0.0/DeepSeek\350\201\224\347\275\221\346\220\234\347\264\242.tool" create mode 100644 tools/tool_deepseek_search/README.md create mode 100644 tools/tool_deepseek_search/data.yaml create mode 100644 tools/tool_deepseek_search/deepseek_search.py create mode 100644 tools/tool_deepseek_search/logo.png diff --git "a/tools/tool_deepseek_search/1.0.0/DeepSeek\350\201\224\347\275\221\346\220\234\347\264\242.tool" "b/tools/tool_deepseek_search/1.0.0/DeepSeek\350\201\224\347\275\221\346\220\234\347\264\242.tool" new file mode 100644 index 0000000000000000000000000000000000000000..9640679b7a499048ded513d492bb6fa291cffcf8 GIT binary patch literal 9020 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 literal 0 HcmV?d00001 diff --git a/tools/tool_deepseek_search/README.md b/tools/tool_deepseek_search/README.md new file mode 100644 index 0000000..70a04a7 --- /dev/null +++ b/tools/tool_deepseek_search/README.md @@ -0,0 +1,161 @@ +# DeepSeek 联网搜索工具 + +基于 DeepSeek 官方 Search API()封装的联网搜索工具,为 MaxKB 智能体提供实时网页检索能力。 + +## 一、功能说明 + +- **实时检索**:调用 `https://api.deepseek.com/v1/tools/search`,返回标题、链接、短摘要、长摘要、站点名、发布时间、权威性与重排分数 +- **搜索语法**:支持 `site:` / `-site:` / `"精确短语"` / `-关键词` / `lang:` / `after:` / `before:`,可在一次 query 中组合使用 +- **正文可选**:`need_content=true` 时额外返回网页正文,适合需要深读的场景;默认关闭以节省上下文 +- **双形态返回**:同时给出结构化 `results` 数组与拼装好的 `markdown`,后者带链接与来源标注,模型可直接用于回答 +- **降级友好**:任何失败都返回 `success: false` 而非抛异常,工作流可据此决定是否退化为"不联网回答" + +## 二、参数说明 + +### 2.1 启动参数 + +| 参数 | 组件类型 | 必填 | 说明 | +| :--- | :--- | :--- | :--- | +| `api_key` | 密码框 | 是 | DeepSeek API Key,在 获取 | + +> 启动参数与输入参数分离,工作流中只需关注 `query`,可避免 Key 在编排界面反复暴露。 + +### 2.2 输入参数 + +| 参数名 | 数据类型 | 必填 | 来源 | 说明 | +| :--- | :--- | :--- | :--- | :--- | +| `query` | string | 是 | 引用参数 | 搜索关键词,1~128 个字符,不能全为空白 | +| `count` | int | 否 | 自定义参数 | 返回条数,默认 10,范围 1~50,越界自动夹逼 | +| `need_content` | string | 否 | 引用参数 | 是否返回网页正文,`false`(默认)/ `true` | +| `ori_question` | string | 否 | 引用参数 | 用户原始问题,有助于搜索意图理解,可留空 | + +### 2.3 输出参数 + +返回一个 dict(已可 JSON 序列化): + +| 字段 | 类型 | 说明 | +| :--- | :--- | :--- | +| `success` | bool | 是否成功 | +| `query` | string | 实际发出的搜索词 | +| `count` | int | 实际返回条数 | +| `results` | array | 结果数组,元素字段见下 | +| `markdown` | string | 拼装好的 Markdown,含序号、站点、时间、权威性、链接与摘要 | +| `notice` | string | 无结果时的提示信息 | +| `error` / `hint` / `status` | string / string / int | 失败时的错误信息、排查建议与原样状态码 | + +`results` 元素字段: + +| 字段 | 类型 | 说明 | +| :--- | :--- | :--- | +| `title` | string | 标题 | +| `url` | string | 结果链接 | +| `snippet` | string | 短摘要(100~200 字符),适合前端展示 | +| `summary` | string | 长摘要(600~800 字符),适合给模型阅读 | +| `content` | string | 网页正文,`need_content=false` 时为空串 | +| `site_name` | string / null | 站点名称 | +| `date` | string / null | 发布时间,UTC RFC 3339 | +| `authority` | int | 权威性 0~4,`≥3` 为极权威,`-1` 表示未知 | +| `rerank_score` | number | 相关度 0~3,`≥1.5` 为优质结果 | + +## 三、工具内容(Python) + +完整源码见本目录下 `deepseek_search.py`(同时内嵌于 `1.0.0/DeepSeek联网搜索.tool`)。 + +核心实现约 200 行,结构如下: + +```python +def main(api_key, query, count=10, need_content="false", ori_question=""): + # 1. 参数校验:api_key / query 必填,count 夹逼到 1~50 + # 2. POST https://api.deepseek.com/v1/tools/search + # headers: Authorization: Bearer , Content-Type: application/json + # 3. 状态码分级:400/401/402/429/500/504 分别给出排查提示 + # 4. 结果整形:抽取字段 + 拼接 Markdown + return {"success": True, "query": ..., "count": ..., "results": [...], "markdown": "..."} +``` + +编写时遵守的三条 MaxKB 规范: + +1. 入口函数名必须是 `main`,形参名与工具面板中的参数名逐字一致(MaxKB 以 `main(**keywords)` 调用) +2. `main` 必须位于代码末尾(MaxKB 取最后一个定义的对象作为入口) +3. 返回值需可 JSON 序列化;`print` 会被重定向到 devnull,不要依赖它调试 + +## 四、调用示例 + +### 4.1 工作流节点调用 + +工具节点参数配置: + +| 节点参数 | 取值 | +| :--- | :--- | +| `api_key` | 启动参数中已配置,无需重复填写 | +| `query` | 引用「开始」节点的 `question` | +| `count` | 5 | +| `need_content` | false | + +下游 AI 对话节点提示词中加入: + +``` +参考资料: +{{搜索节点.markdown}} +``` + +### 4.2 直接调用(Python) + +```python +from deepseek_search import main + +r = main( + api_key="sk-xxxxxxxx", + query="DeepSeek 模型 site:github.com lang:zh after:2025", + count=5, + need_content="false", +) +print(r["markdown"]) +``` + +### 4.3 典型返回 + +```json +{ + "success": true, + "query": "DeepSeek V4 模型 发布", + "count": 2, + "results": [ + { + "title": "DeepSeek V4 模型发布", + "url": "https://example.com/deepseek-v4", + "snippet": "DeepSeek V4 已上线,支持工具调用与长上下文。", + "summary": "DeepSeek 发布了 V4 模型。此次发布改进了工具调用与长上下文能力,适合 Agent 工作流。", + "content": "", + "site_name": "Example News", + "date": "2026-06-01T00:00:00+00:00", + "authority": 3, + "rerank_score": 2.4 + } + ], + "markdown": "### 1. DeepSeek V4 模型发布\n- 站点:Example News | 时间:2026-06-01T00:00:00+00:00 | 权威性:3/4 | 相关度:2.4/3\n- 链接:https://example.com/deepseek-v4\n\n长摘要..." +} +``` + +## 五、错误处理 + +| 现象 | 原因 | 处理 | +| :--- | :--- | :--- | +| `HTTP 401 Authentication Fails` | Key 错误或失效 | 检查启动参数中的 `api_key`,去掉首尾空格 | +| `HTTP 402` | 账户余额不足 | 充值后重试 | +| `HTTP 429` | 超过账号级 QPS(Search 5 QPS) | 稍后重试,或在编排中增加延时 | +| `HTTP 400` | query 或 count 不合法 | 检查 query 长度与 count 范围 | +| `HTTP 5xx` | 服务端错误或下游超时 | 稍后重试 | +| 请求异常 / 超时 | MaxKB 容器无出网权限 | 在容器内执行 `curl -I https://api.deepseek.com` 验证 | +| `ModuleNotFoundError: requests` | 精简镜像缺包 | `docker exec -it maxkb pip install requests`(代码已内置 urllib 回退,通常无需安装) | + +## 六、计费与限速 + +- 计费:Search 按成功请求计费,**5 元 / 1000 次**;`count` 不影响单价;请求失败、无结果、超时不计费 +- 限速:每个账号独立 **5 QPS**,与模型并发限制互不影响 + +## 七、版本记录 + +| 版本 | 日期 | 说明 | +| :--- | :--- | :--- | +| 1.0.0 | 2026-10-19 | 首发:Search 接口封装,支持搜索语法、可选正文、Markdown 输出与分级错误处理 | diff --git a/tools/tool_deepseek_search/data.yaml b/tools/tool_deepseek_search/data.yaml new file mode 100644 index 0000000..777c3d4 --- /dev/null +++ b/tools/tool_deepseek_search/data.yaml @@ -0,0 +1,5 @@ +name: DeepSeek 联网搜索 +tags: + - 联网搜索 +title: 使用 DeepSeek Search API 执行联网搜索,返回结构化结果与带引用的 Markdown +description: 基于 DeepSeek 官方 Search API(https://api.deepseek.com/v1/tools/search)的联网搜索工具。支持 site:、lang:、after:、before:、-关键词 等搜索语法,可控制返回条数并选择是否抓取网页正文;结果同时提供结构化 JSON 与可直接用于回答的 Markdown(含链接、站点、发布时间与权威性)。 diff --git a/tools/tool_deepseek_search/deepseek_search.py b/tools/tool_deepseek_search/deepseek_search.py new file mode 100644 index 0000000..7b23bd2 --- /dev/null +++ b/tools/tool_deepseek_search/deepseek_search.py @@ -0,0 +1,203 @@ +# -*- coding: utf-8 -*- +# ============================================================================= +# MaxKB 脚本工具:DeepSeek 联网搜索(Search API) +# 接口文档:https://api-docs.deepseek.com/zh-cn/guides/search +# +# MaxKB 工具规范要点(照抄即可生效): +# 1. 代码里必须有一个 main() 作为入口,参数名/顺序须与工具「参数」面板完全一致; +# 2. MaxKB 内部以 main(**keywords) 的关键字方式调用,参数名不一致会直接 TypeError; +# 3. main() 必须写在代码的【最后】,MaxKB 取「最后一个定义的对象」作为入口函数; +# 4. 返回值需可 JSON 序列化,建议统一返回 dict(success / results / markdown / error); +# 5. 不要在此文件里调用 main(),也不要写 `if __name__ == '__main__'`。 +# ============================================================================= +import json + +SEARCH_URL = "https://api.deepseek.com/v1/tools/search" +TIMEOUT = 30 # 秒;MaxKB 函数执行默认超时为 3600s,这里只限制单次 HTTP +MAX_CONTENT_CHARS = 1500 # 单条正文截断长度,避免把上下文塞爆 + +STATUS_HINT = { + 400: "请求参数不合法(检查 query 长度 1~128 字符、count 在 1~50 之间)", + 401: "API Key 缺失或无效,请检查工具参数中的 api_key", + 402: "DeepSeek 账户余额不足,请充值后重试", + 429: "触发 QPS 限速(Search 每账号 5 QPS),请稍后重试", + 500: "DeepSeek 服务端内部错误,建议稍后重试", + 504: "DeepSeek 下游请求超时,建议稍后重试", +} + + +def _to_int(value, default): + """把模型传进来的 "5" / 5.0 / None 统一成 int。""" + try: + return int(float(str(value).strip())) + except Exception: + return default + + +def _to_bool(value): + """把 "true" / 1 / True 统一成 bool。""" + if isinstance(value, bool): + return value + return str(value).strip().lower() in ("1", "true", "yes", "y", "on") + + +def _http_post_json(url, headers, payload, timeout=TIMEOUT): + """POST JSON,返回 (status_code, text)。优先 requests,缺失时回退 urllib。""" + body = json.dumps(payload, ensure_ascii=False).encode("utf-8") + + try: + import requests + except Exception: + requests = None + + if requests is not None: + resp = requests.post(url, headers=headers, data=body, timeout=timeout) + return resp.status_code, resp.text + + import urllib.request + import urllib.error + + req = urllib.request.Request(url, data=body, headers=headers, method="POST") + try: + with urllib.request.urlopen(req, timeout=timeout) as r: + return r.status, r.read().decode("utf-8", "replace") + except urllib.error.HTTPError as e: + return e.code, e.read().decode("utf-8", "replace") + + +def _build_markdown(items): + """把结构化结果拼成给模型看的 Markdown,减少模型二次解析成本。""" + if not items: + return "未检索到相关网页。可尝试更换关键词、放宽 site:/after: 等限定条件。" + + blocks = [] + for i, it in enumerate(items, 1): + lines = ["### %d. %s" % (i, it.get("title") or "(无标题)")] + meta = [] + if it.get("site_name"): + meta.append("站点:%s" % it["site_name"]) + if it.get("date"): + meta.append("时间:%s" % it["date"]) + if it.get("authority") is not None: + meta.append("权威性:%s/4" % it["authority"]) + if it.get("rerank_score") is not None: + meta.append("相关度:%s/3" % it["rerank_score"]) + if meta: + lines.append("- " + " | ".join(meta)) + lines.append("- 链接:%s" % (it.get("url") or "")) + summary = it.get("summary") or it.get("snippet") or "" + if summary: + lines.append("") + lines.append(summary.strip()) + content = (it.get("content") or "").strip() + if content: + if len(content) > MAX_CONTENT_CHARS: + content = content[:MAX_CONTENT_CHARS] + "……(正文已截断)" + lines.append("") + lines.append("> " + content.replace("\n", "\n> ")) + blocks.append("\n".join(lines)) + return "\n\n".join(blocks) + + +def main(api_key, query, count=10, need_content="false", ori_question=""): + """DeepSeek 网页搜索:返回结构化结果 + 可直接喂给大模型的 Markdown。""" + api_key = (api_key or "").strip() + query = (query or "").strip() + ori_question = (ori_question or "").strip() + + # ---------- 参数校验 ---------- + if not api_key: + return { + "success": False, + "error": "缺少 api_key:请在工具参数中填入 DeepSeek API Key", + "results": [], + "count": 0, + "markdown": "", + } + if not query: + return { + "success": False, + "error": "query 不能为空:请把用户问题提炼成 1~128 字符的搜索词", + "results": [], + "count": 0, + "markdown": "", + } + + count = _to_int(count, 10) + count = max(1, min(50, count)) + + payload = {"query": query[:128], "count": count} + if _to_bool(need_content): + payload["need_content"] = True + if ori_question: + payload["ori_question"] = ori_question[:128] + + headers = { + "Authorization": "Bearer " + api_key, + "Content-Type": "application/json", + "Accept": "application/json", + } + + # ---------- 调用接口 ---------- + try: + status, text = _http_post_json(SEARCH_URL, headers, payload) + except Exception as e: + return { + "success": False, + "error": "请求异常:%s" % e, + "hint": "检查 MaxKB 容器到 api.deepseek.com 的网络连通性(出网/代理/防火墙)", + "results": [], + "count": 0, + "markdown": "", + } + + try: + data = json.loads(text) if text else {} + except Exception: + return { + "success": False, + "status": status, + "error": "响应不是合法 JSON:%s" % (text or "")[:300], + "results": [], + "count": 0, + "markdown": "", + } + + # ---------- 错误处理 ---------- + if status != 200: + err = data.get("error") or {} + message = err.get("message") or data.get("message") or ("HTTP %s" % status) + return { + "success": False, + "status": status, + "error": message, + "hint": STATUS_HINT.get(status, "未预期状态码,请对照官方文档排查"), + "results": [], + "count": 0, + "markdown": "", + } + + # ---------- 结果整形 ---------- + raw_items = data.get("results") or [] + items = [] + for r in raw_items: + items.append({ + "title": r.get("title") or "", + "url": r.get("url") or "", + "snippet": r.get("snippet") or "", + "summary": r.get("summary") or "", + "content": r.get("content") or "", + "site_name": r.get("site_name"), + "date": r.get("date"), + "authority": r.get("authority"), + "rerank_score": r.get("rerank_score"), + }) + + return { + "success": True, + "query": payload["query"], + "count": len(items), + "results": items, + "markdown": _build_markdown(items), + "notice": "" if items else "搜索正常结束但无匹配结果(本次不计费)", + } diff --git a/tools/tool_deepseek_search/logo.png b/tools/tool_deepseek_search/logo.png new file mode 100644 index 0000000000000000000000000000000000000000..d903a38dbc7920dd65bab1f9cae290a4a53187fc GIT binary patch literal 260 zcmV+f0sH=mP)jY|nHp+OkOwjTwb(X3`Qt65PjA}Yb6RRLJUt2`FT zE1X5z3S@~U^OA{K?(b_@x;gB!5z$iirR1`t%KPvrmSy8@7KAnL6;5kgy570vySJM4 z=3U-P_L~z#-Sd`xDQlhOubXh8n~y~ndrwr$!f(|&Sw;q-kR?SUQdwSNBa)>CNqkv$ zzQvRQVmFQqlLjtju#j>tLuRKF8935?lHrr)a*H_mu=ekQ53o*47SVBF*LUmy0000< KMNUMnLSTY@(Qk