Files
blog-press/docs/Web/AI/RAG.md

5.4 KiB
Raw Blame History

title, date
title date
RAG开发 2026-06-24

一、工作流程

原始文档 / 数据
        ↓
【文档收集与清洗】
        ↓
【文档切分Chunking】
        ↓
【向量化Embedding】
        ↓
【向量数据库存储】
        ↓
用户自然语言问题
        ↓
【检索系统】召回相关知识片段
        ↓
【上下文组装】构建增强 Prompt
        ↓
【大模型】基于知识生成回答
        ↓
【引用 / 校验】
        ↓
最终返回给用户(答案 + 来源)

二、实战

2.1 资料处理

2.1.1 pdf处理

PaddleOCR是百度飞桨PaddlePaddle团队开源的产业级 OCR光学字符识别与文档智能开发套件。

JOB_URL = "https://paddleocr.aistudio-app.com/api/v2/ocr/jobs"
TOKEN = ""
MODEL = "PP-OCRv5"

FILE_URL = ""

HEADERS = {
    "Authorization": f"bearer {TOKEN}",
    "Content-Type": "application/json",
}

# ========== 提交 OCR 任务 ==========
payload = {
    "fileUrl": FILE_URL,
    "model": MODEL,
    "optionalPayload": {
        "useDocOrientationClassify": False,
        "useDocUnwarping": False,
        "useChartRecognition": False,
    },
}

print("🚀 Submitting OCR job...")
resp = requests.post(JOB_URL, json=payload, headers=HEADERS)
resp.raise_for_status()

job_id = resp.json()["data"]["jobId"]
print(f"✅ Job submitted, jobId={job_id}")

# ========== 轮询任务状态 ==========
while True:
    r = requests.get(f"{JOB_URL}/{job_id}", headers=HEADERS)
    r.raise_for_status()

    data = r.json()["data"]
    state = data["state"]

    if state == "pending":
        print("⏳ Job status: pending")
    elif state == "running":
        prog = data.get("extractProgress", {})
        total = prog.get("totalPages")
        done = prog.get("extractedPages")
        if total and done:
            print(f"📄 Processing: {done}/{total} pages")
        else:
            print("📄 Processing...")
    elif state == "done":
        prog = data["extractProgress"]
        print(
            f"✅ Job finished | Pages: {prog['extractedPages']} | "
            f"Start: {prog['startTime']} | End: {prog['endTime']}"
        )
        break
    elif state == "failed":
        print("❌ Job failed:", data.get("errorMsg", "Unknown error"))
        sys.exit(1)

    time.sleep(5)

# ========== 获取 OCR 结果 ==========
jsonl_url = data["resultUrl"]["jsonUrl"]
print(f"📥 Fetching result: {jsonl_url}")

resp = requests.get(jsonl_url)
resp.raise_for_status()

这里只处理了url形式的文档实际开发中可以搭建一个管理页面集中处理文档资料。
  获取到的结果是按照区域划分的,还需要进一步的处理:

pages = []

for line in resp.text.strip().splitlines():
    if not line.strip():
        continue

    obj = json.loads(line)

    for page in obj.get("result", {}).get("ocrResults", []):
        pruned = page.get("prunedResult", {})
        texts = pruned.get("rec_texts", [])

        # 一页一段
        page_text = "\n".join(texts)
        pages.append(page_text)

# ✅ 最终文档
full_doc = "\n\n".join(pages)

2.2 向量化

  使用在线或者离线向量化模型处理:

text_splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,      # 每个 chunk 最大长度
    chunk_overlap=50,    # 重叠,防止切坏语义
    separators=["\n\n", "\n", "。", "", "", "", "", " ", ""]
)

chunks = text_splitter.split_text(full_doc)
print(f"✅ 切分成 {len(chunks)} 个 chunk")

for i, chunk in enumerate(chunks):
    print(f"\n===== Chunk {i} =====")
    print(chunk)

model = SentenceTransformer("BAAI/bge-base-zh", cache_folder="./models")

embeddings = model.encode(
    chunks,
    normalize_embeddings=True
)

print(f"✅ 向量维度: {embeddings.shape}")

client = chromadb.PersistentClient(path="./vector_db/train_docs")
collection = client.get_or_create_collection(name="train_docs")

for i, (chunk, emb) in enumerate(zip(chunks, embeddings)):
    collection.add(
        ids=[str(i)],
        documents=[chunk],
        embeddings=[emb.tolist()]
    )

print("✅ 向量库写入完成")

  这里使用的是BAAI/bge-base-zh模型,第一次会将模型下载到本地。
将处理结果存储到chroma向量数据库中。

2.3 检索系统

model_path = "./models/models--BAAI--bge-base-zh/snapshots/0e5f83d4895db7955e4cb9ed37ab73f7ded339b6"
model = SentenceTransformer(model_path, local_files_only=True)

client = chromadb.PersistentClient(path="./vector_db/train_docs")
collection = client.get_or_create_collection(name="train_docs")

q = "培训计划怎么制定?"
q_emb = model.encode([q], normalize_embeddings=True)

res = collection.query(
    query_embeddings=q_emb.tolist(),
    n_results=3
)

print("\n\n".join(res["documents"][0]))

::: tip 注意这里使用离线模型时的路径 :::

2.4 结合大模型

  常用提示词:

你是专业的知识问答助手
请严格基于下方提供的参考资料回答问题
如果参考资料中不包含答案请明确说明当前资料无法回答该问题”。
不要编造推测或引入外部知识

参考资料
"""
{{context}}
"""

用户问题
{{question}}

请按以下要求回答
1. 回答简洁准确有条理
2. 必要时使用列表或分点说明
3. 避免冗余描述