# OpenAI-style tool schema (note the `type: function` wrapper).
TOOLS = [{
"type": "function",
"function": {
"name": "bash",
"description": "Run a shell command.",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
},
}]
这段代码是 告诉大模型"你有什么工具可用" 的声明,遵循 OpenAI Function Calling Schema。模型读完这段就知道:可以调一个叫 bash 的工具,怎么传参,要传什么。
整体结构(OpenAI 标准 Schema)
TOOLS = [ # ← 工具列表(可以声明多个)
{
"type": "function", # ← 工具类型:固定 "function"
"function": { # ← 工具元数据
"name": ..., # 工具名(模型靠它识别要调哪个)
"description": ..., # 工具描述(模型靠它判断何时调)
"parameters": ... # 入参 Schema(JSON Schema 格式)
}
}
]
逐字段拆解
TOOLS = [{
外层是数组,因为可以声明多个工具:
TOOLS = [
{"type": "function", "function": {"name": "bash", ...}},
{"type": "function", "function": {"name": "read_file", ...}},
{"type": "function", "function": {"name": "search_web", ...}},
]
你目前只有一个 bash,但仍用数组包着,方便以后扩展。
"type": "function"
工具类型固定为 "function"。这是 OpenAI 设计上的留扩展位 —— 未来可能加 "retrieval"、"code_interpreter" 等内置工具。目前只有这一个合法值,按规矩写就行。
"function": {...}
工具元信息容器。三个核心字段:
"name": "bash" ⭐ 关键字段
工具的唯一标识,模型返回 tool_calls 时会用它指明调哪个:
"tool_calls": [{
"id": "call_xxx",
"function": {"name": "bash", "arguments": "..."}
}]
代码里靠 name 路由到具体执行函数:
if name == "bash":
output = run_bash(command)
else:
output = f"Error: unknown tool {name}"
命名规则(OpenAI 要求):
- 只允许
a-z A-Z 0-9 _ - - 长度 ≤ 64
- 不能有空格、中文、特殊字符
- 区分大小写(
bash≠Bash)
"description": "Run a shell command." ⭐ 关键字段
告诉模型这工具是干啥的,模型靠它判断何时该调。
这是最重要的"提示"字段。写得好坏直接决定模型用工具的准确度。
本例太简略,模型不知道边界:
"description": "Run a shell command." # ← 你现在的写法
优秀写法:
"description": (
"Execute a bash shell command on the local machine and return its stdout/stderr. "
"Use this for: listing files (ls), reading files (cat), searching (grep), "
"running scripts. Do NOT use for destructive operations like 'rm -rf' "
"or commands requiring sudo."
)
模型把这段当 "工具说明书" 读,写多详细都不亏(虽然占点 token)。当前写法够用,但生产级 agent 建议加上:
- 何时该用
- 何时不该用
- 返回什么格式
- 危险操作的警告
"parameters": {...} ⭐ 关键字段
入参定义,必须是合法的 JSON Schema:
"parameters": {
"type": "object", # ← 顶层永远是 object
"properties": { # ← 各参数定义
"command": {"type": "string"}
},
"required": ["command"], # ← 哪些参数必填
}
逐项拆:
"type": "object"
顶层永远是 object。OpenAI 协议规定参数必须是个对象(即字典),不能是裸字符串/数字/数组。即使只有一个参数,也要包成对象。
"properties": {"command": {"type": "string"}}
定义有哪些字段,每个字段什么类型。这里就一个 command,类型是字符串。
支持的 JSON Schema 类型:
| type | 例子 |
|---|---|
string | "ls -la" |
integer | 42 |
number | 3.14 |
boolean | true/false |
array | ["a", "b"] |
object | {"k": "v"} |
也可以加更多约束:
"command": {
"type": "string",
"description": "The shell command to execute, e.g., 'ls -la /tmp'",
"minLength": 1,
"maxLength": 1000,
}
强烈建议给每个 property 加 description,模型生成 arguments 时质量会显著提升。你现在没加,是简化版。
"required": ["command"]
列出必填字段。没列在这里的就是可选。不写或写空数组 [] = 全部可选。
完整流程:模型怎么用这段声明
Step 1:声明发给模型
"tools": TOOLS
Step 2:模型读懂后决定要调
模型内部"读"到这段后心想:
"哦,我有个
bash工具,可以执行 shell 命令。 用户问'当前目录有啥' → 我应该调bash,传command='ls'。"
Step 3:模型按 schema 生成 tool_calls
{
"tool_calls": [{
"id": "call_2e547...",
"function": {
"name": "bash", ← 跟你声明的 name 一致
"arguments": "{\"command\": \"ls -la\"}" ← 严格遵守 properties
}
}]
}
注意 arguments 是 JSON 字符串(不是对象),所以你代码里要 json.loads(raw_args) 解析。
Step 4:你执行并回传
args = json.loads(call["function"]["arguments"])
command = args["command"] # ← "ls -la"
output = run_bash(command)
messages.append({"role": "tool", "tool_call_id": call["id"], "content": output})
多工具示例
如果想加更多工具(比如读文件、网络搜索),就这样写:
TOOLS = [
{
"type": "function",
"function": {
"name": "bash",
"description": "Run a shell command.",
"parameters": {
"type": "object",
"properties": {"command": {"type": "string"}},
"required": ["command"],
},
},
},
{
"type": "function",
"function": {
"name": "read_file",
"description": "Read the contents of a file.",
"parameters": {
"type": "object",
"properties": {
"path": {"type": "string", "description": "Absolute file path"},
"max_bytes": {"type": "integer", "description": "Max bytes to read", "default": 4096},
},
"required": ["path"],
},
},
},
]
然后在 agent_loop 里加路由:
if name == "bash":
output = run_bash(args.get("command", ""))
elif name == "read_file":
output = read_file_safe(args.get("path"), args.get("max_bytes", 4096))
else:
output = f"Error: unknown tool {name}"
这段写法的小优化建议(非必须)
当前:
"description": "Run a shell command.",
"properties": {"command": {"type": "string"}},
升级版(提升模型调用准确度):
"description": (
"Run a shell command on the local machine and return its output. "
"Use for inspecting files, running scripts, system queries."
),
"properties": {
"command": {
"type": "string",
"description": "The shell command to execute, e.g., 'ls -la' or 'cat README.md'.",
}
},
加 description 的意义:模型知道怎么用、何时用,幻觉率显著下降。token 多花一点点,收益巨大。
一图速记
TOOLS = [
{ ┐
"type": "function", │ ← OpenAI 工具协议外壳(固定写法)
"function": { ┘
"name": "bash", ← 工具 ID(模型 tool_calls 用它)
"description": "...", ← 何时调(模型决策依据)
"parameters": { ┐
"type": "object", │
"properties": {...}, │ ← JSON Schema:参数怎么传
"required": [...] │
} ┘
}
}
]
一句话总结
本工具集是给模型的**"工具说明书"** —— 用 OpenAI Function Calling Schema 声明了一个名为
bash的工具,告诉模型:"你可以执行 shell 命令,参数叫command,类型字符串,必填"。模型读完后就能在合适时机生成 tool_calls 调用它。name是路由 key,description是决策依据,parameters是 JSON Schema 入参契约,三者缺一不可。