PraisonAI Agents Tools 开发指南:从函数工具到 pip 插件包的完整实践
【免费下载链接】PraisonAIPraisonAI 🦞 — Hire a 24/7 AI Workforce. Stop writing boilerplate and start shipping autonomous self-improving agents that research, plan, code, and execute tasks. Deployed in 5 lines of code with built-in memory, RAG, and support for 100+ LLMs.项目地址: https://gitcode.com/GitHub_Trending/pr/PraisonAI
导读:本文以 PraisonAI Agents 的 Tools 官方指南 为核心骨架,系统讲解 PraisonAI 中"工具(Tool)"的核心概念、函数式与类式两种创建范式、基于 entry_points 的插件分发机制,并结合
praisonaiagents/tools目录下的真实源码(BaseTool、@tool 装饰器、ToolRegistry)深入剖析工具注册、Schema 自动生成与自动发现的底层原理。读完本文,你将能够独立为 Agent 编写、测试并打包分发自己的工具,让 Agent 获得搜索、抓取、行情查询等任意"特殊能力"。
什么是 Tool:Agent 的"特殊能力"
在 PraisonAI 中,一个Tool(工具)就是一段赋予 AI Agent 特定能力的代码。可以把工具想象成我们给 Agent 装备的"特殊能力":
- 互联网搜索工具:让 Agent 具备联网检索网页的能力;
- 股票行情工具:让 Agent 能查询实时股价与公司信息;
- 天气工具:让 Agent 能获取指定地点的天气状况。
从源码实现看,工具的本质是"可以被 LLM 调用并返回可序列化结果的函数或对象"。PraisonAI 为工具建立了一套完整的抽象体系,核心定义位于 tools/base.py:BaseTool是全部工具的抽象基类,它强制要求子类实现run()方法,并通过get_schema()输出 OpenAI 兼容的 function calling 结构({"type": "function", "function": {...}}),这样无论是 OpenAI、Gemini 还是其他 100+ 兼容 LLM,都能理解并调用你的工具。
插件系统:把工具打包成 pip 安装包
PraisonAI 现在支持完整的插件系统(Plugin System),外部开发者可以创建可通过pip install安装的工具包,安装后工具会被 PraisonAI 自动发现。这是将自定义工具分发给社区或其他团队的标准方式。
快速开始:两种在代码中定义工具的方式
from praisonaiagents import BaseTool, tool, Agent # 方法一:基于类(Class-based) class WeatherTool(BaseTool): name = "get_weather" description = "Get weather for a location" def run(self, location: str) -> dict: return {"temp": 72, "condition": "sunny"} # 方法二:基于装饰器(Decorator-based) @tool def search(query: str) -> list: """Search the web.""" return [{"title": "Result", "url": "https://..."}] # 两种方式都可以直接挂载到 Agent 上 agent = Agent( name="Assistant", tools=[WeatherTool(), search] )从源码看,BaseTool(定义于 tools/base.py)是一个抽象基类,子类必须覆盖两个类属性name(工具的唯一标识)和description(供 LLM 阅读的人性化描述,直接影响模型何时选择调用该工具),并实现抽象方法run()。@tool装饰器(定义于 tools/decorator.py)则把普通函数包装成FunctionTool实例——它是BaseTool的子类,因此两种方式产出的对象在 Agent 眼中完全等价,可以混用。
创建插件包(Plugin Package)的完整流程
1. 创建工具包目录结构:
praisonai-weather/ ├── pyproject.toml ├── src/ │ └── praisonai_weather/ │ ├── __init__.py │ └── tools.py2. 在tools.py中定义工具:
from praisonaiagents import BaseTool class WeatherTool(BaseTool): name = "weather" description = "Get current weather" def run(self, location: str) -> dict: # Your implementation return {"temp": 72}3. 在pyproject.toml中通过 entry_points 注册:
[project] name = "praisonai-weather" version = "1.0.0" [project.entry-points."praisonaiagents.tools"] weather = "praisonai_weather.tools:WeatherTool"注意(源码实证):README 中使用的入口组名
praisonaiagents.tools是历史组名。查看 tools/registry.py 可以发现,当前官方规范入口组(canonical group)是praisonai.tools,而praisonaiagents.tools与praisonai.tool_sources已降级为向后兼容的别名——别名组首次被发现时会发出DeprecationWarning,且名称冲突时 canonical 组优先。因此新插件建议直接使用[project.entry-points."praisonai.tools"],两种写法当前都能被识别。
4. 用户安装并直接使用:
pip install praisonai-weatherfrom praisonaiagents import Agent # Tool is auto-discovered! agent = Agent(tools=["weather"])这里的"自动发现"机制实现在 tools/registry.py 的ToolRegistry.discover_plugins()中:注册表通过importlib.metadata.entry_points扫描安装包中声明为工具入口点的类(或可调用对象),类会被自动实例化并注册到全局注册表;该扫描只执行一次(_discovered标志),且整个过程由threading.RLock保护,天然适配多 Agent 并发场景。
创建新工具:两种范式
1. 函数式(Function-Based):适合简单工具
函数式最适合"只做一件具体事情"的简单工具,就像只做加法的计算器。
适用场景:
- 工具只完成一个简单任务;
- 不需要在多次调用之间记忆状态;
- 不需要与其他工具共享信息;
- 是一次性的快速操作。
示例:
def internet_search(query: str): # Search the internet and return results return search_results使用方式:
from praisonaiagents.tools import internet_search results = internet_search("AI news")源码层面,internet_search是 tools/tools.py 中懒加载的 DuckDuckGo 搜索函数,同时在 tools/init.py 的TOOL_MAPPINGS注册表中映射到duckduckgo_tools模块。你完全可以照葫芦画瓢,把自己写的纯函数直接作为工具使用——PraisonAI 的解析器resolve_tool_name(见 tools/resolver.py)会依次尝试"注册表 → 内置 TOOL_MAPPINGS → 可选外部包"三级解析,普通函数只要能被找到就能被 Agent 调用。
2. 类式(Class-Based):适合复杂工具
类式适合"包含多个相关功能、或需要记忆信息"的复杂工具,就像一台能记住历史运算、支持多种数学操作的智能计算器。
适用场景:
- 工具包含多个相互关联的功能;
- 需要记忆或共享信息;
- 需要高效管理资源(如连接池);
- 有复杂的初始化配置需求。
示例:
class StockTools: def get_stock_price(self, symbol): # Get current stock price return price def get_stock_info(self, symbol): # Get detailed stock information return info使用方式:
from praisonaiagents.tools import get_stock_price, get_stock_info price = get_stock_price("AAPL") info = get_stock_info("AAPL")类式的底层优势在于可以复用BaseTool的完整能力矩阵。从 tools/base.py 可以梳理出子类可用的关键资产:
| 成员 | 作用 |
|---|---|
name/description | 必填类属性,工具的标识与 LLM 可读描述 |
version | 工具版本号,默认"1.0.0" |
parameters | 参数 JSON Schema,不提供时会根据run()签名自动生成 |
restart_safe | 声明工具的"重启安全"契约:True表示只读/幂等、崩溃后可安全重跑;False表示有副作用、恢复时绝不静默重放;None(默认)表示未声明 |
input_guardrails/output_guardrails | 仅作用于该工具的输入/输出护栏(与Agent(guardrails=...)的全局护栏不同) |
run(**kwargs) | 抽象方法,必须实现,返回任意类型(会字符串化后交给 LLM) |
safe_run(**kwargs) | 带异常捕获的执行入口,统一返回ToolResult(含success/error/metadata字段) |
get_schema() | 输出 OpenAI 兼容的 function schema,支持动态覆写 |
validate()/validate_schema_roundtrip() | 定义期校验:检查 name/description/run 是否齐全、Schema 能否通过 JSON 序列化往返 |
此外,ToolResult还支持多模态内容通道:工具可以通过multimodal_content()、text_part()、image_part()、file_part()(见 tools/base.py)返回结构化文本/图片/文件片段,使截图工具、图表渲染器等产出能直接成为下一轮对话中模型可见的消息部件。
@tool装饰器的进阶参数
@tool装饰器(tools/decorator.py)远不止包装函数那么简单,它还暴露了与 Agent 对齐的完整配置面:
@tool(name="web_search", description="Search the internet") def search(query: str, max_results: int = 5) -> list: return [...]availability:传入() -> (is_available, reason)回调,运行时检查工具是否可用(例如 API Key 缺失时自动对模型隐藏该工具),结果会被注册表以 30 秒 TTL 缓存;retry_policy:为工具执行配置指数退避重试策略;approval:标记该工具需要人工审批,True使用默认"high"风险等级,字符串可显式指定"critical"/"high"/"medium"/"low",定义时即注册到全局 ApprovalRegistry,本地、网关与服务化运行都会强制执行;to_model_output:提供result -> compact_view回调,给 LLM 喂精简摘要以节省上下文 token,完整结果仍保留给展示、钩子与追踪;restart_safe:与BaseTool相同的重启安全声明;input_guardrails/output_guardrails:本工具专属的参数/结果护栏,例如拦截发往外部域名的邮件、脱敏结果中的密钥;Injected[T]参数:将参数声明为Injected[dict]类型即可在调用时由框架自动注入会话状态(如session_id),此类参数会被自动排除出对外暴露的 Schema(见 tools/decorator.py)。
如何选择你的实现方式
动手前先问自己四个问题:
- 你的工具只做一件简单的事吗?
- 是 → 用函数式;否 → 考虑类式
- 你的工具需要记忆信息吗?
- 是 → 用类式;否 → 用函数式
- 工具的多个操作之间相互关联吗?
- 是 → 用类式;否 → 用函数式
- 你的工具需要高效管理资源吗?
- 是 → 用类式;否 → 用函数式
核心原则:简单工具用函数,复杂、有状态、需要资源管理的工具用类。
真实世界示例:两种范式的典型代表
PraisonAI 内置工具本身就是两种范式的最佳范本,全部可以通过from praisonaiagents.tools import ...导入:
互联网搜索工具(函数式)
- 只做一件事:搜索互联网;
- 无需记忆历史搜索;
- 每次搜索相互独立;
- 输入输出简单直接。
SearxNG 搜索工具(函数式)
- 基于本地 SearxNG 实例的隐私优先网页搜索;
- 支持可定制参数(
max_results结果数量上限、searxng_url实例地址); - 每次搜索独立且安全;
- 是传统搜索引擎的隐私替代方案。
其实现在 tools/searxng_tools.py,默认连接http://localhost:32768/search,聚合 google/bing/duckduckgo 多个引擎,结果统一标准化为{"title", "url", "snippet"}格式,并对连接失败、超时、解析错误均返回带error键的字典而不是抛异常——这正是"错误处理优雅"的代码示范。
Spider 工具(函数式)
- 通用网页抓取与爬取;
- 支持 CSS 选择器进行精确内容提取;
- 可抓取多页面并抽取链接/图片;
- 灵活应对各类爬取需求。
Newspaper 工具(函数式)
- 专注新闻文章抽取;
- 提取文章标题、正文、作者与发布日期;
- 内置 NLP 处理生成关键词与摘要;
- 按主题对新闻来源分类。
股票行情工具(类式)
- 做多件事:查价格、查公司信息、查历史数据;
- 记忆股票信息以避免重复下载;
- 各操作相互关联(都围绕股票);
- 高效管理连接资源。
类式工具的典型调用链可以对照 tools/init.py:注册表对类式工具采用"缓存类、每次新建实例"的工厂策略(_create_tool_instance),确保并发 Agent 之间不会共享可变状态、避免状态泄漏。
上手步骤:从零开始写一个工具
- 选择范式:依据上面的决策清单确定函数式还是类式;
- 创建工具文件:起一个描述性的文件名(如
weather_tools.py),放在praisonaiagents/tools目录下(或你自己的包目录中); - 编写工具:添加清晰的文档字符串,包含类型注解(type hints)以便于 Schema 自动生成,优雅处理错误;
- 测试工具:验证功能符合预期,覆盖错误分支,检查性能。
关于 Schema 自动生成,可以补充一个有趣的源码细节:tools/schema.py 的annotation_to_json_schema会把 Python 类型注解翻译成 JSON Schema——Optional[int]变成{"anyOf": [{"type": "integer"}, {"type": "null"}]},Literal["fast", "deep"]变成带枚举的字符串,List[T]、Dict[K, V]、Enum 子类也各有对应。这意味着写好类型注解,就等于给 LLM 写好了调用参数约束,所以编写工具时务必为参数和返回值标注完整类型。
最佳实践
文档(Documentation):
- 说明工具做什么、给什么输入、返回什么;
- 提供使用示例;
- 列出依赖与前置条件(如 API Key、本地服务)。
错误处理(Error Handling):
- 始终处理可能的异常;
- 返回有帮助的错误信息(参考 searxng_tools 返回
{"error": ...}字典的做法); - 绝不让工具崩溃中断整个 Agent 运行——
safe_run()会在底层兜住异常并封装为ToolResult(success=False, error=...)。
性能(Performance):
- 保持高效,不浪费资源;
- 适合时使用缓存(股票工具记忆行情避免重复下载就是典型);
- 善用
availability回调 + TTL 缓存避免频繁探测不可用工具。
用户友好(User-Friendly):
- 让工具易于使用;
- 使用清晰的函数/方法命名(name 会直接暴露给 LLM 作为调用标识);
- 保持简单,不过度设计。
需要帮助?
- 查看 tools 目录 下 60+ 个内置工具模块作为范例;
- 参考 examples/tools/example_tools_discover.py 了解如何枚举内置工具与外部工具包;
- 阅读 examples/tools/example_tools_resolve.py 与 examples/tools/example_tools_sources.py 学习工具名称解析与来源排查;
- 查阅 examples/tools/example_tools_discover.py 之外的工具示例文件,理解常见用法;
- 查阅项目文档,或查看官方 CLI 命令
praisonai tools list的解析提示(见 tools/resolver.py 中的_format_unknown)。
记住:目标是做出易于使用、易于维护的工具。选择最适合你具体工具需求的方式,让 Agent 的能力边界由你亲手定义。
【免费下载链接】PraisonAIPraisonAI 🦞 — Hire a 24/7 AI Workforce. Stop writing boilerplate and start shipping autonomous self-improving agents that research, plan, code, and execute tasks. Deployed in 5 lines of code with built-in memory, RAG, and support for 100+ LLMs.项目地址: https://gitcode.com/GitHub_Trending/pr/PraisonAI
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考