LlamaIndex 工具系统实战用 FunctionTool、QueryEngineTool 与 ToolSpec 为 Agent 扩展能力【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index本文基于 LlamaIndex 官方文档 Tools系统讲解 LlamaIndex Agent 体系中的工具Tool抽象如何将任意 Python 函数、查询引擎、社区 ToolSpec 转换为 Agent 可调用的工具以及用OnDemandLoaderTool、LoadAndSearchToolSpec等 utility tools 解决工具返回数据过大撑爆上下文窗口的问题。读完后你将能够独立完成工具定义、schema 调试与load search模式的落地并理解每种工具背后llama-index-core的具体实现机制。Tool 与 ToolSpecAgent 能力的抽象底座在 LlamaIndex 中合适的工具抽象是构建 agentic 系统的核心。定义一组 Tools 与定义普通 API 接口类似区别在于这些工具的使用者是 AgentLLM而不是人。LlamaIndex 同时提供两层抽象Tool单个可被 LLM 调用的工具ToolSpec内部封装一组函数的工具包围绕某个服务如 Gmail提供多个协同工作的工具。一个关键实践要点当 Agent 或 LLM 使用 function calling 时工具被选中以及为工具填写的调用参数高度依赖工具的name和description——即工具用途与参数的描述。花时间调优这两个字段会显著改变 LLM 调用工具的行为。从源码看工具遵循一个非常通用的接口实现__call__并返回基础元数据name、description、function schema。核心类型定义位于 types.py# llama-index-core/llama_index/core/tools/types.py dataclass class ToolMetadata: description: str name: Optional[str] None fn_schema: Optional[Type[BaseModel]] DefaultToolFnSchema return_direct: bool Falsename工具名发给 LLM 的 function namedescription工具用途描述直接影响 LLM 的选工具决策fn_schemaPydantic 模型描述函数参数若为Noneget_parameters_dict()会退化为默认的{input: str}单参数 schema见 ToolMetadata.get_parameters_dictreturn_direct是否在工具返回后直接结束 Agent 推理循环后文详述。工具调用结果统一封装为ToolOutputtypes.py#L106-L165字段包括blocksContentBlock列表文本、图片等content属性会拼接其中的文本块raw_input/raw_output保留调用参数与原始返回值便于调试与二次加工is_error标记工具执行是否出错。基类BaseTool是抽象的要求实现metadata属性与__call__而实际使用的工具大多继承AsyncBaseTool它要求同时实现同步的call与异步的acall。对于只有同步实现的旧式工具可用adapt_to_async_tool将其包装为异步工具内部通过asyncio.to_thread桥接。完整导出清单可参考 tools/init.py包括FunctionTool、QueryEngineTool、RetrieverTool、QueryPlanTool等。官方文档将 LlamaIndex 的工具归纳为四类FunctionTool把任意用户自定义函数转成工具支持自动推断 schemaQueryEngineTool包装一个已有的 query engine由于 Agent 抽象继承自BaseQueryEngine这类工具也可以包装其他 Agent社区贡献的ToolSpecs围绕单一服务如 Gmail定义一个或多个工具Utility tools包装其他工具处理工具返回大量数据的场景。FunctionTool把任意函数变成工具FunctionTool是任意现成函数同步和异步均支持的简单包装。最基础用法from llama_index.core.agent.workflow import ReActAgent from llama_index.core.tools import FunctionTool def get_weather(location: str) - str: Usfeful for getting the weather for a given location. ... tool FunctionTool.from_defaults( get_weather, # async_fnaget_weather, # optional! ) agent ReActAgent(llmllm, toolstools)用 Annotated 补充参数描述为了让 LLM 更准确地填写参数可以用Annotated类型给参数附加描述from typing import Annotated def get_weather( location: Annotated[ str, A city name and state, formatted like name, state ], ) - str: Useful for getting the weather for a given location. ... tool FunctionTool.from_defaults(get_weather)默认情况下工具名就是函数名docstring 就是工具描述。也可以用name/description参数覆盖tool FunctionTool.from_defaults(get_weather, name..., description...)from_defaults 的完整参数与底层机制FunctionTool.from_defaults的完整签名function_tool.py#L171-L184FunctionTool.from_defaults( fn, # 同步函数可选 nameNone, # 工具名默认取 fn.__name__ descriptionNone, # 工具描述默认由签名 docstring 生成 return_directFalse, fn_schemaNone, # 显式指定参数 schemaPydantic 模型 async_fnNone, # 异步函数版本可选 tool_metadataNone,# 整体提供 ToolMetadata跳过自动推断 callbackNone, # 同步回调可返回 ToolOutput 或 str 覆盖输出 async_callbackNone, partial_paramsNone,# 预先固定的参数会从 schema 中剔除 )结合 FunctionTool 的构造函数可以从源码结构看出几个值得注意的实现细节同步/异步自动互转如果只提供同步fn框架会用sync_to_async将其包装为异步版本通过loop.run_in_executor在线程池中执行避免阻塞事件循环如果只提供协程函数则自动包装出同步入口。因此文档示例中async_fn注释为 optional——两种风格都可用。参数 schema 自动推断from_defaults内部调用 create_schema_from_function 将函数签名转换为 Pydantic 模型。该函数会解析Annotated[type, 描述]将字符串提取为参数description对无注解参数回退为Any类型自动为datetime.date/datetime.datetime/datetime.time添加 JSON Schema 的formatdate/date-time/time支持additional_fields追加字段与ignore_fields剔除字段。docstring 参数说明解析from_defaults会先用 extract_param_docs 从 docstring 中按三种风格提取参数描述——Sphinx 风格:param x: ...、Google 风格x (type): ...、Javadoc 风格param x ...作为未单独标注的参数的兜底描述。也就是说认真写 docstring 也会直接改善发给 LLM 的 schema。Workflow Context 感知构造函数会检查函数签名中是否有Context或Context[SomeType]类型的参数_is_context_param如有则自动在调用时注入 workflow 的Context且该参数不会出现在发给 LLM 的 schema 中也不会泄漏进ToolOutput.raw_input。partial_params 与回调partial_params中预填的参数会从自动推断的 schema 中剔除LLM 无需感知调用时自动合并进kwargscallback/async_callback可以在工具结果返回后覆盖输出返回新的ToolOutput或一段字符串适合做结果压缩、脱敏等后处理。多模态输出call/acall通过_parse_tool_output将原始返回值解析为ContentBlock列表——函数返回TextBlock/ImageBlock等内容块会原样保留返回BaseNode/Document会提取正文文本其他类型则str()化为文本块。这意味着工具函数可以直接返回多模态内容块给多模态 LLM。QueryEngineTool把查询引擎变成工具任何 query engine 都可以用QueryEngineTool变成一个工具from llama_index.core.tools import QueryEngineTool tool QueryEngineTool.from_defaults( query_engine, name..., description... )从 QueryEngineTool 实现 可以看到几个默认行为若不传name默认为query_engine_tool若不传description默认描述是 Useful for running a natural language query against a knowledge base and get back a natural language response.。在多引擎混合的场景下建议显式给出能体现该知识库范围的 name/description帮助 LLM 区分该查哪个引擎call/acall均只接收一个自然语言查询串_get_query_str优先取位置参数args[0]其次取kwargs[input]当两者都没有时若resolve_input_errorsTrue默认会把整个 kwargs 序列化为字符串作为查询而不是抛错——这是一种对 LLM 传参不规范的容错返回值会封装为ToolOutput其中raw_output保留原始Response对象供下游读取来源source_nodes等信息。由于 Agent 抽象本身继承自BaseQueryEngine同样的方式可以把另一个 Agent 包装成工具实现Agent as Tool的多 Agent 组合仓库中 agents_as_tools.ipynb 给出了对应示例。Tool Specs围绕单一服务的工具包LlamaHub 生态提供了丰富的 Tools 与 ToolSpecs。可以把 ToolSpec 理解为捆绑在一起使用的工具集合通常覆盖某个接口/服务的常用操作如 Gmail。安装对应的集成包后直接使用pip install llama-index-tools-googlefrom llama_index.core.agent.workflow import FunctionAgent from llama_index.tools.google import GmailToolSpec tool_spec GmailToolSpec() agent FunctionAgent(llmllm, toolstool_spec.to_tool_list())仓库中llama-index-integrations/tools/目录下可以看到全部社区工具集成例如 llama-index-tools-wikipedia、llama-index-tools-mcp、llama-index-tools-duckduckgo 等可浏览各集成的pyproject.toml与示例了解可用工具清单。从源码看所有 ToolSpec 都继承 BaseToolSpec其工作方式是子类声明spec_functions列表函数名或 (同步名, 异步名) 元组调用to_tool_list()时框架按名称从 spec 实例上取方法自动识别协程函数读取 docstring 生成 description最终统一用FunctionTool.from_defaults批量转换为FunctionTool列表。也就是说编写自定义 ToolSpec 只需要把若干方法写在类里并列出spec_functionsschema 推断、描述生成、同步/异步适配全部复用FunctionTool的机制。BaseToolSpec还提供to_tool_list_async的异步入口。Utility Tools驯服工具返回的大量数据直接查询 API 往往返回海量数据这些数据本身可能就超出 LLM 上下文窗口至少会无谓地推高 token 消耗。为此 LlamaIndex 提供了一组 utility tools。它们与具体服务Gmail、Notion 等解耦用于增强已有工具的能力——本例中是抽象化对任何 API 返回的数据做缓存/索引 按需查询这一通用模式。仓库核心实现了两个主要的 utility tool。OnDemandLoaderToolOnDemandLoaderTool把任意 LlamaIndex 数据加载器BaseReader类变成一个 Agent 可用的工具。工具调用时传入触发load_data所需的全部参数外加一个自然语言查询串。执行时它在单次工具调用内完成三步从数据加载器加载数据 → 建立索引例如向量索引→ 按需查询。pip install llama-index-readers-wikipediafrom llama_index.readers.wikipedia import WikipediaReader from llama_index.core.tools.ondemand_loader_tool import OnDemandLoaderTool tool OnDemandLoaderTool.from_defaults( reader, nameWikipedia Tool, descriptionA tool for loading data and querying articles from Wikipedia, )从 OnDemandLoaderTool 实现 可以看到from_defaults的关键参数readerBaseReader、index_cls默认VectorStoreIndex、index_kwargs透传给from_documents、use_query_str_in_loader是否把查询串也传给load_data、query_str_kwargs_key查询串的参数名默认query_str、fn_schema可显式指定参数 schema。工具的参数 schema 默认由reader.load_data的签名加上query_str字段自动推断生成call的执行链ondemand_loader_tool.py#L141-L154_parse_args先取出query_str并调用self._loader(**kwargs)得到Document列表然后self._index_cls.from_documents(docs)现场建索引index.as_query_engine().query(query_str)查询并返回文本相比自己维护一份持久索引它每次调用都临时建索引牺牲复用性换取任意 API 调用都能临时获得一个索引来吸收上下文压力的简单性若数据可复用LoadAndSearchToolSpec是更合适的选择另有from_tool类方法可以把已有的FunctionTool的底层函数当作 loader 复用ondemand_loader_tool.py#L87-L124。LoadAndSearchToolSpecLoadAndSearchToolSpec接收任意现成 Tool 作为输入。作为 ToolSpec它实现to_tool_list返回两个工具一个load工具和一个searchread工具。load工具执行时调用底层工具并把输出写入索引默认向量索引search工具接收自然语言查询并查询该索引。这对任何默认返回大量数据的 API 端点都很有用——例如 WikipediaToolSpec 默认返回整个维基页面很容易撑爆大多数 LLM 的上下文窗口。pip install llama-index-tools-wikipediafrom llama_index.core.agent.workflow import FunctionAgent from llama_index.core.tools.tool_spec.load_and_search import ( LoadAndSearchToolSpec, ) from llama_index.tools.wikipedia import WikipediaToolSpec wiki_spec WikipediaToolSpec() # Get the search wikipedia tool tool wiki_spec.to_tool_list()[1] # Create the Agent with load/search tools agent FunctionAgent( llmllm, toolsLoadAndSearchToolSpec.from_defaults(tool).to_tool_list() )结合 LoadAndSearchToolSpec 源码有几个实现细节决定了 Agent 与它的交互方式生成的两个工具名为原名与read_原名两个工具的 description 分别由loader_prompt与reader_prompt模板渲染——明确告诉 LLM先调用 load 加载再用 read_xxx 以自然语言查询这对引导 LLM 按正确顺序调用工具至关重要load函数base.py#L125-L146调用被包装工具取其raw_output把字符串/列表/Document等形态统一转换为Document后写入索引已有索引则逐条insert否则from_documents新建最后返回固定提示语 Content loaded! You can now search the information using read_...提示 LLM 下一步该查read函数base.py#L148-L157在未加载数据时会返回明确错误信息 Error: No content has been loaded into the index. You must call {name} first把顺序约束的兜底交给 LLM 自我纠正与OnDemandLoaderTool的关键区别索引在这里是跨调用持久的load一次后可多次search避免重复加载 API 数据两者都支持通过index_cls/index_kwargs定制索引类型与参数。Return Direct工具构造函数中有return_direct选项。若设为True工具返回的结果会直接作为 Agent 的最终响应返回不再经过 Agent 的解读与改写。这有助于降低运行时的 token 与延迟开销也适合设计终结型工具调用即结束推理循环tool QueryEngineTool.from_defaults( query_engine, namename, descriptiondescription, return_directTrue, ) agent FunctionAgent(llmllm, tools[tool]) response await agent.run(question that invokes tool)在上例中query engine tool 被调用后其结果直接作为最终响应返回执行循环随即结束。若使用return_directFalseAgent 会基于对话历史改写工具结果甚至继续发起下一次工具调用。return_direct最终落在ToolMetadata.return_direct字段上types.py#L24-L28由 Agent 的工具调用流程读取。仓库中还提供了 return_direct 使用示例 notebook可结合上文示例对照阅读。Debugging Tools检查真正发给 API 的工具 schema排查 Agent 行为时经常需要确认实际发给 LLM API 的工具定义到底是什么。可以用ToolMetadata上的底层函数获取当前工具 schema——这正是 OpenAI、Anthropic 等 API 实际消费的格式schema tool.metadata.get_parameters_dict() print(schema)从 get_parameters_dict 实现 看当fn_schema存在时它调用fn_schema.model_json_schema()并只保留type、properties、required、definitions、$defs五个键输出标准 JSON Schemafn_schema is None时则返回默认的input: str单参数 schema。因此打印出来的schema就是 LLM 看到的参数契约可据此排查LLM 为什么把参数填错的问题。此外当工具最终通过 OpenAI 风格 API 下发时ToolMetadata.to_openai_tool还会做两项约束types.py#L89-L103description 超过 1024 字符会直接抛错提示请缩短描述或将其移入 prompt——这解释了为何工具描述应精炼工具名会被清洗为符合^[a-zA-Z0-9_-]$的形式非法字符替换为_避免含特殊字符的函数名导致 API 报错。小结LlamaIndex 的工具体系围绕让 LLM 可靠地选对工具、填对参数展开用FunctionTool.from_defaults包装函数时重点打磨name、description、Annotated参数描述与 docstring——它们逐字进入发给 LLM 的 schema用QueryEngineTool把知识查询能力并入工具集时为每个引擎给出可区分的名字与范围描述用LoadAndSearchToolSpec/OnDemandLoaderTool处理API 返回体过大这一高频痛点前者持久索引、后者一次性即席索引按数据复用需求二选一用return_directTrue让关键工具直接终结推理循环节省 token 与延迟调不通时用tool.metadata.get_parameters_dict()对照真实 schema 定位问题。核心实现均可在当前仓库中追踪工具基类与元数据见 types.py函数工具见 function_tool.pyschema 推断见 utils.pyutility tools 见 ondemand_loader_tool.py 与 load_and_search/base.py。【免费下载链接】llama_indexLlamaIndex is the document processing platform for AI项目地址: https://gitcode.com/GitHub_Trending/ll/llama_index创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考