RAG初探:让 AIOps Agent 学会查询历史故障
前言
LLM 本身是个通才,它的知识截止到训练日期,也不知道你们系统的任何细节。系统前几天出了事故,并且有详细的事故报告,包含了根因分析以及解决方案,但是LLM根本不知道这些,他还在洋洋洒洒用他知道的知识来回答问题,这 时候恨不得拍拍LLM的脑袋:小伙子,天才有99%是汗水,你应该持续学习
那如何让LLM持续学习,熟悉系统的各方面以及历史事故报告,就是本节要分享的内容
RAG 是什么
RAG,全称 Retrieval-Augmented Generation,检索增强生成。先查询最新的知识,如果有合适,就基于这些知识回答;如果没有,再用历史的知识回答
RAG 的思路是:用户提问的时候,先去知识库里捞几条相关文档,把这些文档和问题一起拼进 prompt,再让 LLM 基于这些"事实"来回答。相当于给LLM配置了一个内部资料包
先看代码结构
blog/code/
├── main.py # 主流程:分词检索 + prompt 拼装 + LLM 调用
└── knowledge_base.py # 知识库:存放运维文档
- knowledge_base.py
这个文件很简单,就是一个 Python 列表,每个元素是一条运维文档,包含 title 和 content 两个字段:
docs = [
{
"title": "Nginx 504 排查手册",
"content": "Nginx 504 通常表示网关等待上游服务响应超时,需要检查 upstream 服务耗时、业务服务日志、数据库慢查询和连接池。"
},
{
"title": "订单服务历史故障",
"content": "订单服务曾经因为数据库连接池耗尽,导致 /api/orders 接口大量超时,最终在 Nginx 中表现为 504。"
},
# ...
]
在真实生产里,这里可以换成从 Confluence、内部 Wiki、Elasticsearch 里拉取的文档,甚至是历史故障复盘记录。这个 Demo 用硬编码列表,是为了让整个流程零依赖、能直接跑。
-
main.py
_tokenize函数负责把文本拆成 token:
def _tokenize(text: str): text = text.lower() tokens = re.findall(r"[a-z0-9]+", text) # 英文/数字按词切分 chinese_parts = re.findall(r"[\u4e00-\u9fff]+", text) for part in chinese_parts: if len(part) == 1: tokens.append(part) else: tokens.extend(part[i:i + 2] for i in range(len(part) - 1)) # 中文按 2 字片段切 return set(tokens)retrieve函数用关键词命中次数给文档打分,取 top_k,用集合求交集来算相关度,简单粗暴但在小型知识库里够用
def retrieve(query: str, top_k: int = 2): query_tokens = _tokenize(query) results = [] for doc in docs: text = doc["title"] + " " + doc["content"] doc_tokens = _tokenize(text) score = len(query_tokens & doc_tokens) # 交集大小 = 相关度 if score > 0: results.append({...}) results.sort(key=lambda x: x["score"], reverse=True) return results[:top_k]
整条 RAG 链路在 main 里串起来:
用户问题
↓
retrieve() ← 关键词检索,拿 top_k 文档
↓
build_prompt() ← 把文档 + 问题拼成 prompt
↓
call_llm() ← 发给 LLM,拿回答
↓
打印结果
以 "订单服务出现大量 504,应该怎么排查?" 为例跑一遍,控制台输出大致如下:
=== 检索到的文档 ===
- Nginx 504 排查手册,score=4
- 订单服务历史故障,score=3
=== 构造出来的 Prompt ===
你是一个 Kubernetes 运维分析 Agent。
...
文档标题:Nginx 504 排查手册
文档内容:Nginx 504 通常表示...
文档标题:订单服务历史故障
文档内容:订单服务曾经因为数据库连接池耗尽...
...
=== LLM 最终回答 ===
根据知识库,建议优先排查以下几点:
1. 检查订单服务数据库连接池是否耗尽(历史上曾出现此问题)
2. 查看 Nginx upstream 日志确认超时节点
3. 确认 Pod 实例数和 CPU/内存状态
...
LLM 的回答里会引用到"历史上连接池耗尽"这条,这就是 RAG 发挥作用的地方——纯靠通用知识是不会提这条的
如果匹配的文档有5000字,那全部都要塞给llm吗?
假设有 100 个故障案例文档,每个文档 5000 字,那就是50w字的,全部发给llm,不但提高了回答成本,回答速度也会大大降低,造成了大量浪费
如果内容太长,放进大模型上下文会浪费 token。并且里面主题太杂,内容检索可能不知道这篇文档到底主要讲什么
所以文档选取的时候,一般选取最相关的top5,并且文档需要拆分成更小的chunk,类似这种:
标题:订单服务大量 504
现象:
- Nginx 出现大量 504
- upstream_response_time 超过 60s
- Pod CPU/内存正常
- Redis、MySQL、Kafka 连接正常
排查过程:
1. 查询 Nginx 日志,确认是 upstream timeout
2. 查询业务日志,发现调用三方接口耗时异常
3. 查询 Prometheus,Pod 资源无明显瓶颈
4. 查询链路追踪,确认耗时集中在 external-api span
根因:
三方接口响应慢,导致业务线程阻塞,Nginx 等待超时。
处理:
- 临时调大线程池
- 降级三方接口
- 增加超时控制
- 增加熔断策略
关键词:
504, upstream timeout, external api, thread blocked, nginx
这已经是一个完整知识单元:现象 → 排查过程 → 根因 → 处理方案
chunk和文档有什么关系
简单来说,完整的文档就相当于一本书,一个chunk就是某页或者某一小节,查资料时只摘抄相关小节,而不是读完整本书,和 RAG 用 chunk 逻辑完全一致
chunk向量化
原始文档切割后的成为不同的chunk,通过embedding 模型转换成一段向量,而提出的问题也会被转换成一段向量,用户问题向量和所有 chunk 向量看哪个最相似,最终返回最相似的几个 chunk,提交给llm
例如:
chunk:订单服务大量 504,Pod 正常,Redis MySQL 正常,最终是三方接口慢导致线程阻塞,转换成向量:[0.012, -0.233, 0.891, 0.056, ...]
用户问题:订单接口大量 504,但是 Pod 和数据库都正常,怎么排查?,也转换成向量:[ 0.021564, -0.156489, 0.089451, ...]
然后向量库会比较两者的向量,看哪个最相似,返回相似的chunk,提交给llm
总结
- 一个最简 RAG 只需要三件事:知识库、检索函数、拼 prompt 的逻辑,RAG 的本质不是让 LLM 变聪明,而是内部资料及时喂给它,让它回答的更切近内部服务的内容,而不是通用的内容
- RAG需要在海量的文档中搜索出最佳匹配的chunk,来给llm提供依据回答问题
联系我
- 联系我,做深入交流

至此,本文结束。
在下才疏学浅,有撒汤漏水的,请各位不吝赐教。
本文来自博客园,作者:it排球君,转载请注明原文链接:https://www.cnblogs.com/MrVolleyball/p/21737210

浙公网安备 33010602011771号