news 2026/9/11 23:16:21

LlamaIndex QueryEngineTool 深度指南:将查询引擎封装为 Agent 工具的完整实战

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
LlamaIndex QueryEngineTool 深度指南:将查询引擎封装为 Agent 工具的完整实战

LlamaIndex QueryEngineTool 深度指南:将查询引擎封装为 Agent 工具的完整实战

【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index

导读

本指南以 LlamaIndex 核心库中llama_index.core.tools.query_engine模块为骨架,系统讲解如何将任意 Query Engine(查询引擎)封装为可被 Agent 直接调用的工具QueryEngineTool,并延伸到带质量评估的EvalQueryEngineTool、工具元数据ToolMetadata、输出结构ToolOutput,以及它们在SubQuestionQueryEngine等复合查询引擎中的典型应用。读完本文,你将掌握工具化封装的全部关键 API、参数语义、输入解析规则与底层调用链,能够把知识库问答能力无缝接入 ReAct 等 Agent 工作流。

说明:docs/api_reference/api_reference/tools/query_engine.md是使用 mkdocstrings 语法::: llama_index.core.tools.query_engine生成的 API 参考页,其完整内容由源码模块 query_engine.py 提供。本文即以此模块及其测试、应用场景为事实依据展开。


一、QueryEngineTool 是什么:查询引擎与 Agent 之间的适配层

在 LlamaIndex 中,Query Engine 负责“接收自然语言查询 → 检索索引 → 合成回答”的完整链路;而 Agent 需要的是“可被 LLM 理解、按函数签名调用”的工具抽象。QueryEngineTool(位于 query_engine.py)正是这两者之间的桥梁:

  • 它继承自AsyncBaseTool(定义于 types.py),既满足 LlamaIndex 自身 Agent 的工具接口,也兼容工具选择(ToolSelection)等调用机制;
  • 它持有BaseQueryEngine实例和ToolMetadata元数据,LLM 通过元数据中的名称与描述来决定“何时调用、怎样调用”;
  • 每次调用等价于执行一次query_engine.query(query_str),并把返回的Response包装成统一的ToolOutput

模块级默认值清晰表达了其定位(query_engine.py):

DEFAULT_NAME = "query_engine_tool" DEFAULT_DESCRIPTION = """Useful for running a natural language query against a knowledge base and get back a natural language response. """

也就是说:不显式指定namedescription时,工具默认名为query_engine_tool,默认描述为“对知识库执行自然语言查询并返回自然语言回复”。实际生产环境中强烈建议自定义更精确的描述——因为 LLM 正是依靠描述来判断何时选用该工具的。

二、快速上手:三行代码把索引变成 Agent 工具

from_defaults工厂方法为入口,封装过程最简形式如下:

from llama_index.core.tools import QueryEngineTool from llama_index.core import VectorStoreIndex index = VectorStoreIndex.from_documents(documents) query_engine = index.as_query_engine() tool = QueryEngineTool.from_defaults( query_engine=query_engine, name="docs_query", description="查询公司内部产品文档知识库,支持自然语言提问。", )

之后即可把tool交给 Agent(如ReActAgentFunctionCallingAgent),或直接手动调用:

# 同步调用 output = tool("什么是 LlamaIndex?") print(output.content) # 异步调用 output = await tool.acall(input="什么是 LlamaIndex?")

工厂方法的完整签名(query_engine.py):

@classmethod def from_defaults( cls, query_engine: BaseQueryEngine, name: Optional[str] = None, description: Optional[str] = None, return_direct: bool = False, resolve_input_errors: bool = True, ) -> "QueryEngineTool"

各参数语义如下:

参数类型默认值作用
query_engineBaseQueryEngine必填被包装的查询引擎,任意实现了query/aquery的引擎均可(索引查询引擎、CustomQueryEngine、复合引擎等)
namestr"query_engine_tool"工具名称,Agent 与 LLM 据此引用该工具
descriptionstr模块默认描述工具的用途说明,是 LLM 决定“是否调用、何时调用”的关键信号
return_directboolFalse若为True,工具返回结果将直接作为 Agent 最终回复,不再进入后续推理循环
resolve_input_errorsboolTrue输入参数不匹配时是否容错解析(详见第五节)

三、核心调用链:call / acall 如何执行一次查询

QueryEngineTool实现了AsyncBaseTool的两个核心方法(query_engine.py):

def call(self, *args: Any, **kwargs: Any) -> ToolOutput: query_str = self._get_query_str(*args, **kwargs) response = self._query_engine.query(query_str) return ToolOutput( content=str(response), tool_name=self.metadata.get_name(), raw_input={"input": query_str}, raw_output=response, ) async def acall(self, *args: Any, **kwargs: Any) -> ToolOutput: query_str = self._get_query_str(*args, **kwargs) response = await self._query_engine.aquery(query_str) return ToolOutput( content=str(response), tool_name=self.metadata.get_name(), raw_input={"input": query_str}, raw_output=response, )

从中可以看到完整的底层调用链:

  1. _get_query_str(*args, **kwargs)从调用参数中提取查询字符串;
  2. 同步路径调用query_engine.query(query_str),异步路径调用query_engine.aquery(query_str)
  3. 结果被封装为ToolOutput,其中:
    • contentstr(response)(响应文本,供 LLM 直接阅读);
    • tool_name为工具元数据中的名称;
    • raw_input记录原始查询输入;
    • raw_output保留完整Response对象(含source_nodes等结构化信息,可供后处理或评估器使用)。

query_enginemetadata均以只读 property 暴露(query_engine.py),便于外部获取底层引擎与元数据。

四、输入解析规则:_get_query_str的三种匹配路径

工具被 LLM 或手动调用时,参数形态并不总是标准化的。_get_query_str(query_engine.py)按优先级处理三种情况:

def _get_query_str(self, *args: Any, **kwargs: Any) -> str: if args is not None and len(args) > 0: query_str = str(args[0]) # 1) 位置参数 elif kwargs is not None and "input" in kwargs: query_str = kwargs["input"] # 2) input 关键字参数 elif kwargs is not None and self._resolve_input_errors: query_str = str(kwargs) # 3) 容错:整体转字符串 else: raise ValueError( "Cannot call query engine without specifying `input` parameter." ) return query_str

三种路径逐一说明:

  • 位置参数tool("hello world")直接把第一个位置参数作为查询串;
  • input关键字tool(input="foo")—— 这是默认函数签名DefaultToolFnSchema的标准字段(见第六节),也是 Agent 函数调用场景下最常见的形态;
  • 容错解析:当传入tool(tmp="hello")这类未匹配参数时,若resolve_input_errors=True,会整体转成字符串"{'tmp': 'hello'}"交给查询引擎,避免工具调用崩溃;若resolve_input_errors=False,则抛出ValueError

这一容错行为在单元测试 test_query_engine_tool.py 中有完整覆盖:测试同时验证了位置参数、input关键字、按fn_schema生成的参数字典、以及resolve_input_errors开启/关闭两种场景下的行为。

五、ToolMetadata:LLM 眼中的工具说明书

每个工具都携带一个ToolMetadata实例(定义于 types.py):

@dataclass class ToolMetadata: description: str name: Optional[str] = None fn_schema: Optional[Type[BaseModel]] = DefaultToolFnSchema return_direct: bool = False

默认函数签名DefaultToolFnSchema只有一个字段:

class DefaultToolFnSchema(BaseModel): """Default tool function Schema.""" input: str

即:默认情况下,LLM 眼中的该工具就是一个“只接受一个input字符串参数”的函数。这解释了为什么_get_query_str专门兼容input关键字。

ToolMetadata还负责把工具描述转换成 LLM 供应商可识别的 JSON Schema:

  • get_parameters_dict()(types.py)基于fn_schema.model_json_schema()生成参数声明,fn_schemaNone时退化为{"input": "input query string", "type": "string"}的兜底结构;
  • to_openai_tool()(types.py)生成 OpenAI 风格的{"type": "function", "function": {...}}工具描述,并做两项校验:描述超过 1024 字符会抛出ValueError;名称通过_sanitize_name清洗为只含[a-zA-Z0-9_-]的合法函数名;
  • 旧版to_openai_function()已被标记@deprecated,应改用to_openai_tool

六、ToolOutput:统一的工具输出结构

ToolOutput(types.py)是所有工具的标准化返回类型:

class ToolOutput(BaseModel): blocks: List[ContentBlock] # 内容块列表(含文本块) tool_name: str raw_input: Dict[str, Any] raw_output: Any is_error: bool = False

构造时content字符串会被自动包装为TextBlock,且contentblocks不能同时传入(否则抛ValueError)。content属性则将多个文本块拼接为纯文本,方便 Agent 直接消费。

七、进阶:EvalQueryEngineTool——带质量评估闸门的工具

EvalQueryEngineTool继承自QueryEngineTool(实现于 eval_query_engine.py),它在查询后追加一步回答相关性评估:只有评估通过,工具结果才会原样返回;评估失败则把内容替换为失败模板:

FAILED_TOOL_OUTPUT_TEMPLATE = ( "Could not use tool {tool_name} because it failed evaluation.\nReason: {reason}" )

关键点:

  • from_defaults中未显式传入evaluator时,默认使用AnswerRelevancyEvaluator(eval_query_engine.py);
  • call/acall在父类执行后调用evaluator.evaluate_response(query, response),依据EvaluationResult.passing决定放行还是替换输出(eval_query_engine.py);
  • 评估者本身可以是任意BaseEvaluator实现,例如自定义的基于 LLM 的评估器。

使用示例:

from llama_index.core.tools.eval_query_engine import EvalQueryEngineTool eval_tool = EvalQueryEngineTool.from_defaults( query_engine=query_engine, name="verified_docs_query", description="查询知识库,且回答必须通过相关性评估。", )

其行为在 test_eval_query_engine_tool.py 中通过 mock 评估器分别验证了“评估通过返回原始输出”与“评估失败返回失败模板”两条路径。这类工具适合对回答质量敏感、需要过滤幻觉式回复的场景。

八、典型应用:作为子问题路由与复合查询的基础单元

QueryEngineTool是多个高级查询引擎的构建单元,最典型的当属SubQuestionQueryEngine(sub_question_query_engine.py)。它的工作机制是:

  1. LLM 依据各工具元数据,把复杂查询拆解为多个子问题(question_gen 阶段);
  2. 每个子问题交给对应的QueryEngineTool执行(self._query_enginestool.metadata.name为键保存引擎映射);
  3. 汇总所有子问题答案与来源节点,交给response_synthesizer合成最终回答。

典型用法(sub_question_query_engine.py):

from llama_index.core.query_engine import SubQuestionQueryEngine from llama_index.core.tools import QueryEngineTool tool1 = QueryEngineTool.from_defaults( query_engine=engine_a, name="engine_a", description="回答关于文档A的问题" ) tool2 = QueryEngineTool.from_defaults( query_engine=engine_b, name="engine_b", description="回答关于文档B的问题" ) engine = SubQuestionQueryEngine.from_defaults( query_engine_tools=[tool1, tool2], use_async=True, ) response = engine.query("比较文档A与文档B中的方案差异")

类似的组合还出现在RouterQueryEngine(router_query_engine.py)、SQL 向量联合查询等场景中,QueryEngineTool因而成为跨引擎编排的通用“接口单元”。上述所有工具类统一从llama_index.core.tools包导出(见 tools/init.py),使用时直接from llama_index.core.tools import QueryEngineTool即可。

九、与 LangChain 互操作:as_langchain_tool

QueryEngineTool提供as_langchain_tool()方法(query_engine.py),可将其转换为 LangChain 的LlamaIndexTool

def as_langchain_tool(self) -> "LlamaIndexTool": from llama_index.core.langchain_helpers.agents.tools import ( IndexToolConfig, LlamaIndexTool, ) tool_config = IndexToolConfig( query_engine=self.query_engine, name=self.metadata.get_name(), description=self.metadata.description, ) return LlamaIndexTool.from_tool_config(tool_config=tool_config)

转换过程中保留查询引擎、工具名称与描述,使你可以在 LangChain 的 Agent 体系中复用同一个 LlamaIndex 查询引擎。

十、设计要点回顾与最佳实践

结合源码与测试,总结QueryEngineTool的使用要点:

  1. 名称与描述是工具的灵魂:LLM 依赖description判断工具适用场景,务必写清“查询什么数据、何时使用”,名称保持唯一且仅含字母、数字、下划线、连字符;
  2. 默认签名为单一input字符串:无需自定义fn_schema即可被函数调用型 Agent 正确调用;若要扩展参数,可自定义fn_schemaBaseModel子类)传入ToolMetadata
  3. return_direct控制是否结束推理:对“查询即答案”的简单检索,置为True可减少一次多余的 LLM 生成;
  4. resolve_input_errors决定容错策略:默认开启可避免参数不匹配导致工具崩溃,代价是查询串可能包含额外噪音;追求严格性时关闭并依赖ValueError快速失败;
  5. 同步/异步一致调用:Agent 异步执行时走acallaquery,同步时走callquery,两条路径输出结构完全一致;
  6. 质量敏感场景叠加评估EvalQueryEngineTool可在回答进入 Agent 上下文前完成相关性过滤,配合failed_tool_output_template自定义失败反馈。

十一、进一步阅读

  • 工具基类与元数据定义:tools/types.py
  • 查询引擎工具核心实现:tools/query_engine.py
  • 带评估的查询引擎工具:tools/eval_query_engine.py
  • 单元测试(输入解析与容错):tests/tools/test_query_engine_tool.py
  • 单元测试(评估闸门):tests/tools/test_eval_query_engine_tool.py
  • 子问题查询引擎应用示例:query_engine/sub_question_query_engine.py
  • 工具包统一导出入口:tools/init.py

【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/11 23:15:38

context-mode 调优指南:让 AI 编程助手告别幻觉代码的上下文管理策略

说实话,我刚开始接触 context-mode 这个概念的时候,完全没把它当回事。那时候觉得,不就是编辑器里的一个上下文切换开关吗?能有多复杂。直到有一次,我在一个大型 monorepo 项目里写重构脚本,AI 编程助手连着…

作者头像 李华
网站建设 2026/9/11 23:14:54

基于Spark的网易云音乐数据分析:从数据清洗到图计算与机器学习实战

简介:一套面向Spark大数据分析毕业设计的网易云音乐数据分析实战资料,涵盖图计算、机器学习歌曲分类预测、评论词云与评论时间段统计等核心模块,适合计算机专业高年级学生、课程设计及毕业设计开发者,以及想快速上手Spark完整项目…

作者头像 李华
网站建设 2026/9/11 23:14:43

YOLOv8道路车流量检测系统:从目标检测到跨帧计数的完整实践

简介:面向交通管理、智慧城市及毕业设计场景的道路车流量检测系统,基于YOLOv8与Python实现,提供一套开箱即用的完整方案。系统可直接运行,适用于需要快速部署实时车辆识别与计数的研究人员、开发者及学生。压缩包共307个文件&…

作者头像 李华
网站建设 2026/9/11 23:14:38

基于Matlab的CNC刀具RUL预测:特征提取与神经网络实时回归实践

简介:面向CNC机床状态监测与剩余寿命(RUL)预测需求,这套基于Matlab的代码资源实现了刀具状态的实时监测与寿命预估功能。系统采用参数化编程,参数可灵活修改,代码注释明细,附赠可直接运行的案例…

作者头像 李华
网站建设 2026/9/11 23:13:58

Java图书管理系统实战:从Servlet/JSP到MySQL事务与部署

简介:基于JavaJSPMySQL实现的Web图书管理系统,定位于帮助Java Web初学者和高校学生理解B/S架构下的完整业务闭环,可作为课程设计、毕业设计或入门实战项目参考。资源压缩包为ZIP格式,体积约4.04MB,围绕图书查询、借阅、…

作者头像 李华