Tools 工具
Tool 对 LLM 的作用,可以用一句话概括:它让 LLM 从一个“只能盲猜的聊天机器人”,变成了“拥有手和眼的实干家”。
在本框架中,每个Tool实现类的run()方法都要求返回Message类型的数据
ToolRegistry
ToolRegistry 是一个工具注册表,用于管理和调用工具(Tool)。它在整个 Agent 系统中扮演着"工具调度中心"的角色。以下是其核心功能模块:
1. 工具注册管理
ToolRegistry 维护了两套工具字典:
| 字典 | 说明 |
|---|---|
_tools | 普通工具字典,对 LLM 可见(通过 get_openai_tools() 暴露给 OpenAI API) |
_defer_tools | 延迟工具字典,不对外暴露,专门存储那些需要 Agent 发现后才能调用的工具 |
两种工具的主要区别在于:get_openai_tools()在没有任何参数情况下只会返回普通工具列表对应的List[Dict[str, Any]]数据以供LLM调用,而延迟工具列表对应的数据不能通过get_openai_tools()返回。简单说,就是普通工具列表对外暴露,延迟工具列表不对外暴露
2. 审批机制
ToolRegistry 在构造函数中接受一个可选的 ApprovalTool(审批工具)实例。当设置了审批工具后,每次执行工具前都会先经过用户审批,审批不通过则返回拒绝消息,不会真正执行工具。这为敏感操作提供了安全兜底。
框架提供了默认的ApprovalTool实现类,如果你想使用默认的ApprovalTool实现类,在创建ToolRegistry实例时传入DefaultApprovalTool()
tool_registry = ToolRegistry(DefaultApprovalTool(require_approval_tools=[TerminalTool],
max_attempts=5,
auto_approve_if_no_rules=True))你也可以实现ApprovalTool类,并传入给ToolRegistry实例
默认审批实现思路博客:模板方法模式实战:重构Agent工具审批,告别重复代码 - 知乎
如何注册工具
推荐使用register_tools()注册工具
from violet_agents import ToolRegistry
from violet_agents import SkillsTool, TerminalTool
tool_registry = ToolRegistry()
tool_registry.register_tools(SkillsTool(), TerminalTool())当然,你也可以使用register_tools(... , is_defer=False)来注册延迟工具。延迟工具目前需要配合内置的SearchToolsTool使用
from violet_agents import ToolRegistry
from violet_agents import SkillsTool, TerminalTool
tool_registry = ToolRegistry()
tool_registry.register_tools(SearchToolsTool(get_deferTools_callback=tool_registry.get_defer_tools, search_strategy="subAgent"))
tool_registry.register_tools(SkillsTool(), TerminalTool(), is_defer=True)获取工具列表
若你需要获取工具列表,可以使用get_openai_tools()方法,返回类型为List[Dict[str, Any]]
tools = self.tool_registry.get_openai_tools()内置Tool
本框架提供了常用的内置Tool以供调用
SearchToolsTool
一个专门用于搜索工具的工具,目前支持基于子Agent的搜索和基于keyword的搜索两种策略。
SearchToolsTool可接收的参数为
| 参数 | 类型 | 是否必传 | 说明 |
|---|---|---|---|
action | string | 是 | 用于指定当前的操作类型,支持两种操作:search表示搜索相关工具,get表示获取具体工具信息。 |
query | string | 否 | 搜索查询字符串,仅在action为search时使用 |
tool_name | string | 否 | 要获取的工具名称,仅在action为get时使用 |
当action == search时,该工具基于SearchToolsTool实例的get_deferTools_callback搜索工具。搜索成功将会返回Message类型的数据。
当action == get时,该工具会基于Agent传入的tool_name在SearchToolsTool实例的get_deferTools_callback中进行搜索,搜索成功则会返回Message类型的数据,message.content即为工具参数
WARNING
当action == get并搜索成功时,仅返回包含工具参数的Message数据,若想让Agent实现懒加载工具可以结合钩子函数与ToolRegistry,具体实现可以参考ReactAgent
实现思路博客:Tool 太多怎么办?基于渐进式披露实现 Agent Tool Search - 知乎
使用示例:
from violet_agents import ReactAgent
from violet_agents import VioletAgentsLLM
from violet_agents import ToolRegistry
from violet_agents import SkillsTool, TerminalTool, SearchToolsTool
from violet_agents import DefaultApprovalTool
tool_registry = ToolRegistry()
tool_registry.register_tools(SkillsTool(), SearchToolsTool(get_deferTools_callback=tool_registry.get_defer_tools, search_strategy="subAgent"))
tool_registry.register_tool(TerminalTool(), is_defer=True)
agent = ReactAgent(
name="VioletAgent",
llm=VioletAgentsLLM(provider="deepseek", model="deepseek-v4-flash"),
tool_registry=tool_registry
)
response = agent.run("看看我当天文件夹有什么文件?")TerminalTool
TerminalTool 是一个 沙箱化的命令行执行工具,继承自 Tool 基类。它的核心作用是让Agent 能够在受控的安全环境中执行操作系统命令,并获取输出结果。
SkillsTool
SkillsTool 是一个技能发现与加载工具,用于在项目中管理和使用可复用的"技能"(Skills)。技能是以 SKILL.md 文件的形式组织的,每个技能包含 YAML 前置元数据(frontmatter)和 Markdown 正文内容。
目前该框架仅支持采用utf-8编码读取SKILL.md文件
核心功能
1. 技能发现(_discover_skills)
- 遍历多个技能路径(
self.skill_paths),查找每个子目录下的SKILL.md文件 - 解析
SKILL.md中的 YAML 前置元数据(name、description、triggers等) - 返回所有已发现技能的字典,键为技能名称,值为
(文件路径, 元数据字典)元组
2. 技能列表(action="list")
- 返回当前所有可用技能的概览信息,包括名称、描述和触发条件
3. 技能加载(action="load")
- 加载指定技能的完整内容(去除前置元数据后的正文部分),供 AI 模型使用
4. 系统提示注入(get_system_prompt_section)
- 生成一段技能列表文本,用于注入到系统提示(System Prompt)中,让模型知道有哪些可用技能
自定义Tool
如果你需要自定义Tool,无论是有状态Tool还是无状态Tool,你需要实现两个方法,分别是run() -> Message和get_parameters() -> ToolParameters
get_parameters()用于定义工具的输入参数结构,返回ToolParameters对象。该方法会告诉LLM如何传参
在触发工具调用时会执行run()方法。
自定义无状态Tool
如果你需要自定义无状态Tool,只需实现run()以及get_parameters()即可
from violet_agents import Tool, Message, ToolParameters, ToolProperty
from typing import Dict, Any
class WeatherTool(Tool):
def __init__(self):
super().__init__(
name="get_weather",
description="获取指定城市的天气信息"
)
def run(self, parameters: Dict[str, Any], tool_call_id: str) -> Message:
city = parameters.get("city")
# 这里可以调用实际的天气API获取数据,以下是模拟数据
weather_info = f"{city}的天气是晴朗,温度25°C。"
return Message(
content=weather_info,
role="tool",
tool_call_id=tool_call_id
)
def get_parameters(self) -> ToolParameters:
return ToolParameters(
type="object",
properties={
"city": ToolProperty(
type="string",
description="要查询天气的城市名称"
)
},
required=["city"]
)自定义有状态Tool
如果你想自定义的Tool是有状态的,除了实现run()和get_parameters(),对于有状态属性,你需要通过_get_state()方法设置和修改它们,比如
from violet_agents import Tool, Message, ToolParameters, ToolProperty
from typing import Dict, Any
class WeatherTool(Tool):
def __init__(self):
super().__init__(
name="get_weather",
description="获取指定城市的天气信息"
)
def run(self, parameters: Dict[str, Any], tool_call_id: str) -> Message:
city = parameters.get("city")
# 这里可以调用实际的天气API获取数据,以下是模拟数据
weather_info = f"{city}的天气是晴朗,温度25°C。"
self.times += 1
print(f"WeatherTool has been called {self.times} times.")
return Message(
content=weather_info,
role="tool",
tool_call_id=tool_call_id
)
@property
def times(self) -> int:
return self._get_state().get("times", 0)
@times.setter
def times(self, value: int):
self._get_state()["times"] = value
def get_parameters(self) -> ToolParameters:
return ToolParameters(
type="object",
properties={
"city": ToolProperty(
type="string",
description="要查询天气的城市名称"
)
},
required=["city"]
)通过该方式,框架会自动维护可变状态属性,使其支持多线程/协程。