跳至主要内容

Qwen3-Next:含演示项目的指南

学习如何构建一个 Streamlit 应用,将 Qwen3-Next-80B-A3B 与其他 Qwen 模型进行并排比较,重点关注真实场景下的性能指标。
· 12分钟 阅读

使用 AI 探索

Qwen 团队近期发布了 Qwen3-Next-80B-A3B 模型,采用混合专家混合(MoE)架构,将高级推理、超长上下文处理和卓越的推理速度融为一体。

在本指南中,我将重点展示 Qwen3-Next-80B-A3B 在实际任务中的表现,并与此前的 Qwen 模型(如 Qwen3-30B-A3B)进行对比。我们不做抽象基准测试,而是进行动手的并排测试,涵盖推理、代码以及超长文档提示,帮助您直观看到速度、输出质量和 GPU 显存占用之间的权衡。

我将一步步演示如何:

  • 通过 OpenRouter API 直接访问并交互 Qwen3-Next-80B-A3B 与 Qwen3-30B-A3B,无需复杂配置
  • 构建一个 Streamlit 应用,实现模型响应、延迟和资源使用的即时并排对比
  • 运行超长上下文检索实验(10 万+ tokens),测试模型的真正极限
  • 分析延迟、tokens/秒和显存占用等真实世界性能指标。

最终,您的应用将如下所示:

Demo

什么是 Qwen3-Next?

Qwen3-Next 是一款具有 800 亿参数的 专家混合(MoE) 模型。在实际运行中,它每个 token 仅激活 30 亿参数,使其在训练成本上最多降低 10 倍、推理速度最多提升 10 倍(相较于早期稠密模型,如 Qwen3-32B),同时在质量上可媲美甚至超越这些模型。

该模型的关键进展包括:

  • 混合架构:Qwen3-Next 采用 75:25 的 Gated DeltaNet(用于快速线性注意力)与 Gated Attention(用于深度回忆)混合,比照结合两者优势。
  • 超稀疏 MoE:拥有 512 位专家,但每次推理仅激活 10+1 位专家,与稠密 LLM 相比将计算量减少 90% 以上
  • 多 token 预测:该模型支持 猜测式解码,在流式或聊天场景中带来大幅提速。
  • 超长上下文:原生支持最多 256K tokens(配合 YaRN 扩展可达 100 万+),性能下降极小,适合长篇书籍、代码库等场景。

Qwen3-Next architecture

来源: Qwen

Qwen 3 Next 模型提供两个变体:

  • Qwen3-Next-80B-A3B-Instruct:面向 通用聊天、编程和开放式问答。
  • Qwen3-Next-80B-A3B-Thinking:用于高级推理、链式思维和研究类工作流。

演示:使用 OpenRouter API 进行 Qwen3-Next 的问答与推理

本节将演示如何通过 OpenRouter API 使用 Qwen3-Next 模型,构建一个交互式的问答与推理助手。

其工作流程如下:

  • 在文本框中输入问题,或拖放一份较长的 PDF 文档。
  • 从下拉菜单中选择最大 token 数量和模型名称。
  • 即时并行查询两个模型,直接比较输出。
  • 查看每次运行的详细指标,包括响应时间(延迟)、每秒 token 数以及输出长度,从而在真实场景中同时考量性能与效率。

步骤 1:先决条件

在运行本演示之前,请确保已准备好以下先决条件。

步骤 1.1:安装依赖 

首先,确保安装以下依赖:

pip install streamlit requests pypdf

该命令会安装构建应用所需的核心依赖:Streamlit(用于 UI)、requests(用于发起 API 调用)以及 pypdf(用于处理 PDF 文档抽取)。

步骤 1.2:配置 OpenRouter API 密钥 

Qwen3-Next 模型也可通过官方 Qwen API(AlibabaCloud)、HuggingFace 以及通过 Ollama 提供的量化版本获取。本文演示使用 OpenRouter,它允许您使用一把 API 密钥访问多种模型。以下是为 Qwen3-next-80b-a3b-instruct 设置 API 密钥的方法:

  • 在 https://openrouter.ai/ 注册并创建账户
  • 进入“Models”选项卡,搜索 “Qwen3-next-80b-a3b-instruct” 和 “Qwen3-30B-A3B” 模型。可根据应用选择 Thinking 或 Instruct 变体。本文比较的是两款 Instruct 变体。

Qwen 3 Next model on OpenRouter

  • 向下滚动并点击 Create API Key。填写密钥名称与信用额度(可选),然后点击 Create。请保存该 API 密钥以备后用。 

API key

  • 接着前往 Credits 选项卡,添加银行卡或银行账户信息。您也可以通过 Amazon Pay 或加密货币支付。本次演示我充值了约 8 美元,已足够使用。

Add credits

现在,在运行应用前将 API 密钥设置为环境变量:

export OPENROUTER_API_KEY=your_api_key

步骤 2:配置模型参数

我们先配置需要比较的模型。每个模型包含一个标识符(用于 API 调用)、总参数量以及激活参数量。

import streamlit as st
import requests
import time
import PyPDF2
MODEL_CONFIG = {
    "Qwen3-Next-80B-A3B": {
        "id": "qwen/qwen3-next-80b-a3b-instruct",
        "params_billion": 80,
        "active_params_billion": 3
    },
    "Qwen3-30B-A3B": {
        "id": "qwen/qwen3-30b-a3b-instruct-2507",
        "params_billion": 30,
        "active_params_billion": 3
    },
    "Qwen3-30B-A3B": {
        "id": "qwen/qwen3-next-80b-a3b-thinking",
        "params_billion": 30,
        "active_params_billion": 3
    },
}

我们首先导入构建 Streamlit 网页应用所需的库,包括用于 UI 的 streamlit、用于 API 调用的 requests、用于计时的 time,以及用于 PDF 文本抽取的 PyPDF2。

随后定义 MODEL_CONFIG 字典,涵盖可参与比较的模型,包括其用于 API 调用的唯一 ID 和参数规模,便于在整个应用中扩展不同的 Qwen3 模型。

步骤 3:辅助函数

接下来我们定义若干后端辅助函数,包括估算显存需求、向 OpenRouter 发起实时 API 调用,以及从上传的 PDF 中抽取文本。

def estimate_vram(params_billion, fp16=True, active_params_billion=None):
    if active_params_billion and active_params_billion != params_billion:
        total_params = params_billion * 1e9
        active_params = active_params_billion * 1e9
        size_bytes = 2 if fp16 else 4
        storage_vram = total_params * size_bytes / 1e9
        moe_overhead = active_params * 0.5 / 1e9  
        system_overhead = storage_vram * 0.2  
        total_vram = storage_vram + moe_overhead + system_overhead
    else:
        size_bytes = 2 if fp16 else 4
        num_params = params_billion * 1e9
        base_vram = num_params * size_bytes / 1e9
        total_vram = base_vram * 1.3 
    return round(total_vram, 1)
def query_openrouter(model_id, prompt, max_tokens=2048):
    api_key = "sk-or-v1-xxxxxxxxxxxxxxxxxxxxx" # Add your API Key here
    url = "https://openrouter.ai/api/v1/chat/completions"
    headers = {
        "Authorization": f"Bearer {api_key}",
        "Content-Type": "application/json"
    }
    data = {
        "model": model_id,
        "messages": [{"role": "user", "content": prompt}],
        "max_tokens": max_tokens
    }
    try:
        start = time.time()
        response = requests.post(url, headers=headers, json=data, timeout=180)
        elapsed = time.time() - start
        response.raise_for_status()
        j = response.json()
        output = j["choices"][0]["message"]["content"].strip()
        usage = j.get("usage", {})
        prompt_tokens = usage.get("prompt_tokens", 0)
        completion_tokens = usage.get("completion_tokens", 0)
        tokens_per_sec = (completion_tokens or 0) / elapsed if elapsed > 0 else 0
        return {
            "output": output,
            "latency": elapsed,
            "output_tokens": completion_tokens,
            "tokens_per_sec": tokens_per_sec,
            "prompt_tokens": prompt_tokens
        }
    except Exception as e:
        return {"error": str(e)}
def extract_pdf_text(uploaded_pdf):
    text = ""
    try:
        reader = PyPDF2.PdfReader(uploaded_pdf)
        for page in reader.pages:
            text += page.extract_text() or ""
        return text[:120000]  
    except Exception as e:
        return f"[PDF extract error: {str(e)}]"

下面说明各函数在流程中的作用:

  • estimate_vram() 函数:用于估算运行模型所需的 GPU 显存,会根据模型是稠密还是 MoE 而有所不同。对 MoE 模型,会同时考虑总参数与激活参数,并加入路由与系统开销。
  • query_openrouter() 函数:负责将提示发送至托管在 OpenRouter 上的目标 LLM 并获取响应。它会测量总延迟(端到端 API 调用时间),提取已处理 token 数等实用指标,并计算生成速度(tokens/秒)。 

请注意,这里的延迟包含网络、排队和服务端的全部延时,而非纯模型推理速度。因此它反映了端到端的真实响应情况,会随 API 负载和网络状况而波动。

  • extract_pdf_text() 函数:允许用户上传并分析 PDF,作为模型的输入上下文。它使用 PyPDF2 解析 PDF 并抽取原始文本,对超长文档进行 token 安全截断。若解析失败,将返回错误信息。

这些辅助函数确保了高效的显存使用、可靠的基于 API 的推理以及顺畅的上下文抽取,从而带来无缝的用户体验。

步骤 4:Streamlit 应用

在核心组件就绪后,我们开始构建 Streamlit 应用。

步骤 4.1:自定义 CSS

构建用户工作流的第一步是视觉设计与布局。在此我们配置 Streamlit 页面并注入自定义 CSS,以获得更有条理的外观。

st.set_page_config(page_title="Qwen3-Next QA & Reasoning", layout="wide", page_icon=" ")
st.markdown("""
<style>
    .main-header {
        text-align: center;
        
        
    }
    .metric-card {
        background-
        
        border-radius: 0.5rem;
        border-left: 4px solid #1f77b4;
        
    }
    .output-box {
        background-
        border: 2px solid #e1e5e9;
        border-radius: 0.5rem;
        
        
        box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    }
    .model-header {
        background: linear-gradient(90deg, #1f77b4, #ff7f0e);
        color: white;
        
        border-radius: 0.5rem;
        
        text-align: center;
        font-weight: bold;
    }
</style>
""", unsafe_allow_html=True)
st.markdown('<h1 class="main-header"> QA and Reasoning With Qwen3-Next</h1>', unsafe_allow_html=True)
st.markdown("""
<div style="text-align: center; ">
    <p style="">
        Compare two Qwen3 models side-by-side with real-time performance metrics
    </p>
</div>
""", unsafe_allow_html=True)

我们同时使用了 Streamlit 的配置选项与自定义 CSS 样式参数。st.set_page_config 设置页面标题与宽屏布局以支持并排输出。通过 st.markdown 注入自定义 CSS,用于美化主要 UI 元素。

.main-header 用于居中并着色标题,.metric-card 与 .output-box 强化指标与输出的展示,.model-header 通过渐变背景区分不同模型的响应。最后一个 st.markdown 块渲染标题与简短的应用说明。

步骤 4.2:输入区域

我们设计了输入区域,为用户提供两种直观方式:上传一份长 PDF 作为上下文,或直接输入自定义问题。

st.markdown("### Input")
with st.container():
    col_upload, col_text = st.columns([2, 3])
    with col_upload:
        uploaded_pdf = st.file_uploader(
            "Upload PDF for context", 
            type=["pdf"],
            help="Upload a PDF document to provide context for your question"
        )
    with col_text:
        user_question = st.text_area(
            "Ask a question:", 
            height=100, 
            max_chars=12000,
            placeholder="Enter your question here...",
            help="Ask any question or provide a topic to analyze"
        )

该输入区域展示两个并排列:第一列提供 PDF 文件上传器,用于添加上下文文档;第二列提供文本输入框,便于输入问题或话题。用户既可以上传 PDF,也可以输入问题,或两者同时作为后续模型比较的输入。

步骤 4.3:模型选择

模型选择部分让用户可以完全掌控要比较的 Qwen 模型,以及模型回复的长度。设置好文档上下文后,用户可从下拉菜单中选择任意两个模型,并指定最大响应长度(以 tokens 计),以便在短文与长文输出之间灵活试验。

context = ""
if uploaded_pdf:
    with st.spinner(" Extracting PDF text..."):
        context = extract_pdf_text(uploaded_pdf)
    st.success(f" PDF loaded: {len(context)//1000}K characters extracted.")
st.markdown("### Model Selection")
model_names = list(MODEL_CONFIG.keys())
col1, col2, col3 = st.columns([2, 2, 1])
with col1:
    model1 = st.selectbox(
        "Model A", 
        model_names, 
        index=0,
        help="Select the first model to compare"
    )
with col2:
    model2 = st.selectbox(
        "Model B", 
        model_names, 
        index=1 if len(model_names) > 1 else 0,
        help="Select the second model to compare"
    )
with col3:
    max_tokens = st.selectbox(
        "Max Tokens",
        [512, 1024, 2048, 4096],
        index=2,  
        help="Maximum length of response (higher = longer responses). Use 2048+ for reasoning questions."
    )

上述代码首先使用辅助函数从上传的 PDF 中抽取文本。随后展示三个下拉框:两个用于选择要比较的 Qwen 模型(或其他模型),一个用于指定响应的最大 token 限制。

注意:token 上限用于控制最大输出长度。对于复杂或多步推理任务,设置更高的 token 上限可确保模型给出完整回答。

步骤 4.4:提交

此步骤实现主要的模型比较逻辑。当用户点击 "Submit & Compare Models" 时,应用会校验输入、构建包含上下文的提示词,并并行向所选模型发起查询。

st.markdown("---")
col_btn1, col_btn2, col_btn3 = st.columns([1, 2, 1])
with col_btn2:
    if st.button("Submit & Compare Models", type="primary", use_container_width=True):
        if not user_question and not uploaded_pdf:
            st.warning(" Please enter a question or upload a PDF.")
        else:
            if context:
                base_prompt = f"DOCUMENT:\n{context}\n\nQUESTION: {user_question or 'Summarize the above document.'}"
            else:
                base_prompt = user_question
            prompt = f"""Please provide a detailed and thorough response. Think step by step and explain your reasoning clearly.
{base_prompt}
Please provide a comprehensive answer with clear reasoning and examples where appropriate."""           
            st.markdown("### Results")
            col_left, col_right = st.columns(2)
            models_to_process = [(model1, col_left, "Model A"), (model2, col_right, "Model B")]          
            for model_key, col, model_label in models_to_process:
                with col:
                    st.markdown(f'<div class="model-header">{model_label}: {model_key}</div>', unsafe_allow_html=True)                   
                    progress_bar = st.progress(0)
                    status_text = st.empty()                    
                    try:
                        progress_bar.progress(25)
                        status_text.text("Initializing model...")
                        model_info = MODEL_CONFIG[model_key]
                        vram_estimate = estimate_vram(
                            model_info["params_billion"], 
                            active_params_billion=model_info["active_params_billion"]
                        )
                        progress_bar.progress(50)
                        status_text.text("Querying model...")
                        result = query_openrouter(model_info["id"], prompt, max_tokens=max_tokens)                        
                        progress_bar.progress(100)
                        status_text.text("Complete!")
                        if "error" in result:
                            st.error(f"Error: {result['error']}")
                        else:
                            st.markdown(f'''
                            <div class="metric-card">
                                <strong>Performance Metrics</strong><br>
                                <strong>Latency:</strong> {result['latency']:.2f}s<br>
                                <strong>Speed:</strong> {result['tokens_per_sec']:.2f} tokens/sec<br>
                                <strong>Output tokens:</strong> {result['output_tokens']}<br>
                                <strong>Prompt tokens:</strong> {result['prompt_tokens']}<br>
                                <strong>Est. VRAM:</strong> {vram_estimate} GB
                            </div>
                            ''', unsafe_allow_html=True)
                            st.markdown("** Response:**")
                            st.markdown(f'''
                            <div class="output-box">
                                {result["output"] if result["output"] else "<em>No output received</em>"}
                            </div>
                            ''', unsafe_allow_html=True)                
                    except Exception as e:
                        st.error(f" Unexpected error: {str(e)}")                    
                    finally:
                        progress_bar.empty()
                        status_text.empty()            
            st.markdown("---")            

提交处理逻辑按如下方式组织基准测试流程: 

  • 当用户点击 "Submit & Compare Models",应用会检查是否提供了问题或 PDF;若均未提供,则给出提示。
  • 随后应用将文档上下文与用户问题合成为提示词,并添加详细的逐步解答指引。
  • 结果区域分为两列,以实现模型输出的直接并排比较。
  • 针对每个模型,会显示进度条、估算显存占用、调用 OpenRouter API,并在处理请求时同步更新状态信息。
  • 完成后,应用将以卡片形式展示延迟、tokens/秒、token 计数与显存占用等性能指标。

测试 Qwen3-Next

为了检验 Qwen3-Next 的实际表现,我进行了几组实验。

测试 1:长上下文问答

为评估 Qwen3-Next 的长上下文能力,我使用 “战争与和平”古腾堡计划电子书,这部小说是有史以来篇幅最长、结构最复杂的作品之一,开展了多次动手实验。

下面的三个测试用例展示了模型如何处理超长上下文、避免幻觉,并输出高质量的摘要。

角色跟踪 

首先,我让两款模型找出在小说第一章与最后一章中都出现的所有主要角色,并简要说明他们在故事进程中的角色变化。

提示词: “列出在小说第一章与最后一章中均出现的所有主要角色。简要描述他们的角色如何随时间变化。”

Example 1.1

该问题要求模型处理全文并准确跟踪角色弧线。两款模型都能快速响应,但 Qwen3-Next 的回答更整洁,并且更契合该书各章节的脉络。

幻觉测试

接着,我想观察模型是否能分辨真实内容和错误信息。我提供了一句并未出现在《战争与和平》中的名句,但这句话出自托尔斯泰的另一部名作。

提示词: “小说中是否包含‘幸福的家庭都是相似的,不幸的家庭各有各的不幸’这句话?如果有,请指出出现在文本的何处?”

Example 1.2

这句话实际上出自《安娜·卡列尼娜》,而非《战争与和平》。理想的模型应回答“没有”,以体现较强的事实准确性和较低的幻觉倾向。令人印象深刻的是,Qwen3-Next 给出了正确回答,而 Qwen3-30B 则给出了不正确的句子。

摘要

最后,我让两款模型在 300 词内对小说进行总结。

提示词: “在 300 词内总结《战争与和平》,重点关注皮埃尔·别祖霍夫的个人成长之旅。”

Example 1.3

尽管摘要评估具有一定主观性,但我认为 Qwen3-Next 的输出更有结构、更连贯且更具洞察力。该摘要不仅简洁,而且清晰地刻画了皮埃尔角色的演变。

示例 2:多步推理与逻辑

为测试两款模型的高级推理能力,我给出了复杂的多步逻辑谜题——这类任务不仅考验记忆,更要求逐步推演与严谨排除。

座位安排逻辑题

我首先提供了一道座位安排的脑筋急转弯:

提示词: “在婚礼宴会上,有五位宾客 Colin、Emily、Kate、Fred 和 Irene,不确定自己在餐桌上的座位。新娘的母亲说:‘我记得,Colin 不挨着 Kate,Emily 不挨着 Fred 或 Kate。Kate 和 Emily 都不挨着 Irene。还有,Fred 应该坐在 Irene 的左边。’从餐桌的对面看过去,您能否从左到右为他们安排正确的座次?

请逐步解释您的推理过程。”

Example 2.1

该谜题需要分析多条约束并推导出唯一可行的座次顺序:

  • 标准答案: Emily, Colin, Irene, Fred, Kate
  • Qwen3-Next 输出: Kate, Fred, Irene, Colin, Emily(几乎正确,但顺序反了)
  • Qwen3-30B 输出: 未能找到有效答案

虽然两款模型都未给出完全正确的序列,但 Qwen3-Next 非常接近,仅是反向排列;而 30B 模型则未能得出解。可见 Qwen3-Next 具备多步推理能力,尽管部分边界情形仍有挑战。

演绎式排除 

随后,我用一道受经典“斑马谜题”启发的推理题测试两款模型。

提示词: “四个人(Lisa、Mike、Nora 和 Omar)各自购买了不同的水果(苹果、香蕉、樱桃、枣),并驾驶不同的汽车(福特、 本田、起亚、马自达)。

  • Lisa 没有买香蕉。
  • 开福特车的人买了樱桃。
  • Mike 开的是本田。
  • Nora 买了苹果,而且不开马自达。
  • 开起亚的人买了枣。
  • Omar 没有开福特。

谁买了哪种水果、又开了哪辆车?请展示您的推理过程。”

Example 2.2

该问题在给定约束下是有意设定为无解,但模型的回答如下:

  • Qwen3-Next 输出: 正确判断不存在有效分配。
  • Qwen3-30B 输出:陷入逻辑循环,在给定的输出 token 数内未能得出结论。

此例展示了 Qwen3-Next 能进行演绎式推理,并识别无解情形。

结语

本文演示了如何使用 Qwen3-Next-80B-A3B 处理问答与推理任务,并在一个并排对比的 Streamlit 应用中与 Qwen3-30B-A3B 进行基准对照。结果凸显了 Qwen3-Next 在处理超长文档、实现快速推理并保持输出质量方面的优势,即使在复杂推理问题上亦表现出色。

Aashi Dutt

作者

Aashi Dutt

我是一名 Google Developers 机器学习(生成式 AI)领域的专家、Kaggle 三项专家,以及 Women Techmakers 大使,拥有 3 年以上的技术从业经验。2020 年我共同创办了一家健康科技初创公司,目前在佐治亚理工学院攻读计算机科学硕士,专攻机器学习。

主题