使用LLaMA3构建本地RAG代理,展示RAG概念的示意图,由DALL·E 3提供。
在人工智能和机器学习的领域中,检索增强生成(RAG)获得了显著的关注和影响力,用于增强语言模型的能力。我们将使用LLaMA3和LangChain构建一个本地RAG代理,利用来自不同RAG论文的先进概念,创建一个灵活且能自我纠正的系统。我们将详细探讨技术细节,实施步骤,并提供示例来解释每一步骤。
背景与概览检索增强生成(RAG)涉及通过从知识库检索相关信息来增强语言模型的响应。该过程结合了检索系统和生成模型的优点,以提供更准确和上下文合适回答。本教程结合了几篇 RAG 研究论文中的想法,构建了一个功能强大的 RAG 代理,具有以下特点:
1. 自适应路由: 根据问题的特点,将问题分配给不同的检索策略。
2. 纠正性回退: 如果初始文档检索未满足相关性要求,就转而使用网络搜索。
3.自我校正:调整并修正答案,避免胡说,确保答案准确相关。
搭建环境首先,我们需要安装一些必要的库和工具。以下命令用于安装LangChain及其他依赖项:
pip install langchain
pip install -U langchain-nomic langchain_community tiktoken langchainhub chromadb langchain langgraph tavily-python nomic[local] langchain-text-splitters
设置本地模型
我们将使用LLaMA3作为我们的本地语言模型(LLM),并结合GPT4All的嵌入模型。通过运行以下命令来确保你已经安装了所需的模型:
ollama pull llama3 # 拉取llama3模型
pip install langchain-nomic # 安装langchain-nomic库
文档加载及索引
我们将从特定的URL加载文档,并把它们拆分成可管理的小块,以实现高效获取。
from langchain_community.document_loaders import WebBaseLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_nomic.embeddings import NomicEmbeddings
urls = [
"https://example.com/post1",
"https://example.com/post2",
"https://example.com/post3",
]
docs = [WebBaseLoader(url).load() for url in urls] # 加载URL列表中的每个网页
docs_list = [item for sublist in docs for item in sublist] # 将多维列表展平为一维列表
text_splitter = RecursiveCharacterTextSplitter.from_tiktoken_encoder(chunk_size=250, chunk_overlap=0) # 创建一个字符分割器,将文本分割成大约250字符的块
doc_splits = text_splitter.split_documents(docs_list)
vectorstore = Chroma.from_documents(
documents=doc_splits,
collection_name="rag-chroma",
embedding=NomicEmbeddings(model="nomic-embed-text-v1.5", inference_mode="local"),
) # 创建一个Chroma向量存储
retriever = vectorstore.as_retriever() # 获取检索器对象
实施检索分级
我们确保找到的文档与查询相关,为此我们实现了一个检索评分系统:
from langchain_community.chat_models import ChatOllama
from langchain_core.prompts import PromptTemplate
from langchain_core.output_parsers import JsonOutputParser
llm = ChatOllama(model="llama3", format="json", temperature=0)
prompt = PromptTemplate(
template="""system
你是一个评判检索文档与用户问题相关性的评委。若文档中出现与用户问题相关的关键词,则将其评为相关。这不需要是一个严格的测试。目标是筛选掉错误的检索结果。
请提供一个二进制评分 '是' 或 '否',以表示文档是否与问题相关。
请以一个只包含键 'score' 的 JSON 格式输出,无需任何额外说明。
user
这里是检索到的文档:\n\n {document} \n\n
这里是用户的问题:{question} \n assistant""",
input_variables=["question", "document"],
)
retrieval_grader = prompt | llm | JsonOutputParser()
自适应问题分配
系统根据问题内容决定是否使用向量存储查找或转而使用网页搜索。
prompt = PromptTemplate(
模板为"""系统 你擅长将用户问题导向向量数据库或网络搜索。对于涉及LLM代理、提示工程和对抗性攻击的问题,请使用向量数据库。你不必严格匹配问题中的关键词。对于其他问题,请使用网络搜索。根据问题,给出二元选择 'web_search' 或 'vectorstore'。返回仅包含键 'datasource' 的JSON,无需前言或解释。问题:{question}""",
input_variables=["question"],
)
question_router = prompt | llm | JsonOutputParser()
生成并验证答案的有效性
一旦找到文档,系统就会生成答案并进行验证,以避免出错。
prompt = PromptTemplate(
template="""系统 你是一个用于回答问题的任务助手。使用以下检索到的上下文来回答问题。如果你不知道答案,就说你不知道。最多三句话,保持简洁。
问题: {question}
上下文: {context}
回复: 助理""",
input_variables=["question", "context"],
)
rag_chain = prompt | llm | JsonOutputParser()
这生成的回答然后会被评价是否有幻觉内容,以及与问题的相关性。
prompt = PromptTemplate(
template="""系统,你是一个评估答案是否基于一组事实的评分者。给一个“是”或“否”的二进制分数来表示该答案是否基于一组事实。仅提供一个仅包含一个键'score'的JSON格式的二进制分数,不附加任何说明。user
以下是事实:
\n ------- \n
{documents}
\n ------- \n
这是答案:{generation} assistant""",
input_variables=["generation", "documents"],
)
hallucination_grader = prompt | llm | JsonOutputParser()
利用LangGraph构建控制流程
最后,我们通过LangGraph把这些组件结合起来,形成一个控制流程。
from langgraph.graph import END, StateGraph, START
class GraphState(TypedDict):
question: str
generation: str
web_search: str
documents: List[str]
workflow = StateGraph(GraphState)
workflow.add_node("websearch", web_search)
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade_documents", grade_documents)
workflow.add_node("generate", generate)
workflow.add_conditional_edges(
START,
route_question,
{
"websearch": "websearch",
"vectorstore": "retrieve",
},
)
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
"grade_documents",
decide_to_generate,
{
"websearch": "websearch",
"generate": "generate",
},
)
workflow.add_edge("websearch", "generate")
workflow.add_conditional_edges(
"generate",
grade_generation_v_documents_and_question,
{
"not supported": "generate",
"useful": END,
"not useful": "websearch",
},
)
app = workflow.compile()
总结
我们这儿提供了一个详细的指南,教你如何用LLaMA3和LangChain来构建本地的RAG代理,整合了先进检索和生成技术。按照这些步骤做,你可以造出一个能准确回答复杂问题的强大系统。
示例查询语句
让我们用一个示例查询来测试系统:“智能代理有哪些类型的记忆?”
inputs = {"question": "什么是代理记忆的类型?"}
for output in app.stream(inputs): # app.stream(inputs) is a specific function that processes the inputs
for key, value in output.items():
print(f"完成运行:{key}:") # "print" is used similarly to "pprint" for displaying output
print(value["生成"]) # "生成" is used for "generation" in this context
中文:
预期结果:
根据提供的上下文,提到了几种不同类型的记忆:
1. 感觉记忆:这是记忆的最早阶段,能够保留感觉印象(如视觉、听觉等),即使在原始刺激结束后。
2. 最大内积搜索(MIPS):这是一个长期记忆模块,记录了代理人在自然语言中的全面列表。(最大内积搜索)
通过利用所讨论的概念和实现,你可以将这个框架扩展以适应各种应用场景,从而增强你的人工智能解决方案的能力和可靠性。
参考文献和资源:LangGraph 网站 (langchain-ai.github.io) 官方网站
奥拉玛(Ollama,https://ollama.com/)
注意:只有在依赖库和API没有变化的情况下,代码才能正常运行。如果代码未按预期工作,你可能需要做一些必要的调整。