这段代码使用了 OpenAI 兼容的 API 接口(阿里云 DashScope 的 Qwen 模型),结合 函数调用(Function Calling / Tools) 功能,实现了一个简单的“股票收盘价查询”对话系统。下面我将逐部分解析其逻辑和功能。


一、导入依赖

1
2
3
import json
import os
from openai import OpenAI
  • json:用于解析模型返回的函数参数(字符串形式的 JSON)。
  • os:用于读取环境变量中的 API 密钥。
  • OpenAI:使用官方 OpenAI SDK,但实际调用的是阿里云 DashScope 的兼容接口。

二、初始化 OpenAI 客户端

1
2
3
4
client = OpenAI(
api_key=os.getenv("AliDeep"),
base_url="https://dashscope.aliyuncs.com/compatible-mode/v1 "
)
  • 使用环境变量 AliDeep 作为 API Key(需提前设置)。
  • base_url 指向 阿里云 DashScope 的 OpenAI 兼容模式接口(注意末尾有空格,可能是个笔误,但不影响运行)。
  • 这样就可以用 OpenAI SDK 调用阿里云的 Qwen 模型。

三、定义工具(Tools)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
tools = [
{
"type": "function",
"function": {
"name": "get_closing_price",
"description": "使用该工具获取指定股票的收盘价",
"parameters": {
"type": "object",
"properties": {
"name": {"type": "string", "description": "股票名称"}
},
"required": ["name"]
}
}
}
]
  • 定义了一个名为 get_closing_price 的工具(函数)。
  • 模型在需要查询股票价格时,会“调用”这个工具,并传入 name 参数(如“青岛啤酒”)。

四、定义实际的工具函数

1
2
3
4
5
6
7
def get_closing_price(name):
if name == "青岛啤酒":
return "67.92"
elif name == "贵州茅台":
return "1488.21"
else:
return "未搜到该股票"
  • 这是一个模拟的股票查询函数,仅支持两个股票,其余返回“未搜到”。

五、主流程(__main__

1. 初始化用户消息

1
messages = [{"role": "user", "content": "青岛啤酒的收盘价是多少?"}]
  • 用户提问:“青岛啤酒的收盘价是多少?”

2. 第一次调用模型

1
2
response = send_messages(messages)
messages.append(response.choices[0].message)
  • 调用 qwen-max 模型,传入 tools
  • 模型判断需要调用 get_closing_price 工具,不会直接回答,而是返回一个 tool_calls 字段。
  • 将模型的回复(包含工具调用信息)追加到 messages 中,用于后续对话上下文。

3. 打印第一次回复

1
2
3
4
print("回复:")
print(response.choices[0].message.content) # 通常是 None 或空
print("工具选择:")
print(response.choices[0].message.tool_calls) # 显示要调用的函数和参数
  • 此时 content 通常为 None,因为模型选择调用工具而非直接回答。
  • tool_calls 包含函数名和参数(如 {"name": "青岛啤酒"})。

4. 执行工具调用

1
2
3
4
5
6
7
8
9
10
if response.choices[0].message.tool_calls != None:
tool_call = response.choices[0].message.tool_calls[0]
if tool_call.function.name == "get_closing_price":
arguments_dict = json.loads(tool_call.function.arguments)
price = get_closing_price(arguments_dict['name'])
messages.append({
"role": "tool",
"content": price,
"tool_call_id": tool_call.id
})
  • 解析参数(JSON 字符串转字典)。
  • 调用本地 get_closing_price 函数获取价格。
  • 工具执行结果作为 role="tool" 的消息加入对话历史(必须包含 tool_call_id 以匹配调用)。

5. 第二次调用模型(传入工具结果)

1
2
3
response = send_messages(messages)
print("回复:")
print(response.choices[0].message.content)
  • 模型收到工具返回的“67.92”后,生成自然语言回答,如:

    “青岛啤酒的收盘价是67.92元。”


六、整体流程总结(Function Calling 两步走)

  1. 第一步:用户提问 → 模型识别需调用工具 → 返回 tool_calls
  2. 第二步:程序执行工具 → 将结果返回给模型 → 模型生成最终回答。

这是标准的 LLM Function Calling 工作流,适用于需要外部数据或操作的场景。


七、潜在问题与改进建议

✅ 优点

  • 正确实现了工具调用的完整流程。
  • 使用 OpenAI SDK 兼容阿里云接口,代码简洁。

⚠️ 注意事项

  1. base_url 末尾空格

    1
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1  "
    • 建议去掉多余空格,避免潜在解析问题。
  2. 硬编码股票数据

    • 实际应用中应对接真实股票 API(如 Tushare、新浪财经等)。
  3. 错误处理缺失

    • 未处理 json.loads 失败、网络异常、模型无响应等情况。
  4. 仅支持单次工具调用

    • 代码假设只有一个 tool_call,若模型返回多个,会出错。可改为循环处理。

八、示例输出(预期)

1
2
3
4
5
6
7
8
9
10
11
回复:
None
工具选择:
[ChatCompletionMessageToolCall(... function=Function(name='get_closing_price', arguments='{"name": "青岛啤酒"}') ...)]
messages: [
{'role': 'user', 'content': '青岛啤酒的收盘价是多少?'},
{'role': 'assistant', 'content': None, 'tool_calls': [...]},
{'role': 'tool', 'content': '67.92', 'tool_call_id': '...'}
]
回复:
青岛啤酒的收盘价是67.92元。