9.1 KiB
素材专家检索 —— 内网接口对接说明
本文档说明 AI 智能写作中「素材收集专家」的检索机制、内网知识库接口的对接逻辑、 涉及的代码文件,以及常见错误的排查方式。
1. 检索来源切换
素材专家支持多种检索来源,通过后端配置文件统一切换,无需改动任何代码。
配置位置:offline-backend-20260512/backend/config.yaml
ai_writing:
# 检索来源开关:internet=互联网检索(默认)/ intranet=内网知识库 / tavily / demo
search_provider: internet
# 以下两项仅在 search_provider=intranet 时生效
intranet_search_url: http://10.106.57.180:7863/knowledge_base/search_docs_es
intranet_knowledge_base: samples
search_provider 取值 |
含义 |
|---|---|
internet |
互联网检索(DuckDuckGo),失败时回退到 demo 演示素材。当前默认值 |
intranet |
内网知识库检索(调用 search_docs_es 接口) |
tavily |
Tavily 互联网检索 |
demo |
内置演示素材,仅用于本地调试 |
内网接口联通后,把
search_provider改为intranet即可切换。 配置文件支持热加载(get_app_config()按 mtime 自动重载),改完一般无需重启后端。
1.5 检索关键词数量
素材专家的检索词数量由用户在写作表单中指定(「检索关键词数量」输入框,
范围 1~10,默认 6)。数量越多素材越全面,但检索与排序更耗时。
- 该参数随
POST /api/ai-writing/start的keyword_count字段传入,写入AIWritingState.keyword_count。 researcher_node用它决定让 LLM 生成多少个检索词、并发检索多少个方向 (EXTRACT_KEYWORDS_USERprompt 按该数量格式化,search_keywords = keywords[:keyword_count])。- 非法值(缺省 / 越界 / 非数字)由前后端双重兜底:后端
_resolve_keyword_count()规整到[1, 10],越界回退默认6。 - 补充搜索(
re_search)时用户额外添加的关键词不会被该上限截断。
2. 内网接口规格
-
地址:
http://10.106.57.180:7863/knowledge_base/search_docs_es -
方法:
POST -
请求体:
{ "query": "检索关键词", "knowledge_base_name": "samples", "ts": "yes" } -
响应体:一个数组,每个元素结构如下:
[ { "page_content": "正文片段", "metadata": { "m_title": "标题", "content": "内容", "recUuid": "原文id", "m_publish":"发布时间", "source1": "来源" } } ]我们需要的字段都在
metadata里。
字段映射
后端把内网响应映射为统一的 SearchResult 结构:
| 内网响应字段 | SearchResult 字段 | 说明 |
|---|---|---|
metadata.m_title |
title |
标题,缺省为「(无标题)」 |
metadata.content |
content |
内容,截断到 500 字 |
metadata.source1 |
source |
来源,缺省为「内网知识库」 |
metadata.m_publish |
published_at |
发布时间 |
metadata.recUuid |
recUuid |
原文 id,用于前端拼接「查看原文」链接 |
| — | url |
固定为空,原文地址由前端依据 recUuid 拼接 |
| — | relevance_score |
接口不返回分值,按返回顺序赋递减分以保留 ES 排序 |
| — | source_type |
固定为 "intranet" |
3. 对接逻辑与涉及文件
后端
| 文件 | 作用 |
|---|---|
packages/harness/deerflow/agents/ai_writing/search/intranet_stub.py |
内网检索适配器 IntranetSearchProvider。发起 POST 请求、解析响应、映射为 SearchResult |
packages/harness/deerflow/agents/ai_writing/search/__init__.py |
get_search_provider() 工厂函数,依据 search_provider 配置返回对应 provider |
packages/harness/deerflow/agents/ai_writing/search/base.py |
SearchResult 数据结构 + BaseSearchProvider 抽象基类 |
packages/harness/deerflow/agents/ai_writing/nodes/researcher.py |
素材收集专家节点,调用 provider 检索并把结果合并、排序为素材包 |
app/gateway/routers/ai_writing.py |
_ai_writing_config() 读取 config.yaml 的 ai_writing 段,经 _make_config() 注入运行时 |
config.yaml |
ai_writing 段,检索来源开关与内网接口地址 |
调用链:
ai_writing.py(_make_config 注入 ai_writing_config)
→ researcher_node
→ get_search_provider(ai_writing_config) # 依据 search_provider 选择
→ IntranetSearchProvider.search() # POST search_docs_es
→ SearchResult[] # 字段映射
前端
| 文件 | 作用 |
|---|---|
src/core/config/index.ts |
INTRANET_SOURCE_URL_BASE 原文详情页地址前缀 + buildIntranetSourceUrl() 构造函数 |
src/open-canvas/components/ai-writing/MaterialItem.tsx |
素材卡片,recUuid 非空时显示「查看原文」按钮 |
src/open-canvas/components/ai-writing/CompletedInterventionCard.tsx |
历史回显的素材卡,「原文」链接同样基于 recUuid |
src/open-canvas/api/ai-writing.ts |
normalizeMaterial() 把后端 snake_case 字段转为前端 camelCase(含 recUuid) |
「查看原文」链接拼接规则:
INTRANET_SOURCE_URL_BASE + recUuid
= https://chlib.b.uat.4.cn/knowlibrary/#/knowledge/mid_detail?recUuid={recUuid}
- 仅当素材的
recUuid非空时才显示「查看原文」按钮。 - 点击直接
window.open(...)新标签页打开,不做站内跳转。
4. 错误处理与排查
4.1 内网检索返回空 / 调不通
现象:切到 intranet 后,素材专家时间线出现琥珀色告警「ES 波动导致检索异常」,
下方显示真实报错原因;若完全无结果,再提示「未检索到任何素材」。
IntranetSearchProvider.search() 对所有异常(连接失败、超时、非 2xx、JSON 解析失败)
都做了兜底:捕获后记录 warning 日志、把原因收集到 provider.errors,并返回空列表,
不会让整个写作流程崩溃。researcher_node 在检索结束后取出 provider.errors,
以非致命的 notice 事件推送到前端时间线(标题「ES 波动导致检索异常」+ 真实原因),
接口报错不影响写作流程继续。
容错相关代码:
- 检索异常收集:
intranet_stub.py/duckduckgo_search.py的errors列表- 告警事件发出:
researcher.py的_notice()与progress_events- 模型调用异常:
researcher.py(检索词/排序)、writer_outline.py/writer_draft.py/editor.py均会发出「模型错误」事件(致命错误用error,可兜底的用notice)- 前端展示:
TimelineStep.tsx渲染notice(琥珀)/error(红色)的标题 + 真实原因
排查步骤:
- 确认网络可达:在后端所在机器执行
curl -X POST http://10.106.57.180:7863/knowledge_base/search_docs_es -H "Content-Type: application/json" -d "{\"query\":\"测试\",\"knowledge_base_name\":\"samples\",\"ts\":\"yes\"}" - 查看后端日志:搜索关键字
intranet search failed,会打印具体异常(连接超时 / DNS / 401 等)。 - 确认配置:
config.yaml的intranet_search_url、intranet_knowledge_base是否正确。
空结果时前端已有兜底:素材确认卡会弹出「未检索到任何素材,是否在无素材的情况下继续写作」, 用户可以选择「重新搜索」或「无数据继续写作」。
4.2 字段缺失 / 响应结构变化
search() 对每个字段都用 or 兜底(如 m_title 缺失时回退为「(无标题)」),
单条数据字段缺失不会导致整批失败。
若内网接口改为「数组外包一层 data/results」的结构,适配器已兼容;
若改成其它结构,需修改 intranet_stub.py 中解析 items 的逻辑。
4.3 「查看原文」按钮不显示
原因:该素材的 recUuid 为空。这是预期行为 —— 只有内网知识库素材才带 recUuid,
互联网检索(internet/tavily/demo)的素材没有 recUuid,不显示「查看原文」。
如需让互联网素材也能跳转,需另行调整 MaterialItem.tsx 的显示条件。
4.4 原文链接打不开 / 地址不对
「查看原文」地址前缀写在 src/core/config/index.ts 的 INTRANET_SOURCE_URL_BASE。
若内网原文详情页地址有变,只改这一个常量即可。
5. 联调上线步骤
- 确认后端机器能访问
10.106.57.180:7863。 - 把
config.yaml的ai_writing.search_provider改为intranet。 - 后端配置热加载生效(或重启后端)。
- 进入 AI 智能写作页面,发起一次写作,观察素材收集专家是否返回内网素材。
- 校验素材卡片的标题/来源/时间显示正确,「查看原文」能跳到内网详情页。
- 如检索为空,按 4.1 排查。