(各位访问大哥能否留一些宝贵建议给小弟,没人的话就当个人日常blog)今天对RAG的学习暂时告一段落,开始学习 Function Calling。RAG主要是向量检索,一般搭配知识库使用,根据用户提问输出的回答都会基于知识库的内容;而Function Calling可以让ai调用外部函数,根据用户语义自己决定要调哪个函数,然后把函数返回的结果和会话内容加入prompt一起发给模型,思考后再输出符合当前语境的回答。
为了方便理解,我依旧让opencode写了一个demo:
""" day7: Function Calling 演示 — 让 AI 决定调用你的 Python 函数 """ import json import os from pathlib import Path from openai import OpenAI from dotenv import load_dotenv root = Path(__file__).parent.parent load_dotenv(root / ".env") client = OpenAI( api_key=os.getenv("DEEPSEEK_API_KEY"), base_url=os.getenv("DEEPSEEK_BASE_URL"), ) # ============================================================ # 你写的 Python 函数 — AI 可以调用它们 # ============================================================ def get_weather(city: str) -> str: """模拟查天气(实际应该调天气 API)""" weather_data = { "北京": "晴天,25°C", "上海": "多云,28°C", "深圳": "雷阵雨,30°C", } return weather_data.get(city, f"未找到 {city} 的天气数据") def calculator_add(a: float, b: float) -> float: return a + b def calculator_multiply(a: float, b: float) -> float: return a * b # ============================================================ # 工具描述 — 告诉 AI "你可以用哪些函数" # ============================================================ TOOLS = [ { "type": "function", "function": { "name": "get_weather", "description": "查询指定城市的天气", "parameters": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如:北京、上海、深圳" } }, "required": ["city"] } } }, { "type": "function", "function": { "name": "calculator_add", "description": "计算两个数字的和", "parameters": { "type": "object", "properties": { "a": {"type": "number", "description": "第一个数字"}, "b": {"type": "number", "description": "第二个数字"} }, "required": ["a", "b"] } } }, { "type": "function", "function": { "name": "calculator_multiply", "description": "计算两个数字的乘积", "parameters": { "type": "object", "properties": { "a": {"type": "number", "description": "第一个数字"}, "b": {"type": "number", "description": "第二个数字"} }, "required": ["a", "b"] } } } ] # ============================================================ # 主程序 # ============================================================ SYSTEM_PROMPT = "你是一个有用的助手,可以查天气和做计算。用中文回答。" messages = [{"role": "system", "content": SYSTEM_PROMPT}] print("=" * 50) print(" Function Calling 演示 (quit 退出)") print(" 比如:北京天气怎么样 / 3加5等于多少 / 3+5后再乘以2") print("=" * 50) while True: user_input = input("\n你: ").strip() if not user_input: continue if user_input.lower() in ("quit", "exit", "q"): break messages.append({"role": "user", "content": user_input}) # 第一次调用:AI 决定要不要调函数 response = client.chat.completions.create( model="deepseek-v3.2", messages=messages, tools=TOOLS, # ← 告诉 AI 有哪些工具可用 tool_choice="auto", # ← AI 自己决定用不用、用哪个 ) msg = response.choices[0].message # 检查 AI 是不是想调函数 if msg.tool_calls: print(f"\n[AI 决定调用函数:]", end="") # 处理每个函数调用 for tool_call in msg.tool_calls: func_name = tool_call.function.name func_args = json.loads(tool_call.function.arguments) print(f"\n → {func_name}({func_args})") # 执行对应的 Python 函数 if func_name == "get_weather": result = get_weather(**func_args) elif func_name == "calculator_add": result = calculator_add(**func_args) elif func_name == "calculator_multiply": result = calculator_multiply(**func_args) else: result = f"未知函数: {func_name}" print(f" ← 返回结果: {result}") # 把函数调用和结果告诉 AI messages.append({ "role": "assistant", "content": None, "tool_calls": [tool_call] }) messages.append({ "role": "tool", "tool_call_id": tool_call.id, "content": str(result) }) # 第二次调用:AI 根据函数结果生成最终回答 final_response = client.chat.completions.create( model="deepseek-v3.2", messages=messages, ) print(f"\n小码: {final_response.choices[0].message.content}") else: # AI 不需要调函数,直接回答 print(f"\n小码: {msg.content}") messages.append({"role": "assistant", "content": msg.content or ""})运行测试结果:
可以很清楚地看到,用户询问时,ai会根据问题语义判断是否要调用外部函数。当我询问城市天气、两个数的加法和乘法时ai都会调用对应的函数,而不是直接思考(从打印信息[AI 决定调用函数:]及其它内容可以看出ai确认在调用函数)。当询问跟函数无关的问题,比如我提问“java的创始人是谁”时 ,ai根据语义判断找不到代码中的相关函数 ,就回归正常聊天模式,自己思考后回答。
TOOLS部分的代码作用是告诉ai在什么情况下调用哪种函数,比如要查询天气就会调用“get_weather”函数,计算加法就调用“calculator_add”,算乘法就调用“calculator_multiply”。一开始我认为ai能精准地调用函数是因为“name”:函数名,但是我修改函数名后ai仍能正确调用函数:
"name": "calculator_add", "description": "查询指定城市的天气","name": "get_weather", "description": "计算两个数字的和",由结果可知改变TOOLS中调用的函数名对ai选取函数进行调用没有影响。在查询相关资料后我才知道,影响ai判断的不是函数名name,而是下一行的 description(描述)。在ai的Function Calling机制中,description 通常指的是函数/工具的说明文本,告诉ai在什么情况下要调用这个函数/工具。但是我把TOOLS中调用的两个函数的函数名调换后,其描述就会匹配不上(description告诉ai要查天气就调用““calculator_add”函数,要算加法就调用“get_weather”函数),理论上得到的结果是错误的,可ai仍能正确调用函数。
在进一步学习后,我发现不光函数名下面有 description,其传入的参数也有对应的 description(如下所示)。虽然前面的描述和函数名不匹配,但ai根据用户语义和参数 description,综合判断出真正要调用的函数是什么。接着把调用后的结果和会话内容作为prompt进行思考,根据会话语境输出正常的回答。
"city": { "type": "string", "description": "城市名称,例如:北京、上海、深圳" }"a": {"type": "number", "description": "第一个数字"}, "b": {"type": "number", "description": "第二个数字"}总结:今天入门学习Function Calling,它是ai实现动态交互的核心机制。创建的demo中通过让ai自己判断和调用自定义函数来模拟ai从决策到执行的过程。在学习中我发现description(描述)对ai的判断有很大作用,不仅函数名有description,参数名也有对应的description,ai在判断时会综合所有description进行考量来决定要不要调用函数和调用哪个函数,不会只看某个单一的description。通过学习今天的内容,我对agent应用有了新的理解,RAG和Function Calling都可作为实用的组件为agent提供高效便捷的服务。
第七天就到这吧。