
1. 项目概述为什么工具系统是Agent的“双手”在AI Agent的开发领域我们常常把大语言模型LLM比作Agent的“大脑”它负责思考、规划和决策。但一个只有大脑的Agent就像一位满腹经纶的哲学家被禁锢在房间里空有思想却无法对现实世界产生任何实质影响。工具系统Tool System正是打破这层禁锢的关键它赋予了Agent与外部世界交互、执行具体任务的能力堪称Agent的“双手”。最近在深入研读Hermes Agent的源码时我对这套工具系统的设计感触颇深。它远不止是简单地将API封装成函数调用那么简单。一个设计精良的工具系统需要解决工具的动态发现、统一调度、安全执行、结果解析以及异常处理等一系列复杂问题。Hermes Agent在这方面的实现提供了一套非常清晰、可扩展的架构范式。无论是让Agent去查询天气、控制智能家居还是执行一段代码、操作数据库其底层支撑都是这套工具系统。理解它就等于掌握了让Agent从“纸上谈兵”到“真刀真枪”实干的核心钥匙。接下来我将结合源码带你层层剥开Hermes Agent工具系统的设计奥秘。我们会从最核心的抽象基类开始看它如何定义工具的“标准动作”再到工具注册与发现机制理解Agent如何“知道”自己有哪些工具可用最后深入到执行引擎看看这双“手”是如何精准、安全地完成每一个指令的。无论你是想基于Hermes进行二次开发还是希望为自己的Agent项目设计一套类似的系统相信这些探秘都能给你带来直接的启发和可复用的代码思路。2. 工具系统的核心架构与设计哲学2.1 工具的抽象定义BaseTool类解析一切复杂系统的起点往往是一个精炼的抽象。在Hermes Agent中这个起点就是hermes.agent.tools.base_tool模块下的BaseTool类。它定义了一个工具所必须拥有的最小契约是所有具体工具的“蓝图”。首先一个工具最核心的属性是它的“身份”和“能力说明书”。在BaseTool中这通过几个关键属性实现name: 工具的唯一标识符。这就像是工具的名字Agent在思考时通过这个名字来指代和调用它。命名需要清晰、无歧义通常采用snake_case格式例如get_current_weather,execute_python_code。description: 工具的功能描述。这是给LLM看的“说明书”至关重要。一段好的描述应该清晰说明工具的功能、输入参数的含义、以及返回值的格式。LLM依靠这段描述来决定在什么场景下调用这个工具。例如一个数据库查询工具的描述可能会是“根据给定的SQL查询语句在指定的数据库连接上执行并返回结果。输入应为包含‘query’和‘connection_string’键的JSON对象。”parameters与return_schema: 分别定义了工具的输入参数和返回值的JSON Schema。这是实现结构化调用和解析的基石。通过Schema系统可以在调用前验证输入是否符合预期也能将工具返回的复杂对象如字典、列表规范化为LLM易于理解的格式。其次工具的核心行为被定义在run方法中。这是工具被调用时真正执行的逻辑。BaseTool的run方法是一个异步方法async def这是为了兼容现代IO密集型操作如网络请求、数据库查询。它接收参数执行任务并返回结果。任何具体的工具无论是调用外部API还是执行本地计算最终都需要实现这个run方法。注意在设计自己的工具时务必确保description足够精准。一个常见的误区是描述过于简略比如只写“查询天气”这会导致LLM无法准确判断何时使用、如何传参。好的描述应该像给一个新手程序员写API文档一样详尽。2.2 工具注册与发现机制ToolRegistry的奥秘单个工具能力有限一个强大的Agent需要装备一个丰富的“工具箱”。如何管理这些工具让Agent能方便地发现和调用它们这就是ToolRegistry工具注册表的职责。在Hermes中它通常以单例或全局可访问的形式存在充当所有工具的中央目录。注册表的核心功能是“注册”和“获取”。注册 (register_tool)当一个工具类被定义后需要向注册表进行“报到”。这个过程通常会将工具类实例化或使用类本身并以工具的name为键将工具对象存储在一个内部字典中。在Hermes的源码中你可能会看到使用装饰器register_tool的方式这使工具的定义和注册一气呵成代码非常优雅。发现与获取 (get_tool,get_tools)当Agent的“大脑”LLM决定要使用某个工具时它会向注册表请求该工具。注册表根据工具名从字典中快速查找并返回工具对象。此外注册表还提供一个方法如get_tools_descriptions来获取所有已注册工具的“说明书”即name和description的列表这个列表会被拼接到给LLM的提示词Prompt中告知LLM当前可用的工具集。这种集中式管理的优势非常明显解耦工具的开发者只需关心工具本身的逻辑无需关心它如何被Agent集成。动态性工具可以在程序运行时动态地注册或注销从而实现插件化架构。例如可以根据用户配置加载不同的工具模块。统一管理便于实现工具级别的功能如调用日志记录、性能监控、权限校验等这些都可以在注册表或工具调用链中统一实现。2.3 工具执行引擎从指令到动作的桥梁有了工具定义和注册表接下来就需要一个“执行引擎”来串联整个调用流程。这个引擎是Agent核心逻辑的一部分它负责与LLM协作完成“思考-调用-执行-反馈”的循环。执行引擎的工作流程在Hermes的源码中通常体现在Agent主循环或特定的Step中可以概括为以下几步意图解析LLM根据用户请求和当前上下文判断是否需要调用工具。如果需要它会生成一个结构化的调用请求。在现代LLM中这通常表现为一个特殊的“函数调用”Function Calling格式的响应其中包含了要调用的工具名和具体的参数。工具查找与验证执行引擎从LLM的响应中提取工具名和参数然后向ToolRegistry请求获取对应的工具对象。获取到工具后引擎会利用工具定义的parametersschema 对传入的参数进行验证确保类型、格式符合要求这是一个重要的安全性和鲁棒性保障。安全执行这是核心步骤。引擎调用工具的run方法。但这里的调用并非简单的函数执行一个好的引擎会在此处包裹多层处理超时控制为工具执行设置超时防止某个工具调用卡死整个Agent。异常捕获优雅地处理工具执行中可能抛出的各种异常如网络错误、API限流并将错误信息转化为LLM能理解的格式而不是让整个程序崩溃。上下文注入有些工具的执行可能需要访问当前的会话上下文或Agent的状态引擎需要负责将这些信息传递给工具。结果处理与反馈工具执行完成后会返回一个结果。这个结果可能是字符串、字典、列表等任何格式。执行引擎需要对这个结果进行适当的处理有时需要简化例如将一个庞大的JSON摘要成关键信息有时需要格式化转换为纯文本。处理后的结果连同工具调用成功的状态被反馈给LLM作为下一轮“思考”的输入。实操心得在执行引擎中实现完善的错误处理和日志记录至关重要。我建议为每一个工具调用记录以下信息工具名、输入参数、开始时间、结束时间、执行状态成功/失败、返回结果或错误信息。这些日志对于后期调试Agent的决策逻辑、分析工具性能瓶颈有不可估量的价值。3. 内置工具集深度剖析与实战3.1 网络交互工具WebSearchTool与WebRequestsTool让Agent获取实时信息网络交互工具是必不可少的。Hermes Agent通常会内置或通过示例提供这类工具。我们来看两种典型设计WebSearchTool这类工具并不直接爬取网页而是封装了一个搜索引擎的API如Serper API、Google Search API。它的run方法接收一个搜索查询词query然后调用后端API获取搜索结果的摘要列表。其返回的Schema通常会包含一个结果数组每个结果有标题、链接和摘要片段。这个工具极大地扩展了Agent的知识时效性使其能回答关于最新事件、新闻、股价等问题。WebRequestsTool这是一个更底层、更强大的工具。它本质上是一个可控的HTTP客户端。其参数可能包括method: HTTP方法如 GET, POST。url: 请求地址。headers: 请求头。body: 请求体对于POST/PUT。 它的run方法会使用如aiohttp或requests库来发送请求并返回状态码和响应内容。这个工具赋予了Agent与任意Web API交互的能力从获取特定API数据到提交表单几乎无所不能。踩坑记录使用WebRequestsTool需要格外注意安全性和可控性。绝对不能允许用户直接输入任意URL让Agent去访问这会导致服务器端请求伪造SSRF等严重安全风险。在实践中必须对该工具进行限制例如限制可访问的域名白名单、禁止访问内网IP、对请求体大小和超时时间进行严格限制。最好是在Agent的决策层面由LLM根据明确指令生成具体的、安全的请求参数而不是让用户“告诉Agent一个网址去访问”。3.2 代码执行工具PythonREPLTool的设计与安全考量这是最具威力也最危险的一类工具。PythonREPLTool允许Agent在沙箱环境中执行Python代码片段。想象一下Agent可以自己写代码来计算复杂数学问题、处理字符串、甚至进行数据分析这能力提升是质的飞跃。在Hermes的源码实现中这个工具的核心是创建一个安全的、隔离的执行环境。它不会直接使用主进程的exec或eval而是通常会采用以下一种或多种策略使用标准库ast.literal_eval对于仅需计算简单表达式的场景这是一个安全的起点但它只能处理基本的Python字面量结构。使用沙箱模块如RestrictedPython这是一个更专业的方案它通过编译时和运行时的检查禁用不安全的模块如os,sys,subprocess和危险的操作如文件读写、网络访问。在独立子进程中运行最彻底的隔离方式。工具将代码传递给一个全新的Python子进程执行子进程运行在严格的权限控制下如无网络、只读文件系统并通过管道或队列获取结果。即使代码有问题也只会影响子进程。其run方法接收一个code字符串参数。执行后它会捕获代码的标准输出stdout和最终表达式的值作为结果返回。同时必须捕获任何异常如语法错误、运行时错误并将错误信息清晰返回以便LLM能理解哪里出错了并尝试修复代码。重要警告在任何生产环境或向公众开放的服务中启用代码执行工具都必须经过极其严格的安全评审。即使有沙箱历史上也存在过许多沙箱逃逸漏洞。一个基本原则是永远不要相信由LLM生成、且可能受用户输入影响的代码。仅在高度受控的内部环境或明确知晓风险的研究场景中使用此类工具。3.3 文件系统工具在权限笼中跳舞与代码执行类似允许Agent操作文件系统读、写、列表文件也充满了风险但又是许多自动化任务所必需的功能。Hermes中可能提供的FileReadTool和FileWriteTool是这类工具的代表。一个安全的文件系统工具设计必须遵循“最小权限原则”和“路径隔离原则”工作目录限制工具不应允许访问任意路径。通常会为Agent分配一个专属的、隔离的工作目录sandbox directory。所有文件操作都被限制在这个目录及其子目录下。在工具实现中任何用户提供的文件路径都需要与这个基础工作目录进行拼接并检查规范化后的路径是否仍然位于工作目录之内防止../../../这样的路径遍历攻击。操作权限细分FileReadTool只开放读权限FileWriteTool只开放写权限。并且写工具可能需要更复杂的策略例如禁止覆盖某些关键文件或对写入内容进行病毒扫描。输入输出标准化FileReadTool的返回结果可能是文件内容的字符串对于大文件可能需要实现分页或摘要功能。FileWriteTool则需要接收文件路径和内容两个参数。在源码中你会看到大量的路径安全校验代码。这是工具系统从“可用”到“可靠”的关键一步。这些工具使得Agent能够处理用户上传的文档、保存生成的报告或读取配置文件实用性极强。4. 自定义工具开发全流程指南4.1 从零开始创建一个新工具理解了核心架构后创建自定义工具就变得有章可循。假设我们需要为Hermes Agent添加一个“查询指定城市当前时间”的工具。下面我们一步步来实现第一步定义工具类我们创建一个新的Python文件例如world_clock_tool.py。首先导入必要的基类然后定义我们的工具类。from hermes.agent.tools.base_tool import BaseTool from pydantic import BaseModel, Field import pytz from datetime import datetime from typing import Optional # 首先定义工具的输入参数模型。这有助于结构化验证和生成Schema。 class WorldClockInput(BaseModel): city_name: str Field(description城市名称例如Shanghai, New York, London。) timezone: Optional[str] Field(defaultNone, description可选的IANA时区名称例如 Asia/Shanghai。如果提供将优先于city_name使用。) # 然后定义工具类本身。 class WorldClockTool(BaseTool): 一个用于查询世界各地当前时间的工具。 # 工具的唯一标识 name: str get_world_time # 给LLM看的详细描述 description: str ( 根据提供的城市名称或IANA时区名称查询该地的当前日期和时间。 如果同时提供city_name和timezone将以timezone为准。 ) # 将Pydantic模型作为参数schema args_schema: type[BaseModel] WorldClockInput # 一个简单的城市到时区的映射字典实际项目可使用更完整的数据库 _city_to_tz { shanghai: Asia/Shanghai, beijing: Asia/Shanghai, new york: America/New_York, london: Europe/London, tokyo: Asia/Tokyo, # ... 可以扩展更多 } async def run(self, city_name: str, timezone: Optional[str] None) - str: 工具的核心执行逻辑。 Args: city_name: 城市名 timezone: 时区名 Returns: 格式化后的时间字符串 # 确定时区 tz_str timezone if not tz_str: tz_str self._city_to_tz.get(city_name.lower()) if not tz_str: # 如果城市名不在映射中尝试一个通用的回退这里简单返回错误实际可更智能 return f错误无法识别城市 {city_name}。请提供已知城市或直接的IANA时区名称如Asia/Shanghai。 try: # 获取时区对象 tz pytz.timezone(tz_str) # 获取该时区的当前时间 now datetime.now(tz) # 格式化为易读的字符串 formatted_time now.strftime(%Y-%m-%d %H:%M:%S %Z%z) return f{city_name} ({tz_str}) 的当前时间是{formatted_time} except pytz.exceptions.UnknownTimeZoneError: return f错误未知的时区名称 {tz_str}。请提供有效的IANA时区名称。第二步注册工具为了让Agent能发现这个工具我们需要在合适的地方注册它。通常Hermes会有一个工具初始化的地方。# 在你的主程序或工具初始化模块中 from hermes.agent.tools.registry import tool_registry from .world_clock_tool import WorldClockTool # 实例化并注册工具 world_clock_tool WorldClockTool() tool_registry.register_tool(world_clock_tool) # 或者如果你的BaseTool和装饰器支持也可以用装饰器方式更简洁 # tool_registry.register # class WorldClockTool(BaseTool): # ...第三步验证工具注册后你可以通过注册表获取工具描述看看它是否已正确集成。tools_desc tool_registry.get_tools_descriptions() print(tools_desc) # 你应该能看到 get_world_time 的描述信息。至此一个功能完整、描述清晰、具备基本错误处理的自定义工具就创建完成了。当用户问“纽约现在几点了”LLM就能调用这个get_world_time工具并传入city_name: New York来获取答案。4.2 高级技巧工具依赖注入与上下文感知简单的工具是自包含的但更强大的工具往往需要访问外部资源或Agent的状态。例如一个DatabaseQueryTool需要数据库连接池一个SendEmailTool需要邮件服务器的配置。这时我们就需要依赖注入。在Hermes的架构中一种常见的模式是在Agent或执行引擎初始化时将这些共享资源如配置对象、数据库会话、API客户端创建好然后在注册或调用工具时将它们“注入”到工具实例中。这可以通过多种方式实现构造函数注入在工具类的__init__方法中接收依赖项。注册工具时传入已创建好的依赖对象。class DatabaseQueryTool(BaseTool): def __init__(self, db_connection_pool): self.pool db_connection_pool super().__init__(namequery_db, ...) # 注册时 db_tool DatabaseQueryTool(my_db_pool) tool_registry.register_tool(db_tool)设置器注入工具类提供set_dependency之类的方法在注册后由框架统一调用设置。上下文传递更灵活的方式是通过工具run方法的**kwargs或一个额外的context参数在执行时由引擎传入当前会话的上下文。上下文可以包含用户ID、会话历史、全局配置等。这要求BaseTool的run方法签名支持可变参数。async def run(self, *args, context: Optional[AgentContext] None, **kwargs): # 可以从context中获取用户信息、配置等 user_id context.user_id if context else None # ... 工具逻辑实现上下文感知的工具能让Agent的行为更加个性化和智能化。例如一个文件操作工具可以根据context.user_id来访问相应用户的隔离存储空间。4.3 工具测试与集成验证开发完工具绝不能直接丢给Agent使用。必须进行严格的单元测试和集成测试。单元测试针对工具的run方法测试各种正常和异常输入。import pytest from your_tools import WorldClockTool pytest.mark.asyncio async def test_world_clock_tool_success(): tool WorldClockTool() result await tool.run(city_nameShanghai) assert Shanghai in result assert 当前时间是 in result pytest.mark.asyncio async def test_world_clock_tool_unknown_city(): tool WorldClockTool() result await tool.run(city_nameUnknownCity) assert 无法识别 in result or 错误 in result集成测试将工具注册到真实的ToolRegistry并模拟Agent的执行引擎调用流程。验证工具是否能被正确发现get_tools_descriptions。模拟LLM生成一个函数调用请求交给执行引擎处理。检查引擎是否能正确找到你的工具、传入参数、执行并返回格式正确的结果。测试工具调用失败时引擎的错误处理是否得当返回的信息是否有助于LLM进行下一步决策。通过完善的测试可以确保你的自定义工具在复杂的Agent交互中稳定可靠避免因为一个工具的小bug导致整个Agent对话崩溃或产生荒谬的结果。5. 工具系统的性能优化与最佳实践5.1 工具调用的异步化与并发控制Agent在执行复杂任务时可能需要连续或并行调用多个工具。例如为了比较两个城市的天气它需要先后调用两次天气查询工具。如果工具是同步的def run那么第二个调用必须等待第一个完全结束这会造成不必要的延迟。Hermes的BaseTool将run定义为异步方法async def run这鼓励开发者将工具实现为异步的。对于涉及网络IOAPI调用、数据库查询或磁盘IO文件读写的工具使用aiohttp,asyncpg,aiofiles等异步库可以极大地提升吞吐量避免在等待IO时阻塞整个事件循环。当Agent需要并行执行多个独立工具时例如同时获取新闻摘要和股票价格执行引擎可以利用asyncio.gather来并发调度。# 在引擎中并发执行多个工具调用的伪代码 async def execute_parallel_tools(tool_calls: List[ToolCall]): tasks [] for call in tool_calls: tool registry.get_tool(call.name) task asyncio.create_task( safe_tool_run(tool, call.arguments) # safe_tool_run 包裹了超时和异常处理 ) tasks.append(task) results await asyncio.gather(*tasks, return_exceptionsTrue) # 处理results将异常转换为LLM可理解的错误信息然而并发不是无限制的。必须实施并发控制信号量Semaphore限制同时进行的最大工具调用数防止对某个外部API如搜索引擎造成过大的并发压力导致被限流或封禁。针对特定工具的速率限制为某些敏感或资源受限的工具如发送邮件、调用付费API单独设置调用频率限制。5.2 工具结果的缓存与状态管理很多工具调用是幂等的即相同输入总是产生相同输出如查询静态数据、计算数学公式。对于这类工具引入缓存可以显著减少不必要的重复计算和外部调用提升Agent响应速度并降低成本如减少API调用次数。一个简单的缓存策略可以在工具类内部或执行引擎层面实现from functools import lru_cache import hashlib import json class CachedWebSearchTool(WebSearchTool): lru_cache(maxsize128) async def run(self, query: str) - str: # 父类的run方法已经实现了搜索逻辑 # LRU缓存会根据query参数自动缓存结果 return await super().run(query)对于更复杂的场景可能需要分布式缓存如Redis并设置合理的过期时间TTL。缓存键Cache Key的设计很重要通常由工具名和参数的哈希值组成。另一个相关概念是工具的状态管理。有些工具可能需要维护跨多次调用的状态。例如一个BrowserNavigationTool可能需要维护一个浏览器会话。这种状态不应该存储在工具类的静态变量中这会导致不同用户会话间状态污染而应该与会话上下文AgentContext绑定在会话开始时创建会话结束时清理。5.3 设计可观测性日志、监控与调试当你的Agent搭载了数十个工具每天处理成千上万的请求时可观测性Observability就变得至关重要。你需要清楚地知道哪个工具被调用了调用参数是什么执行成功了还是失败了耗时多久结构化日志为每一个工具调用记录结构化的日志条目。日志应至少包含时间戳、会话ID、工具名、输入参数可脱敏、执行耗时、结果状态成功/失败、错误信息如果有。使用JSON格式的日志便于后续用日志分析工具如ELK Stack进行聚合查询。性能指标Metrics集成监控系统如Prometheus为工具调用暴露关键指标tool_calls_total工具调用总次数按工具名和状态成功、失败分类。tool_call_duration_seconds工具调用耗时直方图按工具名分类。 这能帮助你快速发现性能瓶颈哪个工具最慢和可靠性问题哪个工具失败率最高。分布式追踪Tracing在微服务架构中一个用户请求可能触发Agent调用多个工具而这些工具又可能去调用下游服务。使用OpenTelemetry等标准集成分布式追踪可以为每个请求生成一个唯一的Trace ID并记录完整的调用链使得调试复杂的跨工具、跨服务的问题变得可能。实现这些可观测性功能的最佳位置是在工具执行引擎的调用封装层。在这里你可以无侵入地为所有工具调用统一添加日志记录、指标收集和追踪span的创建。6. 常见问题排查与实战避坑指南即使理解了所有原理在实际开发和运行中你依然会遇到各种各样的问题。下面是我在实战中积累的一些常见问题及其排查思路。6.1 工具调用失败LLM不调用或调用错误症状Agent似乎“忘记”了可用的工具或者生成了错误的工具调用参数。可能原因1工具描述description质量差。LLM完全依赖描述来理解工具功能。描述模糊、不准确或过长都会导致LLM无法正确使用。排查检查你的工具描述。是否清晰说明了功能、输入和输出是否使用了LLM容易理解的自然语言可以尝试让另一个LLM来评审你的工具描述是否清晰。可能原因2提示词Prompt中工具列表过长或格式混乱。如果注册了太多工具或者工具描述在Prompt中格式不对可能会干扰LLM的决策。排查查看发送给LLM的最终Prompt。工具列表部分是否整洁可以尝试对工具进行分组或者在Prompt中强调最相关的工具。可能原因3LLM的“函数调用”能力未正确激发。确保你使用的LLM API如OpenAI的ChatCompletion正确设置了tools或functions参数并且响应格式解析逻辑正确。排查打印出LLM的原始响应检查是否包含了结构化的工具调用请求如function_call字段。如果没有可能是模型版本不支持或Prompt引导不够。6.2 工具执行异常超时、错误与资源泄漏症状工具调用抛出异常导致Agent流程中断或者系统资源如内存、连接数持续增长。可能原因1缺乏超时机制。一个网络请求如果卡住会永远阻塞。解决务必在工具执行引擎中使用asyncio.wait_for或类似机制为每个工具调用设置超时。超时时间应根据工具类型合理设置如网络请求5-10秒计算密集型工具更长。可能原因2异常未被妥善处理。工具run方法中可能抛出各种异常ValueError, ConnectionError, TimeoutError等。解决在引擎调用工具的地方使用try...except捕获所有异常。不要简单地让异常向上抛出导致Agent崩溃。应该将异常信息转化为对LLM友好的错误消息例如“调用工具X失败原因网络连接超时。请稍后再试或检查网络。”可能原因3资源未正确释放。例如数据库连接、文件句柄、HTTP会话在使用后没有关闭。解决对于需要管理资源的工具使用上下文管理器with语句或async with语句来确保资源被正确清理。在工具的run方法中实现严谨的清理逻辑。6.3 安全漏洞防范输入验证与权限控制症状Agent被诱导执行危险操作如读取敏感文件、访问内网、或进行恶意代码执行。高危点1用户输入直接传递给工具。这是最大的风险源。防御对所有工具输入进行严格的验证和清洗。使用工具定义的args_schemaPydantic模型进行类型和约束验证。对于文件路径、URL等参数进行白名单或安全规则校验如禁止../ 限制协议为http/https。高危点2工具权限过高。例如一个文本处理工具却拥有执行系统命令的能力。防御遵循最小权限原则。在沙箱中运行代码执行工具。为文件系统工具设定严格的访问边界。考虑为不同的工具或工具组配置不同的“权限等级”并在执行前进行检查。高危点3敏感信息泄露。工具返回的结果可能包含API密钥、内部IP、系统信息等。防御在工具返回结果给LLM前进行内容过滤或脱敏。避免在日志中记录完整的敏感参数和结果。6.4 性能瓶颈定位与优化症状Agent响应缓慢吞吐量低。排查步骤1监控工具调用耗时。使用前面提到的指标系统找出平均耗时最长或P95/P99延迟最高的工具。排查步骤2分析耗时工具。针对慢工具进一步分析是网络延迟吗考虑使用更快的API端点、增加重试机制、实现本地缓存是计算复杂吗考虑算法优化、或引入结果缓存是资源竞争吗如数据库连接池过小排查步骤3检查并发和序列化。Agent是顺序执行工具还是并行执行不必要的顺序执行会极大拉长整体耗时。检查是否有工具调用可以并行化。同时检查传递给LLM和从LLM返回的数据特别是包含工具结果的长文本是否过大导致序列化/反序列化开销大。工具系统作为Agent的“双手”其稳定性、安全性和性能直接决定了整个Agent系统的可用性和可靠性。投入时间精心设计和维护这套系统远比追求更多、更花哨的工具数量来得重要。从清晰的抽象开始逐步构建起包含注册、发现、安全执行、状态管理、可观测性在内的完整生态你的Agent才能真正成为可靠的生产力伙伴。