Haystack Tools 统一工具抽象解析:从 Tool、ComponentTool 到 Toolset 的完整实战指南
发布时间:2026/9/13 12:03:10来源:尧图网络
Haystack Tools 统一工具抽象解析从 Tool、ComponentTool 到 Toolset 的完整实战指南【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystackHaystack 是面向生产环境的开源 LLM 编排框架其haystack.tools模块提供了贯穿整个框架的统一工具抽象Unified abstractions to represent tools across the framework无论是普通 Python 函数、Haystack 组件还是来自外部服务的动态工具集合都可以被统一表示为可供大语言模型LLM调用的 Tool。本篇技术指南以 docs-website/reference_versioned_docs/version-2.19/haystack-api/tools_api.md 为核心骨架结合当前仓库源码系统讲解Tool、ComponentTool、Toolset三类核心抽象的定义、使用与序列化机制并给出可在 Agent/Pipeline 中直接落地的实操示例。读完本文你将掌握如何把业务函数与 Haystack 组件封装成 LLM 可调用的工具如何通过Toolset对工具进行分组与动态加载以及如何让工具与 Agent 状态State系统协同工作。一、为什么要统一工具抽象在 LLM 应用中工具调用function calling / tool calling让模型能够按需调用外部能力搜索网络、查询数据库、执行计算等。Haystack 用一套统一的数据结构来表示所有这类能力文档将其定位为Unified abstractions to represent tools across the framework。统一抽象带来两个直接好处模型侧只需学习一种协议所有工具都通过name、description、parametersJSON Schema三个字段向 LLM 描述自身Chat Generator 无需区分工具背后是函数还是组件框架侧只需实现一种执行路径无论是函数、组件还是外部服务最终都通过统一的invoke()接口被调用Agent 与ToolInvoker等执行器可以无差别地消费它们。从源码结构看这一抽象的核心实现集中在 haystack/tools/ 目录下包括tool.pyTool数据类、from_function.py函数转工具、component_tool.py组件转工具、toolset.py工具集合等模块公共导出入口为 haystack/tools/init.py。二、Tool最基础的工具数据类2.1 字段定义与语义Tool是一个 dataclass定义在 haystack/tools/tool.py表示Language Models can prepare a call for语言模型可以为其准备一次调用的工具。核心字段如下字段类型说明namestr工具名称。文档特别强调name与description等文本属性的准确定义对 LLM 正确准备调用至关重要descriptionstr工具描述供 LLM 判断何时使用该工具parametersdict[str, Any]定义工具期望参数的 JSON SchemafunctionCallable \| None调用invoke()时同步执行的函数。必须是普通函数协程函数应放入async_functionoutputs_to_stringdict[str, Any] \| None可选定义工具输出如何转换为字符串若提供source仅将指定输出键发送给handler省略source则将整个工具结果发送给handlerinputs_from_statedict[str, str] \| None可选将 Agent 状态State中的键映射到工具参数名。例如{repository: repo}表示把状态中的repository映射到工具的repo参数outputs_to_statedict[str, dict[str, Any]] \| None可选定义工具输出如何映射到状态中的键以及可选的处理函数async_functionCallable \| None可选供invoke_async()等待的协程函数outputs_to_string与outputs_to_state的示例照录自文档# outputs_to_stringsource 提供时只把 docs 这一输出键发送给 format_documents { source: docs, handler: format_documents } # outputs_to_statesource 提供时只把 docs 输出键发送给 custom_handler { documents: {source: docs, handler: custom_handler} } # outputs_to_state省略 source 时整个工具结果发送给 custom_handler { documents: {handler: custom_handler} }2.2 构造时的校验逻辑源码级Tool在__post_init__中执行了严格的输入校验见 haystack/tools/tool.py这意味着错误配置会在工具构造阶段被立即发现而非等到运行时function与async_function至少设置一个否则抛出ValueErrorfunction必须是同步函数若传入协程函数会提示改放到async_function反之async_function必须是async def定义的协程函数parameters必须是通过Draft202012Validator.check_schema校验的合法 JSON Schemaoutputs_to_state中每个配置必须是字典source必须是字符串、handler必须可调用若工具可推断输出集合还会校验source引用的输出确实存在inputs_from_state中的值必须是字符串且引用的参数名必须存在于工具签名或 schema 的properties中防止拼写错误在构造期静默通过。此外若你的工具涉及建立远程连接或加载模型等资源密集型初始化可以覆写warm_up()方法源码 haystack/tools/tool.py该方法会在工具被使用前调用且必须幂等因为在 pipeline/Agent 搭建过程中可能被多次调用。2.3 核心方法tool_specproperty返回供 LLM 使用的工具规格即{name: ..., description: ..., parameters: ...}三元组见 haystack/tools/tool.pyinvoke(**kwargs)以关键字参数同步调用工具底层函数若工具没有同步function例如只提供了async_function则抛出ToolInvocationError底层函数抛出的任何异常也会被包装成带tool_name信息的ToolInvocationError见 haystack/tools/tool.pyinvoke_async(**kwargs)异步版本。若设置了async_function则直接等待它否则通过asyncio.to_thread将同步function派发到工作线程执行见 haystack/tools/tool.pyto_dict()/from_dict()序列化与反序列化。to_dict返回{type: 类全限定名, data: {...}}结构其中函数与 handler 通过serialize_callable序列化为字符串from_dict再通过deserialize_callable还原见 haystack/tools/tool.py。相关异常定义在 haystack/tools/errors.pySchemaGenerationError自动生成 JSON Schema 失败时抛出与ToolInvocationError工具调用失败时抛出携带tool_name属性。三、把普通函数变成 Tool对于最简单的场景你不需要手写Tool的全部字段。Haystack 提供了两个入口create_tool_from_function函数与tool装饰器二者实现在 haystack/tools/from_function.py。3.1 create_tool_from_function函数签名为来自文档与 haystack/tools/from_function.pydef create_tool_from_function( function: Callable, name: Optional[str] None, description: Optional[str] None, inputs_from_state: Optional[dict[str, str]] None, outputs_to_state: Optional[dict[str, dict[str, Any]]] None, ) - Tool文档给出的完整示例from typing import Annotated, Literal from haystack.tools import create_tool_from_function def get_weather( city: Annotated[str, the city for which to get the weather] Munich, unit: Annotated[Literal[Celsius, Fahrenheit], the unit for the temperature] Celsius): A simple function to get the current weather for a location. return fWeather report for {city}: 20 {unit}, sunny tool create_tool_from_function(get_weather) print(tool) Tool(nameget_weather, descriptionA simple function to get the current weather for a location., parameters{ type: object, properties: { city: {type: string, description: the city for which to get the weather, default: Munich}, unit: { type: string, enum: [Celsius, Fahrenheit], description: the unit for the temperature, default: Celsius, }, } }, functionfunction get_weather at 0x7f7b3a8a9b80)从输出可以看出 Schema 的生成规则city的Annotated元数据the city for which to get the weather被用作参数描述Literal[Celsius, Fahrenheit]被转换为enum枚举约束默认值被保留为default字段函数 docstring 自动成为工具描述。3.2 生成原理与约束源码级在 haystack/tools/from_function.py 中schema 的生成流程为通过inspect.signature遍历函数参数若参数名出现在inputs_from_state的值中或参数类型为State含Optional[State]或类型包含Callable则跳过该参数——前两者由 Agent 在运行时注入后者无法用 Pydantic 生成 JSON Schema参数缺少类型注解时抛出ValueError无默认值的参数以...Ellipsis标记为必填用 Pydantic 的create_model动态建模型并生成model_json_schema()失败时抛出SchemaGenerationError通过_remove_title_from_schema移除冗余的title关键字Pydantic 无法编程式地阻止生成它们再把Annotated描述写入对应属性最终用这些信息构造Tool实例若原函数是async def协程函数则自动放入async_function字段。类型约束文档明确说明函数所有参数必须有类型提示输入类型期望为 Python 基本类型str, int, float, bool, list, dict, tuple其他类型可能可用但不保证。参数注解为typing.Annotated时其元数据将用作参数描述。参数说明name工具名称缺省时使用函数名description工具描述缺省时使用函数 docstring若想刻意留空传入空字符串inputs_from_state可选将状态键映射到工具参数名如{repository: repo}outputs_to_state可选定义工具输出到状态与消息处理的映射例如{ documents: {source: docs, handler: custom_handler}, message: {source: summary, handler: format_summary} }3.3 tool 装饰器tool是create_tool_from_function的语法糖支持带参数与不带参数两种用法签名见 haystack/tools/from_function.pytool # 不带参数 def my_function(): ... tool(namecustom_name) # 带参数 def my_function(): ...文档给出的完整示例from typing import Annotated, Literal from haystack.tools import tool tool def get_weather( city: Annotated[str, the city for which to get the weather] Munich, unit: Annotated[Literal[Celsius, Fahrenheit], the unit for the temperature] Celsius): A simple function to get the current weather for a location. return fWeather report for {city}: 20 {unit}, sunny print(get_weather) Tool(nameget_weather, descriptionA simple function to get the current weather for a location., parameters{...}) # 与 create_tool_from_function 完全一致的 Schema装饰后函数名直接指向Tool实例。装饰器的参数与create_tool_from_function对齐name、description、inputs_from_state、outputs_to_state、outputs_to_string无参使用时返回Tool带参使用时返回一个接受函数并生成Tool的装饰函数。四、ComponentTool把 Haystack 组件变成 Tool4.1 设计动机与特性许多场景下你要暴露给 LLM 的能力本身就是现成的 Haystack 组件。ComponentToolhaystack/tools/component_tool.py让组件可以直接作为工具被 LLM 使用其关键特性文档原文从组件输入 Socket 自动生成 LLM 工具调用 Schema输入 Socket 源自组件run方法的签名与类型注解对组件输入做类型转换与校验支持的类型dataclass、dataclass 列表、基本类型str, int, float, bool, dict、基本类型列表工具名自动从组件类名生成描述自动从组件 docstring 提取。4.2 构造函数def __init__( component: Component, name: Optional[str] None, description: Optional[str] None, parameters: Optional[dict[str, Any]] None, *, outputs_to_string: Optional[dict[str, Union[str, Callable[[Any], str]]]] None, inputs_from_state: Optional[dict[str, str]] None, outputs_to_state: Optional[dict[str, dict[str, Union[str, Callable]]]] None ) - None各参数说明component要包装成工具的 Haystack 组件实例name可选工具名缺省时取组件类名的 snake_case 形式例如类名SerperDevWebSearch→ 工具名serper_dev_web_searchdescription可选描述缺省时取组件 docstringparameters可选的 JSON Schema不提供时回退到组件run方法签名推导出的参数outputs_to_string/inputs_from_state/outputs_to_state语义与Tool同名参数完全一致。异常传入对象不是 Haystack 组件时抛TypeError组件已被加入 pipeline、或 Schema 生成失败时抛ValueError源码见 haystack/tools/component_tool.py。源码还提供_get_valid_inputs/_get_valid_outputs覆写见 haystack/tools/component_tool.py从而对inputs_from_state与outputs_to_state引用到不存在的组件输入/输出 Socket 的情况做构造期校验。4.3 完整用法示例文档给出的完整示例基于 SerperDev 网络搜索组件serperdev-haystack集成包from haystack import component, Pipeline from haystack.tools import ComponentTool from haystack.components.websearch import SerperDevWebSearch from haystack.utils import Secret from haystack.components.tools.tool_invoker import ToolInvoker from haystack.components.generators.chat import OpenAIChatGenerator from haystack.dataclasses import ChatMessage # 创建 SerperDev 搜索组件 search SerperDevWebSearch(api_keySecret.from_env_var(SERPERDEV_API_KEY), top_k3) # 从组件创建工具 tool ComponentTool( componentsearch, nameweb_search, # 可选缺省为 serper_dev_web_search descriptionSearch the web for current information on any topic # 可选缺省为组件 docstring ) # 用 OpenAIChatGenerator 与 ToolInvoker 搭建 pipeline pipeline Pipeline() pipeline.add_component(llm, OpenAIChatGenerator(modelgpt-4o-mini, tools[tool])) pipeline.add_component(tool_invoker, ToolInvoker(tools[tool])) # 连接组件 pipeline.connect(llm.replies, tool_invoker.messages) message ChatMessage.from_user(Use the web search tool to find information about Nikola Tesla) # 运行 pipeline result pipeline.run({llm: {messages: [message]}}) print(result)注意ToolInvoker组件位于独立的工具执行组件模块haystack.components.tools.tool_invoker负责接收 LLM 生成的工具调用消息并实际执行工具。在最新版源码中SerperDevWebSearch等第三方搜索组件随serperdev-haystack集成包发布导入路径为haystack_integrations.components.websearch.serperdev核心仓库内已不包含其实现请以你安装的集成包版本为准。4.4 Schema 生成与类型转换原理源码级_create_tool_parameters_schemahaystack/tools/component_tool.py遍历组件的输入 Socket被inputs_from_state引用的输入、Callable类型输入、State类型输入会被跳过每个输入从组件run方法 docstring 提取参数描述docstring_parser解析见 haystack/tools/parameters_schema_utils.py缺省时使用Input name for the component.必填参数用...标记可选参数保留默认值dataclass 类型会被递归转换为 Pydantic 模型_resolve_type/_dataclass_to_pydantic_model见 haystack/tools/parameters_schema_utils.pyChatMessage这类带下划线字段的 dataclass 有专门处理。调用阶段component_invokerhaystack/tools/component_tool.py会把 LLM 传来的参数按输入 Socket 类型逐一转换_convert_param目标类型有from_dict时优先用from_dict还原 dataclass包括列表内元素否则回退到 PydanticTypeAdapter.validate_python做校验。若组件支持异步__haystack_supports_async__为 True还会自动挂载调用run_async的异步执行器见 haystack/tools/component_tool.py。ComponentTool同样实现了tool_spec、invoke、to_dict、from_dict其warm_up()会委托给内部组件并保证只预热一次见 haystack/tools/component_tool.py。五、Toolset工具集合与动态加载基类5.1 两大用途Toolsethaystack/tools/toolset.py是一个 dataclass表示可以作为一个内聚单元使用和管理的相关工具的集合服务于两个主要目的分组管理相关工具把相关工具组织到单一集合便于在 pipeline/Agent 中作为一个整体管理与使用作为动态工具加载的基类通过子类化Toolset可以实现从 OpenAPI URL、MCP 服务器等外部来源动态加载工具的实现。5.2 分组示例手动构造 Tool文档示例——手动构造两个数学工具并用Toolset分组交给ToolInvoker或 Chat Generator 使用from haystack.tools import Tool, Toolset from haystack.components.tools import ToolInvoker # 定义数学函数 def add_numbers(a: int, b: int) - int: return a b def subtract_numbers(a: int, b: int) - int: return a - b # 用完整 Schema 创建工具 add_tool Tool( nameadd, descriptionAdd two numbers, parameters{ type: object, properties: { a: {type: integer}, b: {type: integer} }, required: [a, b] }, functionadd_numbers ) subtract_tool Tool( namesubtract, descriptionSubtract b from a, parameters{ type: object, properties: { a: {type: integer}, b: {type: integer} }, required: [a, b] }, functionsubtract_numbers ) # 创建数学工具集 math_toolset Toolset([add_tool, subtract_tool]) # 交给 ToolInvoker 或 ChatGenerator 组件使用 invoker ToolInvoker(toolsmath_toolset)该示例展示了Tool的手工构造方式parameters需要完整的 JSON Schema含type、properties、requiredfunction绑定实际执行逻辑。相比tool装饰器手工构造给予了对 Schema 的完全控制。5.3 动态加载示例子类化文档示例——通过子类化Toolset实现动态工具加载from haystack.core.serialization import generate_qualified_class_name from haystack.tools import Tool, Toolset from haystack.components.tools import ToolInvoker class CalculatorToolset(Toolset): A toolset for calculator operations. def __init__(self): tools self._create_tools() super().__init__(tools) def _create_tools(self): # 这些 Tool 实例仅为示意而静态定义。 # 真实场景中你应该在这里从外部来源动态加载工具。 tools [] add_tool Tool( nameadd, descriptionAdd two numbers, parameters{ type: object, properties: {a: {type: integer}, b: {type: integer}}, required: [a, b], }, functionlambda a, b: a b, ) multiply_tool Tool( namemultiply, descriptionMultiply two numbers, parameters{ type: object, properties: {a: {type: integer}, b: {type: integer}}, required: [a, b], }, functionlambda a, b: a * b, ) tools.append(add_tool) tools.append(multiply_tool) return tools def to_dict(self): return { type: generate_qualified_class_name(type(self)), data: {}, # 工具是动态定义的无需序列化数据 } classmethod def from_dict(cls, data): return cls() # 反序列化时重新动态创建工具 # 创建动态工具集并交给 ToolInvoker calculator_toolset CalculatorToolset() invoker ToolInvoker(toolscalculator_toolset)5.4 集合接口与运行机制源码级Toolset实现了完整的集合接口__iter__、__contains__、__len__、__getitem__表现得就像一个Tool列表见 haystack/tools/toolset.py因此与期望可迭代工具的组件如ToolInvoker、Haystack 的 Chat Generator、Agent完全兼容。各方法要点__iter__返回遍历Tool实例的迭代器__contains__同时支持按Tool实例与按工具名字符串判断tool_name in toolset__len__返回工具数量__getitem__按索引取工具__add__支持与Tool、Toolset或Tool列表拼接返回包含全部工具的新Toolset遇到重复工具名抛ValueError类型不符抛TypeErroradd(tool)追加单个Tool同样做重复名检查__post_init__初始化时校验——直接传入单个Tool会抛TypeError需用列表Toolset([tool])并检查初始工具集中是否有重名工具warm_up()默认逐个调用工具自身的warm_up()子类可覆写为建立共享连接、动态加载工具并赋给self.tools等见 haystack/tools/toolset.py。该方法可能被多次调用实现必须保证幂等例如以if self._client is not None: return守卫get_selectable_tools()返回可用于按名选择如Agent.run(tools[tool_name])的工具列表会先触发warm_up()以保证懒加载的工具也可被选择spawn()返回本Toolset或其隔离副本供单次运行使用带有运行态run-scoped state的子类如SearchableToolset会覆写它避免并发运行互相污染。ToolsType Sequence[Tool | Toolset] | Toolset见 haystack/tools/tool_types.py允许在tools参数中混用Tool与Toolset既可传单个Toolset也可传包含两者任意混合的序列。5.5 序列化建议面向子类实现者文档对to_dict()给出明确建议见 haystack/tools/toolset.py默认实现适合工具静态解析的场景若子类从外部来源MCP 服务器、OpenAPI URL 或本地 OpenAPI 规范动态解析工具应序列化端点描述符而非Tool实例本身。这样做可以保留Toolset的动态特性、避免序列化潜在的大量Tool对象带来的开销同时确保反序列化时能准确重建工具——即使自上次序列化后工具已被修改或移除。否则可能加载到过时或错误的工具配置引发错误或意外行为。实现动态加载子类时的三条要点文档原文在__init__方法中执行动态加载若工具是动态定义的覆写to_dict()与from_dict()若工具来自外部来源序列化端点描述符而非工具实例。六、序列化与反序列化让工具可持久化工具系统整体遵循 Haystack 的序列化约定to_dict()输出{type: 类全限定名, data: {...}}from_dict()据此重建对象。Tool的to_dict使用asdict展开字段并通过serialize_callable把function、async_function与各类handler序列化为可恢复的字符串形式见 haystack/tools/tool.pyComponentTool的to_dict会额外通过component_to_dict序列化内部组件见 haystack/tools/component_tool.pyToolset的to_dict逐个序列化其成员工具from_dict通过import_class_by_name按类型名动态导入并调用对应类的from_dict见 haystack/tools/toolset.py。对于 Agent/Chat Generator 等组件中的tools参数haystack/tools/serde_utils.py 提供serialize_tools_or_toolset与deserialize_tools_or_toolset_inplace可在保留 Tool/Toolset 边界的前提下对混合列表进行整体序列化并校验反序列化得到的类确实继承自Tool或Toolset。七、在 Agent 中组合使用Tool、Toolset与状态系统配合使用时inputs_from_state与outputs_to_state承担了桥接职责inputs_from_state让工具从 Agent 状态中取值作为参数如把状态里的repository注入工具的repo参数这样 LLM 无需重复生成该参数outputs_to_state让工具结果写回状态配合handler对结果做格式化或后处理。一个典型组合参考 haystack/tools/toolset.py 中的示例写法是用tool定义业务工具用Toolset打包再整体交给 Agentfrom typing import Annotated from haystack.tools import tool, Toolset from haystack.components.agents import Agent from haystack.components.generators.chat import OpenAIChatGenerator tool def add(a: Annotated[int, first number], b: Annotated[int, second number]) - int: Add two numbers. return a b tool def subtract(a: Annotated[int, first number], b: Annotated[int, second number]) - int: Subtract b from a. return a - b math_toolset Toolset([add, subtract]) agent Agent(chat_generatorOpenAIChatGenerator(), toolsmath_toolset)此时 Agent 内的 Chat Generator 会基于每个工具的tool_specname/description/parameters生成调用ToolInvoker或 Agent 内部执行器通过统一的invoke()执行工具从而形成模型决策 → 统一执行 → 结果回写的完整闭环。八、小结Haystack 的tools模块用三个层次的抽象覆盖了工具的全部使用场景抽象适用场景核心文件Tool手写 JSON Schema 绑定函数的通用工具表示haystack/tools/tool.pycreate_tool_from_function/tool从普通含异步Python 函数自动生成工具haystack/tools/from_function.pyComponentTool把 Haystack 组件包装成工具自动生成 Schema 并做类型转换haystack/tools/component_tool.pyToolset工具分组、动态加载OpenAPI/MCP 等外部来源的基类haystack/tools/toolset.py所有工具都遵循统一的tool_spec描述协议、统一的invoke/invoke_async执行协议、统一的to_dict/from_dict序列化协议并通过inputs_from_state/outputs_to_state与 Agent 状态系统深度集成。源码测试覆盖在 test/tools/ 目录下如test_from_function.py、test_component_tool.py、test_toolset.py可以进一步阅读以了解各边界行为的验证细节。在构建 RAG、Agent 或多工具工作流时这套统一抽象是让 LLM 安全、可控地调用外部能力的基础设施。【免费下载链接】haystackOpen-source AI orchestration framework for building context-engineered, production-ready LLM applications. Design modular pipelines and agent workflows with explicit control over retrieval, routing, memory, and generation. Built for scalable agents, RAG, multimodal applications, semantic search, and conversational systems.项目地址: https://gitcode.com/GitHub_Trending/ha/haystack创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
网站建设高端定制企业官网