写在前面
上回这简单过了一下LLM,Document Loader 和 text splitter. 这一把构建VectorBase和Retriever.
正文
VectoreBase
在获取文本块之后,需要将其编码成向量,方便进行检索召回,此时就要用到Embedding模型。通常使用SentenceTransformer模型。使用LangChain提供的接口:
embedding = OpenAIEmbeddings(
base_url=os.environ.get("DOUBAO_ENDPOINT"),
api_key = os.environ.get("DOUBAO_API_KEY"),
model=os.environ.get("DOUBAO_EMBEDDING"),
encoding_format="float",
check_embedding_ctx_length=False
)
从火山引擎官网手册中可得知,字节提供的Embedding模型是兼容OpenAI协议进行调用的,但是若不设置check_embedding_ctx_length为false进行向量化的话会报错。走读源码:
经过测试,调用openAI client的返回格式是没问题的,报错出现在最后一行。Langchain将embedding文本进一步进行分词,再将分此后的tokens作为输入,向服务端发起请求。分词后的格式不再符合服务端处理的格式,因而报错。所以要将check_embedding_ctx_length设为False,再进行向量化。
构建Vectore Store,并将其保存,程序如下所示:
from langchain_chroma import Chroma
vectore_store = Chroma.from_documents(documents=splited_docs,embedding=embedding,persist_directory="./sample_vdb")
# print(vectore_store.search("rag",search_type="similarity"))
retriever = vectore_store.as_retriever(search_type='similarity',k=3)
print(retriever.invoke("RAG"))
RAG-Agent
在完成Vectore Base的构建之后,就可以构建简单的RAG demo了。
首先拉取RAG Prompt
You are an assistant for question-answering tasks. Use the following pieces of retrieved context to answer the question. If you don't know the answer, just say that you don't know. Use three sentences maximum and keep the answer concise.
Question: filler question
Context: filler context
Answer:
然后把他们串行地连接起来,就构成了一个最简单的RAG系统
from langchain import hub
question = "介绍一下RAG系统"
prompt=hub.pull("rlm/rag-prompt")
response = llm.invoke(prompt.format(question=question,context=retriever.invoke(question)))
print(response)
但模型的输出并不尽如人意。
下面将问题分析列举如下:
- 首先是召回的信息质量比较低。传入有关问题的上下文信息明显与RAG系统并无太大关系。
- 其次是提示词的质量仍有优化的空间。 针对召回信息,有这么几个优化的思路:
- 重写检索query,使得其输出更相关的上下文结果,可以使用LLM进行重写。
- 重新构建vectorebase,增加更优质的数据,引入知识图谱结构,分层索引
- 优化prompt
- 引入多路召回、重排序策略
写在后面
这一把跑通了基本的RAG流程,实现细节与RAG官网略有不同,最近半年里langchain的官网教程变化有点大,一方面在汲取新知识,另一方面在恐慌自己的水平远赶不上时代发展。希望下一篇,变得更强。