【项目实训3】RAG多维优化工程

—— written by Unalome (2025.04.15-16)

一. 引入jieba分词提升Haystack-RAG中文后续检索精度

通过替换Haystack默认的英文分词工具,结合jieba的精确模式和搜索引擎模式对中文文本进行切分,同时过滤停用词并加载领域自定义词典,显著提升了文档预处理的语义准确性。这种改进使检索模型能更精准地捕捉中文词汇的边界和专业术语,减少因分词不当导致的语义偏差,从而在检索中提高匹配效率,尤其在垂直领域场景下效果更为明显。

import jieba

# === 中文处理函数 ===
def chinese_text_processor(text: str) -> str:
    return " ".join(jieba.lcut(text))
# === 预处理 ===
processed_docs = []
document_cleaner = DocumentCleaner()
text_splitter = DocumentSplitter(
    split_by="word",
    split_length=200,
    split_overlap=20
)


for doc_text in raw_docs:
    # 创建文档对象
    doc = Document(content=doc_text)

    # 清理文档
    cleaned_result = document_cleaner.run(documents=[doc])
    cleaned_docs = cleaned_result["documents"]

    # 处理每个清理后的文档
    for cleaned_doc in cleaned_docs:
        # 中文分词处理
        cleaned_doc.content = chinese_text_processor(cleaned_doc.content)

        # 文档分割
        split_result = text_splitter.run(documents=[cleaned_doc])
        processed_docs.extend(split_result["documents"])

document_store.write_documents(processed_docs)

二、修改提示词以限制输出结果范围

针对输出结果范围的限制,通过在生成提示词中加入显式约束指令(禁止编造信息)和结构化输出格式,有效约束了模型的生成行为。这不仅减少了模型因自由生成导致的幻觉问题,还通过格式化响应提升了结果的可解析性,使其更适配后续自动化处理流程,同时缩短了生成时间,降低了计算资源的消耗。

template = [
    ChatMessage.from_user(
        """
        {% if documents|length == 0 %}
            该知识尚未被学习
        {% else %}
            Answer the question based on the context:
            {% for document in documents %}
                {{ document.content }}
            {% endfor %}

            Question: {{ query }}
        {% endif %}
        """
    )
]
prompt_builder = ChatPromptBuilder(template=template)

三、使用流式输出优化检索体验

流式输出的优化则通过分块生成和实时传输技术实现,将模型生成的内容拆分为多个片段逐步返回,并在前端通过SSE实时渲染(开发中)。这种方式让用户无需等待完整结果即可获取初步信息,显著降低了感知延迟,尤其适用于长文本生成场景。
同时,后端通过缓存部分生成结果平衡实时性与稳定性,既提升了用户体验,又避免了服务阻塞问题,实现了交互友好性与资源利用率的双重优化。

首先定义回调函数

from haystack.dataclasses import StreamingChunk

# 定义流式回调处理函数
def streaming_callback(chunk: StreamingChunk):
    print(chunk.content, end="", flush=True)

# 创建生成器时配置回调
chat_generator = OpenAIChatGenerator(
    # model="gpt-4o-mini",
    model="DeepSeek-R1",
    api_base_url=os.environ["OPENAI_API_BASE"],
    generation_kwargs={
        "temperature": 0.0,
        "stream": True
    },
    streaming_callback=streaming_callback  # 异步回调
)

并修改输出逻辑

while True:
    query = input("\n你的问题: ")
    if query.lower() in ["exit", "quit"]:
        break

    processed_query = chinese_text_processor(query)
    
    print("\n--- 流式输出 ---")
    try:
        _ = pipeline.run(
            data={
                "retriever": {"query": processed_query},
                "prompt_builder": {"query": query},
            }
        )
    except Exception as e:
        print(f"处理出错: {str(e)}")
    finally:
        print("\n--- 输出完成 ---")

四、使用chromadb实现长期存储和加速检索

基于Haystack与ChromaDB构建高效可靠的RAG文档存储与检索架构,通过ChromaDocumentStore实现持久化存储,结合动态滑动窗口分块、中文分词优化及实时去重算法,形成多阶段智能预处理流水线。采用余弦相似度计算,实现了高维向量空间快速检索响应。
自定义ChromaRetriever组件实现灵活的top-k检索与元数据过滤,结合按需加载和内存缓存策略,为大规模知识库应用提供了高效且经济的技术支持。

# 1. 创建Chroma文档存储(持久化到本地)
chroma_store = ChromaDocumentStore(
    persist_path="chroma_db",  # 本地存储路径
    distance_function="cosine",
    # index="hnsw",
    # persist=True
    # embedding_function=ef
)

# 原始文档(首次运行时写入)
raw_docs = [
    "***********"
]

# 检查是否已存在数据(避免重复写入)
if chroma_store.count_documents() == 0:
    processed_docs = []
    document_cleaner = DocumentCleaner()
    text_splitter = DocumentSplitter(
        split_by="word",
        split_length=200,
        split_overlap=50,
        split_threshold=0.5
    )

    text_splitter.warm_up()

    for doc_text in raw_docs:
        doc = Document(content=doc_text)
        cleaned_result = document_cleaner.run(documents=[doc])
        cleaned_docs = cleaned_result["documents"]

        for cleaned_doc in cleaned_docs:
            # 中文分词处理
            cleaned_doc.content = chinese_text_processor(cleaned_doc.content)
            
            # 内容去重
            word_list = cleaned_doc.content.split()
            unique_words = []
            seen = set()
            for word in word_list:
                if word not in seen:
                    seen.add(word)
                    unique_words.append(word)
            cleaned_doc.content = " ".join(unique_words)
            
            split_result = text_splitter.run(documents=[cleaned_doc])
            processed_docs.extend(split_result["documents"])

    chroma_store.write_documents(processed_docs)

# 2. 自定义Chroma检索器
@component
class ChromaRetriever:
    def __init__(self, document_store: ChromaDocumentStore, top_k: int = 10):
        self.document_store = document_store
        self.top_k = top_k

    @component.output_types(documents=List[Document])
    def run(self, query: str, filters: Optional[Dict] = None):
        results = self.document_store.search(
            queries=[query], top_k=self.top_k, filters=filters)
        return {"documents": results[0]}


# 初始化检索器
retriever = ChromaRetriever(document_store=chroma_store, top_k=10)

五、下一步开发目标

(1)修改文档处理机制,加入增量更新功能,目前的数据库只支持一次性更新。
(2)引入混合检索机制,进一步提升复杂场景下的语义理解能力与数据维护效率。(optional)
(3)前端工程:使用Cherry Studio作为输出媒介,将生成的流式答案通过该媒介展示给用户
(4)数据库工程:接入山东大学数据中台,获取准数据集
posted @ 2025-04-16 22:35  Unalome  阅读(110)  评论(0)    收藏  举报