文章目录
1. 引言 2. 简介 3. 带关键字的查询方案 4. 不带关键字的总结询问 5. 实现代码1. 引言
在调用ChatGPT接口时,我们常常受到4096个字符(token)的限制。这种限制对于处理长文本或者需要对文档进行相似查询和询问的场景来说是一个挑战。然而,通过结合使用langchain和llama_index这两个强大的工具,我们可以克服这个限制,实现对长文本的高效查询和询问。
2. 简介
langchain是一个功能强大的库,它为我们提供了许多方便的工具和模型,包括OpenAI模型。它通过链式调用的方式将这些组件连接在一起,创造出一个连贯的应用程序。同时,langchain还提供了内存组件Memory,可以帮助我们管理之前的聊天消息,以及Indexes和Agents等功能。
LlamaIndex(GPT Index)是一个用于LLM应用的数据框架,集成了langchain及chatgpt相关应用,更便于我们实现结构化数据和高级检索的相关功能。
3. 带关键字的查询方案
基于文档的查询场景有一种情况是,提问的内容与全部文档中的一小块相关,而其他内容无关。比如《百草园到三味书屋》中美女蛇的故事。
若询问中带有关键字,我们推荐使用相似匹配的方式进行筛选有关内容。
拆分文档:首先,我们将长文本拆分成较小的块,并使用OpenAI的Embeddings功能将每个块向量化。 相似性匹配:当用户提出查询时,我们将用户的查询文本也进行向量化。 相似查询:然后,我们遍历已拆分并向量化的文档块,将其与向量化后的查询文本进行相似性比较。通过计算相似度,我们可以找到最相似的文档块。 传递上下文:根据之前的映射关系,我们找到与最相似文档块相对应的原始文档内容。将这个内容作为上下文传递给ChatGPT模型。 询问与回答:最后,ChatGPT(LLM)根据这个上下文,对用户的查询进行回答。总体langchain的内容如下:
对于文本的处理流程如下:
看上去很复杂,但是langchain都替我们做好了。
我们在库源码里面一层层输出中间变量,可以验证它确实是一种相似匹配。
如果不想使用OpenAI的API接口,也可以使用Hugginface上的模型来做相似匹配,从而传入自身的llm模型中。
4. 不带关键字的总结询问
如果用户询问的是“这篇文章写了什么”,这种无关键字的询问,这时候我们不能使用相似查询了,这样会有上下文的缺失。
这里我们推荐使用tree_summarize的方式进行询问。它的工作原理如下:
例如可以生成如下结果:
当然,除此之外还有其他的响应方式,比如简单总结simple_summarize
、轮询迭代的refine
等等,我们修改下方的response_mode
即可。
不同的响应类型ResponseMode可见下篇博客:llama_index中query_engine的response_mode详解
5. 实现代码
import re
import os
from langchain import OpenAI
os.environ["OPENAI_API_KEY"] = 'sk-xx(apikey)'
from llama_index import SimpleDirectoryReader,LLMPredictor,ServiceContext
from llama_index import GPTListIndex, SimpleDirectoryReader
from llama_index.indices.response.type import ResponseMode
from llama_index import SimpleDirectoryReader
from llama_index.readers.schema.base import Document
# 可以直接输入文本
def textToDocuments(text):
documents = [Document(text)]
return documents
# 输入目录后将目录下的所有文件转成Documents对象数组,同上
def fileToDocuments(filePath):
documents = SimpleDirectoryReader(filePath).load_data()
return documents
# 模型名称参数
model_name = "text-davinci-003"
"""
其他模型名称及对应可接收每段的最大token数
"gpt-4": 8192,
"gpt-4-0314": 8192,
"gpt-4-32k": 32768,
"gpt-4-32k-0314": 32768,
"gpt-3.5-turbo": 4096,
"gpt-3.5-turbo-0301": 4096,
"text-ada-001": 2049,
"ada": 2049,
"text-babbage-001": 2040,
"babbage": 2049,
"text-curie-001": 2049,
"curie": 2049,
"davinci": 2049,
"text-davinci-003": 4097,
"text-davinci-002": 4097,
"code-davinci-002": 8001,
"code-davinci-001": 8001,
"code-cushman-002": 2048,
"code-cushman-001": 2048,
"""
llm_predictor = LLMPredictor(llm=OpenAI(temperature=0, model_name=model_name,max_tokens=1800))
service_context = ServiceContext.from_defaults(llm_predictor=llm_predictor)
query_str = "美女蛇的故事是什么?"
response_mode = "compact"
"""
REFINE = "refine"
COMPACT = "compact"
SIMPLE_SUMMARIZE = "simple_summarize"
TREE_SUMMARIZE = "tree_summarize"
GENERATION = "generation"
NO_TEXT = "no_text"
"""
documents = fileToDocuments("./data")
# documents2 = textToDocuments("不必说碧绿的菜畦,光滑的石井栏,高大的皂荚树,紫红的桑椹;也不必说鸣蝉在树叶里长吟,肥胖的黄蜂伏在菜花上,轻捷的叫天子(云雀)忽然从草间直窜向云霄里去了。单是周围的短短的泥墙根一带,就有无限趣味。油蛉在这里低唱,蟋蟀们在这里弹琴。翻开断砖来,有时会遇见蜈蚣;还有斑蝥,倘若用手指按住它的脊梁,便会拍的一声,从后窍喷出一阵烟雾。何首乌藤和木莲藤缠络着,木莲有莲房一般的果实,何首乌有拥肿的根。有人说,何首乌根是有象人形的,吃了便可以成仙,我于是常常拔它起来,牵连不断地拔起来,也曾因此弄坏了泥墙,却从来没有见过有一块根象人样。如果不怕刺,还可以摘到覆盆子,象小珊瑚珠攒成的小球,又酸又甜,色味都比桑椹要好得远。")
index = GPTListIndex.from_documents(documents,service_context=service_context)
query_engine = index.as_query_engine(
response_mode=response_mode
)
response = query_engine.query(query_str)
print(response)