"""练习 1:Function Calling —— 单工具、单次调用
本练习只想说明一件事:
模型【不会】执行函数。它只负责【决定要不要调用】以及【吐出参数】,
真正去执行 get_weather() 的,是下面我们自己写的 Python 代码。
一次完整的工具调用包含三步:
1) 我们把「问题 + 工具说明」发给模型;
2) 模型回一个 tool_call(函数名 + JSON 参数),它自己不执行;
3) 我们用这些参数真正运行函数,把结果再发回给模型,让它说人话。
云服务用 DeepSeek(OpenAI 兼容接口),密钥/地址/模型名都在根目录 .env:
DEEPSEEK_API_KEY、DEEPSEEK_BASE_URL、MODEL
"""
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
# 从根目录 .env 读取配置
load_dotenv()
client = OpenAI(
api_key=os.environ["DEEPSEEK_API_KEY"],
base_url=os.environ["DEEPSEEK_BASE_URL"],
)
MODEL = os.environ["MODEL"]
# ── 第 1 步:我们的「手」——一个真实的本地函数 ────────────────────────────
# 这就是会被真正执行的代码。这里用假数据,重点不在天气,而在“谁来执行”。
def get_weather(city: str) -> dict:
"""根据城市名返回天气(演示用假数据)。"""
fake_db = {
"北京": {"weather": "晴", "temperature": 26},
"上海": {"weather": "多云", "temperature": 24},
"广州": {"weather": "雷阵雨", "temperature": 30},
}
return fake_db.get(city, {"weather": "未知", "temperature": None})
# ── 第 2 步:工具 schema —— 给模型看的「说明书」 ──────────────────────────
# 注意 description 写的是【什么时候用】,而不是【做什么】。这是 schema 设计的黄金规则。
TOOLS = [
{
"type": "function",
"function": {
"name": "get_weather",
"description": "当用户询问某个城市当前的天气状况或气温时调用。",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,例如:北京、上海",
}
},
"required": ["city"],
},
},
}
]
def run(user_question: str) -> str:
"""跑一遍完整的单工具调用流程,返回模型最终的自然语言回答。"""
messages = [{"role": "user", "content": user_question}]
# ── 第 3 步:把问题 + 工具说明发给模型,看它要不要调用 ──────────────
first = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
)
msg = first.choices[0].message
# 模型决定不调用工具,直接回答(说明这个问题不需要工具)
if not msg.tool_calls:
print("【模型判断】无需工具,直接回答。")
return msg.content
# ── 第 4 步:模型决定调用,但它只给了「函数名 + 参数」,并没有执行 ──
tool_call = msg.tool_calls[0]
fn_name = tool_call.function.name
fn_args = json.loads(tool_call.function.arguments) # 模型吐出的 JSON 参数
print(f"【模型决定】调用 {fn_name},参数 = {fn_args}")
# ── 第 5 步:真正执行的是我们的代码,不是模型 ──────────────────────
if fn_name == "get_weather":
result = get_weather(**fn_args)
else:
result = {"error": f"未知工具 {fn_name}"}
print(f"【我们执行】{fn_name} 返回 = {result}")
# ── 第 6 步:把执行结果发回模型,让它结合结果说人话 ────────────────
messages.append(msg) # 模型刚才那条「我要调用工具」的消息
messages.append(
{
"role": "tool",
"tool_call_id": tool_call.id,
"content": json.dumps(result, ensure_ascii=False),
}
)
second = client.chat.completions.create(
model=MODEL,
messages=messages,
tools=TOOLS,
)
return second.choices[0].message.content
if __name__ == "__main__":
question = "北京今天天气怎么样?"
print(f"【用户提问】{question}\n")
answer = run(question)
print(f"\n【最终回答】{answer}")