在自然语言处理的应用中,查询引擎和对话引擎是非常重要的组件,它们能帮助我们更好地处理自然语言查询和对话。接下来,我们就深入探讨一下 LlamaIndex 中的查询引擎和对话引擎。
查询引擎
概念与应用场景
查询引擎是一种 RAG(Retrieval Augmented Generation)查询引擎,通过自然语言查询与提问接口获取一次性响应内容。它在自然语言搜索、问答、数据查询分析等场景中有着广泛应用。在实际应用里,查询引擎的使用者可以是人,也可以是其他应用系统。
构造方法
高层 API 快速构造
使用高层 API 构造查询引擎简单快速,不过会牺牲部分可配置性。示例代码如下:
python
query_engine = vector_index.as_query_engine()
response = query_engine.query('xxx')
底层 API 组合构造
使用底层 API 构造可以获得更精细的配置能力。具体步骤如下:
- 构造检索器
python
retriever = VectorIndexRetriever(
index=vector_index,
similarity_tok_k=2
)
- 构造响应生成器
python
reponse_synthesizer = get_response_synthesizer()
- 组合构造查询引擎
python
query_engine = RetieverQueryEngine(
retriever=retriever,
response_synthesizer=reponse_synthesizer
)
深入理解内部结构和运行原理
我们可以从 as_query_engine
这个 API 的实现来了解查询引擎背后的运行机制。
python
def as_query_engine( self,llm:Option[LLMType]=None,**kwargs:Any) ->BaseQueryEngine:
# llm:一个可选的大语言模型(LLM)对象,默认值为 None。
# **kwargs:可变关键字参数,用于接收其他额外的参数。
# 该方法返回一个 BaseQueryEngine 类型的对象。
#note:lazy import
from llama_index.core.query_engine.retriever_query_engine import (RetrieverQueryEngine)
retriever=self.as_retriever(**kwargs)
# 调用当前对象的 as_retriever 方法,将 **kwargs 作为参数传入,得到一个检索器对象。
llm=(
resolve_llm(llm,callback_manager=self._callback_manager)
if llm
else llm_from_settings_or_context(Settings,self.service_context)
)
# 确定要使用的大语言模型(LLM):
# 如果传入了 llm 参数,则调用 resolve_llm 函数对其进行处理,并传入回调管理器。
# 如果没有传入 llm 参数,则调用 llm_from_settings_or_context 函数,根据 Settings 和 service_context 来获取默认的 LLM。
return RetrieverQueryEngine.from_args(
retriever,
llm=llm,
**kwargs
)
# 使用 RetrieverQueryEngine 类的 from_args 方法创建一个查询引擎实例。
# 传入检索器对象、LLM 对象和其他额外的参数。
# 最后返回这个查询引擎实例。
from_args
这种命名方式通常是为了提供一种便捷的方式,通过传入一系列参数来创建类的实例。相较于直接使用类的构造函数(__init__
方法),from_args
类方法可以在创建实例之前进行一些额外的处理,如参数验证、参数转换或者从其他数据源获取参数等。
对话引擎
概念与特点
在需要通过多次对话来满足使用者需求的场景中,就需要用到对话引擎。它需要跟踪过去对话的上下文,以便更好地理解和回答当前问题。对话引擎本质上是查询引擎的有状态版本。
构造方法
使用索引快速构造
python
chat_engine = vector_index.as_chat_engine(chat_mode="condense_question")
与查询引擎不同,对话引擎支持带有上下文的连续对话,存在会话(Session)的概念。我们可以使用 reset
接口来重新开始新的会话:
python
chat_engine.reset()
对话引擎还提供了简单的方法进入连续多轮的交互式对话:
python
chat_engine.chat_repl()
使用底层 API 组合构造
对话引擎通常需要在查询引擎的基础上增加记忆等功能。示例代码如下:
python
# 历史对话记录
custom_chat_history = [
ChatMessage(
role=MessageRole.USER,
content="我们来讨论关于 deepseek 的问题吧"
),
ChatMessage(role=MessageRole.ASSISTANT, content="好的")
]
# 先构造查询引擎,这里省略构造 vector_index 对象
query_engine = vector_index.as_query_engine()
# 再构造对话引擎
chat_engine = CondenseQuestChatEngine.from_defaults(
query_engine=query_engine, # 对话引擎基于查询引擎构造
condense_question_prompt=custom_prompt, # 设置重写问题的 prompt
chat_history=custom_chat_history, # 携带历史对话记录
verbose=True
)
chat_engine.chat_repl()
深入理解内部结构和运行原理
下面是 as_chat_engine
方法的实现:
python
def as_chat_engine(
self,
chat_mode:ChatMode=ChatMode.BEST,
llm:Optional[LLMType] = None,
**kwargs:Any,
) -> BaseChatEngine:
# self: 代表类的实例对象。
# chat_mode: 聊天模式,默认值为 ChatMode.BEST,类型为 ChatMode 枚举。
# llm: 可选的大语言模型,默认值为 None,类型为 LLMType 或 None。
# **kwargs: 可变关键字参数,用于接收其他额外的参数。
# 该方法返回一个 BaseChatEngine 类型的对象。
******
#先构造查询引擎
query_engine=self.as_query_engine(llm=llm,**kwargs)
#再构造对话引擎
if chat_mode in [ChatMode.REACT,ChatMode.OPENAI,ChatMode.BEST]:
query_engine_tool= QueryEngineTool.from_defaults(query_engine=query_engine)
# 使用 QueryEngineTool 的 from_defaults 类方法,将查询引擎封装成一个工具。
return AgentRunner.from_llm(
tools=[query_engine_tool],
llm = llm,
**kwargs
)
# 使用 AgentRunner 的 from_llm 类方法,传入工具列表、大语言模型和其他参数,创建并返回一个 AgentRunner 类型的对话引擎实例。
if chat_mode == ChatMode.QUESTION:
return CondenseQuestionChatEngine.from_defaults(
query_engine=query_engine,
llm = llm,
**kwargs
)
# 如果聊天模式为 QUESTION,使用 CondenseQuestionChatEngine 的 from_defaults 类方法,传入查询引擎、大语言模型和其他参数,创建并返回一个 CondenseQuestionChatEngine 类型的对话引擎实例。
elif chat_mode == ChatMode.CONTEXT:
return ContextChatEngine.from_defaults(
retriever=self.as_retriever(**kwargs),
llm = llm,
**kwargs
)
# 如果聊天模式为 CONTEXT,使用 ContextChatEngine 的 from_defaults 类方法,传入检索器、大语言模型和其他参数,创建并返回一个 ContextChatEngine 类型的对话引擎实例。
elif chat_mode == ChatMode.CONDENSE_PLUS_CONTEXT:
return CondensePlusContextChatEngine.from_defaults(
retriever=self.as_retriever(**kwargs),
llm = llm,
**kwargs
)
# 如果聊天模式为 CONDENSE_PLUS_CONTEXT,使用 CondensePlusContextChatEngine 的 from_defaults 类方法,传入检索器、大语言模型和其他参数,创建并返回一个 CondensePlusContextChatEngine 类型的对话引擎实例。
elif chat_mode == ChatMode.SIMPLE:
return SimpleChatEngine.from_defaults(
retriever=self.as_retriever(**kwargs),
llm = llm,
**kwargs
)
# 如果聊天模式为 SIMPLE,使用 SimpleChatEngine 的 from_defaults 类方法,传入检索器、大语言模型和其他参数,创建并返回一个 SimpleChatEngine 类型的对话引擎实例。
else:
raise ValueError(f"")
不同的对话模式
Simple 模式
在这种模式下,使用者直接与大模型对话,不会使用查询引擎或检索器。
Condense_question 模式
该模式会在理解历史对话记录的基础上,将当前输入的问题重写成一个独立的、具备完整语义的问题,然后通过查询引擎获取答案。其优点是在每次检索上下文之前都会根据历史记忆完善本次输入问题的语义,大大提高了召回知识的相关性,适合 RAG 应用场景中的连续对话;缺点是会增加大模型的调用次数。
Context 对话模式
对话引擎会借助检索器从知识库中检索出相关的上下文,并将其插入 system 提示信息中,然后利用大模型回答输入的问题。这种模式过程简单,响应速度快,但检索出的上下文可能包含无关信息,导致响应生成质量下降。
Condense_plus_context 对话模式
它先完成 condens_question
对话模式的重写过程,结合历史对话记录与当前问题生成新的语义,再完成 context
过程,调用检索器召回新问题相关的上下文,最后利用大模型进行响应生成。这种模式结合了上述两种模式的优点,提高了上下文召回的精确性,但没有采用查询引擎,在响应生成模式上缺乏灵活性。
Agent 对话模式
我们把 react
、openai
、best
这三种对话模式都称为 Agent 对话模式,因为其本质上构造的都是 Agent。它把查询引擎作为一个工具交给 Agent 使用,由 Agent 参考当前的输入问题与历史对话记录,规划并使用工具来输出答案。这三种模式的区别仅在于支持的大模型不一样,如果模型为 OpenAI 的大模型或其他支持函数调用的大模型,则构造的对话引擎为 OpenAIAgent
类型,否则为 ReActAgent
类型。
python
chat_engine = ReActAgent.from_tools(
tools=[query_engine_tool]
)
简单的 Agent 推理过程如下:
- 通过调用大模型推理下一步动作。
- 使用第一步推理出的输入信息调用工具,如查询引擎。在查询引擎响应生成的过程中,会产生
retrieve
(检索相关知识)和第二次模型调用,然后输出大模型调用的结果。 - 在完成工具调用后,大模型根据结果再次进行推理,重复上述步骤或执行结束。
使用输出解析器结构化输出
我们可以使用输出解析器来实现结构化输出。具体步骤如下:
- 定义响应的格式
python
response_schemas = [] # 此处为复杂 json
- 构造 langchain 框架的输出解析器
python
lc_output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
output_parser = LangChainOutPutParser(lc_output_parser)
- 设置大模型使用构造的输出解析器
python
llm = OpenAI(output_parser=output_parser)
以上就是关于 LlamaIndex 中查询引擎和对话引擎的详细介绍,希望能帮助大家更好地理解和使用它们。如果觉得这篇文章对你有帮助,别忘了点赞、收藏并关注我,后续会为大家带来更多相关的知识分享。