mirror of
http://47.106.207.27:3000/Jeremy_liu/AI0814_jiaoan_public.git
synced 2026-09-27 06:14:14 +08:00
126 lines
3.9 KiB
Python
126 lines
3.9 KiB
Python
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)
|