2.5 Tools (工具)


2.5 Tools (工具)

本节约处在第二章最后一站,也是组件篇的收口。工具和 Agent 的关系,像工匠和器具:工匠决定怎么用,器具决定能碰什么。CrewAI 把"外部能力"统一成 Tool 注入角色,核心逻辑不用改一行,这是它"独立扩展"价值的落点。

先看清 Tool 在运行时怎么被

先看清 Tool 在运行时怎么被调用,避免以为模型能直接执行任意函数:

先看清 Tool 在运行时怎么被

理解这一点很重要:模型不会直接运行代码,它只是"建议"调用某个工具并给出参数,框架负责真正执行、再把结果塞回上下文。所以工具的描述(让模型知道何时用、怎么传参)往往比实现更影响成败。

CrewAI 自带 crewai

CrewAI 自带 crewai_tools 集成了一批现成工具:搜索、网页抓取、代码执行、文档读取等。用法是实例化后挂到 Agent 或 Task:

from crewai import Agent, Task, Crew, Process from crewai_tools import SerperDevTool, ScrapeWebsiteTool search = SerperDevTool() # 需要对应 API 凭证 scrape = ScrapeWebsiteTool() researcher = Agent( role="研究员", goal="用真实来源回答问题", backstory="重证据", tools=[search, scrape], # 把工具挂到角色 verbose=True, ) t = Task(description="搜并抓取 {topic} 的官方介绍", expected_output="摘要加来源链接", agent=researcher) crew = Crew(agents=[researcher], tasks=[t], process=Process.sequential)

运行后,模型在觉得"需要查"时会输出工具调用请求,框架执行搜索/抓取,把结果回灌,模型再据此作答。如果你忘了配工具对应的环境变量(如搜索服务的 key),运行会直接报错——这是工具集成最常见的失败点。

更可控的是自定义工具。用 @tool 装饰器把一个函数变成 Tool,文档字符串就是给模型看的"说明书",写得准,模型才调得对:

from crewai.tools import tool from crewai import Agent, Task @tool("汇率换算") def convert_currency(amount: float, from_cur: str, to_cur: str) -> str: """把金额从一种货币换算成另一种。参数 amount 为数值,from_cur 与 to_cur 为三位货币代码。""" rates = {"USD": 1.0, "CNY": 7.2, "EUR": 0.92} return str(amount * rates[to_cur] / rates[from_cur]) fx_agent = Agent(role="财务助手", goal="做币种换算", backstory="细心", tools=[convert_currency], verbose=True) t = Task(description="把 100 USD 换成 CNY", expected_output="数字加单位", agent=fx_agent)

注意文档字符串里明确写了参数含义与取值形式。模型靠这段说明决定传 "CNY" 还是 "cny",传错就抛 KeyError。我们建议:自定义工具的 docstring 必须写清每个参数的类型、单位和合法取值,比函数体本身更值得打磨。

工具设计有几个工程取舍:

  • 工具粒度别太细也别太粗。太细("打开网页""读第一行")模型要调很多次;太粗("完成整个调研")失去可控性。一个工具干一件有明确产出的事最稳。
  • 工具要做输入校验。模型会传奇怪参数,函数里先校验再执行,别把异常抛给框架吞掉。
  • 危险操作别暴露。删除、外发、付费类动作,要么不做成工具,要么加人工确认(见第四章 Human-in-the-Loop)。
  • 工具描述讲"何时用"比讲"怎么实现"重要。模型选工具靠描述,不读源码。

再给一个把工具限制在单个 Task 的例子,体现"最小权限":

delete_tool = ... # 假设存在高风险工具 safe_search = SerperDevTool() # 研究员可用搜索,写手只拿到低风险工具 t_research = Task(description="搜资料", expected_output="要点", agent=researcher, tools=[safe_search]) t_write = Task(description="成文", expected_output="文章", agent=writer, tools=[]) # 写手无工具

收尾:Tools 让 CrewA

收尾:Tools 让 CrewAI 从"只会说话"变成"能办事"。第二章到这里把四个核心对象加工具全部拆完——Agent 是谁、Task 干啥、Crew 编组、Process 定序、Tools 接外力。第三章我们不再讲字段,直接把它们拼起来跑通完整工作流,你会看到这些声明在 kickoff 一刻如何变成真实的多角色协作。

给 Agent 接上工具:从 @tool 到官方集成

工具让 Agent 不只是"嘴上说说",能查资料、算数、调 API。CrewAI 支持两种接法:用 crewai_tools 里的官方工具,或用 @tool 装饰器把任意函数包成工具。

方式 适用 示例
官方工具 常见能力(搜索、网页、数据库) SerperDevTool、WebsiteSearchTool
@tool 自定义 私有逻辑、内部 API 把内部查价函数包成工具

⚠️ 常见坑:工具的返回最好是干净字符串或结构化文本,别直接返回复杂对象——Agent 拿到的是它的文本表示。另一个坑是工具里抛异常会中断整队,务必在工具内部捕获并返回友好提示。

from crewai import Agent from crewai_tools import SerperDevTool from crewai.tools import tool search = SerperDevTool() # 官方搜索工具 @tool("汇率换算") def fx(amount: float, rate: float) -> str: """把金额按汇率换算成目标币种字符串。""" try: return f"{amount} 原币 = {amount * rate:.2f} 目标币" except Exception as e: return f"换算失败: {e}" agent = Agent( role="跨境分析师", goal="评估成本", backstory="懂汇率", tools=[search, fx], # 同时拥有搜索与自定义换算 verbose=True, ) print(agent.tools[1].name) # 输出: 汇率换算

💡 关键直觉:工具是 Agent 的"手脚"。一个 Agent 强不强,七分看它挂了哪些工具,三分看人设。给研究员一把搜索刀,他才能产出有依据的结论;只给嘴不给刀,再好的人设也只是空谈。

# 同一把工具可被多个 agent 共享,避免重复初始化 shared_tools = [search] a1 = Agent(role="A", goal="g", backstory="b", tools=shared_tools, verbose=True) a2 = Agent(role="B", goal="g2", backstory="b2", tools=shared_tools, verbose=True)

作者与出处
原作者: 灏天文库
来源:灏天文库
整理: 灏天文库整理
由灏天文库平台收录,内容或由平台用户上传,仅供学习交流
发布者: 作者: 灏天文库 转发
评论区 (0)
U