You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

面向本地LLM的大规模文档检索方案实现技术咨询

基于llama-server + GPT-OSS 20B实现本地RAG交互式问答方案

一、核心架构流程

先明确RAG的核心链路,所有操作围绕这个流程展开:
文档解析 → 文本分片 → Embedding生成 → 向量库存储 → 检索召回 → LLM生成限定范围的回答

二、文档处理:用Docling完成多格式解析

Docling的核心作用是统一提取PDF、Word、Excel等多格式文档的纯文本内容,后续所有操作都基于这些文本展开。

基础使用示例

from docling.document_converter import DocumentConverter

# 初始化文档转换器
converter = DocumentConverter()
# 加载并转换目标文档
doc = converter.convert("your_target_document.pdf")
# 提取所有文本块并拼接成完整文本
full_text = "\n".join([block.text for block in doc.content])

# 文本分片(避免过长文本丢失上下文,带重叠窗口)
def split_text(text, chunk_size=1000, chunk_overlap=200):
    chunks = []
    start_idx = 0
    text_len = len(text)
    while start_idx < text_len:
        end_idx = min(start_idx + chunk_size, text_len)
        chunks.append(text[start_idx:end_idx])
        start_idx += chunk_size - chunk_overlap
    return chunks

# 生成可用于Embedding的文本分片
text_chunks = split_text(full_text)

三、通过llama-server API生成Embedding

llama-server默认支持/completion接口返回Embedding,只需在请求中指定embed: true且不生成文本(n_predict: 0)即可。如果GPT-OSS 20B本身不支持Embedding,建议单独启动一个llama-server加载轻量级量化Embedding模型(如all-MiniLM-L6-v2的GGUF版),效率更高。

调用示例

import requests
import json

def get_embedding(text_chunk, server_url="http://localhost:8080"):
    payload = {
        "prompt": text_chunk,
        "n_predict": 0,  # 仅返回Embedding,不生成回答
        "embed": True,
        "temperature": 0.0  # Embedding生成不需要随机性
    }
    resp = requests.post(f"{server_url}/completion", json=payload)
    if resp.status_code == 200:
        return resp.json()["embedding"]
    raise Exception(f"Embedding生成失败: {resp.text}")

# 批量生成所有文本分片的Embedding
embeddings = [get_embedding(chunk) for chunk in text_chunks]

四、本地向量存储与检索

用FAISS做本地向量库,无需依赖外部服务,适合大规模文档存储:

import faiss
import numpy as np

# 初始化FAISS索引(维度与Embedding一致)
embed_dim = len(embeddings[0])
index = faiss.IndexFlatL2(embed_dim)
# 将Embedding转为float32格式后加入索引
index.add(np.array(embeddings).astype("float32"))

# 检索与查询相关的文本分片
def retrieve_relevant_chunks(query, top_k=3):
    query_embed = get_embedding(query)
    distances, indices = index.search(np.array([query_embed]).astype("float32"), top_k)
    return [text_chunks[i] for i in indices[0]]

五、对接LLM生成限定回答

拿到检索到的文本后,构造明确的Prompt,要求LLM仅基于给定文档内容回答问题。

对接llama-server

def generate_answer(query, server_url="http://localhost:8080"):
    relevant_chunks = retrieve_relevant_chunks(query)
    # 构造约束性Prompt,避免LLM生成无关内容
    prompt = f"""仅基于以下文档内容回答问题,不得使用外部知识:
{"\n\n".join(relevant_chunks)}

问题:{query}
回答:"""
    payload = {
        "prompt": prompt,
        "n_predict": 512,
        "temperature": 0.1,  # 降低随机性,保证回答准确性
        "stop": ["\n问题:"]  # 设置停止符,避免生成多余内容
    }
    resp = requests.post(f"{server_url}/completion", json=payload)
    if resp.status_code == 200:
        return resp.json()["content"].strip()
    raise Exception(f"LLM请求失败: {resp.text}")

对接Ollama

如果用Ollama管理模型,直接调用其API即可,无需手动启动llama-server:

import requests

def generate_answer_with_ollama(query):
    relevant_chunks = retrieve_relevant_chunks(query)
    prompt = f"""仅基于以下文档内容回答问题,不得使用外部知识:
{"\n\n".join(relevant_chunks)}

问题:{query}
回答:"""
    payload = {
        "model": "gpt-oss:20b",  # 你的Ollama模型名称
        "prompt": prompt,
        "stream": False,
        "options": {"temperature": 0.1}
    }
    resp = requests.post("http://localhost:11434/api/generate", json=payload)
    if resp.status_code == 200:
        return resp.json()["response"].strip()
    raise Exception(f"Ollama请求失败: {resp.text}")

六、Docling与llama.cpp/Ollama的对接逻辑

Docling仅负责文档解析环节,与llama.cpp/Ollama的对接是通过中间脚本串联的:

  1. 用Docling解析文档得到纯文本
  2. 文本分片后,通过llama-server API或Ollama的/api/embeddings接口生成向量
  3. 向量存入FAISS索引,检索后构造Prompt传给LLM生成回答

如果用Ollama生成Embedding,可替换为以下代码:

def get_embedding_with_ollama(text_chunk):
    payload = {
        "model": "all-minilm",  # Ollama上的轻量级Embedding模型
        "prompt": text_chunk
    }
    resp = requests.post("http://localhost:11434/api/embeddings", json=payload)
    if resp.status_code == 200:
        return resp.json()["embedding"]
    raise Exception(f"Ollama Embedding生成失败: {resp.text}")

七、优化建议

  • 文本分片:根据文档类型调整策略,比如代码文档按函数分片,长文档按章节+段落拆分
  • Embedding模型:优先选择量化版轻量级模型(如all-MiniLM-L6-v2 GGUF),平衡速度与内存占用
  • 向量库优化:大规模数据可改用FAISS的IVF索引,提升检索效率
  • 缓存机制:缓存已生成的Embedding和检索结果,减少重复请求

内容的提问来源于stack exchange,提问作者bud

火山引擎 最新活动