乐于分享
好东西不私藏

运维文档喂给AI,新人上手快3倍?知识库这么搭

运维文档喂给AI,新人上手快3倍?知识库这么搭

运维文档喂给AI,新人上手快3倍?知识库这么搭

标签: AI应用 | RAG | 知识库 | 运维文档 | 向量数据库 | 团队协作

我们组去年来了个新人,第一个月我至少被问了 200 次「这个服务在哪台机器上」「这个流程找谁审批」「上次那个故障是怎么解决的」。

不是他懒,是我们的文档散在 Confluence、Git 仓库、几百个聊天记录和三个人的脑子里。搜索框搜出来 40 条结果,没一条对得上。

后来我花了两天搭了个 RAG 知识库,现在新人有问题先问 AI,问不出来再问我。我的打扰次数掉了大概三分之二。

今天讲讲怎么搭,以及那些踩过才知道的坑。

RAG 到底是个啥

一句话:先从你的文档里搜出相关段落,再连同问题一起喂给大模型,让它基于这些内容回答。

它和「微调模型」的区别很关键:

  • 微调:把知识焊进模型里,改一次文档要重训,贵且慢
  • RAG:文档放外面,改了立刻生效,便宜且实时
  • 运维文档天天在变,RAG 才是对的选择,别被人忽悠去微调。

  • 第一步:把文档收拢

  • 这一步最脏最累,但决定了后面 70% 的效果。

  • 我的做法是全部转成 Markdown 塞进一个 Git 仓库:

  • mkdir -p /data/kb/{架构,流程,故障复盘,巡检,应急预案}
    

    Confluence 导出的 HTML 批量转 md

    find ./confluence_export -name "*.html" -exec sh -c \ 'pandoc "$1" -f html -t markdown -o "/data/kb/架构/$(basename "$1" .html).md"' _ {} \;
  • 重点是给每个文件加头部元信息
  • ,这比你想的重要得多:
  • ---
    title: 订单服务部署架构
    system: order-service
    owner: 张伟
    updated: 2026-07-15
    env: 生产
    ---
  • 为什么重要?因为检索出来的片段如果不带这些信息,AI 就分不清「这段说的是测试环境还是生产环境」,然后一本正经地告诉新人一个错误的 IP。

  • 再强调一句:过期文档一定要删掉或标注废弃。
  • 垃圾进,垃圾出,RAG 没有魔法。
  • 第二步:切片,这是最大的坑

  • 新手最容易在这翻车——按固定字数切。

  • 比如按 500 字一刀切,结果一个部署步骤被从中间劈开,检索出来是「……第三步,然后」,AI 拿着半句话开始编。

  • 正确做法是按标题层级切,保证语义完整:

  • import re
    def split_by_heading(md_text, source, max_len=1200):
        """按 ## 二级标题切分,超长的再按段落二次切"""
        parts = re.split(r'\n(?=##\s)', md_text)
        chunks = []
        for p in parts:
            p = p.strip()
            if not p:
                continue
            head = p.split('\n')[0][:60]
            if len(p) <= max_len:
                chunks.append({'text': p, 'source': source, 'section': head})
            else:
                # 超长按空行二次切,但每块都补回标题作为上下文
                for sub in re.split(r'\n\n+', p):
                    if sub.strip():
                        chunks.append({
                            'text': f"{head}\n{sub.strip()}",
                            'source': source,
                            'section': head
                        })
        return chunks
  • 注意最后那个 f"{head}\n{sub}"——二次切分时把标题补回每一块。这个小动作让我的检索准确率提升了非常明显的一截。

  • 第三步:向量化 + 存起来

  • 轻量方案直接上 Chroma,不用单独起数据库:

  • pip install chromadb sentence-transformers
  • import chromadb
    from sentence_transformers import SentenceTransformer
    

    中文向量模型,这个在中文技术文档上表现最稳

    model = SentenceTransformer('BAAI/bge-small-zh-v1.5') client = chromadb.PersistentClient(path="/data/kb_vec") col = client.get_or_create_collection("ops", metadata={"hnsw:space": "cosine"}) def index(chunks): embeds = model.encode( [c['text'] for c in chunks], normalize_embeddings=True # 必须归一化,否则余弦相似度算不准 ).tolist() col.upsert( ids=[f"{c['source']}#{i}" for i, c in enumerate(chunks)], documents=[c['text'] for c in chunks], embeddings=embeds, metadatas=[{'source': c['source'], 'section': c['section']} for c in chunks] )
  • 用 upsert 不用 add,这样文档更新重跑一遍就是覆盖,不会重复堆积。

  • 顺手挂个定时任务,让知识库自己保持新鲜:
  • # 每天凌晨2点重建索引
    0 2 * * * cd /data/kb && git pull && /usr/bin/python3 /opt/kb/reindex.py >> /var/log/kb.log 2>&1
  • 第四步:查询 + 回答

  • import requests
    

    SYS = """你是运维知识库助手。只能依据下面提供的【参考资料】回答问题。

    规则:

  • 资料里没有的,直接说"知识库里没有找到,建议问 @{owner} 或查 xxx",绝对不许编
  • 回答末尾必须列出你引用的文档名
  • 涉及生产环境的操作,末尾加一句"执行前请二次确认"
  • 【参考资料】 {context} """

  • def ask(question, top_k=5):    q_vec = model.encode([question], normalize_embeddings=True).tolist()    res = col.query(query_embeddings=q_vec, n_results=top_k)

  • ctx = "\n\n---\n\n".join(        f"[来源: {m['source']} / {m['section']}]\n{d}"        for d, m in zip(res['documents'][0], res['metadatas'][0])    )

  • r = requests.post('http://localhost:11434/api/generate', json={        'model': 'qwen2.5:7b',        'prompt': SYS.format(context=ctx, owner='运维组') + f"\n\n问题:{question}",        'options': {'temperature': 0.1},        'stream': False    }, timeout=120)    return r.json()['response']
  • temperature
  • 压到
  • 0.1
  • 。知识库问答要的是忠实复述,一点创意都不需要。
  • 三个必须知道的坑

  • 坑一:不设相似度阈值,什么都能"答"
  • 哪怕知识库里根本没有相关内容,向量检索也一定会返回 5 条「最像的」。然后 AI 拿着不相关的资料硬答。

  • 加个阈值过滤:

  • hits = [
        (d, m) for d, m, dist in zip(
            res['documents'][0], res['metadatas'][0], res['distances'][0]
        ) if dist < 0.45          # cosine距离,越小越像
    ]
    if not hits:
        return "知识库里没有相关内容,建议直接问运维组。"
  • 坑二:只用向量检索,搜不到专有名词
  • 用户搜 order-svc-prod-03 这种机器名,向量模型根本理解不了,反而是关键词匹配更准。

  • 混合检索
  • 才是正解——向量结果和关键词结果各取一半合并。简单粗暴版:
  • kw_hits = col.query(query_texts=[question], n_results=3,
                        where_document={"$contains": question.split()[0]})
  • 坑三:没有反馈闭环
  • 答错了没人纠正,就会一直错。我在问答界面加了个「这个回答有用吗」,点了「没用」的问题自动记到一个文件里,我每周看一眼,补文档。

  • 这一步是知识库能不能活下去的关键。
  • 没有人维护的知识库,三个月就死了。
  • 真实收益

  • 搭完三个月,说点实在的:

  • 新人独立处理常规工单的时间:从 6 周缩短到 2 周左右
  • 「这个怎么搞」类的重复提问:肉眼可见地少了大半
  • 意外收获:为了喂 AI,我们终于把文档补全了
  • 最后这条其实才是最大的价值。以前写文档没人看,现在写了 AI 会用、新人会问到,大家反而有动力写了。


  • 你们团队的运维文档是什么状态?是「有但没人看」,还是「压根就没有」?

  • 评论区聊聊你踩过的文档坑。想要我这套完整脚本的,留言「知识库」,攒够 50 个我整理成开箱即用的仓库发出来。

相关学习资料