import smtplib from email.mime.text import MIMEText from openai import OpenAI import os import json from dotenv import load_dotenv load_dotenv() client = OpenAI( api_key=os.environ.get("DEEPSEEK_API_KEY"), base_url="https://api.deepseek.com", ) # 1. 定义工具描述 tools = [ { "type": "function", "function": { "name": "get_weather", "description": "获取指定城市的天气", "parameters": { "type": "object", "properties": { "location": {"type": "string", "description": "城市名,如杭州"}, }, "required": ["location"], }, }, }, { "type": "function", "function": { "name": "send_email_qq", "description": "使用QQ邮箱SMTP发送纯文本邮件", "parameters": { "type": "object", "properties": { "text": {"type": "string", "description": "邮件正文内容"}, "subject": {"type": "string", "description": "邮件主题"}, }, "required": ["text", "subject"], }, }, } ] # 工具实现 def get_weather(location: str) -> str: return f"{location}的天气为 8℃" def send_email_qq(text, subject): mail_host = "smtp.qq.com" mail_port = 465 mail_user = "wolin105@qq.com" mail_pass = "pingnllumhbzbceg" # QQ邮箱授权码 sender = mail_user receivers = "jzk.jie@qq.com" message = MIMEText(text, 'plain', 'utf-8') message["From"] = sender message["To"] = receivers message["Subject"] = subject try: smtp = smtplib.SMTP_SSL(mail_host, mail_port) smtp.login(mail_user, mail_pass) smtp.sendmail(sender, [receivers], message.as_string()) smtp.quit() print("邮件发送成功") return "邮件发送成功" except Exception as e: err_msg = f"邮件发送失败: {e}" print(err_msg) return err_msg tools_map = {'get_weather': get_weather, 'send_email_qq': send_email_qq} def execute_tool_calls(tool_calls, tools_map: dict) -> list: """执行模型返回的tool_calls,返回tool角色消息字典列表""" tool_messages = [] for tool_call in tool_calls: func_name = tool_call.function.name print(f"\n调用工具:{func_name}") args = json.loads(tool_call.function.arguments) print(f"参数:{args}") func = tools_map[func_name] result = func(**args) tool_msg = { "role": "tool", "tool_call_id": tool_call.id, "content": result } tool_messages.append(tool_msg) return tool_messages if __name__ == "__main__": messages = [ {"role": "user", "content": "帮我发一个邮件,邮件内容是描述今天学了functioncalling的心得,主题是学习心得,只输出文本即可"} ] # ✅ while循环自动多轮工具调用,直到模型不再返回tool_calls while True: response = client.chat.completions.create( model="deepseek-v4-pro", messages=messages, tools=tools, ) assistant_msg = response.choices[0].message print("\n=====模型返回assistant消息对象=====") print(assistant_msg) # ✅ 关键修复:转成字典,再追加到messages,不能直接append对象! messages.append(assistant_msg.model_dump()) # 判断是否还有工具调用 if not assistant_msg.tool_calls: # 没有工具调用,拿到最终回答,退出循环 print("\n模型最终回复:", assistant_msg.content) break # 执行工具,拿到tool消息 tool_msg_list = execute_tool_calls(assistant_msg.tool_calls, tools_map) messages.extend(tool_msg_list)