
1. 案例目标本案例展示了如何使用LlamaIndex的工作流(Workflow)系统实现一个子问题查询引擎(Sub Question Query Engine)。该系统能够将复杂问题分解为多个子问题并行处理这些子问题然后将所有子问题的答案合并成一个全面的回答。这种方法特别适用于需要从多个角度或数据源获取信息才能完整回答的复杂查询场景。与LlamaIndex内置的子问题查询引擎相比这个基于工作流的实现具有更强的灵活性特别是能够使用多个工具来回答子问题而内置版本只能使用单个工具。2. 技术栈与核心依赖llama-index-corellama-index-llms-openaillama-index-embeddings-openaillama-index-readers-filellama-index-utils-workflowOpenAI GPT模型ReAct智能体向量存储索引这个实现主要依赖LlamaIndex的核心工作流系统结合OpenAI的语言模型和嵌入模型以及ReAct智能体框架来处理子问题。向量存储索引用于高效检索和查询文档内容。3. 环境配置安装依赖!pip install llama-index-core llama-index-llms-openai llama-index-embeddings-openai llama-index-readers-file llama-index-utils-workflow环境变量设置import os from google.colab import userdata os.environ[OPENAI_API_KEY] userdata.get(openai-key)注意需要设置有效的OpenAI API密钥才能运行此示例。在Colab环境中可以使用userdata.get()安全地获取API密钥。4. 案例实现4.1 事件定义class QueryEvent(Event): question: str class AnswerEvent(Event): question: str answer: str4.2 SubQuestionQueryEngine工作流类工作流包含三个主要步骤query步骤接收原始查询、LLM和工具列表使用LLM将原始问题分解为子问题为每个子问题创建QueryEventsub_question步骤接收QueryEvent创建ReAct智能体处理子问题生成AnswerEventcombine_answers步骤收集所有AnswerEvent使用LLM合并所有答案生成最终响应4.3 工作流可视化draw_all_possible_flows( SubQuestionQueryEngine, filenamesub_question_query_engine.html )4.4 数据准备与工具创建folder ./data/sf_budgets/ files os.listdir(folder) query_engine_tools [] for file in files: year file.split( - )[0] # 为每个年份的预算文档创建向量索引和查询工具 index_persist_path f./storage/budget-{year}/ if os.path.exists(index_persist_path): storage_context StorageContext.from_defaults( persist_dirindex_persist_path ) index load_index_from_storage(storage_context) else: documents SimpleDirectoryReader( input_files[folder file] ).load_data() index VectorStoreIndex.from_documents(documents) index.storage_context.persist(index_persist_path) engine index.as_query_engine() query_engine_tools.append( QueryEngineTool( query_engineengine, metadataToolMetadata( namefbudget_{year}, descriptionfInformation about San Franciscos budget in {year}, ), ) )4.5 工作流运行engine SubQuestionQueryEngine(timeout120, verboseTrue) llm OpenAI(modelgpt-4o) result await engine.run( llmllm, toolsquery_engine_tools, queryHow has the total amount of San Franciscos budget changed from 2016 to 2023?, ) print(result)5. 案例效果当用户询问旧金山2016年至2023年的预算总额变化情况时系统会将问题分解为8个子问题每个年份一个为每个子问题创建ReAct智能体并行查询对应年份的预算数据收集所有子问题的答案使用LLM将所有答案合并成一个全面的回答展示预算随时间的变化趋势输出示例从2016年到2023年旧金山预算总额发生了显著变化。2016年预算为96亿美元。2017年增加到101.069亿美元2018年进一步增加到126.59306亿美元。2019年预算为123亿美元。2020年预算大幅上升达到153.73192亿美元以千美元计约合154亿美元。2021年预算为141.66496亿美元2022年为145.5006亿美元。到2023年预算已增加到146亿美元。总体而言从2016年到2023年旧金山的预算从96亿美元增长到146亿美元。6. 案例实现思路6.1 工作流设计模式该案例采用了事件驱动的工作流设计模式通过定义不同类型的事件(QueryEvent和AnswerEvent)来控制工作流的执行流程。这种设计使得系统能够灵活地处理并行任务和异步操作。StartEvent→query步骤→QueryEvent(多个)→sub_question步骤→AnswerEvent(多个)→combine_answers步骤→StopEvent6.2 问题分解策略系统使用LLM将复杂问题分解为多个子问题提示模板要求LLM以纯JSON格式返回子问题列表。这种设计确保了输出的一致性和可解析性。6.3 并行处理机制通过为每个子问题创建独立的QueryEvent系统能够并行处理多个子问题。每个子问题由一个ReAct智能体处理该智能体可以根据问题选择最合适的工具。6.4 答案合并策略使用ctx.collect_events()方法等待所有AnswerEvent完成后系统将所有子问题的答案合并并使用LLM生成一个连贯、全面的最终回答。6.5 工具管理每个年份的预算数据被转换为独立的查询工具ReAct智能体可以根据子问题的内容选择使用哪个工具。这种设计使得系统能够处理跨多个数据源的复杂查询。7. 扩展建议7.1 智能问题分解当前的问题分解完全依赖于LLM可以考虑添加更智能的分解策略例如基于问题类型的模板化分解根据可用工具动态调整子问题添加子问题优先级排序7.2 结果验证与优化添加子问题答案的一致性检查实现子问题答案的置信度评分对于低置信度的答案可以触发重新查询或人工干预7.3 性能优化实现子问题处理的优先级队列添加缓存机制避免重复查询相同子问题优化工具选择策略减少不必要的工具调用7.4 用户体验增强添加问题分解过程的可视化展示提供中间结果的实时反馈允许用户调整问题分解策略或合并方式7.5 应用场景扩展多语言支持处理非英语查询多模态查询结合文本、图像等多种数据源领域特定优化如医疗、法律等专业领域的查询处理8. 总结子问题查询引擎工作流是一个强大而灵活的复杂查询处理系统它通过将复杂问题分解为多个子问题、并行处理这些子问题然后合并答案的方式来提供全面的回答。与传统的单一查询方法相比这种方法能够更好地处理需要多角度、多数据源信息的复杂查询。该实现展示了LlamaIndex工作流系统的几个关键优势模块化设计通过事件驱动的方式将复杂的查询处理流程分解为独立的步骤并行处理能力能够同时处理多个子问题提高查询效率工具集成可以灵活集成多种查询工具增强系统的适应能力可扩展性工作流系统易于扩展和定制可以根据特定需求进行调整这个案例为构建复杂的查询处理系统提供了一个很好的基础框架可以应用于各种需要处理复杂查询的场景如知识库问答、数据分析、决策支持等。通过适当的扩展和优化它可以成为一个强大的智能问答系统核心组件。