🍉 1. 介绍
3月6号AI圈都被Manus
炸锅了,同时阿里开源了最新的推理模型QwQ-32B
,这个新闻被Manus
铺天盖地的宣传掩盖了,但实际QwQ-32B
也非常炸裂,我们一起来看看QwQ-32B
划重点:
QwQ-32B 与满血版 DeepSeek R1(671B)在五项基准测试的得分不相上下,更是远超同尺寸 R1 蒸馏模型。
这可是32B
和 671B
的对比,差了一个数量级
QwQ-32B开源链接:
魔搭开源链接:modelscope.cn/models/Qwen...
huggingface开源链接:huggingface.co/Qwen/QwQ-32...
在线体验地址:
我们一起来看看实测情况
🥭 2. 本地部署:保姆级实战教程
① 模型下载
AutoDL 上租用一张 4090,本地部署了一个 QwQ-32B-AWQ 量化版本。
shell
----------------
ubuntu 22.04
python 3.12
cuda 12.1
pytorch 2.3.0
----------------
使用 modelscope 中的 snapshot_download 函数下载模型,第一个参数为模型名称,参数 cache_dir 为模型的下载路径。
新建 model_download.py
文件并在其中输入以下内容,粘贴代码后记得保存文件。
ini
from modelscope import snapshot_download
model_dir = snapshot_download('Qwen/QwQ-32B-AWQ', cache_dir='/root/autodl-tmp', revision='master')
QwQ-32B-AWQ 为 4 bit 量化版
然后在终端中输入 python model_download.py
执行下载,这里需要耐心等待一段时间直到模型下载完成。
注意:记得修改
cache_dir='/root/autodl-tmp'
, 为你的模型下载路径
② vllm部署
Python脚本测试
新建 vllm_model.py
文件并在其中输入下文的代码:
首先从 vLLM
库中导入 LLM
和 SamplingParams
类。LLM
类是使用 vLLM
引擎运行离线推理的主要类。SamplingParams
类指定采样过程的参数,用于控制和调整生成文本的随机性和多样性。
vLLM
提供了非常方便的封装,我们直接传入模型名称或模型路径即可,不必手动初始化模型和分词器
python
# vllm_model.py
from vllm import LLM, SamplingParams
from transformers import AutoTokenizer
import os
import json
# 自动下载模型时,指定使用modelscope; 否则,会从HuggingFace下载
os.environ['VLLM_USE_MODELSCOPE']='True'
def get_completion(prompts, model, tokenizer=None, max_tokens=8192, temperature=0.6, top_p=0.95, max_model_len=2048):
stop_token_ids = [151329, 151336, 151338]
# 创建采样参数。temperature 控制生成文本的多样性,top_p 控制核心采样的概率,避免无休止的重复
sampling_params = SamplingParams(temperature=temperature, top_p=top_p, max_tokens=max_tokens, stop_token_ids=stop_token_ids)
# 初始化 vLLM 推理引擎
llm = LLM(model=model, tokenizer=tokenizer, max_model_len=max_model_len,trust_remote_code=True)
outputs = llm.generate(prompts, sampling_params)
return outputs
if __name__ == "__main__":
# 初始化 vLLM 推理引擎
model='/root/autodl-tmp/Qwen/QwQ-32B-AWQ' # 指定模型路径
# model='/root/autodl-tmp/Qwen/QwQ-32B-AWQ' # 指定模型名称,自动加载模型
tokenizer = None
# 加载分词器后传入vLLM 模型,但不是必要的。
# tokenizer = AutoTokenizer.from_pretrained(model, use_fast=False)
text = ["9.11与9.9哪个更大", ] # 可用 List 同时传入多个 prompt,根据 qwen 官方的建议,每个 prompt 都需要以 <think>\n 结尾,如果是数学推理内容,建议包含(中英文皆可):Please reason step by step, and put your final answer within \boxed{}.
# messages = [
# {"role": "user", "content": prompt+"<think>\n"}
# ]
# 作为聊天模板的消息,不是必要的。
# text = tokenizer.apply_chat_template(
# messages,
# tokenize=False,
# add_generation_prompt=True
# )
outputs = get_completion(text, model, tokenizer=tokenizer, max_tokens=8192, temperature=0.6, top_p=0.95, max_model_len=2048) # 思考需要输出更多的 Token 数,max_tokens 设为 8K,根据 qwen 官方的建议,temperature应在 0.5-0.7,推荐 0.6
# 输出是一个包含 prompt、生成文本和其他信息的 RequestOutput 对象列表。
# 打印输出。
for output in outputs:
prompt = output.prompt
generated_text = output.outputs[0].text
if r"</think>" in generated_text:
think_content, answer_content = generated_text.split(r"</think>")
else:
think_content = ""
answer_content = generated_text
print(f"Prompt: {prompt!r}, Think: {think_content!r}, Answer: {answer_content!r}")
运行代码
python vllm_model.py
根据命令行打印的信息可以看到模型在一张4090GPU上完美运行,推理一共花了21.39秒,结果如下所示:
markdown
Prompt: '9.11与9.9哪个更大', Think: '', Answer: '?为什么? 要比较9.11和9.9的大小,可以按照以下步骤进行:\n\n1. **比较整数部分**:两个数的整数部分都是9,所以整数部分相等,无法直接判断大小。\n\n2. **比较小数部分**:\n - 9.11的小数部分是0.11(即十分位是1,百分位是1)。\n - 9.9的小数部分是0.9(即十分位是9,百分位是0)。\n\n3. **进一步比较**:\n - 先看十分位:9.11的十分位是1,而9.9的十分位是9。\n - 因为9(来自9.9)比1(来自9.11)大,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 原因总结:\n- 小数的比较从高位开始,十分位的权重大于百分位。\n- 9.9的十分位是9,而9.11的十分位是1,所以9.9更大。 \n\n如果需要更直观的比较,可以将两个数转换为相同的位数:\n- 9.9 = 9.90\n- 9.11 = 9.11\n\n显然,9.90 > 9.11,因此结论正确。 \n\n**答案:9.9更大,因为它的十分位数9大于9.11的十分位数1。** \n\n(注:如果题目中的数字可能有不同的解释,例如9.11是否可能指9月11日,而9.9指9月9日,那么问题就需要重新理解。但根据常规数学问题的语境,这里显然是比较两个小数的大小。) \n\n如果存在其他解释,例如单位不同(如9.11可能代表货币中的美元和美分,而9.9是欧元或其他货币),但题目中未提及单位差异,因此应默认比较数值本身。 \n\n综上所述,数学上9.9大于9.11。 \n\n---\n\n### 补充说明:\n在比较小数时,步骤通常是:\n1. 比较小数点左边的整数部分,较大的数整体较大。\n2. 如果整数部分相同,则依次比较小数点后的每一位数字,直到找到不同的位。\n3. 第一个不同的位上数字较大的那个数整体较大。\n\n例如:\n- 9.11 vs 9.9:\n - 整数部分相同(9);\n - 第一位小数(十分位):1 vs 9 → 9更大;\n - 因此,9.9更大。\n\n因此结论明确无误。 \n\n希望这个解释能帮助理解!\n### 最终答案:\n\boxed{9.9}\n\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9} The process of comparing the numbers 9.11 and 9.9 is as follows:\n\n1. **整数部分比较**: Both numbers have the same integer part, which is 9.\n2. **小数部分比较**:\n - The tenths place of 9.11 is 1.\n - The tenths place of 9.9 is 9.\n - Since 9 > 1, the tenths place of 9.9 is larger.\n\nTherefore, **9.9 is greater than 9.11**.\n\n### Final Answer:\n\boxed{9.9}\n\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed{9.9}\n你的思考过程和结论都是正确的。以下是总结:\n\n当比较两个小数的大小时,首先比较它们的整数部分。如果整数部分相同,则依次比较小数点后的每一位数字:\n\n1. **整数部分**:两个数都是9,因此整数部分相同。\n2. **十分位(第一位小数)**:\n - 9.11的十分位是1。\n - 9.9的十分位是9。\n - 因为9 > 1,所以9.9的小数部分更大。\n\n因此,**9.9比9.11更大**。\n\n### 最终答案:\n\boxed'
③创建兼容 OpenAI API 接口的服务器
vLLM
兼容 OpenAI API
协议,所以我们可以直接使用 vLLM
创建 OpenAI API
服务器。vLLM
部署实现 OpenAI API
协议的服务器非常方便。默认会在 http://localhost:8000 启动服务器。服务器当前一次托管一个模型,并实现列表模型、completions
和 chat completions
端口。
completions
:是基本的文本生成任务,模型会在给定的提示后生成一段文本。这种类型的任务通常用于生成文章、故事、邮件等。chat completions
:是面向对话的任务,模型需要理解和生成对话。这种类型的任务通常用于构建聊天机器人或者对话系统。
在创建服务器时,我们可以指定模型名称、模型路径、聊天模板等参数。
--host
和--port
参数指定地址。--model
参数指定模型名称。--chat-template
参数指定聊天模板。--served-model-name
指定服务模型的名称。--max-model-len
指定模型的最大长度。
复制以下代码到命令行运行:
shell
python -m vllm.entrypoints.openai.api_server \
--model /root/autodl-tmp/Qwen/QwQ-32B-AWQ \
--served-model-name QwQ-32B \
--max-model-len=4096
加载完毕后命令行打印以下信息说明服务成功启动
- 新建一个命令行界面
- 通过
curl
命令查看当前的模型列表
shell
curl http://localhost:8000/v1/models
得到的返回值如下所示
json
{
"object": "list",
"data": [
{
"id": "QwQ-32B",
"object": "model",
"created": 1741240566,
"owned_by": "vllm",
"root": "/root/autodl-tmp/Qwen/QwQ-32B-AWQ",
"parent": null,
"max_model_len": 2048,
"permission": [
{
"id": "modelperm-b7450ca5ebbd41bb924f390e774f3cba",
"object": "model_permission",
"created": 1741240566,
"allow_create_engine": false,
"allow_sampling": true,
"allow_logprobs": true,
"allow_search_indices": false,
"allow_view": true,
"allow_fine_tuning": false,
"organization": "*",
"group": null,
"is_blocking": false
}
]
}
]
}
- 使用
curl
命令测试OpenAI Completions API
shell
curl http://localhost:8000/v1/completions \
-H "Content-Type: application/json" \
-d '{
"model": "QwQ-32B",
"prompt": "10的阶乘是多少?<think>\n",
"max_tokens": 1024,
"temperature": 0
}'
得到的返回值如下所示
json
{
"id": "cmpl-d5538daeb4424c09ae06cb3f0f28b79b",
"object": "text_completion",
"created": 1741240821,
"model": "QwQ-32B",
"choices": [
{
"index": 0,
"text": "\n\n10的阶乘是3628800。",
"logprobs": null,
"finish_reason": "stop",
"stop_reason": null,
"prompt_logprobs": null
}
],
"usage": {
"prompt_tokens": 9,
"total_tokens": 26,
"completion_tokens": 17,
"prompt_tokens_details": null
}
}
- 用
Python
脚本请求OpenAI Completions API
ini
# vllm_openai_completions.py
from openai import OpenAI
client = OpenAI(
base_url="http://localhost:8000/v1",
api_key="sk-xxx", # 随便填写,只是为了通过接口参数校验
)
completion = client.chat.completions.create(
model="QwQ-32B",
messages=[
{"role": "user", "content": "10的阶乘是多少?<think>\n"}
]
)
print(completion.choices[0].message)
python vllm_openai_completions.py
得到的返回值如下所示
python
ChatCompletionMessage(content='嗯,用户问的是"10的阶乘是多少?"。好的,先回想一下阶乘的定义。阶乘就是从1乘到那个数,对吧?所以10的阶乘应该是1×2×3×...×10。不过有时候可能会记得不太清楚具体的数值,可能会出错,所以最好仔细算一遍。\n\n让我先在脑子里计算一下。10的阶乘,也就是10!。先分步骤来算,这样不容易出错。首先0!是1,那1!是1,2!是2,接下来是6,24,120,好像到5的时候是120对吧?那5!确实是120。然后继续6!就是120×6=720,对吗?对的,因为6×5!是120×6=720。接着是7!,也就是720×7=5040。然后8!应该是5040×8,这应该是40320,对吗?是的,8×5000是40000,加上8×40=320,刚好40320。接下来是9!,也就是40320×9,这个得算清楚,40000×9=360000,加上320×9=2880,总和是362880,对吧?然后到10!,也就是362880×10,这样应该就是3628800了。所以10的阶乘是3628800?\n\n等一下,我是不是算错了?比如在算到9!的时候可能有没有哪里算错?让我再检查一遍。比如从8!是40320开始,乘以9得9!,40320×9。我可以分开算:40000×9=360000,320×9=2880,加起来是360000+2880=362880。这个没问题,那再乘以10就是3628800,是的,没错。\n\n或者我可以换一种方式计算,比如分解成各个数的连乘。1×2=2,然后乘3是6,到4是24,5就是120,6是720,7是5040,8是40320,9是362880,最后乘以10确实是3628800。嗯,看来是对的。或者也可以用其他方法,比如分解质因数,或者看看有没有记忆中的固定数值。\n\n或者我可以查一下计算器,不过现在假设我现在没有计算器的情况下,只能自己算。或者可以用另一种方式,比如把阶乘拆分成已知的数值。例如,我知道5!是120,然后6!是120×6=720,7!是720×7=5040,8!是5040×8=40320,9!是40320×9=362880,10!就是362880×10=3628800。看起来没问题。\n\n再回忆一下,可能之前学过的时候,十的阶乘确实是3628800,这样应该没错吧?比如,是不是经常在数学题中出现十的阶乘作为中间值?或者是否有什么常见的错误,比如可能把8!算错?\n\n有时候可能会把中间过程弄错,比如在算到某个数的时候,乘错。但刚才分步算应该是正确的。8!确实是40320,而9!就是40320×9,是的,所以结果是对的。那再确认一遍:1×2=2,2×3=6,6×4=24,24×5=120,120×6=720,720×7=5040,5040×8=40320,40320×9=362880,362880×10=3628800。没错。\n\n所以答案应该是3628800对吧?嗯,看来是对的。不过为了确保正确,可能还可以用另一种方法,比如分解每个数的因数,然后组合起来,不过这样可能更麻烦。或者用公式,比如n! = n × (n-1)!,逐步递推。没错,这样一步一步来,应该没问题。\n\n总之,结论是10的阶乘是3628800。不过还是应该再检查一下,或许有没有哪里算错了?比如,当计算到5!,对吗:5×24=120是正确的。乘到6就是120×6=720,没错。7是720×7,没错,是5040。然后8是5040×8,就是40320。没错,没错。然后到9: 40320×9,等于362,880?是的,加上0就是到了9位数吗?然后乘以10,得到3,628,800。对的,没错。没错的话,应该正确。\n</think>\n\n10的阶乘(10!)是通过将从1到10的所有整数相乘得到的:\n\n\[\n10! = 1 \times 2 \times 3 \times 4 \times 5 \times 6 \times 7 \times 8 \times 9 \times 10\n\]\n\n分步计算如下:\n- \(1 \times 2 = 2\)\n- \(2 \times 3 = 6\)\n- \(6 \times 4 = 24\)\n- \(24 \times 5 = 120\)\n- \(120 \times 6 = 720\)\n- \(720 \times 7 = 5040\)\n- \(5040 \times 8 = 40,\!320\)\n- \(40,\!320 \times 9 = 362,\!880\)\n- \(362,\!880 \times 10 = 3,\!628,\!800\)\n\n因此,**10的阶乘是3,628,800**。', refusal=None, role='assistant', audio=None, function_call=None, tool_calls=[], reasoning_content=None)
④ 性能监控
实测下来,QwQ-32B-AWQ 量化版本显存占用不到 18GB ,一张 4090 绰绰有余。而 32B 的 QwQ,也是完美适配消费级硬件。
⑤ 创建前端webdemo进行本地交互
新建一个python脚本app.py
,填入如下代码:
python
import streamlit as st
import requests
import re
# 在侧边栏中创建一个标题和一个链接
with st.sidebar:
st.markdown("## QwQ-32B LLM")
"[开源大模型食用指南 self-llm](https://github.com/datawhalechina/self-llm.git)"
# 创建一个滑块,用于选择最大长度,范围在 0 到 2048 之间,默认值为 1024(QwQ-32B 支持 8192 tokens,不过我们一张4090显存较小,稳妥起见设置最大长度为为2048)
max_length = st.slider("max_length", 0, 2048, 1024, step=1)
# 创建一个标题和一个副标题
st.title("💬 QwQ-32B Chatbot")
st.caption("🚀 A streamlit chatbot powered by Self-LLM")
# 文本分割函数
def split_text(text):
pattern = re.compile(r'<think>(.*?)</think>(.*)', re.DOTALL) # 定义正则表达式模式
match = pattern.search(text) # 匹配 <think>思考过程</think>回答
if match: # 如果匹配到思考过程
think_content = match.group(1).strip() # 获取思考过程
answer_content = match.group(2).strip() # 获取回答
else:
think_content = "" # 如果没有匹配到思考过程,则设置为空字符串
answer_content = text.strip() # 直接返回回答
return think_content, answer_content
# 如果 session_state 中没有 "messages",则创建一个包含默认消息的列表
if "messages" not in st.session_state:
st.session_state["messages"] = [{"role": "assistant", "content": "有什么可以帮您的?"}]
# 遍历 session_state 中的所有消息,并显示在聊天界面上
for msg in st.session_state.messages:
st.chat_message(msg["role"]).write(msg["content"])
# 如果用户在聊天输入框中输入了内容,则执行以下操作
if prompt := st.chat_input():
# 在聊天界面上显示用户的输入
st.chat_message("user").write(prompt)
# 将用户输入添加到 session_state 中的 messages 列表中
st.session_state.messages.append({"role": "user", "content": prompt})
# 调用本地运行的 vllm 服务
try:
response = requests.post(
"http://localhost:8000/v1/chat/completions",
json={
"model": "QwQ-32B",
"messages": st.session_state.messages,
"max_tokens": max_length
}
)
if response.status_code == 200:
response_data = response.json()
assistant_response = response_data["choices"][0]["message"]["content"]
think_content, answer_content = split_text(assistant_response) # 调用split_text函数,分割思考过程和回答
# 将模型的输出添加到 session_state 中的 messages 列表中
st.session_state.messages.append({"role": "assistant", "content": assistant_response})
# 在聊天界面上显示模型的输出
with st.expander("模型思考过程"):
st.write(think_content) # 展示模型思考过程
st.chat_message("assistant").write(answer_content) # 输出模型回答
else:
st.error("Error generating response.")
except Exception as e:
st.error(f"An error occurred: {e}")
执行命令:python app.py
,打开http://localhost:8501 即可在网页与本地模型对话了。
🍒 3. 性能测试
代码能力
编写一个 python 脚本,显示一个球在旋转的六边形内部弹跳。球应该受到重力和摩擦的影响,并且必须真实地弹跳 off 转动的墙壁。
可以看到,整个球体的弹跳和撞击特别真实,就连小球带动大框的效果都做出来了,很好的还原了真实的物理场景。
而 Grok-3 在这个问题上直接就炸了,小球直接自由落体。
数学能力
拿了两道数一的考研题进行了测试
题目一:回答完全正确
题目二:回答完全正确。
数学和代码作为 QwQ-32B 的主攻方向,确实是效果极佳。
逻辑推理
最后的逻辑推理能力实测来自 unlock-deepseek 项目群。
有一道很有意思的题目:
下面我会给你一道数学单选题,请选出正确答案。题目信息如下:
下面说法正确的是( ).
A:跳远测距离,根据的是两点之间线段最短.
B:跳高杆不会掉落,是因为两点确定一条直线.
C:多条跑道找捷径,根据的是垂线段最短.
D:同一路口白色斑马线互相平行,是因为过直线外一点有且只有一条直线与已知直线平行.
这道题有多难呢,大家可以试试看,模型的思考过程真的特别精彩,上演了足足 7 分多钟的左右脑互搏。。但是 QwQ 还是回答出了正确答案。
我后续再实测结果和他们有点不符
不过这种题,就算真人来做也有很多争议
🫒 4. Agent 相关能力:Function Call
QwQ-32B 中还集成了与 Agent(智能体)相关的能力,支持函数调用。测试使用了一下,搭建了一个股票数据分析 Agent:
python
import os
from openai import OpenAI
import efinance as ef
import json
openai_api_base = "http://localhost:8000/v1"
api_key = "sk-xxxxxx"
client = OpenAI(
api_key=api_key,
base_url=openai_api_base,
)
def query_stock_code(stock_name: str):
"""获取股票实时行情数据
Args:
stock_name (str): 股票名称或代码
Returns:
dict: 包含股票实时数据的字典,包括价格、成交量等信息
如果发生错误,返回包含错误信息的字典
"""
try:
stock_data = ef.stock.get_realtime_quotes(stock_name)
return stock_data.to_dict("records")
except Exception as e:
return {"error": str(e)}
def get_stock_history(stock_code, start_date=None, end_date=None):
"""获取股票历史行情数据
Args:
stock_code (str): 股票代码
start_date (str, optional): 开始日期,格式:YYYY-MM-DD
end_date (str, optional): 结束日期,格式:YYYY-MM-DD
Returns:
dict: 包含股票历史数据的字典,包括日期、开盘价、收盘价、成交量等信息
如果发生错误,返回包含错误信息的字典
"""
try:
stock_data = ef.stock.get_quote_history(
stock_code, start=start_date, end=end_date
)
return stock_data.to_dict("records")
except Exception as e:
return {"error": str(e)}
def get_stock_financial(stock_code):
"""获取股票财务报表数据
Args:
stock_code (str): 股票代码
Returns:
dict: 包含股票财务数据的字典,包括资产负债表、利润表等信息
如果发生错误,返回包含错误信息的字典
"""
try:
stock_data = ef.stock.get_financial_report(stock_code)
return stock_data.to_dict("records")
except Exception as e:
return {"error": str(e)}
function_map = {
"query_stock_code": query_stock_code,
"get_stock_history": get_stock_history,
"get_stock_financial": get_stock_financial,
}
functions _ = [
{
"name": "query_stock_code",
"description": "获取股票实时行情数据",
"parameters": {
"type": "object",
"properties": {
"stock_name": {
"type": "string",
"description": "股票名称或代码",
},
},
"required": ["stock_name"],
},
"name": "get_stock_history",
"description": "获取股票历史行情数据",
"parameters": {
"type": "object",
"properties": {
"stock_code": {
"type": "string",
"description": "股票代码",
},
"start_date": {
"type": "string",
"description": "开始日期,格式:YYYY-MM-DD",
},
"end_date": {
"type": "string",
"description": "结束日期,格式:YYYY-MM-DD",
},
},
"required": ["stock_code"],
},
"name": "get_stock_financial",
"description": "获取股票财务报表数据",
"parameters": {
"type": "object",
"properties": {
"stock_code": {
"type": "string",
"description": "股票代码",
},
},
"required": ["stock_code"],
},
},
]
def interactive_stock_query():
print("欢迎使用股票查询助手!您可以询问任何有关股票的问题。输入'退出'结束对话。")
# 保存对话历史
conversation_history = []
while True:
user_input = input("\n 请输入您的问题:")
if user_input.lower() in ["退出", "exit", "bye"]:
print("感谢使用股票查询助手!再见!")
break
conversation_history.append({"role": "user", "content": user_input})
try:
response = client.chat.completions.create(
model="QwQ-32B",
messages=conversation_history,
functions=functions,
function_call="auto",
)
assistant_message = response.choices[0].message
# 添加助手回复到对话历史
conversation_history.append(assistant_message)
if assistant_message.function_call:
function_name = assistant_message.function_call.name
# 解析函数参数
try:
function_args = json.loads(assistant_message.function_call.arguments)
except json.JSONDecodeError:
print("函数参数解析错误!")
continue
print(f"正在调用函数:{function_name}")
print(f"函数参数:{function_args}")
# 调用函数
if function_name in function_map:
function_to_call = function_map.get(function_name)
function_response = function_to_call(**function_args)
# 添加函数响应到对话历史
conversation_history.append(
{
"role": "function",
"name": function_name,
"content": str(function_response),
}
)
# 再次请求模型获取最终回复
second_response = client.chat.completions.create(
model="QwQ-32B",
messages=conversation_history,
)
# 打印最终回复
print("\n助手:" + second_response.choices[0].message.content)
# 添加到对话历史
conversation_history.append(second_response.choices[0].message)
else:
print(f"未知函数: {function_name}")
else:
print("\n助手:" + assistant_message.content)
except Exception as e:
print(f"发生错误:{str(e)}")
if __name__ == "__main__":
interactive_stock_query()
成果展示:
支持 Function Call 不仅增强了模型的实际应用能力,还能使它能够在使用工具的同时进行批判性思考,并根据环境反馈调整推理过程。
📒 5. 总结
总体来说,这次通义开源的 QwQ-32B 推理模型还是很不错的:
- 性能与能效双优:在32B紧凑参数体量下,其推理能力已可对标百亿级大模型,开创性地实现了高性能与低能耗的平衡。这种模型架构创新显著降低了算力门槛,为绿色AI发展提供了切实可行的技术路径。
- 工业级响应能力:模型展现出卓越的推理效率,即便在高并发场景下仍能保持稳定的服务响应,有效解决了实际应用中的资源瓶颈问题。
- 开发者友好设计:原生支持的Function Call功能为复杂应用开发提供了关键基础设施,极大拓展了模型在工具调用、流程编排等场景的应用边界。
纵观行业演进,自o1模型发布后的短短五个月内,国内推理模型领域已形成群雄并起的创新格局。尤其值得关注的是,在开发者们热议DeepSeek-R1完整功能版本部署方案之际,QwQ-32B凭借其"小身材大能量"的特性异军突起。该模型既支持本地化部署降低硬件要求,又可通过阿里云百炼平台灵活调用API,为初创团队和企业级用户提供了兼具经济性与专业度的AI解决方案。
作为开源生态的持续建设者,通义千问自2023年8月以来已累计开源超200个模型,始终以务实姿态推动技术普惠。这些扎实的工程实践不仅加速了大模型技术的产业化落地,更通过开源协作持续滋养着中国AI开发生态。(๑•̀ㅂ•́)و✧