摘要
QueryEngine是LlamaIndex中负责处理用户查询并生成响应的核心组件。它作为索引和用户之间的桥梁,协调检索、后处理和响应生成等各个环节。理解QueryEngine的工作原理并学会如何自定义QueryEngine,对于构建高效、智能的LLM应用至关重要。本文将深入探讨QueryEngine的架构、工作流程、内置类型以及自定义方法。
正文
1. 引言
在前面的博客中,我们详细介绍了LlamaIndex的各种索引类型,包括VectorStoreIndex、TreeIndex、KnowledgeGraphIndex等。这些索引类型为数据组织和检索提供了强大的基础,但要将这些能力转化为实际应用,还需要一个关键组件——QueryEngine。QueryEngine是LlamaIndex中负责处理用户查询并生成响应的核心组件,它协调检索、后处理和响应生成等各个环节,是整个系统的大脑。
2. QueryEngine基础概念
2.1 什么是QueryEngine
QueryEngine是LlamaIndex中负责处理用户查询请求的组件。它接收用户的自然语言查询,通过协调检索器(Retriever)、后处理器(Postprocessor)和响应合成器(ResponseSynthesizer)等组件,最终生成准确、相关的回答。
2.2 QueryEngine的核心特点
- 协调作用:协调检索、后处理和响应生成等各个环节
- 可扩展性:支持多种内置类型和自定义实现
- 灵活性:可以根据不同需求配置不同的组件组合
- 易用性:提供简洁的API接口
3. QueryEngine工作原理
3.1 QueryEngine架构
QueryEngine的整体架构如下所示:
- 查询接收:接收用户的自然语言查询
- 检索阶段:通过检索器从索引中检索相关节点
- 后处理阶段:对检索结果进行优化和过滤
4.4 响应生成:通过响应合成器生成最终回答
3.2 QueryEngine工作流程
4. 内置QueryEngine类型
4.1 RetrieverQueryEngine
RetrieverQueryEngine是最常用的QueryEngine类型,它结合了检索器和响应合成器:
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core import VectorStoreIndex
# 创建索引
index = VectorStoreIndex.from_documents(documents)
# 创建RetrieverQueryEngine
query_engine = RetrieverQueryEngine.from_args(index)
# 执行查询
response = query_engine.query("什么是人工智能?")
print(response)
4.2 TransformQueryEngine
TransformQueryEngine允许在查询前对查询进行转换:
from llama_index.core.query_engine import TransformQueryEngine
from llama_index.core.indices.query.query_transform import HyDEQueryTransform
# 创建HyDE查询转换器
hyde_transform = HyDEQueryTransform(include_original=True)
# 创建TransformQueryEngine
transform_query_engine = TransformQueryEngine(
query_engine,
query_transform=hyde_transform
)
4.3 MultiStepQueryEngine
MultiStepQueryEngine支持多步骤查询处理:
from llama_index.core.query_engine import MultiStepQueryEngine
# 创建多步骤查询引擎
multi_step_engine = MultiStepQueryEngine.from_defaults(
query_engine=query_engine,
index_summary="索引包含人工智能相关文档"
)
4.4 RouterQueryEngine
RouterQueryEngine可以根据查询内容路由到不同的子查询引擎:
from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.tools import QueryEngineTool
# 创建不同的查询引擎工具
tool1 = QueryEngineTool.from_defaults(
query_engine=tech_query_engine,
description="用于技术相关问题"
)
tool2 = QueryEngineTool.from_defaults(
query_engine=business_query_engine,
description="用于商业相关问题"
)
# 创建路由查询引擎
router_engine = RouterQueryEngine.from_defaults(
selector="llm",
query_engine_tools=[tool1, tool2]
)
5. 自定义QueryEngine
5.1 继承BaseQueryEngine
from llama_index.core.base.base_query_engine import BaseQueryEngine
from llama_index.core.base.response.schema import Response
class CustomQueryEngine(BaseQueryEngine):
"""自定义查询引擎示例"""
def __init__(self, custom_retriever, custom_synthesizer):
self.custom_retriever = custom_retriever
self.custom_synthesizer = custom_synthesizer
super().__init__()
def _query(self, query_bundle):
"""实现查询逻辑"""
# 1. 使用自定义检索器检索节点
nodes = self.custom_retriever.retrieve(query_bundle)
# 2. 对节点进行自定义处理
processed_nodes = self._process_nodes(nodes)
# 3. 使用自定义合成器生成响应
response = self.custom_synthesizer.synthesize(query_bundle, processed_nodes)
return response
def _process_nodes(self, nodes):
"""自定义节点处理逻辑"""
# 实现节点处理逻辑
# 例如:过滤、排序、增强等
return nodes
async def _aquery(self, query_bundle):
"""异步查询实现"""
# 实现异步查询逻辑
pass
# 使用自定义查询引擎
custom_engine = CustomQueryEngine(custom_retriever, custom_synthesizer)
response = custom_engine.query("自定义查询示例")
5.2 自定义响应合成
from llama_index.core.response_synthesizers import BaseSynthesizer
from llama_index.core.base.response.schema import Response
class CustomResponseSynthesizer(BaseSynthesizer):
"""自定义响应合成器"""
def __init__(self, llm, custom_prompt_template=None):
self.llm = llm
self.prompt_template = custom_prompt_template or self._default_prompt()
super().__init__()
def _default_prompt(self):
return """基于以下信息回答问题:
信息:{context_str}
问题:{query_str}
请用中文回答,保持简洁明了:
"""
def synthesize(self, query_bundle, nodes):
"""合成响应"""
# 合并节点内容
context_str = "\n".join([node.text for node in nodes])
# 构建提示词
prompt = self.prompt_template.format(
context_str=context_str,
query_str=query_bundle.query_str
)
# 调用LLM生成响应
response_text = self.llm.complete(prompt)
return Response(
response=response_text.text,
source_nodes=nodes
)
async def asynthesize(self, query_bundle, nodes):
"""异步合成响应"""
context_str = "\n".join([node.text for node in nodes])
prompt = self.prompt_template.format(
context_str=context_str,
query_str=query_bundle.query_str
)
response_text = await self.llm.acomplete(prompt)
return Response(
response=response_text.text,
source_nodes=nodes
)
# 使用自定义响应合成器
from llama_index.core import Settings
custom_synthesizer = CustomResponseSynthesizer(Settings.llm)
5.3 自定义检索增强
from llama_index.core.retrievers import BaseRetriever
from llama_index.core.schema import NodeWithScore
class CustomRetriever(BaseRetriever):
"""自定义检索器"""
def __init__(self, index, custom_weights=None):
self.index = index
self.custom_weights = custom_weights or {}
super().__init__()
def _retrieve(self, query_bundle):
"""实现检索逻辑"""
# 使用索引的默认检索器
base_retriever = self.index.as_retriever()
nodes = base_retriever.retrieve(query_bundle)
# 应用自定义权重调整
weighted_nodes = self._apply_custom_weights(nodes)
# 过滤低相关性节点
filtered_nodes = self._filter_low_relevance(weighted_nodes)
return filtered_nodes
def _apply_custom_weights(self, nodes):
"""应用自定义权重"""
weighted_nodes = []
for node in nodes:
# 获取节点的原始分数
original_score = node.score or 0.0
# 应用自定义权重调整
node_type = node.metadata.get("type", "default")
weight = self.custom_weights.get(node_type, 1.0)
# 计算加权分数
weighted_score = original_score * weight
weighted_nodes.append(
NodeWithScore(node=node.node, score=weighted_score)
)
return weighted_nodes
def _filter_low_relevance(self, nodes, threshold=0.5):
"""过滤低相关性节点"""
return [node for node in nodes if node.score >= threshold]
async def _aretrieve(self, query_bundle):
"""异步检索实现"""
# 实现异步检索逻辑
pass
# 使用自定义检索器
custom_retriever = CustomRetriever(
index,
custom_weights={"important": 1.5, "normal": 1.0, "low": 0.5}
)
6. QueryEngine配置选项
6.1 响应模式配置
# 不同的响应模式
query_engines = {
# 紧凑模式 - 适合短回答
"compact": index.as_query_engine(
response_mode="compact"
),
# 树形摘要模式 - 适合长文档
"tree_summarize": index.as_query_engine(
response_mode="tree_summarize"
),
# 简单求和模式 - 适合数值计算
"simple_summarize": index.as_query_engine(
response_mode="simple_summarize"
),
# 逐步细化模式 - 适合复杂问题
"refine": index.as_query_engine(
response_mode="refine"
),
# 上下文压缩模式 - 适合长上下文
"compact_and_refine": index.as_query_engine(
response_mode="compact_and_refine"
)
}
6.2 检索参数配置
# 配置检索参数
query_engine = index.as_query_engine(
# 检索的节点数量
similarity_top_k=5,
# 相似度阈值
similarity_cutoff=0.7,
# 是否使用混合搜索
vector_store_query_mode="hybrid",
# 混合搜索权重
alpha=0.5,
# 是否启用流式响应
streaming=False
)
7. 实际应用案例
7.1 多语言问答系统
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core import Settings
class MultilingualQueryEngine:
"""多语言问答系统"""
def __init__(self, indexes):
self.indexes = indexes # 不同语言的索引
self.language_detector = self._init_language_detector()
def _init_language_detector(self):
"""初始化语言检测器"""
import langdetect
return langdetect.detect
def query(self, query_str):
"""多语言查询"""
# 检测查询语言
try:
query_language = self.language_detector(query_str)
except:
query_language = "en" # 默认英语
# 选择对应语言的索引
target_index = self.indexes.get(query_language, self.indexes.get("en"))
if not target_index:
raise ValueError("未找到合适的索引")
# 创建查询引擎
query_engine = target_index.as_query_engine(
response_mode="tree_summarize"
)
# 执行查询
response = query_engine.query(query_str)
# 如果需要,翻译响应
if query_language != "zh":
response = self._translate_response(response, query_language)
return response
def _translate_response(self, response, target_language):
"""翻译响应"""
# 实现翻译逻辑
return response
# 使用示例
# 假设我们有中英文索引
indexes = {
"zh": chinese_index,
"en": english_index
}
multilingual_qe = MultilingualQueryEngine(indexes)
chinese_response = multilingual_qe.query("人工智能的发展趋势是什么?")
english_response = multilingual_qe.query("What are the trends in AI development?")
7.2 专业领域问答系统
from llama_index.core.query_engine import RouterQueryEngine
from llama_index.core.tools import QueryEngineTool
class DomainExpertQueryEngine:
"""专业领域问答系统"""
def __init__(self, domain_indexes):
self.domain_indexes = domain_indexes
self.router_engine = self._create_router_engine()
def _create_router_engine(self):
"""创建路由查询引擎"""
# 为每个领域创建查询引擎工具
tools = []
for domain, index in self.domain_indexes.items():
query_engine = index.as_query_engine(
response_mode="tree_summarize",
similarity_top_k=3
)
tool = QueryEngineTool.from_defaults(
query_engine=query_engine,
description=f"用于{domain}领域的问题回答"
)
tools.append(tool)
# 创建路由引擎
router_engine = RouterQueryEngine.from_defaults(
selector="llm",
query_engine_tools=tools
)
return router_engine
def query(self, query_str):
"""专业领域查询"""
# 可以添加预处理逻辑
processed_query = self._preprocess_query(query_str)
# 使用路由引擎查询
response = self.router_engine.query(processed_query)
# 可以添加后处理逻辑
final_response = self._postprocess_response(response)
return final_response
def _preprocess_query(self, query_str):
"""查询预处理"""
# 添加领域关键词
# 标准化术语
# 纠正拼写错误
return query_str
def _postprocess_response(self, response):
"""响应后处理"""
# 格式化输出
# 添加引用信息
# 生成相关推荐
return response
# 使用示例
domain_indexes = {
"医疗": medical_index,
"法律": legal_index,
"金融": finance_index,
"技术": tech_index
}
expert_qe = DomainExpertQueryEngine(domain_indexes)
response = expert_qe.query("心脏病的常见症状有哪些?")
7.3 对话式问答系统
from llama_index.core.query_engine import RetrieverQueryEngine
from llama_index.core.memory import ChatMemoryBuffer
class ConversationalQueryEngine:
"""对话式问答系统"""
def __init__(self, index, chat_history_limit=5):
self.index = index
self.chat_history = ChatMemoryBuffer.from_defaults(
token_limit=3000
)
self.query_engine = self._create_query_engine()
def _create_query_engine(self):
"""创建查询引擎"""
return self.index.as_query_engine(
response_mode="refine",
streaming=True,
memory=self.chat_history
)
def query(self, query_str):
"""对话式查询"""
# 将聊天历史添加到查询中
enhanced_query = self._enhance_query_with_history(query_str)
# 执行查询
response = self.query_engine.query(enhanced_query)
# 更新聊天历史
self._update_chat_history(query_str, response)
return response
def _enhance_query_with_history(self, query_str):
"""使用聊天历史增强查询"""
# 获取最近的聊天历史
history_messages = self.chat_history.get_all()
if not history_messages:
return query_str
# 构建上下文增强的查询
history_context = "\n".join([
f"用户: {msg.content}" if msg.role == "user" else f"助手: {msg.content}"
for msg in history_messages[-3:] # 只使用最近3轮对话
])
enhanced_query = f"""
基于以下对话历史回答问题:
对话历史:
{history_context}
当前问题:
{query_str}
"""
return enhanced_query
def _update_chat_history(self, query_str, response):
"""更新聊天历史"""
# 添加用户查询
self.chat_history.put(
{"role": "user", "content": query_str}
)
# 添加助手响应
self.chat_history.put(
{"role": "assistant", "content": str(response)}
)
def clear_history(self):
"""清空聊天历史"""
self.chat_history.reset()
# 使用示例
conv_qe = ConversationalQueryEngine(index)
# 多轮对话
response1 = conv_qe.query("什么是机器学习?")
print(response1)
response2 = conv_qe.query("它有哪些应用?") # 基于上一轮对话历史
print(response2)
response3 = conv_qe.query("请详细介绍一下监督学习") # 继续基于对话历史
print(response3)
8. 性能优化策略
8.1 缓存机制
from functools import lru_cache
import hashlib
class CachedQueryEngine:
"""带缓存的查询引擎"""
def __init__(self, base_query_engine, cache_size=100):
self.base_query_engine = base_query_engine
self.cache_size = cache_size
self._query_cache = {}
def query(self, query_str):
"""带缓存的查询"""
# 生成查询的哈希键
query_hash = hashlib.md5(query_str.encode()).hexdigest()
# 检查缓存
if query_hash in self._query_cache:
print("从缓存返回结果")
return self._query_cache[query_hash]
# 执行实际查询
response = self.base_query_engine.query(query_str)
# 存储到缓存
self._query_cache[query_hash] = response
# 限制缓存大小
if len(self._query_cache) > self.cache_size:
# 移除最旧的缓存项
oldest_key = next(iter(self._query_cache))
del self._query_cache[oldest_key]
return response
def clear_cache(self):
"""清空缓存"""
self._query_cache.clear()
# 使用示例
cached_qe = CachedQueryEngine(query_engine, cache_size=50)
response = cached_qe.query("什么是人工智能?")
8.2 异步处理
import asyncio
from concurrent.futures import ThreadPoolExecutor
class AsyncQueryEngine:
"""异步查询引擎"""
def __init__(self, query_engine):
self.query_engine = query_engine
self.executor = ThreadPoolExecutor(max_workers=4)
async def query_async(self, query_str):
"""异步查询"""
loop = asyncio.get_event_loop()
response = await loop.run_in_executor(
self.executor,
self.query_engine.query,
query_str
)
return response
async def batch_query_async(self, queries):
"""批量异步查询"""
tasks = [
self.query_async(query_str)
for query_str in queries
]
responses = await asyncio.gather(*tasks)
return responses
# 使用示例
async_qe = AsyncQueryEngine(query_engine)
# 单个异步查询
async def single_query():
response = await async_qe.query_async("人工智能的应用有哪些?")
print(response)
# 批量异步查询
async def batch_queries():
queries = [
"什么是机器学习?",
"深度学习和机器学习有什么区别?",
"神经网络的基本原理是什么?"
]
responses = await async_qe.batch_query_async(queries)
for i, response in enumerate(responses):
print(f"问题 {i+1}: {response}")
# 运行异步查询
# asyncio.run(single_query())
# asyncio.run(batch_queries())
9. 故障排除和最佳实践
9.1 常见问题及解决方案
-
响应质量不佳:
# 改进响应质量的方法 def improve_response_quality(index): # 1. 调整检索参数 query_engine = index.as_query_engine( similarity_top_k=10, # 增加检索节点数 similarity_cutoff=0.6, # 调整相似度阈值 response_mode="refine" # 使用逐步细化模式 ) # 2. 自定义提示词 from llama_index.core.prompts import PromptTemplate custom_prompt = PromptTemplate( "请基于以下信息详细回答问题,要求:\n" "1. 回答准确且完整\n" "2. 使用中文回答\n" "3. 如信息不足,请说明\n" "信息:{context_str}\n" "问题:{query_str}\n" "回答:" ) # 3. 配置自定义提示词 query_engine = index.as_query_engine( text_qa_template=custom_prompt ) return query_engine -
查询速度慢:
# 优化查询速度 def optimize_query_speed(index): # 1. 减少检索节点数 fast_engine = index.as_query_engine( similarity_top_k=3, response_mode="compact" ) # 2. 使用缓存 cached_engine = CachedQueryEngine(fast_engine) # 3. 预过滤节点 def create_filtered_engine(index, filter_criteria): retriever = index.as_retriever( filters=filter_criteria ) return RetrieverQueryEngine(retriever=retriever) return cached_engine
9.2 最佳实践建议
-
合理选择响应模式:
def select_appropriate_response_mode(document_length, query_type): """根据文档长度和查询类型选择合适的响应模式""" if document_length < 1000: if query_type == "factual": return "compact" else: return "simple_summarize" elif document_length < 10000: return "refine" else: return "tree_summarize" # 使用示例 response_mode = select_appropriate_response_mode( document_length=5000, query_type="analytical" ) query_engine = index.as_query_engine(response_mode=response_mode) -
监控和日志记录:
import logging from datetime import datetime class MonitoredQueryEngine: """带监控的查询引擎""" def __init__(self, base_query_engine): self.base_query_engine = base_query_engine self.logger = logging.getLogger(__name__) def query(self, query_str): """带监控的查询""" start_time = datetime.now() try: response = self.base_query_engine.query(query_str) end_time = datetime.now() duration = (end_time - start_time).total_seconds() # 记录查询日志 self.logger.info(f"查询成功: {query_str[:50]}... | 耗时: {duration:.2f}秒") return response except Exception as e: end_time = datetime.now() duration = (end_time - start_time).total_seconds() # 记录错误日志 self.logger.error(f"查询失败: {query_str[:50]}... | 耗时: {duration:.2f}秒 | 错误: {str(e)}") raise # 配置日志 logging.basicConfig(level=logging.INFO) # 使用监控查询引擎 monitored_qe = MonitoredQueryEngine(query_engine) response = monitored_qe.query("什么是人工智能?")
10. 高级功能探索
10.1 查询转换和增强
from llama_index.core.indices.query.query_transform import HyDEQueryTransform
from llama_index.core.query_engine import TransformQueryEngine
class AdvancedQueryTransformer:
"""高级查询转换器"""
def __init__(self, base_query_engine):
self.base_query_engine = base_query_engine
self.transformers = self._init_transformers()
def _init_transformers(self):
"""初始化查询转换器"""
return {
"hyde": HyDEQueryTransform(include_original=True),
"step_back": StepBackQueryTransform(),
"custom": self._create_custom_transformer()
}
def _create_custom_transformer(self):
"""创建自定义查询转换器"""
from llama_index.core.indices.query.query_transform.base import BaseQueryTransform
class CustomQueryTransform(BaseQueryTransform):
def _transform(self, query_bundle, **kwargs):
# 自定义查询转换逻辑
# 例如:添加上下文、扩展关键词等
transformed_query = query_bundle.query_str + " 请详细说明"
return query_bundle.copy(update={"query_str": transformed_query})
return CustomQueryTransform()
def query_with_transform(self, query_str, transform_type="hyde"):
"""使用查询转换进行查询"""
if transform_type not in self.transformers:
raise ValueError(f"不支持的转换类型: {transform_type}")
# 创建转换查询引擎
transform_engine = TransformQueryEngine(
self.base_query_engine,
query_transform=self.transformers[transform_type]
)
return transform_engine.query(query_str)
# 使用示例
advanced_transformer = AdvancedQueryTransformer(query_engine)
response1 = advanced_transformer.query_with_transform("机器学习算法", "hyde")
response2 = advanced_transformer.query_with_transform("深度学习原理", "custom")
10.2 多索引融合查询
class MultiIndexFusionQueryEngine:
"""多索引融合查询引擎"""
def __init__(self, indexes):
self.indexes = indexes
self.fusion_weights = self._init_fusion_weights()
def _init_fusion_weights(self):
"""初始化融合权重"""
# 可以根据索引类型、数据质量等设置权重
return {name: 1.0 for name in self.indexes.keys()}
def query(self, query_str):
"""多索引融合查询"""
# 从各个索引获取结果
index_results = {}
for name, index in self.indexes.items():
query_engine = index.as_query_engine(similarity_top_k=3)
try:
result = query_engine.query(query_str)
index_results[name] = result
except Exception as e:
print(f"索引 {name} 查询失败: {e}")
# 融合结果
fused_response = self._fuse_results(index_results, query_str)
return fused_response
def _fuse_results(self, index_results, query_str):
"""融合多个索引的结果"""
if not index_results:
return "未获取到任何结果"
if len(index_results) == 1:
return list(index_results.values())[0]
# 简单的文本融合方法
fused_text = "综合多个数据源的结果:\n\n"
for name, result in index_results.items():
weight = self.fusion_weights.get(name, 1.0)
fused_text += f"来自 {name} (权重: {weight}):\n{result}\n\n"
# 使用LLM生成最终融合回答
from llama_index.core import Settings
fusion_prompt = f"""
基于以下多个来源的信息,综合回答问题:
问题:{query_str}
信息:
{fused_text}
请提供一个综合、准确、简洁的回答:
"""
response = Settings.llm.complete(fusion_prompt)
return response.text
# 使用示例
indexes = {
"技术文档": tech_index,
"用户手册": manual_index,
"FAQ": faq_index
}
fusion_qe = MultiIndexFusionQueryEngine(indexes)
response = fusion_qe.query("如何配置网络连接?")
总结
QueryEngine作为LlamaIndex中负责处理用户查询并生成响应的核心组件,扮演着至关重要的角色。通过本文的详细介绍,我们了解了QueryEngine的工作原理、内置类型、自定义方法以及在实际应用中的使用技巧。
QueryEngine的主要优势包括:
- 模块化设计:通过检索器、后处理器和响应合成器的组合,提供灵活的配置选项
- 丰富的内置类型:支持RetrieverQueryEngine、TransformQueryEngine、MultiStepQueryEngine等多种类型
- 强大的可扩展性:支持自定义实现,满足特定需求
- 性能优化支持:提供缓存、异步处理等优化机制
在实际应用中,我们需要根据具体场景选择合适的QueryEngine类型和配置:
- 简单问答场景:使用RetrieverQueryEngine配合合适的响应模式
- 复杂查询场景:使用TransformQueryEngine或MultiStepQueryEngine
- 多源数据场景:使用RouterQueryEngine或自定义融合查询引擎
- 对话式应用:结合内存组件实现上下文感知的查询
通过合理使用QueryEngine,我们可以构建出高效、智能的LLM应用,为用户提供准确、相关的信息服务。随着大语言模型技术的不断发展,QueryEngine将在更多领域发挥重要作用,成为构建智能应用的核心组件。
2583

被折叠的 条评论
为什么被折叠?



