



























上周给自己做一个小工具,想让 Claude 能查我本地的文件——不只是聊,而是真的去翻目录、读内容。研究了一天 Function Calling,才发现这东西比我想的简单多了。
用大白话说:Function Calling 就是给 AI 一本工具书,让它知道自己能用哪些工具,然后在回答你的时候,可以主动说「我要用这个工具」。剩下的执行逻辑,还是你自己写代码完成。所以它本质上是 AI 和你的代码之间的协议,不是魔法。
纯靠对话,Claude 能帮你写代码、分析文档,但它没法知道你电脑上有什么文件,也没法帮你发邮件、查数据库。
以前我的做法是:让 Claude 输出一段代码 → 我自己跑 → 把结果粘回来继续问。重复三四轮下来,上下文一长,效果越来越差,人也烦。
Function Calling 把这个「中间人」工作自动化了:你定义好工具,AI 自己决定什么时候用,你的代码负责执行,结果自动回传给 Claude。
我的需求:输入一个问题,Claude 可以调用「列出目录文件」和「读取文件内容」两个函数。
第一步:定义工具
tools = [
{
"name": "list_files",
"description": "列出指定目录下的所有文件名",
"input_schema": {
"type": "object",
"properties": {
"path": {
"type": "string",
"description": "目录路径,比如 /Users/xxx/Documents"
}
},
"required": ["path"]
}
},
{
"name": "read_file",
"description": "读取指定文件的文本内容",
"input_schema": {
"type": "object",
"properties": {
"filepath": {
"type": "string",
"description": "文件完整路径"
}
},
"required": ["filepath"]
}
}
]
第二步:主循环
import anthropic
import os
client = anthropic.Anthropic(
base_url="https://api.ofox.ai/v1", # 我用的这个,低延迟直连
api_key="sk-xxx"
)
def list_files(path):
try:
return os.listdir(path)
except Exception as e:
return f"错误: {str(e)}"
def read_file(filepath):
try:
with open(filepath, "r") as f:
return f.read()
except Exception as e:
return f"错误: {str(e)}"
def run_agent(user_message):
messages = [{"role": "user", "content": user_message}]
while True:
response = client.messages.create(
model="claude-opus-4-7",
max_tokens=4096,
tools=tools,
messages=messages
)
# 没有工具调用,直接返回文本
if response.stop_reason == "end_turn":
for block in response.content:
if hasattr(block, "text"):
return block.text
# 处理工具调用
tool_results = []
for block in response.content:
if block.type == "tool_use":
if block.name == "list_files":
result = list_files(block.input["path"])
elif block.name == "read_file":
result = read_file(block.input["filepath"])
else:
result = "未知工具"
tool_results.append({
"type": "tool_result",
"tool_use_id": block.id,
"content": str(result)
})
# 把工具结果发回给 Claude
messages.append({"role": "assistant", "content": response.content})
messages.append({"role": "user", "content": tool_results})
逻辑很清晰:Claude 要用工具 → 你执行 → 结果回传 → 循环,直到 Claude 说完。
坑1:工具描述写太简单
第一次把 description 写得很笼统,就几个字,Claude 经常不知道什么时候该用哪个工具,或者用错了。后来改成详细描述加上使用场景,准确率明显上来了。
坑2:stop_reason 没判断对
Claude 要用工具时,stop_reason 是 "tool_use",不是 "end_turn"。我一开始直接取 content 里的文字,拿到的是空的,愣了好一会儿。要分开处理这两种情况。
坑3:工具结果格式不对
工具结果要放在 "user" 角色的消息里,content 是一个列表,每个元素的 type 是 "tool_result"。格式不对就一直报 422。这个坑文档里有,但藏得比较深,建议直接抄官方示例改。
代码里我用的是 ofox.ai,兼容 OpenAI 协议,一个 API Key 能调 Claude、GPT、Gemini 等 50+ 模型,按量计费、支持支付宝。实测延迟稳定在 200-300ms,用来跑这类 Agent 工具没什么问题。
如果你用 Claude 官方 API,把 base_url 那行删掉、换成官方地址就行,其他代码完全不用改。
做完这个 demo,我觉得 Function Calling 真正的价值不是炫技,而是让 AI 能跟真实世界交互:
最适合那种「半自动化」场景——流程大致确定,但边界情况太多,写死规则很烦,让 AI 灵活处理更合适。
用了几天,发现几个问题:
对我来说,目前用它做一些本地轻量工具还挺顺手的。生产级 Agent 可能还需要更完善的错误处理和状态管理,不能直接把这个 demo 搬上去。
不过入门门槛确实低——有 Python 基础的话,半天能跑通一个 demo。不妨先做一个简单的,感受一下这个交互模式再说。
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。