从零跑通 RAG Agent (LangChain) 的踩坑指南
Overview:
在 LangChain 1.0分包重构之后,Langchain与RAG的结合也有了较大更新。本指南将搭建本地 RAG并将其融合进大模型 Agent ,遇到的踩坑以及经验都列出。Source code here.
Issue 1:
Chroma依赖注入,LangChain 官方为了主包瘦身,防止社区包过于臃肿导致环境冲突,现在将各大第三方工具全部剥离成了独立的母包,如之前的vectorstore中的chroma移动到了单独的langchain_chroma,正确导入from langchain_chroma import Chroma。
Issue 2:
新版的构建是通过LCEL语言去构建ragchain,替代原本的chains和固定的RetrieverQA。像这样:
rag_chain = (
{"context": retriever, "question": RunnablePassthrough()}
| prompt
| model
| StrOutputParser()
)
当然这个基础的配置与经典的RetrieverQA流程是很类似的,所以优势主要在于需求更高自由度的时候,例如调用多个retriever:
from langchain_core.runnables import RunnableParallel, RunnablePassthrough
# 定义一个合并文档的辅助函数
def merge_docs(results):
# results 会收到一个字典,包含两个 retriever 返回的文档列表
local_docs = results["local_docs"]
db_docs = results["db_docs"]
# 把它们拼接成一个超长的上下文文本
return "\n\n".join([doc.page_content for doc in local_docs + db_docs])
# ==================== 🔀 现代化多路检索 RAG 链 ====================
rag_chain = (
# 1. 并发调用两个不同的检索器
{
"local_docs": doc_retriever,
"db_docs": db_db_retriever,
"question": RunnablePassthrough()
}
# 2. 将多路检索出来的文档进行自定义清洗、去重或合并
| RunnableParallel({
"context": merge_docs,
"question": lambda x: x["question"] # 保持原问题继续向下传递
})
# 3. 正常的后续流程
| prompt
| model
| StrOutputParser()
)
更高级的还有动态路由、文档重排等。
Issue 3:RAG如何导入到Agent中
langchain的create agent并没有retriever属性,如果要把RAG链接进去,就需要wrapper成工具,此外,在tool中写的Docstring,在运行时会被大模型当成“工具说明书”去阅读。通过调整工具的 Prompt 备注,可优化工具的使用体验。
Issue 4:多目标查询与拒答
当去询问问题的时候,如果一次询问多个目标object可能会因为找不到目标文档被拒绝回答。
这时候可以关注两个地方进行优化调整:
- 初始化检索器时:在配置
retriever = vdb.as_retriever(search_kwargs={"k": num})时可以把k变大,提高粗筛阶段的文档覆盖率,让多个目标的语料有机会同时进入大模型的视野。 - 丰富本地语料与松绑 Prompt:如果数据过于干瘪,可以调整 RAG 内部的指令,允许模型在本地基础知识无法支撑时,结合其自身知识库进行合理的润色和补充。
浙公网安备 33010602011771号