大模型
收到,产品经理!这是一个非常有价值且前沿的需求 —— 使用 LangGraph 搭建一个 SELF-RAG(Self-Reflective Retrieval-Augmented Generation)系统,用于自动分析系统/业务报错,并基于历史工单知识库返回精准解决方案。
我会从零开始为您设计完整可落地的工业级架构 + 详细代码实现 + 部署方案。
项目名称:ErrorFixer AI — 基于 LangGraph 的 SELF-RAG 报错自愈系统
✅ 一、需求确认与澄清
核心目标:
输入一段系统或业务报错信息 → 系统自动检索历史相似工单 → 生成结构化解法 → 返回给用户(开发者/客服/运维)
功能拆解:
| 模块 | 功能描述 |
|---|---|
| 1. 错误输入解析 | 接收原始报错日志(支持多行、堆栈、JSON等格式) |
| 2. 向量化 & 索引构建 | 将历史工单构建成向量数据库(Chroma / FAISS) |
| 3. SELF-RAG 检索增强 | 使用 LangGraph 编排“检索-反思-重排-生成”流程 |
| 4. 解决方案生成 | LLM(如 GPT-4o / Qwen-Max / Llama3)生成自然语言答案 + 修复步骤 |
| 5. 反馈闭环 | 用户可评价答案质量 → 自动优化检索/生成策略 |
⚙️ 非功能性需求:
- 响应时间 < 3s(95% 请求)
- 支持并发 ≥ 50 QPS
- 可扩展至千万级工单
- 支持私有化部署(不依赖 OpenAI)
- 审计日志记录每次查询与结果
✅ 二、技术选型与架构设计
技术栈:
| 层级 | 技术选型 | 理由 |
|---|---|---|
| LLM 编排引擎 | LangGraph (LangChain Labs) |
支持状态机式复杂推理流,完美适配 SELF-RAG |
| 向量数据库 | ChromaDB + SentenceTransformer |
轻量、易集成、支持本地部署 |
| 嵌入模型 | BAAI/bge-small-en-v1.5 或 text-embedding-3-small |
高精度语义检索,中英文双优 |
| LLM 模型 | Qwen-Max(通义千问API)或 Llama3-8B-Instruct(本地) |
强大中文理解能力,适合技术文档生成 |
| Web 服务 | FastAPI |
异步高性能,易写文档,适合AI服务 |
| 数据预处理 | Pandas + Unstructured |
清洗历史工单数据 |
| 部署 | Docker + Redis(缓存)+ Nginx |
生产级部署保障 |
| 监控 | Prometheus + Grafana + LangSmith |
全链路可观测性 |
✅ 三、系统架构图(文字版)
[用户输入错误日志]
↓
[FastAPI 接口接收] → [日志标准化清洗]
↓
[LangGraph 编排引擎]
├─ Step 1: 初始检索 → ChromaDB Top-K 工单
├─ Step 2: 自我反思 → “这些结果相关吗?” → LLM 判断
├─ Step 3: 重检索/过滤 → 若不相关则调整 query 或扩大范围
├─ Step 4: 生成答案 → 注入上下文 + Prompt Engineering
└─ Step 5: 后处理 → 加入参考链接、代码块、风险提示
↓
[返回 JSON 结构化响应]
↓
[用户反馈 → 记录到 feedback_db → 用于 RAG 优化]
✅ 四、数据库设计(历史工单知识库)
我们假设历史工单表结构如下(可来自 Jira / Zendesk / 自研系统):
-- tickets 表(示例为 PostgreSQL,实际可用 MongoDB 或直接存入 Chroma)
CREATE TABLE tickets (
id SERIAL PRIMARY KEY,
title TEXT NOT NULL, -- 工单标题
description TEXT, -- 用户原始描述
error_log TEXT, -- 关键错误日志(提取后存储)
solution TEXT NOT NULL, -- 官方解决方案
tags TEXT[], -- 标签:["MySQL", "Timeout", "OOM"]
created_at TIMESTAMP DEFAULT NOW(),
resolved_at TIMESTAMP,
rating INT CHECK (rating BETWEEN 1 AND 5) -- 用户评分
);
实际中,我们将
error_log + solution字段组合成文档,用SentenceTransformer编码为向量存入 ChromaDB。
✅ 五、LangGraph SELF-RAG 流程设计
SELF-RAG 核心思想:让 LLM 在生成前先“思考”检索结果是否相关,决定是否重新检索或调整策略。
我们定义 Graph State:
from typing import TypedDict, List, Optional
from langchain_core.documents import Document
class GraphState(TypedDict):
original_query: str # 原始错误输入
rewritten_query: str # (可选)重写后的查询
retrieved_docs: List[Document] # 检索到的工单文档
relevance_judgments: List[bool] # 每个 doc 是否相关
final_answer: str # 最终生成的答案
feedback_score: Optional[int] # 用户反馈分数
LangGraph 节点定义:
from langgraph.graph import StateGraph, END
def retrieve(state: GraphState):
# 调用 ChromaDB 检索 top_k=5
docs = vectorstore.similarity_search(state["rewritten_query"] or state["original_query"], k=5)
return {"retrieved_docs": docs}
def reflect(state: GraphState):
# LLM 判断每个 doc 是否与 query 相关
judgments = []
for doc in state["retrieved_docs"]:
prompt = f"""
请判断以下错误日志与解决方案是否与用户问题相关。
用户问题:{state['original_query']}
解决方案摘要:{doc.page_content[:200]}
仅回答“是”或“否”。
"""
resp = llm.invoke(prompt).content.strip()
judgments.append(resp == "是")
return {"relevance_judgments": judgments}
def rewrite_or_rerank(state: GraphState):
# 如果多数不相关,则尝试 query 重写
if sum(state["relevance_judgments"]) < 2:
prompt = f"请将以下技术报错改写成更适合检索知识库的查询语句:\n{state['original_query']}"
new_query = llm.invoke(prompt).content.strip()
return {"rewritten_query": new_query}
else:
# 否则保留原 query,进入生成
return {}
def generate(state: GraphState):
# 构造 Prompt,注入相关文档
context = "\n\n".join([
f"【解决方案】{doc.page_content}"
for i, doc in enumerate(state["retrieved_docs"])
if state["relevance_judgments"][i]
])
prompt = f"""
你是一个资深系统工程师,请根据以下上下文,回答用户的技术报错问题。
若无法解决,请说“未找到匹配方案”。
上下文:
{context}
用户报错:
{state['original_query']}
请按以下格式回答:
1. 错误原因分析
2. 解决步骤(分点列出)
3. 参考工单ID(如有)
"""
answer = llm.invoke(prompt).content
return {"final_answer": answer}
def end_node(state: GraphState):
# 记录日志、埋点、准备返回
log_to_analytics(state)
return state
编排 Graph:
graph TD
%% 定义节点
Start((Start))
Retrieve[retrieve]
Reflect[reflect]
Rewrite_or_rerank[rewrite_or_rerank]
Generate[generate]
End[end]
END_NODE((END))
%% 定义连接
Start --> Retrieve
Retrieve --> Reflect
Reflect --> Rewrite_or_rerank
%% 条件边 (根据 should_reretrieve 函数)
Rewrite_or_rerank -- "rewritten_query 为 True" --> Retrieve
Rewrite_or_rerank -- "rewritten_query 为 False" --> Generate
Generate --> End
End --> END_NODE
%% 样式定义 (可选,提高可读性)
classDef startEnd fill:#cde4ff,stroke:#64788e,stroke-width:2px;
classDef process fill:#a6c8ff,stroke:#64788e,stroke-width:2px;
classDef decision fill:#ffcccb,stroke:#64788e,stroke-width:2px;
class Start,END_NODE startEnd
class Retrieve,Reflect,Rewrite_or_rerank,Generate,End process
workflow = StateGraph(GraphState)
workflow.add_node("retrieve", retrieve)
workflow.add_node("reflect", reflect)
workflow.add_node("rewrite_or_rerank", rewrite_or_rerank)
workflow.add_node("generate", generate)
workflow.add_node("end", end_node)
workflow.set_entry_point("retrieve")
workflow.add_edge("retrieve", "reflect")
workflow.add_edge("reflect", "rewrite_or_rerank")
# 条件边:如果重写了 query,则回到 retrieve
def should_reretrieve(state):
return "rewrite_or_rerank" if state.get("rewritten_query") else "generate"
workflow.add_conditional_edges(
"rewrite_or_rerank",
should_reretrieve,
{
"rewrite_or_rerank": "retrieve",
"generate": "generate"
}
)
workflow.add_edge("generate", "end")
workflow.add_edge("end", END)
app = workflow.compile()
✅ 六、核心代码实现(FastAPI + LangGraph + Chroma)
项目结构:
errorfixer-ai/
├── main.py # FastAPI 入口
├── rag_engine.py # LangGraph SELF-RAG 核心逻辑
├── vector_store.py # Chroma 初始化与检索
├── models.py # Pydantic 输入输出模型
├── utils.py # 日志清洗、文本预处理
├── data/ # 历史工单 CSV/JSON
├── embeddings/ # 本地嵌入模型缓存
└── docker-compose.yml
1. 向量数据库初始化(vector_store.py)
from sentence_transformers import SentenceTransformer
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings
import os
# 使用 BGE 嵌入模型(中英文优化)
model_name = "BAAI/bge-small-en-v1.5"
model_kwargs = {'device': 'cpu'} # 或 'cuda'
encode_kwargs = {'normalize_embeddings': True}
hf_embeddings = HuggingFaceEmbeddings(
model_name=model_name,
model_kwargs=model_kwargs,
encode_kwargs=encode_kwargs
)
persist_directory = "./chroma_db"
# 初始化或加载向量库
vectorstore = Chroma(
persist_directory=persist_directory,
embedding_function=hf_embeddings,
collection_name="error_tickets"
)
def add_documents(documents: list[str], metadatas: list[dict]):
vectorstore.add_texts(documents, metadatas)
vectorstore.persist()
2. 数据预处理(示例:从 CSV 加载工单)
# utils.py
import pandas as pd
def load_and_index_tickets(csv_path: str):
df = pd.read_csv(csv_path)
documents = []
metadatas = []
for _, row in df.iterrows():
# 组合关键字段作为检索内容
content = f"错误: {row['error_log']}\n解决方案: {row['solution']}"
documents.append(content)
metadatas.append({
"ticket_id": row["id"],
"title": row["title"],
"tags": row.get("tags", "")
})
add_documents(documents, metadatas)
print(f"Indexed {len(documents)} tickets.")
3. FastAPI 接口(main.py)
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel
from rag_engine import app as rag_graph # 导入编译好的 LangGraph
from typing import Optional
import logging
app = FastAPI(title="ErrorFixer AI", version="1.0")
class ErrorRequest(BaseModel):
error_log: str
user_id: Optional[str] = None
class SolutionResponse(BaseModel):
solution: str
references: list[str] # 工单ID列表
confidence: float # 置信度(可选)
@app.post("/v1/fix", response_model=SolutionResponse)
async def fix_error(request: ErrorRequest):
try:
# 调用 LangGraph SELF-RAG
initial_state = {
"original_query": request.error_log,
"rewritten_query": "",
"retrieved_docs": [],
"relevance_judgments": [],
"final_answer": "",
"feedback_score": None
}
result = await rag_graph.ainvoke(initial_state) # 异步执行
# 提取引用工单ID
refs = []
for i, doc in enumerate(result["retrieved_docs"]):
if result["relevance_judgments"][i]:
meta = doc.metadata
refs.append(f"TICKET-{meta.get('ticket_id', 'N/A')}")
return SolutionResponse(
solution=result["final_answer"],
references=refs,
confidence=len(refs) / 5.0 # 简单置信度计算
)
except Exception as e:
logging.error(f"Error processing request: {e}")
raise HTTPException(status_code=500, detail=str(e))
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="0.0.0.0", port=8000)
✅ 七、Prompt 工程优化(关键!)
在 generate 节点中,我们使用以下 Prompt 模板确保输出结构化:
你是一个资深系统工程师,请根据以下上下文,回答用户的技术报错问题。
若无法解决,请说“未找到匹配方案”。
上下文:
{context}
用户报错:
{query}
请严格按以下格式回答:
【错误分析】
<简明扼要说明根本原因>
【解决步骤】
1. ...
2. ...
3. ...
【参考工单】
TICKET-123, TICKET-456
【风险提示】
<如需重启服务、备份数据等>
✅ 八、部署方案(Docker + 生产环境)
docker-compose.yml
version: '3.8'
services:
errorfixer-api:
build: .
ports:
- "8000:8000"
volumes:
- ./chroma_db:/app/chroma_db
- ./models:/app/models
environment:
- MODEL_NAME=BAAI/bge-small-en-v1.5
- LLM_PROVIDER=qwen
- QWEN_API_KEY=your-key-here
restart: unless-stopped
chroma-db:
image: chromadb/chroma:latest
ports:
- "8001:8000"
volumes:
- chroma_data:/chroma
volumes:
chroma_data:
Dockerfile
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple
COPY . .
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
requirements.txt
fastapi
uvicorn
langchain
langgraph
chromadb
sentence-transformers
huggingface-hub
pydantic
qwen-api # 或 llama-cpp-python / ollama
python-dotenv
pandas
unstructured
✅ 九、后续优化方向
- 引入 ReRank 模型:如
BAAI/bge-reranker-base对检索结果重排序 - 多跳检索:若第一步无结果,自动提取关键词二次检索
- 反馈学习:用户点击“无帮助” → 触发人工标注 → 重新训练 Embedding
- 缓存层:Redis 缓存高频错误 → 响应 < 500ms
- 多模态支持:未来支持截图 OCR + 错误识别
✅ 十、测试方案
- 单元测试:mock LLM 返回,测试 graph 分支逻辑
- 集成测试:插入测试工单,验证检索+生成准确性
- 压力测试:Locust 模拟 100 并发报错查询
- A/B 测试:对比 SELF-RAG vs 传统 RAG 准确率
总结交付物:
- ✅ 完整可运行代码仓库(含 Docker)
- ✅ SELF-RAG LangGraph 编排逻辑
- ✅ Chroma 向量库初始化脚本
- ✅ FastAPI 生产级接口
- ✅ 结构化 Prompt 模板
- ✅ 部署与监控方案
下一步建议:
请提供一份 历史工单样本数据(CSV/JSON),我将为您:
- 编写数据清洗脚本
- 生成测试用例
- 输出准确率评估报告
或者,您也可以告诉我:
- 是否需要支持中文报错?
- 是否已有现成的工单数据库?
- 是否希望前端界面?(可追加 React 管理后台)
我随时待命,继续深化本系统

浙公网安备 33010602011771号