阿里云 Milvus 知识库可以把 API 文档、架构说明、Runbook 与故障复盘建成研发排障助手:按接口名或错误码检索,给出有序的排查步骤与原文依据。
方案说明
整体链路与通过阿里云Milvus知识库搭建智能客服问答应用完全一致:控制台定义标签 → 按标签批量导入资料 → 发布版本 → SDK 检索(可按标签过滤)→ 大模型生成带来源标注的回答 → Flask 提供问答页面。工程代码(kb_client.py、upload.py、app.py、templates/index.html、start.sh)请直接复用该文,本文只说明研发排障场景需要改动的部分:模块标签、面向精确词的检索参数、HTML 资料的处理,以及高风险命令与空结果的约束。
建议第一版只选择一个系统的 5~20 篇资料,验证后再扩大范围。整体耗时约 20~30 分钟,其中文档解析耗时取决于资料数量与大小。
前提条件
已创建 Milvus 知识库并记录知识库 ID(形如
kd-803ae9b10cc31)。已使用主账号创建 RAM 用户、勾选使用永久 AccessKey 访问,并授予系统策略
AliyunMilvusFullAccess。已准备支持 OpenAI
chat/completions协议的大模型地址与 API Key。本地已安装 Python 3.8 或以上版本,并已按上文教程建好工程目录。
资料中不包含密钥、Token 与内网账号。
步骤一:定义模块标签
在知识库详情页的基本信息下方单击标签 > 管理,添加以下三个标签,字段类型均选择 string(类型下拉在标签名输入框右侧,可选 string、int64、list、float32、bool)。
标签名 | 说明 | 示例值 |
module | 所属系统或服务 | order-service、user-service |
docType | 资料类型 | API、Runbook、ErrorCode、Postmortem |
version | 接口或文档版本 | v2 |
标签名 version 与检索请求中表示知识库已发布版本的 version 参数同名,但两者互不影响(前者用于 tagFilter.field,后者在请求顶层)。若担心混淆,可把标签命名为 apiVersion。
标签管理对话框是整体保存的。对话框打开后标签列表为异步加载,请等到已有标签全部显示出来,再添加新标签并单击完成;否则可能以"空列表 + 新标签"整体覆盖,导致已有标签定义被清除。
module 标签是本场景最关键的设计。不同系统常有完全同名的接口路径,不加模块过滤时会相互干扰。实测让 user-service 与 order-service 拥有同一个路径 GET /api/v2/orders/{orderId},检索该路径时:
检索条件 | 结果 |
不加过滤 | user-service 的文档排第一(相关度 0.246),命中了错误的模块 |
| order-service 的文档排第一,user-service 的文档被完全排除 |
步骤二:准备资料与导入清单
把资料放进
documents/目录,支持 Markdown、HTML、PDF、DOCX、TXT。文档中保留完整的错误码、接口路径和版本号——实测错误码与完整路径都能被精确检索到。创建
documents.jsonl,为每篇资料标注模块、资料类型与版本。{"path": "documents/order-api.md", "metadata": {"module": "order-service", "docType": "API", "version": "v2"}} {"path": "documents/order-timeout-runbook.md", "metadata": {"module": "order-service", "docType": "Runbook", "version": "v2"}} {"path": "documents/order-error-codes.html", "metadata": {"module": "order-service", "docType": "ErrorCode", "version": "v2"}} {"path": "documents/order-postmortem-2026-06.md", "metadata": {"module": "order-service", "docType": "Postmortem", "version": "v2"}}关于 HTML 资料:
HTML 可以直接上传解析,
<table>中的内容确实会进入索引。实测查询只存在于 HTML 错误码表中的ORD-42901,能精确命中该文件。HTML 的切片粒度比 Markdown 更粗(本次一个含两个表格的 HTML 文件解析为 1 个切片)。这与 HTML 的解析方式有关,选择资料格式前需要了解两点:
代码块不保留原始版式:
<pre>、<code>会被识别为块,但文本是递归提取后以空格拼接的,缩进、换行和代码围栏都会丢失。因此含大量代码或命令的资料建议先转成 Markdown 再导入,否则检索到的代码片段可能无法直接使用。表格整体入库:
<table>会以原始 HTML 字符串的形式作为一个独立片段追加,不会按行拆分,所以大表容易形成粗粒度切片。
结论是:普通说明文字和小表格可以直接用 HTML;需要精确保真、按代码段检索或拆分大表时,优先使用 Markdown 或结构化资料,并在导入后到数据管理页抽查切片。
含大量代码块的 HTML 建议先转换为 Markdown,代码与缩进的保真度更可控。
排障步骤与代码示例不能被切断,可在知识库详情页的处理策略中单击创建策略调整切片粒度。最大分段长度的单位是字符(默认 512),建议设置为 580~770 字符(约相当于 384~512 tokens),让"一个完整的排查步骤"或"一个代码示例"落在同一切片内。
建议把 API 文档、Runbook、错误码表与故障复盘成套导入,并用
docType区分。实测提问某个错误码时,四类资料会同时命中,模型能给出"含义 → 排查步骤 → 历史案例"的完整回答。
步骤三:配置检索参数与提示词
研发排障的查询多为错误码、接口路径与命令,属于精确词匹配,因此参数与其他场景差别较大。
{
"retrieval": {
"page_size": 6,
"candidate_count": 64,
"min_score": 0.05,
"semantic_weight": 0.15,
"enable_query_expansion": false,
"rerank_model_name": "",
"tag_filter": {
"relation": "and",
"conditions": [
{"field": "module", "op": "=", "value": "order-service"}
]
}
},
"scenario": {
"title": "研发文档与排障助手",
"system_prompt": "你是研发文档助手。只依据检索资料回答,优先保留接口名、错误码、命令和代码;排障步骤按顺序列出并标注[来源N];涉及写操作或高风险命令时必须明确标注风险等级并要求人工确认;检索资料为空时,只回复资料不足并提示不要执行任何变更操作。",
"image_enabled": false,
"sample_questions": [
"订单查询接口需要哪些必填参数?",
"连接超时应该按什么顺序排查?",
"这个错误码在哪些文档中出现过?"
]
}
}参数说明:
semantic_weight: 0.15:该参数是最终分数的加权系数,计算方式为score ≈ (1-semantic_weight) × keywordScore + semantic_weight × semanticScore(另可能叠加 rank feature),min_score在这个最终分数上做后置过滤。本场景取 0.15 是为了让错误码、接口路径这类精确词的关键词分数主导排序。
需要注意两点:一是调低该值并不必然压低总分,只有当这批结果的
semanticScore高于keywordScore时总分才会下降,所以本文把min_score一并调到 0.05,两个参数必须结合scoreDetails同步校准;二是把该值设为 0 时,当前实现不会再应用min_score阈值,因此不要用 0 来表示"纯关键词检索"。enable_query_expansion: false:关闭查询扩展,避免错误码与接口名被改写。实测开启后,查询ORD-50021的keywordScore由 0.283 降至 0.226;查询ORD-42901的召回条数由 2 条降至 1 条。精确词检索场景建议保持关闭。tag_filter固定module:避免不同系统的同名接口互相干扰,效果见步骤一。若一个入口需要覆盖多个模块,可用{"field": "module", "op": "in", "value": ["order-service", "user-service"]}。rerank_model_name留空表示不启用重排。错误码这类精确匹配依赖关键词分数,重排收益有限。op只能使用=、in、not in:实测≠、>、≥、<、≤、empty、not empty、start with、end with会被静默忽略并返回全部数据(不报错),contains、not contains只是in/not in的别名,不是字符串包含,按字符串片段传值只会返回 0 条。写成eq、==、like等别名会返回400 Unsupported tag filter operator,导致每次提问都失败。配置好过滤条件后,请与不加过滤的结果对比总条数,确认过滤确实生效。
semantic_weight 与 min_score 必须配套调整
semantic_weight 只改变最终加权分数,不改变检索结果中 scoreDetails 的两个子分数(keywordScore 与 semanticScore)。加权分数约等于:
score ≈ semantic_weight × semanticScore + (1 - semantic_weight) × keywordScore研发资料的 keywordScore 通常明显低于 semanticScore(实测分别约 0.16~0.28 与 0.64~0.78),因此在这种分布下调低 semantic_weight 会把总分拉向较低的一侧。需要说明的是这并不是普遍规律:只有当本批结果的 semanticScore 高于 keywordScore 时,调低权重才会压低总分;反之会抬高。若 min_score 不同步下调,会连语义命中的结果一起筛掉:
查询 | semantic_weight=0.15 | semantic_weight=0.7 |
| 8 条,最高分 0.269 | 8 条,最高分 0.602 |
| 4 条 | 8 条 |
自然语言「订单接口最近为什么会大面积变慢」 | 3 条 | 8 条 |
使用 semantic_weight=0.15 时,min_score 建议设为 0.05 左右(上方示例已按此配置)。若沿用 0.15 及以上,命令类与自然语言类查询的召回会减少一半以上。调参时建议固定其中一项,通过 scoreDetails 观察两个子分数再决定。
高风险命令与空结果的约束
排障助手会直接输出可执行命令,因此有两处必须约束。
一是高风险命令。在资料里就用表格标注风险等级与确认要求,模型会如实传递。实测 Runbook 中标注为"高风险、必须双人确认"的重启命令,被提问时模型给出命令的同时明确输出了"风险等级为高""必须双人确认""不得跳过排障直接执行重启"。
二是检索结果为空。复用的 app.py 中,llm_answer() 在检索结果为空时仍会调用大模型,此时上下文为空字符串,模型会完全依据自身知识作答。实测提问知识库未覆盖的「Redis 集群脑裂了怎么恢复」,模型输出了完整的运维方案与参数,且未作任何提示。排障场景下使用者可能直接照抄命令操作生产环境,必须在代码层拦截:
def llm_answer(question: str, results: list[dict[str, Any]]) -> str:
if not LLM.get("enabled", True):
return "大模型未启用;请查看下方检索结果。"
if not results:
return "知识库中没有检索到相关文档,无法给出排查步骤。请勿据此执行任何变更操作,建议联系模块负责人。"
...仅在提示词中约束并不可靠——即使写明"资料未覆盖时明确提示",模型仍会作答。
步骤四:上传、发布与验证
上传资料(
MetaFields对整批生效,脚本会先按标签分组再分批提交)。python upload.py --manifest documents.jsonl到控制台数据管理页确认资料状态为处理完成。上传接口返回成功仅表示已提交异步解析。
在版本管理页单击发布版本,完成向导后确认新版本状态为已发布。
重要同时最多只能存在 3 个已发布版本。达到上限后发布版本按钮会置灰,而页面仍会显示"当前有 N 条待发布变更",需要先在版本记录中删除不再需要的旧版本。研发文档更新频繁(每次发版都可能更新 API 文档与 Runbook),建议只保留"当前版本 + 最近一个历史版本"。
启动服务并验证。
python app.pycurl -sS http://127.0.0.1:7860/api/ask \ -H 'Content-Type: application/json' \ -d '{"question":"连接超时应该按什么顺序排查?"}'
按以下五条验收:
页面能正常打开,提交问题后同时返回答案与检索来源。
回答中的
[来源N]能在下方来源区找到对应的资料切片。用完整错误码提问,能列出该错误码出现过的所有文档。实测查
ORD-50021正确列出了错误码表、API 文档、Runbook 与故障复盘四份资料及各自位置。提问高风险操作,回答中带有风险等级与人工确认要求。
提问知识库未覆盖的内容时,返回"资料不足"并提示不要执行变更操作,不出现任何编造的命令。这一条务必实测。
研发场景注意事项
按模块建立独立入口:
tag_filter固定module后,该入口只回答该模块的问题。示例问题也应保持同一模块。资料中保留完整的错误码、接口路径、命令与版本号,这些精确词是本场景检索的主要入口。
在资料里就标注风险等级与确认要求,不要指望模型自行判断哪些命令危险。
密钥、Token、内网账号、生产数据库连接串不要上传。切片内容会作为上下文发送给大模型。
保留已下线接口的说明。实测在 API 文档中写明"v1 接口已下线、参数名不兼容"后,模型回答新接口参数时不会混入旧参数。
检索结果中的
tags字段不会回显写入的模块与版本,它与AddDocuments的 MetaFields 是两个不同的字段,也没有参数可以开启返回,为空不代表上传失败。需要在回答中标注模块与版本时,按检索结果的documentId关联documents.jsonl后再拼进上下文。线上部署不要继续使用 Flask 开发服务器,应改用生产 WSGI,并补充密钥管理、鉴权、审计与限流。
常见问题
现象 | 原因与处理 |
提问返回 500,日志显示 |
|
加了标签过滤但结果条数与不加过滤时完全一样 | 使用了不生效的运算符(如 |
检索到了其他系统的同名接口 | 未配置 |
检索结果比预期少很多 |
|
错误码检索不准 | 确认 |
按标签过滤后总是 0 条 | 标签名拼写与写入时不一致(接口不会报错,只返回 0 条),在知识库详情页 → 标签 → 管理中核对。 |
问了知识库里没有的问题,却得到了看似可执行的命令 |
|
HTML 资料检索不到细节 | HTML 切片粒度较粗,可调小最大分段长度;含大量代码的 HTML 建议先转 Markdown。 |
检索时报"失败:None" | 复用代码中 |
检索返回 | 尚未发布版本,或配置中写死的版本号已被删除。建议使用 |