在第1步中,我们通过 Model Adapter 赋予了 Agent "思考"和"交流"的能力。然而,纯粹的语言模型只能基于训练数据回答问题,无法获取实时信息或操作外部系统。为了让 Agent 真正变得强大,我们需要为它装上"手和脚"------这就是**工具(Tools)**的作用。
1. 什么是工具抽象?
大语言模型通常支持 Function Calling(函数调用)机制。这意味着模型可以决定何时调用某个特定函数,并要求我们(客户端)执行该函数并将结果返回给它。
为了让 Agent 能够动态加载、管理和执行不同的函数,我们需要定义一套标准的工具抽象(Tool Interface)。每个工具都应该包含:
- 工具名称 (Name)
- 工具描述 (Description):告诉 LLM 这个工具是干什么的。
- 参数 Schema (Parameters):告诉 LLM 调用该工具需要提供哪些参数。
- 执行逻辑 (Run/Execute):实际运行的 Python 代码。
2. 工具调用工作流

3. Python 代码实现
我们先定义一个基础的工具类 BaseTool,然后实现具体的工具,例如一个查询天气的工具和一个计算器工具。
定义基础抽象类
python
from abc import ABC, abstractmethod
from typing import Dict, Any, Type
from pydantic import BaseModel, Field
class BaseTool(ABC):
name: str
description: str
args_schema: Type[BaseModel]
@abstractmethod
def run(self, **kwargs) -> str:
"""执行工具的核心逻辑"""
pass
def get_function_declaration(self) -> Dict[str, Any]:
"""将工具转换为 LLM 认识的 JSON Schema 格式"""
return {
"type": "function",
"function": {
"name": self.name,
"description": self.description,
"parameters": self.args_schema.model_json_schema()
}
}
实现具体工具:天气查询与计算器
python
class WeatherArgs(BaseModel):
location: str = Field(..., description="城市名称,例如:北京, 上海")
class WeatherTool(BaseTool):
name = "get_weather"
description = "获取指定城市的当前天气情况"
args_schema = WeatherArgs
def run(self, location: str) -> str:
# 这里可以接入真实的第三方天气 API
# 为了演示,我们返回 mock 数据
mock_weather = {
"北京": "晴朗,25度",
"上海": "小雨,22度"
}
return mock_weather.get(location, f"无法获取 {location} 的天气信息")
class CalculatorArgs(BaseModel):
expression: str = Field(..., description="数学表达式,例如:2 + 2 * 3")
class CalculatorTool(BaseTool):
name = "calculator"
description = "计算数学表达式的结果"
args_schema = CalculatorArgs
def run(self, expression: str) -> str:
try:
# 警告:实际生产环境中请使用安全的 eval 替代方案
result = eval(expression)
return str(result)
except Exception as e:
return f"计算错误: {str(e)}"
工具注册与管理
我们需要一个统一的地方来管理这些工具,并在 LLM 请求调用时找到对应的工具实例。
python
class ToolRegistry:
def __init__(self):
self._tools: Dict[str, BaseTool] = {}
def register(self, tool: BaseTool):
self._tools[tool.name] = tool
def get_all_declarations(self) -> list:
"""获取所有工具的声明,传给 LLM"""
return [tool.get_function_declaration() for tool in self._tools.values()]
def execute_tool(self, name: str, kwargs: dict) -> str:
"""根据 LLM 的指令执行对应工具"""
if name not in self._tools:
return f"Error: Tool {name} not found."
tool = self._tools[name]
try:
# 使用 Pydantic 验证参数
validated_args = tool.args_schema(**kwargs)
# 执行工具
return tool.run(**validated_args.model_dump())
except Exception as e:
return f"Tool Execution Error: {str(e)}"
# 初始化注册表并注册工具
registry = ToolRegistry()
registry.register(WeatherTool())
registry.register(CalculatorTool())
# 打印生成的 JSON Schema 给 LLM 看
import json
print(json.dumps(registry.get_all_declarations(), indent=2, ensure_ascii=False))
总结
通过抽象出 BaseTool 和 ToolRegistry,我们极大地方便了后续工具的扩展。开发者只需要继承基础类并定义参数 Schema,即可快速将新的能力接入到 Agent 中。
有了大脑(Model Adapter)和手脚(Tools),下一步就是将它们结合起来,让 Agent 自主地进行"思考、调用工具、观察结果、回答"的循环------这就是我们在下一篇要实现的 Agent Loop。