vLLM 部署 Qwen 系列模型完全指南:从 Qwen2.5-Omni 到 QwQ 再到 Qwen2.5-72B

【LLM】vLLM部署与int8量化 介绍了vLLM原理并讲解如何支持peft,对不同的decoding methods进行了说明,同时详述了int8量化的过程。 阅读详情

玄同 765

大语言模型 (LLM) 开发工程师 | 中国传媒大学 · 数字媒体技术(智能交互与游戏设计)

优快云 · 个人主页 | GitHub · Follow


关于作者

  • 深耕领域:大语言模型开发 / RAG 知识库 / AI Agent 落地 / 模型微调
  • 技术栈:Python | RAG (LangChain / Dify + Milvus) | FastAPI + Docker
  • 工程能力:专注模型工程化部署、知识库构建与优化,擅长全流程解决方案

「让 AI 交互更智能,让技术落地更高效」
欢迎技术探讨与项目合作,解锁大模型与智能交互的无限可能!


vLLM 部署 Qwen 系列模型完全指南:从 Qwen2.5-Omni 到 QwQ 再到 Qwen2.5-72B

在大模型落地应用的时代,如何高效部署和推理成为了每个开发者必须面对的挑战。vLLM 作为当前最流行的大模型推理框架之一,以其卓越的吞吐量和内存效率,成为了部署 Qwen 系列模型的首选方案。本文将深入讲解 vLLM 的核心原理、架构设计,以及如何部署 Qwen 系列模型,并详细介绍如何让大模型输出结构化 JSON 数据。


零、前置知识:深入理解 vLLM 和 Qwen 系列

在深入部署细节之前,让我们先建立对 vLLM 和 Qwen 系列模型的基础理解。这一部分非常重要,因为只有理解了底层原理,才能更好地进行性能调优和问题排查。

0.1 什么是 vLLM?

vLLM 是一个开源的大语言模型推理和服务框架,由加州大学伯克利分校的研究团队开发。它的核心优势在于:

0.1.1 PagedAttention:革命性的内存管理

PagedAttention 是 vLLM 最核心的创新,它解决了大模型推理中的 KV Cache 内存管理问题。要理解 PagedAttention,我们需要先了解传统方法的问题。

传统 KV Cache 管理的问题:

在 Transformer 模型的推理过程中,每个 token 都需要计算 Key 和 Value 向量,这些向量需要存储在 GPU 内存中供后续 token 使用,这就是所谓的 KV Cache。传统的内存管理方式是预先分配连续的内存块,这带来了几个严重问题:

  1. 内存碎片化:不同长度的请求需要不同大小的内存块,导致内存碎片严重
  2. 内存浪费:预分配的内存往往大于实际需要,造成浪费
  3. 无法共享:相同前缀的请求无法共享 KV Cache

生活中的比喻:想象一个停车场。传统的内存管理就像是为每辆车预留一个固定大小的停车位,即使小车来了也要占用大车位,而且车位之间不能共享。PagedAttention 则像是将停车场划分成许多小格子,车辆可以按需占用多个格子,不同车辆可以共享某些格子(比如共享的停车场入口区域)。

对比

PagedAttention 内存管理

请求1: 按需分配页

无碎片

请求2: 按需分配页

高效利用

请求3: 按需分配页

支持共享

传统内存管理

请求1: 预分配大块内存

内存碎片化

请求2: 预分配大块内存

内存浪费

请求3: 预分配大块内存

无法共享

PagedAttention 的工作原理:

PagedAttention 将 KV Cache 划分成固定大小的"页"(Page),每个页可以存储固定数量的 token 的 KV 向量。这种设计带来了几个关键优势:

  • 按需分配:只有当需要存储新的 KV 向量时才分配新的页
  • 内存共享:多个请求可以共享相同的页(在并行采样或 beam search 时特别有用)
  • 零碎片:页是固定大小的,不会产生内存碎片
0.1.2 连续批处理(Continuous Batching)

传统的批处理方式是等待一批请求全部完成后才开始处理下一批,这导致了"队头阻塞"问题——短请求必须等待长请求完成。vLLM 的连续批处理允许在任何时刻加入新请求或移除已完成的请求,大大提高了 GPU 利用率。

0 1 2 3 4 5 6 7 8 9 10 11 12 请求1 (长) 请求2 (短) 请求3 (中) 请求1 (长) 请求2 (短) 请求3 (中) 请求4 (新) 请求5 (新) 等待新请求 传统批处理 连续批处理 传统批处理 vs 连续批处理
0.1.3 vLLM 整体架构

vLLM 的架构设计非常模块化,主要包括以下几个核心组件:

硬件层

执行层

引擎层

服务层

客户端层

OpenAI SDK / HTTP Client

API Router
/v1/chat/completions
/v1/completions

请求队列
Scheduler

Engine Core
核心调度器

Block Manager
KV Cache 管理

GPU Worker
模型执行

Model Runner
前向传播

NVIDIA GPU
CUDA Cores

GPU Memory
HBM

架构说明:

  1. API Router:接收 HTTP 请求,解析 OpenAI 格式的 API 调用
  2. Scheduler:管理请求队列,决定哪些请求可以被批处理
  3. Engine Core:核心调度引擎,协调各个组件
  4. Block Manager:管理 KV Cache 的页分配和回收
  5. GPU Worker:在 GPU 上执行模型推理
  6. Model Runner:执行实际的模型前向传播

0.2 Qwen 系列模型概览

Qwen(通义千问)是阿里巴巴开源的大语言模型系列,本文将重点介绍以下三个代表性模型:

模型参数量特点官方链接
Qwen2.5-Omni-7B7B多模态全能模型,支持文本、图像、音频、视频Hugging Face
QwQ-32B32B推理增强模型,类似 DeepSeek-R1GitHub
Qwen2.5-72B-Instruct72B大规模通用模型,性能强劲Hugging Face
0.2.1 Qwen2.5-Omni-7B:多模态全能模型

Qwen2.5-Omni-7B 是一个端到端的多模态模型,它采用了创新的 Thinker-Talker 架构:

输出

文本响应

语音响应

Talker 模块

语音解码器

流式输出

Thinker 模块

多模态编码器

TMRoPE
时间对齐位置编码

语言模型

输入模态

文本

图像

音频

视频

核心特性:

  • TMRoPE(Time-aligned Multimodal RoPE):创新的位置编码方法,能够同步视频和音频的时间戳,实现真正的多模态理解
  • 流式语音输出:支持实时生成语音响应,延迟极低
  • 全模态支持:一个模型处理文本、图像、音频、视频四种模态
0.2.2 QwQ-32B:推理增强模型

QwQ-32B 是 Qwen 系列中的推理专用模型,类似于 OpenAI 的 o1 系列和 DeepSeek 的 R1 系列。它的核心特点是显式思考(Explicit Thinking):

用户输入

最终输出

答案

思考过程 (隐藏)

分析问题

制定策略

逐步推理

验证结果

复杂问题

使用注意:

  • 需要启用 --enable-reasoning --reasoning-parser deepseek_r1 参数
  • 推荐使用 temperature=0.6, top_p=0.95, top_k=20 的采样参数
  • 需要设置 presence_penalty 防止无限重复
0.2.3 Qwen2.5-72B-Instruct:大规模通用模型

Qwen2.5-72B-Instruct 是 Qwen2.5 系列中的旗舰模型,具有以下特点:

  • 参数量:72B(720亿参数)
  • 上下文长度:支持长达 131,072 tokens
  • 多语言支持:29+ 种语言
  • 性能表现:在代码、数学、通用任务上表现优异

一、环境准备与安装

1.1 安装 vLLM

根据 vLLM 官方文档,推荐使用 pip 在干净的环境中安装:

pip install "vllm>=0.8.5"

重要提示:预构建的 vLLM 对 torch 和 CUDA 版本有严格要求。如果遇到安装问题,请参考 vLLM 安装指南。

推荐的环境配置:

# 创建新的 conda 环境
conda create -n vllm python=3.11 -y
conda activate vllm

# 安装 PyTorch(确保 CUDA 版本匹配)
pip install torch --index-url https://download.pytorch.org/whl/cu121

# 安装 vLLM
pip install "vllm>=0.8.5"

1.2 硬件要求详解

不同模型的 GPU 内存需求差异很大,下面详细分析每个模型的硬件需求:

1.2.1 GPU 内存计算公式

理解 GPU 内存需求的计算公式非常重要:

GPU 内存需求 = 模型权重 + KV Cache + 激活值 + CUDA 开销

模型权重 (FP16) = 参数量 × 2 字节
模型权重 (BF16) = 参数量 × 2 字节
模型权重 (FP8) = 参数量 × 1 字节
模型权重 (INT4/AWQ) = 参数量 × 0.5 字节

KV Cache = 2 × 层数 × 头数 × 头维度 × 上下文长度 × 精度字节数
1.2.2 各模型硬件需求表
模型精度模型权重KV Cache (32K)总需求推荐配置
Qwen2.5-Omni-7BBF16~14GB~4GB~18GB1x A100 40GB / 1x RTX 4090
QwQ-32BBF16~64GB~16GB~80GB2x A100 40GB / 4x A6000
Qwen2.5-72BBF16~144GB~32GB~176GB4x A100 80GB / 8x A6000
Qwen2.5-72B-AWQINT4~36GB~16GB~52GB2x A6000 / 4x RTX 3090

1.3 验证安装

安装完成后,可以通过以下命令验证:

# 检查 vLLM 版本
python -c "import vllm; print(vllm.__version__)"

# 检查 CUDA 可用性
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
python -c "import torch; print(f'GPU count: {torch.cuda.device_count()}')"

二、启动 OpenAI 兼容 API 服务

vLLM 提供了完全兼容 OpenAI API 的服务接口,这意味着你可以直接使用 OpenAI SDK 来调用本地部署的模型。这是 vLLM 最强大的特性之一,因为它允许你无缝地将现有的 OpenAI 应用迁移到本地部署。

2.1 vLLM 服务启动流程

GPU vLLM Engine 命令行 用户 GPU vLLM Engine 命令行 用户 vllm serve Qwen/Qwen2.5-7B-Instruct 初始化引擎配置 加载模型权重 模型加载完成 初始化 KV Cache 启动 API 服务器 服务就绪 Uvicorn running on http://localhost:8000

2.2 基础启动命令

根据 Qwen 官方文档,启动一个基本的 API 服务非常简单:

vllm serve Qwen/Qwen2.5-7B-Instruct \
    --dtype auto \
    --api-key token-abc123

参数详解:

参数说明默认值
--dtype模型权重数据类型,auto 会自动选择最佳精度auto
--api-keyAPI 密钥,用于认证请求无
--host服务监听地址localhost
--port服务监听端口8000

2.3 从 ModelScope 下载模型

如果你在中国大陆,从 ModelScope 下载模型可能更快:

export VLLM_USE_MODELSCOPE=true
vllm serve Qwen/Qwen2.5-7B-Instruct

2.4 多 GPU 张量并行

对于大模型(如 Qwen2.5-72B),需要使用多 GPU 进行张量并行:

GPU 3

L1 部分

L2 部分

GPU 2

L1 部分

L2 部分

GPU 1

L1 部分

L2 部分

GPU 0

L1 部分

L2 部分

模型层

Layer 1

Layer 2

Layer 3

Layer 4

张量并行命令:

vllm serve Qwen/Qwen2.5-72B-Instruct \
    --tensor-parallel-size 4

张量并行原理:

张量并行将模型的每一层切分到多个 GPU 上,每个 GPU 只存储和计算部分权重。在推理时,各个 GPU 并行计算,然后通过 All-Reduce 操作合并结果。

前向传播过程

输入 Tensor

切分到各 GPU

GPU 0: 计算 W0

GPU 1: 计算 W1

GPU 2: 计算 W2

GPU 3: 计算 W3

All-Reduce 合并

输出 Tensor


三、使用 OpenAI SDK 调用 vLLM 服务

3.1 基础调用示例

根据 vLLM OpenAI 兼容服务器文档,你可以这样调用:

from openai import OpenAI

openai_api_key = "EMPTY"
openai_api_base = "http://localhost:8000/v1"

client = OpenAI(
    api_key=openai_api_key,
    base_url=openai_api_base,
)

chat_response = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "user", "content": "Give me a short introduction to large language models."}
    ],
    max_tokens=32768,
    temperature=0.6,
    top_p=0.95,
    extra_body={
        "top_k": 20,
    },
)

print("Chat response:", chat_response)

3.2 流式输出

流式输出对于长文本生成非常重要,它可以让用户实时看到生成的内容:

stream = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "user", "content": "Write a short poem about stars."}
    ],
    temperature=0.7,
    max_tokens=200,
    stream=True
)

for chunk in stream:
    if chunk.choices[0].delta.content:
        print(chunk.choices[0].delta.content, end="", flush=True)

3.3 使用 curl 调用

curl http://localhost:8000/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{
        "model": "Qwen/Qwen2.5-7B-Instruct",
        "messages": [
            {"role": "user", "content": "Give me a short introduction to large language models."}
        ],
        "temperature": 0.6,
        "top_p": 0.95,
        "top_k": 20,
        "max_tokens": 32768
    }'

四、部署 Qwen2.5-Omni-7B:多模态全能模型

4.1 模型简介

根据 Qwen2.5-Omni 官方页面,Qwen2.5-Omni 是一个端到端的多模态模型,能够:

  • 感知多种模态:文本、图像、音频、视频
  • 生成多种输出:文本和自然语音响应
  • 流式处理:支持实时交互

核心架构:Thinker-Talker 架构,以及创新的 TMRoPE(Time-aligned Multimodal RoPE)位置嵌入,用于同步视频和音频的时间戳。

4.2 性能表现

根据官方数据,Qwen2.5-Omni-7B 在多个基准测试中表现出色:

任务类型基准测试性能
语音识别Librispeech test-clean1.8% WER
语音识别Common Voice 15 en7.6% WER
音频理解MMAU Sound70.27%
图像推理MMMU与 Qwen2.5-VL-7B 相当
多模态综合OmniBench56.13%

4.3 vLLM 部署 Qwen2.5-Omni

根据 vLLM Qwen2.5-Omni 离线推理示例,部署方式如下:

4.3.1 启动服务
vllm serve Qwen/Qwen2.5-Omni-7B \
    --dtype auto \
    --max-model-len 32768 \
    --limit-mm-per-prompt '{"image": 2, "video": 1, "audio": 2}'

参数说明:

  • --limit-mm-per-prompt:限制每个请求中各模态的数量,防止内存溢出
  • --max-model-len:最大上下文长度,影响 KV Cache 大小
4.3.2 离线推理示例
from vllm import LLM, SamplingParams

sampling_params = SamplingParams(
    temperature=0.6,
    top_p=0.95,
    max_tokens=32768
)

llm = LLM(
    model="Qwen/Qwen2.5-Omni-7B",
    limit_mm_per_prompt={"audio": 1, "image": 1, "video": 1}
)

prompt = "Describe the content of this image."
messages = [{"role": "user", "content": prompt}]

outputs = llm.chat([messages], sampling_params)

for output in outputs:
    generated_text = output.outputs[0].text
    print(f"Generated text: {generated_text!r}")

4.4 多模态输入处理

Qwen2.5-Omni 支持多种模态的混合输入:

from vllm.assets.image import ImageAsset

image = ImageAsset("cherry_blossom").pil_image

messages = [
    {
        "role": "user",
        "content": [
            {"type": "text", "text": "What's in this image?"},
            {"type": "image_url", "image_url": {"url": image}}
        ]
    }
]

五、部署 QwQ-32B:推理增强模型

5.1 模型简介

根据 QwQ GitHub 仓库,QwQ 是 Qwen 系列中的推理增强模型:

  • 定位:推理专用模型,类似于 DeepSeek-R1 和 o1-mini
  • 特点:具备先进的推理和批判性思维能力
  • 性能:在复杂问题解决任务上表现优异

5.2 性能表现

根据官方数据,QwQ-32B 在多个推理基准测试中与顶级推理模型竞争:

基准测试QwQ-32BDeepSeek-R1o1-mini
AIME 2024优秀优秀优秀
MATH-500优秀优秀优秀
GPQA优秀优秀优秀

5.3 vLLM 部署 QwQ

5.3.1 启动服务(带推理解析器)

根据 Hugging Face 讨论,部署 QwQ 需要启用推理模式:

vllm serve Qwen/QwQ-32B \
    --dtype half \
    --enable-reasoning \
    --reasoning-parser deepseek_r1 \
    --max-model-len 32768

关键参数说明:

  • --enable-reasoning:启用推理模式,允许模型输出思考过程
  • --reasoning-parser deepseek_r1:使用 DeepSeek-R1 风格的推理解析器,将思考过程和最终答案分离
5.3.2 使用 AWQ 量化部署

如果 GPU 内存有限,可以使用 AWQ 量化版本:

vllm serve Qwen/QwQ-32B-AWQ \
    --dtype half \
    --quantization awq \
    --enable-reasoning \
    --reasoning-parser deepseek_r1 \
    --max-model-len 32768

5.4 使用指南

根据 QwQ 官方文档,使用 QwQ 时需要注意以下几点:

5.4.1 强制思考输出

确保模型以 思索\n 开头,防止生成空的思考内容:

from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("Qwen/QwQ-32B")

messages = [{"role": "user", "content": "Solve this math problem: 2x + 5 = 13"}]

text = tokenizer.apply_chat_template(
    messages,
    tokenize=False,
    add_generation_prompt=True
)
5.4.2 推荐的采样参数
sampling_params = {
    "temperature": 0.6,
    "top_p": 0.95,
    "top_k": 20,
    "max_tokens": 32768,
    "presence_penalty": 1.0
}
5.4.3 多轮对话处理

在多轮对话中,历史输出应该只包含最终输出,不包含思考内容:

messages = [
    {"role": "user", "content": "问题1"},
    {"role": "assistant", "content": "最终答案1"},
    {"role": "user", "content": "问题2"}
]

5.5 长文本处理

对于超过 8,192 tokens 的输入,建议启用 YaRN:

vllm serve Qwen/QwQ-32B \
    --dtype half \
    --enable-reasoning \
    --reasoning-parser deepseek_r1 \
    --rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}' \
    --max-model-len 131072

六、部署 Qwen2.5-72B-Instruct:大规模通用模型

6.1 模型简介

根据 Qwen2.5-72B-Instruct Hugging Face 页面,这是 Qwen2.5 系列中的大规模模型:

  • 参数量:72B
  • 上下文长度:支持长达 131,072 tokens
  • 多语言支持:29+ 种语言
  • 特点:在代码、数学、通用任务上表现优异

6.2 GPU 内存需求

根据 Reddit 讨论 和 vLLM 论坛:

精度GPU 内存需求推荐配置
BF16/FP16~140GB+4x A100 40GB / 8x A6000
FP8~70GB2x A100 40GB / 4x A6000
AWQ (INT4)~48GB2x A6000 / 4x RTX 3090

6.3 vLLM 部署 Qwen2.5-72B

6.3.1 BF16 精度部署(需要多 GPU)
vllm serve Qwen/Qwen2.5-72B-Instruct \
    --dtype bfloat16 \
    --tensor-parallel-size 4 \
    --max-model-len 32768 \
    --gpu-memory-utilization 0.9
6.3.2 AWQ 量化部署

AWQ 量化版本可以在更少的 GPU 上运行:

vllm serve Qwen/Qwen2.5-72B-Instruct-AWQ \
    --dtype half \
    --quantization awq \
    --tensor-parallel-size 2 \
    --max-model-len 16384 \
    --gpu-memory-utilization 0.85

注意:根据 vLLM 论坛讨论,即使使用 AWQ 量化,2x24GB GPU 也可能遇到 OOM 问题。建议:

  1. 降低 --max-model-len
  2. 降低 --gpu-memory-utilization
  3. 使用 --enforce-eager 禁用 CUDA Graphs
vllm serve Qwen/Qwen2.5-72B-Instruct-AWQ \
    --dtype half \
    --quantization awq \
    --tensor-parallel-size 2 \
    --max-model-len 8192 \
    --gpu-memory-utilization 0.8 \
    --enforce-eager
6.3.3 FP8 量化部署
vllm serve Qwen/Qwen2.5-72B-Instruct-FP8 \
    --dtype auto \
    --tensor-parallel-size 2

注意:FP8 模型需要 NVIDIA Ada Lovelace、Hopper 或更新架构的 GPU(计算能力 > 8.9)。

6.4 Python 库方式使用

from vllm import LLM, SamplingParams

llm = LLM(
    model="Qwen/Qwen2.5-72B-Instruct",
    tensor_parallel_size=4
)

sampling_params = SamplingParams(
    temperature=0.7,
    top_p=0.8,
    top_k=20,
    max_tokens=8192
)

prompt = "Tell me something about large language models."
messages = [{"role": "user", "content": prompt}]

outputs = llm.chat([messages], sampling_params)

for output in outputs:
    generated_text = output.outputs[0].text
    print(f"Generated text: {generated_text!r}")

七、JSON 结构化输出:让大模型输出可靠的 JSON 数据

在实际应用中,我们经常需要大模型输出结构化的 JSON 数据,例如:

  • 提取实体信息(人名、地点、时间等)
  • 分类任务(情感分析、意图识别等)
  • 生成配置文件
  • API 响应格式化

vLLM 提供了强大的结构化输出功能,可以确保模型输出符合预定义的 JSON Schema。根据 vLLM 官方文档,vLLM 支持多种结构化输出方式。

7.1 结构化输出架构

vLLM 结构化输出

输出

有效 JSON

后端引擎

xgrammar

guidance

outlines

用户请求

Prompt + Schema

Schema 解析器

引导解码器

Token 过滤器

工作原理:

  1. Schema 解析:将 JSON Schema 或 Pydantic 模型解析为约束规则
  2. 引导解码:在生成每个 token 时,根据约束规则限制可选的 token
  3. Token 过滤:过滤掉不符合 Schema 的 token,确保输出始终有效

7.2 方式一:使用 response_format 参数(推荐)

这是最符合 OpenAI API 规范的方式,支持两种方法:直接使用 JSON Schema 或使用 Pydantic 模型。

7.2.1 使用 Pydantic 模型定义 Schema
from pydantic import BaseModel
from enum import Enum
from openai import OpenAI

class CarType(str, Enum):
    sedan = "sedan"
    suv = "SUV"
    truck = "Truck"
    coupe = "Coupe"

class CarDescription(BaseModel):
    brand: str
    model: str
    car_type: CarType
    price: float
    features: list[str]

client = OpenAI(
    base_url="http://localhost:8000/v1",
    api_key="dummy"
)

json_schema = CarDescription.model_json_schema()

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {
            "role": "user",
            "content": "Generate a JSON with the brand, model, car_type, price and features of the most iconic car from the 90's"
        }
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "car-description",
            "schema": json_schema
        }
    }
)

print(completion.choices[0].message.content)

输出示例:

{
    "brand": "Toyota",
    "model": "Supra MK4",
    "car_type": "coupe",
    "price": 45000.0,
    "features": ["twin-turbo engine", "rear-wheel drive", "iconic design", "JDM legend"]
}
7.2.2 嵌套 Pydantic 模型示例

对于复杂的数据结构,可以使用嵌套的 Pydantic 模型:

from typing import List
from pydantic import BaseModel
from openai import OpenAI

class Step(BaseModel):
    explanation: str
    output: str

class MathResponse(BaseModel):
    steps: list[Step]
    final_answer: str

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "system", "content": "You are a helpful expert math tutor."},
        {"role": "user", "content": "Solve 8x + 31 = 2."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "math-response",
            "schema": MathResponse.model_json_schema()
        }
    }
)

print(completion.choices[0].message.content)

输出示例:

{
    "steps": [
        {
            "explanation": "First, let's isolate the term with the variable 'x'. To do this, we'll subtract 31 from both sides of the equation.",
            "output": "8x + 31 - 31 = 2 - 31"
        },
        {
            "explanation": "By subtracting 31 from both sides, we simplify the equation to 8x = -29.",
            "output": "8x = -29"
        },
        {
            "explanation": "Next, let's isolate 'x' by dividing both sides of the equation by 8.",
            "output": "8x / 8 = -29 / 8"
        }
    ],
    "final_answer": "x = -29/8"
}

7.3 方式二:使用 extra_body 参数的 structured_outputs

这是 vLLM 特有的方式,提供了更灵活的选项。

7.3.1 JSON 输出
from openai import OpenAI

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

json_schema = {
    "type": "object",
    "properties": {
        "name": {"type": "string"},
        "age": {"type": "integer"},
        "email": {"type": "string"}
    },
    "required": ["name", "age", "email"]
}

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "user", "content": "Generate a JSON for a random person"}
    ],
    extra_body={"structured_outputs": {"json": json_schema}}
)

print(completion.choices[0].message.content)
7.3.2 Choice 选择输出

用于分类任务,限制输出只能是预定义的选项之一:

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "user", "content": "Classify this sentiment: vLLM is wonderful!"}
    ],
    extra_body={"structured_outputs": {"choice": ["positive", "negative", "neutral"]}}
)

print(completion.choices[0].message.content)

输出:positive

7.3.3 Regex 正则表达式输出

用于生成符合特定格式的文本:

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {
            "role": "user",
            "content": "Generate an example email address for Alan Turing, who works in Enigma. End in .com and new line."
        }
    ],
    extra_body={
        "structured_outputs": {"regex": r"\w+@\w+\.com\n"},
        "stop": ["\n"]
    }
)

print(completion.choices[0].message.content)

输出:alan.turing@enigma.com

7.3.4 Grammar 语法输出

最强大但也最复杂的方式,可以定义完整的语法规则:

simplified_sql_grammar = """
root ::= select_statement
select_statement ::= "SELECT " column " from " table " where " condition
column ::= "col_1 " | "col_2 "
table ::= "table_1 " | "table_2 "
condition ::= column "= " number
number ::= "1 " | "2 "
"""

completion = client.chat.completions.create(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {
            "role": "user",
            "content": "Generate an SQL query to show the 'username' and 'email' from the 'users' table."
        }
    ],
    extra_body={"structured_outputs": {"grammar": simplified_sql_grammar}}
)

print(completion.choices[0].message.content)

7.4 方式三:使用 OpenAI Beta API 的自动解析

OpenAI SDK 提供了 beta 版本的 parse 方法,可以自动将响应解析为 Pydantic 对象:

from pydantic import BaseModel
from openai import OpenAI

class Info(BaseModel):
    name: str
    age: int

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

completion = client.beta.chat.completions.parse(
    model="Qwen/Qwen2.5-7B-Instruct",
    messages=[
        {"role": "system", "content": "You are a helpful assistant."},
        {"role": "user", "content": "My name is Cameron, I'm 28. What's my name and age?"}
    ],
    response_format=Info
)

message = completion.choices[0].message
assert message.parsed
print("Name:", message.parsed.name)
print("Age:", message.parsed.age)

输出:

Name: Cameron
Age: 28

7.5 离线推理中的结构化输出

对于离线批处理场景,可以使用 StructuredOutputsParams:

from vllm import LLM, SamplingParams
from vllm.sampling_params import StructuredOutputsParams

llm = LLM(model="Qwen/Qwen2.5-7B-Instruct")

structured_outputs_params = StructuredOutputsParams(
    choice=["Positive", "Negative"]
)

sampling_params = SamplingParams(
    structured_outputs=structured_outputs_params
)

outputs = llm.generate(
    prompts="Classify this sentiment: vLLM is wonderful!",
    sampling_params=sampling_params
)

print(outputs[0].outputs[0].text)

7.6 推理模型的结构化输出

对于 QwQ 等推理模型,结构化输出同样适用:

from pydantic import BaseModel
from openai import OpenAI

class People(BaseModel):
    name: str
    age: int

client = OpenAI(base_url="http://localhost:8000/v1", api_key="dummy")

completion = client.chat.completions.create(
    model="Qwen/QwQ-32B",
    messages=[
        {"role": "user", "content": "Generate a JSON with the name and age of one random person."}
    ],
    response_format={
        "type": "json_schema",
        "json_schema": {
            "name": "people",
            "schema": People.model_json_schema()
        }
    }
)

print("reasoning:", completion.choices[0].message.reasoning)
print("content:", completion.choices[0].message.content)

注意:当使用 Qwen3 Coder 模型并启用推理模式时,需要显式启用结构化输出:

vllm serve Qwen/QwQ-32B \
    --enable-reasoning \
    --reasoning-parser deepseek_r1 \
    --structured-outputs-config.enable_in_reasoning=True

7.7 结构化输出最佳实践

简单分类

格式约束

复杂对象

自定义语法

是

否

定义 Schema

选择方式

使用 choice

使用 regex

使用 Pydantic

使用 grammar

测试输出

输出正确?

部署上线

优化 Schema/Prompt

最佳实践建议:

  1. 优先使用 Pydantic 模型:类型安全,易于维护,IDE 支持好
  2. 在 Prompt 中说明 JSON 结构:虽然不是必须的,但可以显著提高输出质量
  3. 设置合理的 max_tokens:避免输出被截断导致 JSON 不完整
  4. 使用 temperature=0:对于结构化输出,低温度可以提高一致性
  5. 验证输出:即使有结构化输出保证,也应该验证关键字段

八、vLLM 关键参数详解

8.1 内存管理参数

参数默认值说明
--gpu-memory-utilization0.9GPU 内存预分配比例
--max-model-len模型默认值最大上下文长度
--enforce-eagerFalse禁用 CUDA Graphs,减少内存使用

8.2 并行参数

参数说明
--tensor-parallel-size张量并行度,跨 GPU 切分模型
--pipeline-parallel-size流水线并行度,跨节点切分模型
--data-parallel-size数据并行度,复制模型处理不同请求

8.3 采样参数

根据 Qwen 官方文档,推荐的采样参数:

思考模式(Thinking Mode):

{
    "temperature": 0.6,
    "top_p": 0.95,
    "top_k": 20,
    "max_tokens": 32768
}

非思考模式(Non-Thinking Mode):

{
    "temperature": 0.7,
    "top_p": 0.8,
    "top_k": 20,
    "max_tokens": 8192,
    "presence_penalty": 1.5
}

8.4 长上下文配置

使用 YaRN 扩展上下文长度:

vllm serve Qwen/Qwen2.5-72B-Instruct \
    --rope-scaling '{"rope_type":"yarn","factor":4.0,"original_max_position_embeddings":32768}' \
    --max-model-len 131072

九、常见问题与解决方案

9.1 OOM(内存不足)问题

问题:启动服务或推理时遇到 CUDA out of memory 错误。

解决方案:

# 方案1:降低 max-model-len
vllm serve Qwen/Qwen2.5-72B-Instruct --max-model-len 16384

# 方案2:降低 gpu-memory-utilization
vllm serve Qwen/Qwen2.5-72B-Instruct --gpu-memory-utilization 0.8

# 方案3:使用 enforce-eager
vllm serve Qwen/Qwen2.5-72B-Instruct --enforce-eager

# 方案4:使用量化模型
vllm serve Qwen/Qwen2.5-72B-Instruct-AWQ --quantization awq

9.2 QwQ 无限重复问题

问题:QwQ 模型输出无限重复或性能下降。

解决方案:

# 启用推理模式
vllm serve Qwen/QwQ-32B --enable-reasoning --reasoning-parser deepseek_r1
# 设置 presence_penalty
sampling_params = {"presence_penalty": 1.0}

9.3 FP8 模型张量并行错误

问题:部署 FP8 模型时遇到 ValueError: The output_size of gate's and up's weight 错误。

解决方案:

# 方案1:降低张量并行度
vllm serve Qwen/Qwen2.5-72B-Instruct-FP8 --tensor-parallel-size 4

# 方案2:启用专家并行
vllm serve Qwen/Qwen2.5-72B-Instruct-FP8 --tensor-parallel-size 8 --enable-expert-parallel

十、Docker 部署

10.1 使用官方镜像

docker run --gpus all \
    -v ~/.cache/huggingface:/root/.cache/huggingface \
    -p 8000:8000 \
    --rm \
    vllm/vllm-openai:latest \
    --model Qwen/Qwen2.5-7B-Instruct \
    --dtype auto \
    --api-key token-abc123

10.2 Docker Compose 配置

services:
  vllm:
    image: vllm/vllm-openai:latest
    command: >
      --model Qwen/Qwen2.5-7B-Instruct
      --dtype auto
      --api-key ${VLLM_API_KEY}
      --port 8000
    environment:
      - HUGGING_FACE_HUB_TOKEN=${HF_TOKEN}
    volumes:
      - ~/.cache/huggingface:/root/.cache/huggingface
    ports:
      - "8000:8000"
    deploy:
      resources:
        reservations:
          devices:
            - capabilities: [gpu]
    healthcheck:
      test: ["CMD", "bash", "-lc", "curl -fsS http://localhost:8000/v1/models | grep -q 'id'"]
      interval: 30s
      timeout: 5s
      retries: 5
    restart: unless-stopped

十一、生产环境监控指标

根据 vLLM 生产部署指南,关键监控指标包括:

指标说明
vllm:num_requests_running活跃请求数量
vllm:num_requests_waiting队列深度(容量耗尽的主要指标)
vllm:gpu_cache_usage_percKV 缓存饱和度
vllm:time_to_first_token_secondsTTFT 直方图
vllm:e2e_request_latency_seconds端到端延迟直方图

十二、实战案例:多模态情绪解析系统

本节将详细介绍一个生产级的多模态情绪解析系统的完整实现。该系统基于 vLLM 部署的 Qwen-Omni 模型,实现了语音与文本的统一情绪解析,并针对 LLM 结构化输出幻觉问题,构建了三层防御体系。

12.1 系统概述

12.1.1 业务背景

在客服质检、心理健康监测、用户反馈分析等场景中,准确识别用户情绪至关重要。传统的情绪分析方案存在以下问题:

  1. 单一模态局限:仅分析文本,忽略了语音中的语调、语速、停顿等重要情绪信号
  2. 信息丢失:语音转文字过程中,情绪信息大量丢失
  3. 准确性不足:无法捕捉讽刺、反语等需要语音配合才能理解的情绪

Qwen-Omni 的原生多模态能力为解决这些问题提供了可能:它可以直接处理音频输入,保留语音中的情绪特征,同时结合文本内容进行综合判断。

12.1.2 系统架构

输出层

情绪类型
Enum

置信度
0.0-1.0

分析理由
String

三层防御体系

第一层: 正则切片
快速提取

第二层: json_repair
修复畸形JSON

第三层: Pydantic Enum
强类型校验

模型层 - vLLM + Qwen-Omni

Thinker 模块
多模态理解

语言模型
情绪推理

处理层

音频预处理
采样率转换/分片

文本预处理
清洗/截断

输入层

音频输入
WAV/MP3

文本输入
用户消息

12.1.3 情绪分类体系

根据业务需求,我们定义了以下情绪分类:

情绪分类

正面情绪

快乐 Joy

满意 Satisfaction

感激 Gratitude

期待 Expectation

负面情绪

愤怒 Anger

悲伤 Sadness

焦虑 Anxiety

失望 Disappointment

厌恶 Disgust

中性情绪

平静 Calm

困惑 Confusion

中立 Neutral

12.2 三层防御体系设计

LLM 在输出结构化数据时存在"幻觉"问题,可能输出不符合规范的 JSON。我们设计了三层防御体系来确保输出的可靠性:

第三层防御

第二层防御

第一层防御

是

否

是

否

是

否

兜底

默认值/重试

成功

有效数据

LLM 原始输出

可能畸形的 JSON

正则切片

快速提取关键字段

格式正确?

json_repair

修复畸形 JSON

可修复?

Pydantic Enum

强类型校验

校验通过?

12.2.1 第一层:自定义正则切片

设计目的:快速提取,避免重型解析开销

正则切片是第一道防线,它能够快速从 LLM 输出中提取关键字段,即使 JSON 格式不完全正确也能工作。

实现原理:

import re
from typing import Optional
from dataclasses import dataclass

@dataclass
class ExtractedEmotion:
    emotion: Optional[str] = None
    confidence: Optional[float] = None
    reason: Optional[str] = None

def extract_emotion_with_regex(text: str) -> ExtractedEmotion:
    """
    使用正则表达式从文本中提取情绪信息。
    
    这一层防御的特点:
    1. 速度快:正则匹配是 O(n) 操作
    2. 容错性强:即使 JSON 格式错误也能提取
    3. 轻量级:不依赖外部库
    
    Args:
        text: LLM 输出的原始文本
        
    Returns:
        ExtractedEmotion: 提取的情绪信息
    """
    result = ExtractedEmotion()
    
    emotion_patterns = [
        r'"emotion"\s*:\s*"([^"]+)"',
        r'"emotion"\s*:\s*([^,}\n]+)',
        r'emotion[::]\s*["\']?([^"\'\n,}]+)["\']?',
    ]
    
    for pattern in emotion_patterns:
        match = re.search(pattern, text, re.IGNORECASE)
        if match:
            result.emotion = match.group(1).strip().strip('"\'')
            break
    
    confidence_patterns = [
        r'"confidence"\s*:\s*([0-9.]+)',
        r'"confidence"\s*:\s*"([0-9.]+)"',
        r'confidence[::]\s*([0-9.]+)',
    ]
    
    for pattern in confidence_patterns:
        match = re.search(pattern, text, re.IGNORECASE)
        if match:
            try:
                result.confidence = float(match.group(1))
                if result.confidence > 1.0:
                    result.confidence = result.confidence / 100.0
            except ValueError:
                pass
            break
    
    reason_patterns = [
        r'"reason"\s*:\s*"([^"]+)"',
        r'"reason"\s*:\s*"([^"]*(?:\\.[^"]*)*)"',
        r'reason[::]\s*["\']?([^"\'\n]+)["\']?',
    ]
    
    for pattern in reason_patterns:
        match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
        if match:
            result.reason = match.group(1).strip()
            if result.reason:
                break
    
    return result

正则模式设计要点:

模式类型设计考虑示例
键名匹配支持中英文冒号,忽略空格emotion[::]\s*
值提取支持引号和无引号两种格式"([^"]+)" 或 ([^,}\n]+)
数值处理处理百分比和小数两种格式([0-9.]+)
容错设计多种模式依次尝试列表循环匹配
12.2.2 第二层:json_repair 兜底

设计目的:修复畸形 JSON,处理 LLM 输出的常见错误

当正则切片无法有效提取时,使用 json_repair 库进行修复。该库专门针对 LLM 输出设计,能够处理各种常见的 JSON 畸形问题。

安装:

pip install json-repair

LLM 输出的常见 JSON 问题:

输出

有效 JSON

json_repair 修复

自动修复

常见问题

尾随逗号
{'a': 1,}

缺少引号
{name: 'test'}

单引号
{'a': 'b'}

注释混入
{/* comment */}

不完整对象
{'a':

幻觉文本
Sure! Here is...

实现代码:

import json
from typing import Any, Optional
from json_repair import repair_json

def repair_and_parse_json(text: str) -> Optional[dict[str, Any]]:
    """
    使用 json_repair 修复并解析畸形 JSON。
    
    这一层防御的特点:
    1. 专门针对 LLM 输出优化
    2. 自动移除幻觉文本
    3. 修复常见语法错误
    
    Args:
        text: LLM 输出的原始文本
        
    Returns:
        解析后的字典,如果无法修复则返回 None
    """
    try:
        json_start = text.find('{')
        json_end = text.rfind('}') + 1
        if json_start == -1 or json_end == 0:
            return None
        
        json_text = text[json_start:json_end]
        
        repaired = repair_json(json_text)
        
        return json.loads(repaired)
        
    except Exception:
        return None

def extract_json_from_mixed_content(text: str) -> Optional[dict[str, Any]]:
    """
    从混合内容中提取 JSON。
    
    LLM 经常在 JSON 前后添加解释性文字,如:
    "Sure! Here's the analysis result: {...} Let me know if you need more details."
    
    Args:
        text: 包含 JSON 的混合文本
        
    Returns:
        提取并解析的字典
    """
    json_patterns = [
        r'\{[^{}]*(?:\{[^{}]*\}[^{}]*)*\}',
        r'\{.*\}',
    ]
    
    for pattern in json_patterns:
        matches = re.findall(pattern, text, re.DOTALL)
        for match in matches:
            result = repair_and_parse_json(match)
            if result:
                return result
    
    return None
12.2.3 第三层:Pydantic Enum 强类型校验

设计目的:确保业务逻辑层面的数据正确性

即使 JSON 格式正确,数据内容可能仍然不符合业务要求。Pydantic 的 Enum 校验可以确保情绪类型在预定义范围内。

情绪枚举定义:

from enum import Enum
from typing import Annotated
from pydantic import BaseModel, Field, field_validator

class EmotionType(str, Enum):
    """
    情绪类型枚举。
    
    使用 str 继承 Enum 使得:
    1. JSON 序列化时输出字符串而非枚举对象
    2. 可以直接与字符串比较
    3. Pydantic 可以自动将字符串转换为枚举
    """
    
    JOY = "joy"
    SATISFACTION = "satisfaction"
    GRATITUDE = "gratitude"
    EXPECTATION = "expectation"
    
    ANGER = "anger"
    SADNESS = "sadness"
    ANXIETY = "anxiety"
    DISAPPOINTMENT = "disappointment"
    DISGUST = "disgust"
    
    CALM = "calm"
    CONFUSION = "confusion"
    NEUTRAL = "neutral"
    
    @classmethod
    def get_positive_emotions(cls) -> list["EmotionType"]:
        return [cls.JOY, cls.SATISFACTION, cls.GRATITUDE, cls.EXPECTATION]
    
    @classmethod
    def get_negative_emotions(cls) -> list["EmotionType"]:
        return [cls.ANGER, cls.SADNESS, cls.ANXIETY, cls.DISAPPOINTMENT, cls.DISGUST]
    
    @classmethod
    def get_neutral_emotions(cls) -> list["EmotionType"]:
        return [cls.CALM, cls.CONFUSION, cls.NEUTRAL]


class EmotionAnalysisResult(BaseModel):
    """
    情绪分析结果模型。
    
    Pydantic 模型的优势:
    1. 自动类型转换和校验
    2. 清晰的错误信息
    3. IDE 自动补全支持
    4. 可以导出 JSON Schema 供 LLM 使用
    """
    
    emotion: EmotionType = Field(
        ...,
        description="识别出的主要情绪类型"
    )
    
    confidence: Annotated[float, Field(
        ge=0.0,
        le=1.0,
        description="置信度,范围 0.0-1.0"
    )]
    
    reason: str = Field(
        ...,
        min_length=10,
        max_length=500,
        description="情绪判断的理由说明"
    )
    
    secondary_emotions: list[EmotionType] = Field(
        default_factory=list,
        description="次要情绪列表"
    )
    
    @field_validator('confidence', mode='before')
    @classmethod
    def normalize_confidence(cls, v: Any) -> float:
        if isinstance(v, str):
            v = v.strip()
            if v.endswith('%'):
                v = v[:-1]
            return float(v) / 100.0
        if isinstance(v, (int, float)):
            if v > 1.0:
                return v / 100.0
        return float(v)
    
    @field_validator('reason', mode='before')
    @classmethod
    def clean_reason(cls, v: Any) -> str:
        if isinstance(v, str):
            return ' '.join(v.split())
        return str(v)

校验流程:

from pydantic import ValidationError

def validate_emotion_result(data: dict[str, Any]) -> Optional[EmotionAnalysisResult]:
    """
    使用 Pydantic 校验情绪分析结果。
    
    这一层防御的特点:
    1. 强类型校验:确保字段类型正确
    2. 枚举约束:确保情绪类型在预定义范围内
    3. 范围校验:确保置信度在 0-1 之间
    4. 自动修复:处理常见的格式问题
    
    Args:
        data: 从 JSON 解析得到的字典
        
    Returns:
        校验后的 EmotionAnalysisResult,校验失败返回 None
    """
    try:
        return EmotionAnalysisResult(**data)
    except ValidationError as e:
        print(f"Validation error: {e}")
        return None

12.3 完整系统实现

12.3.1 核心服务类
"""
多模态情绪解析服务

基于 vLLM + Qwen-Omni 实现的语音与文本统一情绪解析系统。
采用三层防御体系确保结构化输出的可靠性。
"""

import base64
import json
import re
from pathlib import Path
from typing import Any, Optional
from dataclasses import dataclass, field

from openai import OpenAI
from pydantic import BaseModel, Field, field_validator, ValidationError
from enum import Enum
from json_repair import repair_json


class EmotionType(str, Enum):
    JOY = "joy"
    SATISFACTION = "satisfaction"
    GRATITUDE = "gratitude"
    EXPECTATION = "expectation"
    ANGER = "anger"
    SADNESS = "sadness"
    ANXIETY = "anxiety"
    DISAPPOINTMENT = "disappointment"
    DISGUST = "disgust"
    CALM = "calm"
    CONFUSION = "confusion"
    NEUTRAL = "neutral"


class EmotionAnalysisResult(BaseModel):
    emotion: EmotionType
    confidence: float = Field(ge=0.0, le=1.0)
    reason: str = Field(min_length=10, max_length=500)
    secondary_emotions: list[EmotionType] = Field(default_factory=list)
    
    @field_validator('confidence', mode='before')
    @classmethod
    def normalize_confidence(cls, v: Any) -> float:
        if isinstance(v, str):
            v = v.strip()
            if v.endswith('%'):
                v = v[:-1]
            return float(v) / 100.0
        if isinstance(v, (int, float)) and v > 1.0:
            return v / 100.0
        return float(v)


EMOTION_SYSTEM_PROMPT = """你是一个专业的情绪分析助手。你的任务是分析用户输入(可能是文本或语音)中的情绪。

请严格按照以下 JSON 格式输出分析结果,不要添加任何额外的解释或文字:

{
    "emotion": "<情绪类型>",
    "confidence": <置信度,0.0-1.0之间的小数>,
    "reason": "<判断理由,10-500字>",
    "secondary_emotions": ["<次要情绪1>", "<次要情绪2>"]
}

情绪类型必须是以下之一:
- 正面情绪:joy(快乐), satisfaction(满意), gratitude(感激), expectation(期待)
- 负面情绪:anger(愤怒), sadness(悲伤), anxiety(焦虑), disappointment(失望), disgust(厌恶)
- 中性情绪:calm(平静), confusion(困惑), neutral(中立)

分析要点:
1. 对于语音输入,注意语调、语速、停顿等语音特征
2. 对于文本输入,注意用词、标点、表情符号等
3. 综合判断主要情绪和次要情绪
4. 给出合理的置信度和判断理由"""


class EmotionAnalysisService:
    """
    多模态情绪解析服务。
    
    该服务实现了:
    1. 语音与文本的统一情绪解析
    2. 三层防御体系确保输出可靠性
    3. 完善的错误处理和日志记录
    
    Attributes:
        client: OpenAI 客户端,连接到 vLLM 服务
        model: 模型名称
        max_retries: 最大重试次数
    """
    
    def __init__(
        self,
        base_url: str = "http://localhost:8000/v1",
        api_key: str = "dummy",
        model: str = "Qwen/Qwen2.5-Omni-7B",
        max_retries: int = 3
    ):
        self.client = OpenAI(base_url=base_url, api_key=api_key)
        self.model = model
        self.max_retries = max_retries
    
    def analyze_text(self, text: str) -> Optional[EmotionAnalysisResult]:
        """
        分析文本情绪。
        
        Args:
            text: 待分析的文本内容
            
        Returns:
            EmotionAnalysisResult 或 None(分析失败时)
        """
        messages = [
            {"role": "system", "content": EMOTION_SYSTEM_PROMPT},
            {"role": "user", "content": text}
        ]
        return self._analyze_with_retry(messages)
    
    def analyze_audio(
        self,
        audio_path: str,
        transcription: Optional[str] = None
    ) -> Optional[EmotionAnalysisResult]:
        """
        分析音频情绪。
        
        利用 Qwen-Omni 的原生多模态能力,直接处理音频输入,
        保留语音中的情绪特征(语调、语速、停顿等)。
        
        Args:
            audio_path: 音频文件路径(支持 WAV、MP3 等格式)
            transcription: 可选的文字转录,用于辅助分析
            
        Returns:
            EmotionAnalysisResult 或 None(分析失败时)
        """
        audio_base64 = self._encode_audio(audio_path)
        audio_url = f"data:audio/wav;base64,{audio_base64}"
        
        content = []
        
        content.append({
            "type": "audio_url",
            "audio_url": {"url": audio_url}
        })
        
        if transcription:
            content.append({
                "type": "text",
                "text": f"语音转录文本:{transcription}"
            })
        
        content.append({
            "type": "text",
            "text": "请分析这段音频中的情绪。"
        })
        
        messages = [
            {"role": "system", "content": EMOTION_SYSTEM_PROMPT},
            {"role": "user", "content": content}
        ]
        
        return self._analyze_with_retry(messages)
    
    def analyze_mixed(
        self,
        text: str,
        audio_path: Optional[str] = None
    ) -> Optional[EmotionAnalysisResult]:
        """
        分析混合输入(文本 + 可选音频)的情绪。
        
        这是主要的分析入口,支持:
        1. 纯文本分析
        2. 纯音频分析
        3. 文本 + 音频联合分析
        
        Args:
            text: 文本内容
            audio_path: 可选的音频文件路径
            
        Returns:
            EmotionAnalysisResult 或 None(分析失败时)
        """
        if audio_path and Path(audio_path).exists():
            return self.analyze_audio(audio_path, transcription=text)
        else:
            return self.analyze_text(text)
    
    def _analyze_with_retry(
        self,
        messages: list[dict[str, Any]]
    ) -> Optional[EmotionAnalysisResult]:
        """
        带重试机制的分析方法。
        
        当分析失败时,会进行重试,每次重试前会调整提示词。
        
        Args:
            messages: OpenAI API 消息格式
            
        Returns:
            EmotionAnalysisResult 或 None
        """
        for attempt in range(self.max_retries):
            try:
                response = self.client.chat.completions.create(
                    model=self.model,
                    messages=messages,
                    temperature=0.3,
                    max_tokens=500
                )
                
                raw_content = response.choices[0].message.content
                if not raw_content:
                    continue
                
                result = self._parse_with_defense(raw_content)
                if result:
                    return result
                
                if attempt < self.max_retries - 1:
                    messages = self._enhance_prompt(messages, attempt)
                    
            except Exception as e:
                print(f"Analysis attempt {attempt + 1} failed: {e}")
        
        return None
    
    def _parse_with_defense(self, raw_content: str) -> Optional[EmotionAnalysisResult]:
        """
        使用三层防御体系解析 LLM 输出。
        
        防御层级:
        1. 正则切片:快速提取关键字段
        2. json_repair:修复畸形 JSON
        3. Pydantic Enum:强类型校验
        
        Args:
            raw_content: LLM 原始输出
            
        Returns:
            EmotionAnalysisResult 或 None
        """
        extracted = self._layer1_regex_extract(raw_content)
        if extracted.emotion and extracted.confidence is not None:
            data = {
                "emotion": extracted.emotion,
                "confidence": extracted.confidence,
                "reason": extracted.reason or "未提供分析理由"
            }
            result = self._layer3_pydantic_validate(data)
            if result:
                return result
        
        data = self._layer2_json_repair(raw_content)
        if data:
            result = self._layer3_pydantic_validate(data)
            if result:
                return result
        
        return None
    
    def _layer1_regex_extract(self, text: str) -> "ExtractedData":
        """
        第一层防御:正则切片。
        
        快速提取关键字段,即使 JSON 格式不完全正确也能工作。
        """
        result = ExtractedData()
        
        emotion_patterns = [
            r'"emotion"\s*:\s*"([^"]+)"',
            r'"emotion"\s*:\s*([^,}\n]+)',
            r'emotion[::]\s*["\']?([^"\'\n,}]+)["\']?',
        ]
        
        for pattern in emotion_patterns:
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                result.emotion = match.group(1).strip().strip('"\'').lower()
                break
        
        confidence_patterns = [
            r'"confidence"\s*:\s*([0-9.]+)',
            r'"confidence"\s*:\s*"([0-9.]+)"',
        ]
        
        for pattern in confidence_patterns:
            match = re.search(pattern, text, re.IGNORECASE)
            if match:
                try:
                    result.confidence = float(match.group(1))
                except ValueError:
                    pass
                break
        
        reason_patterns = [
            r'"reason"\s*:\s*"([^"]+)"',
            r'"reason"\s*:\s*"([^"]*(?:\\.[^"]*)*)"',
        ]
        
        for pattern in reason_patterns:
            match = re.search(pattern, text, re.IGNORECASE | re.DOTALL)
            if match:
                result.reason = match.group(1).strip()
                if result.reason:
                    break
        
        return result
    
    def _layer2_json_repair(self, text: str) -> Optional[dict[str, Any]]:
        """
        第二层防御:json_repair 兜底。
        
        修复畸形 JSON,处理 LLM 输出的常见错误。
        """
        try:
            json_start = text.find('{')
            json_end = text.rfind('}') + 1
            if json_start == -1 or json_end == 0:
                return None
            
            json_text = text[json_start:json_end]
            repaired = repair_json(json_text)
            return json.loads(repaired)
        except Exception:
            return None
    
    def _layer3_pydantic_validate(
        self,
        data: dict[str, Any]
    ) -> Optional[EmotionAnalysisResult]:
        """
        第三层防御:Pydantic Enum 强类型校验。
        
        确保数据符合业务逻辑要求。
        """
        try:
            return EmotionAnalysisResult(**data)
        except ValidationError:
            return None
    
    def _encode_audio(self, audio_path: str) -> str:
        """
        将音频文件编码为 base64。
        
        Args:
            audio_path: 音频文件路径
            
        Returns:
            base64 编码的音频数据
        """
        with open(audio_path, "rb") as f:
            return base64.b64encode(f.read()).decode("utf-8")
    
    def _enhance_prompt(
        self,
        messages: list[dict[str, Any]],
        attempt: int
    ) -> list[dict[str, Any]]:
        """
        增强提示词以应对重试场景。
        
        根据重试次数添加不同的强调信息。
        """
        enhanced = messages.copy()
        
        emphasis = [
            "\n\n重要提醒:请确保输出是有效的 JSON 格式,不要添加任何额外文字。",
            "\n\n再次强调:只输出 JSON,格式如下:{\"emotion\": \"...\", \"confidence\": 0.0, \"reason\": \"...\"}",
            "\n\n最后一次尝试:严格按照 JSON 格式输出,不要有任何偏差。"
        ]
        
        if attempt < len(emphasis):
            for i, msg in enumerate(enhanced):
                if msg["role"] == "system":
                    enhanced[i] = {
                        "role": "system",
                        "content": msg["content"] + emphasis[attempt]
                    }
                    break
        
        return enhanced


@dataclass
class ExtractedData:
    """正则提取的数据结构。"""
    emotion: Optional[str] = None
    confidence: Optional[float] = None
    reason: Optional[str] = None
12.3.2 服务启动与调用
"""
服务启动脚本

启动 vLLM 服务并运行情绪分析示例。
"""

import subprocess
import time
import requests
from pathlib import Path


def start_vllm_service(
    model: str = "Qwen/Qwen2.5-Omni-7B",
    port: int = 8000,
    gpu_memory_utilization: float = 0.9
) -> subprocess.Popen:
    """
    启动 vLLM 服务。
    
    Args:
        model: 模型名称
        port: 服务端口
        gpu_memory_utilization: GPU 内存利用率
        
    Returns:
        子进程对象
    """
    cmd = [
        "vllm", "serve", model,
        "--port", str(port),
        "--gpu-memory-utilization", str(gpu_memory_utilization),
        "--max-model-len", "8192",
        "--limit-mm-per-prompt", '{"audio": 1, "image": 1, "video": 0}'
    ]
    
    process = subprocess.Popen(
        cmd,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE
    )
    
    print(f"Starting vLLM service on port {port}...")
    
    max_wait = 300
    start_time = time.time()
    
    while time.time() - start_time < max_wait:
        try:
            response = requests.get(f"http://localhost:{port}/v1/models")
            if response.status_code == 200:
                print("vLLM service is ready!")
                return process
        except requests.ConnectionError:
            pass
        time.sleep(5)
    
    raise TimeoutError("vLLM service failed to start within timeout")


def main():
    """主函数:演示情绪分析服务。"""
    
    service = EmotionAnalysisService(
        base_url="http://localhost:8000/v1",
        model="Qwen/Qwen2.5-Omni-7B"
    )
    
    print("=" * 50)
    print("示例 1: 文本情绪分析")
    print("=" * 50)
    
    text_result = service.analyze_text(
        "我真的太失望了,等了两个小时还没有人来处理我的问题!"
    )
    if text_result:
        print(f"情绪: {text_result.emotion.value}")
        print(f"置信度: {text_result.confidence:.2%}")
        print(f"理由: {text_result.reason}")
    
    print("\n" + "=" * 50)
    print("示例 2: 音频情绪分析")
    print("=" * 50)
    
    audio_path = "sample_audio.wav"
    if Path(audio_path).exists():
        audio_result = service.analyze_audio(audio_path)
        if audio_result:
            print(f"情绪: {audio_result.emotion.value}")
            print(f"置信度: {audio_result.confidence:.2%}")
            print(f"理由: {audio_result.reason}")
    else:
        print(f"音频文件 {audio_path} 不存在,跳过音频分析示例")


if __name__ == "__main__":
    main()

12.4 三层防御体系的测试验证

为了确保防御体系的可靠性,我们需要对各种异常情况进行测试:

"""
三层防御体系测试用例

验证各种异常输入场景下的处理能力。
"""

import pytest
from unittest.mock import Mock, patch


class TestDefenseSystem:
    """三层防御体系测试类。"""
    
    def setup_method(self):
        self.service = EmotionAnalysisService()
    
    def test_layer1_valid_json(self):
        """测试第一层防御:有效 JSON"""
        raw = '{"emotion": "anger", "confidence": 0.85, "reason": "用户使用了强烈的负面词汇"}'
        result = self.service._parse_with_defense(raw)
        assert result is not None
        assert result.emotion.value == "anger"
        assert result.confidence == 0.85
    
    def test_layer1_missing_quotes(self):
        """测试第一层防御:缺少引号"""
        raw = '{emotion: "joy", confidence: 0.9, reason: "积极情绪表达"}'
        result = self.service._parse_with_defense(raw)
        assert result is not None
    
    def test_layer1_chinese_colon(self):
        """测试第一层防御:中文冒号"""
        raw = '{"emotion":"satisfaction","confidence":0.8,"reason":"用户表示满意"}'
        result = self.service._parse_with_defense(raw)
        assert result is not None
    
    def test_layer2_trailing_comma(self):
        """测试第二层防御:尾随逗号"""
        raw = '{"emotion": "sadness", "confidence": 0.75, "reason": "悲伤的语气",}'
        result = self.service._parse_with_defense(raw)
        assert result is not None
    
    def test_layer2_single_quotes(self):
        """测试第二层防御:单引号"""
        raw = "{'emotion': 'anxiety', 'confidence': 0.6, 'reason': '担忧的语气'}"
        result = self.service._parse_with_defense(raw)
        assert result is not None
    
    def test_layer2_mixed_content(self):
        """测试第二层防御:混合内容"""
        raw = '''好的,我来分析这段文字的情绪:
        {"emotion": "neutral", "confidence": 0.5, "reason": "语气平淡"}
        希望这个分析对你有帮助!'''
        result = self.service._parse_with_defense(raw)
        assert result is not None
    
    def test_layer3_invalid_emotion(self):
        """测试第三层防御:无效情绪类型"""
        raw = '{"emotion": "happiness", "confidence": 0.9, "reason": "开心的语气"}'
        result = self.service._parse_with_defense(raw)
        assert result is None
    
    def test_layer3_confidence_out_of_range(self):
        """测试第三层防御:置信度超出范围"""
        raw = '{"emotion": "joy", "confidence": 1.5, "reason": "非常开心"}'
        result = self.service._parse_with_defense(raw)
        assert result is None
    
    def test_layer3_confidence_percentage(self):
        """测试第三层防御:百分比格式置信度"""
        raw = '{"emotion": "joy", "confidence": "85%", "reason": "开心的语气"}'
        result = self.service._parse_with_defense(raw)
        assert result is not None
        assert result.confidence == 0.85
    
    def test_completely_broken_input(self):
        """测试完全损坏的输入"""
        raw = "这是一段完全没有 JSON 格式的文字"
        result = self.service._parse_with_defense(raw)
        assert result is None
    
    def test_empty_input(self):
        """测试空输入"""
        raw = ""
        result = self.service._parse_with_defense(raw)
        assert result is None


class TestEmotionAnalysisService:
    """情绪分析服务测试类。"""
    
    def setup_method(self):
        self.service = EmotionAnalysisService()
    
    @patch('openai.OpenAI')
    def test_analyze_text_success(self, mock_openai):
        """测试文本分析成功场景"""
        mock_client = Mock()
        mock_response = Mock()
        mock_response.choices = [Mock()]
        mock_response.choices[0].message.content = '{"emotion": "anger", "confidence": 0.9, "reason": "愤怒的语气和用词"}'
        mock_client.chat.completions.create.return_value = mock_response
        self.service.client = mock_client
        
        result = self.service.analyze_text("我很生气!")
        assert result is not None
        assert result.emotion.value == "anger"
    
    @patch('openai.OpenAI')
    def test_analyze_text_with_retry(self, mock_openai):
        """测试重试机制"""
        mock_client = Mock()
        
        mock_client.chat.completions.create.side_effect = [
            Exception("Network error"),
            Mock(choices=[Mock(message=Mock(content='{"emotion": "neutral", "confidence": 0.5, "reason": "普通陈述"}'))])
        ]
        self.service.client = mock_client
        
        result = self.service.analyze_text("今天天气不错")
        assert result is not None

12.5 生产环境部署建议

12.5.1 性能优化

监控指标

延迟 P99

吞吐量

错误率

GPU 利用率

性能优化策略

请求批处理

结果缓存

异步处理

连接池

关键配置:

import asyncio
from functools import lru_cache
from concurrent.futures import ThreadPoolExecutor


class ProductionEmotionService(EmotionAnalysisService):
    """生产环境情绪分析服务。"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self._executor = ThreadPoolExecutor(max_workers=10)
        self._cache: dict[str, EmotionAnalysisResult] = {}
    
    @lru_cache(maxsize=1000)
    def _get_cache_key(self, text: str, audio_hash: Optional[str] = None) -> str:
        """生成缓存键。"""
        import hashlib
        content = text + (audio_hash or "")
        return hashlib.md5(content.encode()).hexdigest()
    
    async def analyze_async(
        self,
        text: str,
        audio_path: Optional[str] = None
    ) -> Optional[EmotionAnalysisResult]:
        """异步分析方法。"""
        loop = asyncio.get_event_loop()
        return await loop.run_in_executor(
            self._executor,
            self.analyze_mixed,
            text,
            audio_path
        )
    
    async def batch_analyze(
        self,
        items: list[dict[str, Any]]
    ) -> list[Optional[EmotionAnalysisResult]]:
        """批量分析。"""
        tasks = [
            self.analyze_async(
                item.get("text", ""),
                item.get("audio_path")
            )
            for item in items
        ]
        return await asyncio.gather(*tasks)
12.5.2 监控与告警
from dataclasses import dataclass
from datetime import datetime
from typing import Callable
import time


@dataclass
class Metrics:
    """监控指标。"""
    total_requests: int = 0
    successful_requests: int = 0
    failed_requests: int = 0
    total_latency_ms: float = 0.0
    layer1_hits: int = 0
    layer2_hits: int = 0
    layer3_failures: int = 0


class MonitoredEmotionService(ProductionEmotionService):
    """带监控的情绪分析服务。"""
    
    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        self.metrics = Metrics()
        self._alert_handlers: list[Callable] = []
    
    def add_alert_handler(self, handler: Callable):
        """添加告警处理器。"""
        self._alert_handlers.append(handler)
    
    def analyze_mixed(
        self,
        text: str,
        audio_path: Optional[str] = None
    ) -> Optional[EmotionAnalysisResult]:
        """带监控的分析方法。"""
        start_time = time.time()
        self.metrics.total_requests += 1
        
        try:
            result = super().analyze_mixed(text, audio_path)
            
            if result:
                self.metrics.successful_requests += 1
            else:
                self.metrics.failed_requests += 1
                self._check_alert_threshold()
            
            latency_ms = (time.time() - start_time) * 1000
            self.metrics.total_latency_ms += latency_ms
            
            return result
            
        except Exception as e:
            self.metrics.failed_requests += 1
            self._send_alert(f"Analysis error: {e}")
            raise
    
    def _check_alert_threshold(self):
        """检查告警阈值。"""
        if self.metrics.total_requests > 10:
            error_rate = self.metrics.failed_requests / self.metrics.total_requests
            if error_rate > 0.1:
                self._send_alert(f"High error rate: {error_rate:.2%}")
    
    def _send_alert(self, message: str):
        """发送告警。"""
        for handler in self._alert_handlers:
            handler(message)
    
    def get_metrics(self) -> dict:
        """获取监控指标。"""
        return {
            "total_requests": self.metrics.total_requests,
            "successful_requests": self.metrics.successful_requests,
            "failed_requests": self.metrics.failed_requests,
            "success_rate": (
                self.metrics.successful_requests / self.metrics.total_requests
                if self.metrics.total_requests > 0 else 0
            ),
            "avg_latency_ms": (
                self.metrics.total_latency_ms / self.metrics.total_requests
                if self.metrics.total_requests > 0 else 0
            ),
            "layer1_hit_rate": (
                self.metrics.layer1_hits / self.metrics.total_requests
                if self.metrics.total_requests > 0 else 0
            ),
            "layer2_hit_rate": (
                self.metrics.layer2_hits / self.metrics.total_requests
                if self.metrics.total_requests > 0 else 0
            ),
        }

12.6 小结

本节详细介绍了一个生产级多模态情绪解析系统的完整实现:

核心技术要点:

  1. 原生多模态处理:利用 Qwen-Omni 直接处理音频输入,保留语音情绪特征
  2. 三层防御体系:
    • 第一层:正则切片,快速提取,容错性强
    • 第二层:json_repair,修复畸形 JSON,处理 LLM 幻觉
    • 第三层:Pydantic Enum,强类型校验,确保业务正确性
  3. 生产级特性:异步处理、批量分析、监控告警

架构优势:

特性传统方案本方案
多模态支持语音转文字后分析原生音频处理
结构化输出可靠性单一 JSON 解析三层防御体系
错误处理简单 try-catch多层兜底 + 重试
可观测性基本日志完整监控指标

十三、总结

vLLM 为部署 Qwen 系列模型提供了高效、易用的解决方案。通过本文,我们详细介绍了:

  1. vLLM 核心原理:PagedAttention 内存管理、连续批处理、整体架构
  2. Qwen2.5-Omni-7B:多模态全能模型,支持文本、图像、音频、视频的端到端处理
  3. QwQ-32B:推理增强模型,需要启用推理模式和正确的采样参数
  4. Qwen2.5-72B-Instruct:大规模通用模型,需要多 GPU 部署或量化
  5. JSON 结构化输出:多种方式确保模型输出符合预定义的 JSON Schema
  6. 实战案例:多模态情绪解析系统,三层防御体系确保生产环境高可用

核心要点:

  • 使用 vllm serve 命令启动 OpenAI 兼容 API 服务
  • 大模型需要配置 --tensor-parallel-size 进行多 GPU 部署
  • 内存不足时可以降低 --max-model-len 或使用量化模型
  • QwQ 需要启用 --enable-reasoning --reasoning-parser deepseek_r1
  • 长文本处理需要配置 YaRN
  • 结构化输出优先使用 Pydantic 模型定义 Schema
  • 生产环境建议采用三层防御体系:正则切片 → json_repair → Pydantic Enum

三层防御体系核心价值:

┌─────────────────────────────────────────────────────────────────┐
│                    LLM 结构化输出防御体系                          │
├─────────────────────────────────────────────────────────────────┤
│  第一层:正则切片                                                  │
│  ├── 优势:速度快 O(n),无外部依赖,容错性强                         │
│  └── 场景:快速提取关键字段,处理格式轻微错误                          │
├─────────────────────────────────────────────────────────────────┤
│  第二层:json_repair                                              │
│  ├── 优势:专门针对 LLM 输出优化,自动修复常见错误                     │
│  └── 场景:处理畸形 JSON、幻觉文本、混合内容                          │
├─────────────────────────────────────────────────────────────────┤
│  第三层:Pydantic Enum                                            │
│  ├── 优势:强类型校验,业务逻辑约束,清晰的错误信息                     │
│  └── 场景:确保数据符合业务规范,枚举值在预定义范围内                    │
└─────────────────────────────────────────────────────────────────┘

参考资料

vLLM 官方文档

Qwen 模型

生产部署与工具

3分钟搞定vLLM+Docker部署:从镜像构建到多模态大模型实战 本文详细介绍了如何使用vLLM和Docker进行高效部署,从基础配置到多模态大模型实战。通过Docker镜像构建、容器启动参数详解、vLLM服务验证及多模态模型部署等步骤,帮助开发者快速掌握AI模型部署技术,提升工作效率。 阅读详情

相关推荐

LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架]

LLM 大模型学习必知必会系列(十二):VLLM性能飞跃部署实践:从推理加速到高效部署的全方位优化[更多内容:XInference/FastChat等框架]

丨汀、的博客 6373

vLLM初探

是一个快速且易于使用的库,用于 LLM 推理和服务,可以和HuggingFace 无缝集成。vLLM利用了全新的注意力算法「PagedAttention」,有效地管理注意力键和值。在吞吐量方面,vLLM的性能比HuggingFace Transformers(HF)高出 24 倍,文本生成推理(TGI)高出3.5倍。是伯克利大学LMSYS组织开源的大语言模型高速推理框架,旨在极大地提升实时场景下的语言模型服务的吞吐与内存使用效率。

邹中凡 7277

大模型推理加速框架vllm部署的实战方案

本文主要介绍了大模型推理加速框架vllm部署的实战方案,希望对学习大语言模型的同学们有所帮助。 文章目录 1. 前言 2. 配置环境 2.1 安装虚拟环境 2.2 安装依赖库 3. 运行vllm

herosunly的博客 3万+

一文说清VLLM .VS.Ollama的区别

对比VLLM与Ollama,解析两者在性能、易用性和应用场景的异同,助你选择合适框架。

打造全国最全的AI Agent开发知识领域的博客 5349

开源模型应用落地-Qwen2.5-Omni-7B模型-部署 “光速” 指南

阿里巴巴开源的全模态大模型Qwen2.5-Omni-7B以其强大的多模态感知能力和创新架构,标志着人机交互的重大突破,为普惠AI时代铺平道路。

以微薄之力,予他人些许温暖. 1万+

开源模型应用落地-Qwen2.5-Omni-7B模型-Gradio-部署 “光速” 指南(二)

集成Gradio将Qwen2.5-Omni的复杂能力封装为交互式Web应用

以微薄之力,予他人些许温暖. 2万+

【AI大模型前沿】Qwen2.5-Omni:阿里巴巴的多模态大模型,看听说写样样精通,多模态交互颠覆想象

在人工智能领域,单一模态的模型(如仅处理文本或图像)已经取得了显著的成果,但人类智能的本质是多模态的。我们通过视觉、听觉等多种感官来感知世界,并通过语言、文字等方式进行表达和交流。因此, 开发能够同时处理多种模态输入并生成相应输出的模型,对于实现更接近人类智能的人工智能系统至关重要。Qwen2.5-Omni 正是在这样的背景下应运而生,它旨在打破模态之间的壁垒,实现更自然、更高效的人机交互。本文将详细介绍 Qwen2.5-Omni 的技术原理、功能特点、应用场景以及如何快速上手使用。

寻道AI,探索AI无限可能! 2115

【算法】图解A* 搜索算法

A* 算法是启发式搜索算法,是根据Dijkstra算法改进而来。 问题引入 如下图所示,S为起始(start)节点,G为目标(goal)节点。 节点之间连线是两点的路径长度,如A到E的路径长度c(A,E) = 9。 节点旁的h值时当前节点到达目标节点(G)的预估值,如h(A)=15, 表示从当前点A到达目标点G的估计路径长度为15,此处h(x)即为启发函数,启发函数的设计有很多方法,具体可......

zhuhua造轮子的博客 4万+

2.2 LLM的调用与使用《AI Agent智能体开发实践》

本节将以ModelScope(魔搭社区)中的Qwen3大模型为例介绍LLM的调用与使用。

夏天又到了的专栏 434

MATLAB新手也能懂:用MATPOWER 8.0b搞定电力系统潮流计算(附case9实战代码)

本文详细介绍了如何使用MATPOWER 8.0b在MATLAB中进行电力系统潮流计算,特别适合新手入门。通过case9实战案例,讲解了从安装配置到运行牛顿-拉夫逊法潮流计算的全过程,包括结果分析和常见问题处理,帮助读者快速掌握这一电力系统分析的核心技能。

weixin_26757925的博客 380

Datalog语言详解

Datalog是一种声明式逻辑编程语言,起源于数据库理论和逻辑编程领域。作为Prolog的子集,它专注于递归查询和推理,通过定义事实和规则来描述数据关系而非计算步骤。Datalog的关键特性包括规则安全、递归、分层否定和扩展聚合功能,保证了程序的终止性和高效性。相较于SQL,Datalog更擅长处理递归查询;相比Prolog,它更注重数据库友好性。Datalog在程序分析、知识图谱、网络安全等领域有广泛应用,其声明式特性使代码简洁易维护,但同时也面临生态系统小、性能调试难等挑战。现代工具如Soufflé、D

bulita great world 1843

PowerInfer 本地部署中的 OpenAI 风格 Function Calling:llama-server 原生工具调用支持与 Jinja 模板实战

Function Calling(函数调用 / 工具调用)让大语言模型在对话中输出结构化的"工具调用"请求,由外部程序执行后把结果回传给模型,是构建 Agent、RAG 检索、代码执行等本地 AI 应用的关键能力。本文以 [smallthinker/docs/function-calling.md](https://link.gitcode.com/i/59acedfe302cb483788541

gitblog_00706的博客 275

UDP协议详解——使用Socket通信

UDP(UserDatagramProtocol)是一个简单的面向消息的传输层协议,尽管UDP提供标头和有效负载的完整性验证(通过校验和),但它不保证向上层协议提供消息传递,并且UDP层在发送后不会保留UDP 消息的状态。因此,UDP有时被称为不可靠的数据报协议。如果需要传输可靠性,则必须在用户应用程序中实现。UDP使用具有最小协议机制的简单无连接通信模型。UDP提供数据完整性的校验和,以及用于在数据报的源和目标寻址不同函数的端口号。它没有握手对话,因此将用户的程序暴露在底层网络的任何不可靠的方面。

qq_3517289697的博客 3385

大模型框架:vLLM

​vLLM是伯克利大学LMSYS组织开源的大语言模型高速推理框架。它利用了全新的注意力算法「PagedAttention」,提供易用、快速、便宜的LLM服务。

m0_37559973的博客 1万+

VMD-CNN-BiLSTM-Attention预测模型 —— 数学模型介绍&完整实例代码

想解决电力负荷预测中的非线性、非平稳难题?这篇博客介绍了一种创新混合模型,结合变分模态分解(VMD)和CNN-BiLSTM-Attention深度学习架构,实现超高精度预测! ✨ 核心亮点: ✅ VMD分解:有效分离复杂负荷信号,提取关键频率特征 ✅ 混合深度学习:CNN捕捉局部模式 + BiLSTM学习长时序依赖 + 注意力机制聚焦关键时段 ✅ 端到端优化:从数据预处理(滑动窗口、归一化)到模型训练(MSE损失+Adam优化),完整流程公开! ✅ 工业级评估:提供MSE、MAE、MAPE、R²多指标验证

qq_42924330的博客 1653

UG NX二次开发(C#)-读取PMI对象的名称

摘要:本文介绍了在UG NX中通过NXOpen获取PMI对象名称的方法。首先说明PMI对象初始无名称,可通过重命名或属性设置名称。然后提供C#代码示例,展示如何遍历PMI集合获取对象名称,并显示名称和类型信息。适用于需要操作PMI对象的开发人员,欢迎加入技术交流QQ群749492565。(99字)

GimiGimmy 592

vLLM介绍

vLLM是伯克利大学LMSYS组织开源的大语言模型高速推理框架,旨在极大地提升实时场景下的语言模型服务的吞吐与内存使用效率。vLLM是一个快速且易于使用的库,用于 LLM 推理和服务,可以和HuggingFace 无缝集成。vLLM利用了全新的注意力算法「PagedAttention」,有效地管理注意力键和值。

哦豁灬 2万+

ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案

本文主要介绍了ImportError: DLL load failed while importing _pydantic_core: 找不到指定的程序对应解决方案,希望能对使用Python的同学们有所帮助。 文章目录 1. 问题描述 2. 解决方案

weixin_43178406的博客 19万+

vLLM深度解析:高性能大语言模型推理引擎全揭秘

vLLM(Vectorized Large Language Model Serving System)是由加州大学伯克利分校团队开发的高性能、易扩展的大语言模型推理引擎。它专注于通过创新的内存管理和计算优化技术,实现高吞吐、低延迟、低成本的模型服务。

2401_84495872的博客 2502

编写一个期货跨期套利的程序,谈谈思路及案例

🏆本文收录于《优快云问答解惑-专业版》专栏,主要记录项目实战过程中的Bug之前因后果及提供真实有效的解决方案,希望能够助你一臂之力,帮你早日登顶实现财富自由🚀;同时,欢迎大家关注&&收藏&&订阅!持续更新中,up!up!up!!

**My Coding Family** 1719

STM32F407中文手册

STM32f407中文手册 本参考手册面向应用开发人员,提供有关使用 STM32F405xx/07xx、STM32F415xx/17xx、 STM32F42xxx 和 STM32F43xxx 微控制器存储器与外设的完整信息。STM32F405xx/07xx、STM32F415xx/17xx、STM32F42xxx 和 STM32F43xxx 构成一个微 控制器系列,各产品具有不同的存储器大小、封装和外设。有关订购信息以及器件的机械与电气特性,请参见数据手册。有关 ARM Cortex™-M4F 内核的信息,请参见《Cortex™-M4F 技术参考手册》。

vLLM 部署大模型

vLLM 是来自 UC Berkeley 的 LMSYS 在 LLM 推理方面的最新工作(没错就是搞出 Vicuna 的那个 group),最大亮点是采用 Paged Attention 技术,结合 Continuous Batching,极大地优化了 realtime 场景下的 LLM serving 的 throughput 与内存使用。

哦豁灬 13万+

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

大模型运行框架 VLLM 深度分析!

VLLM 是伯克利大学 LMSYS 组织开源的大语言模型高速推理框架,旨在极大地提升实时场景下的语言模型服务的吞吐量与内存使用效率。它通过创新的技术和优化策略,有效管理计算资源,为大语言模型的高效运行提供了强大支持。VLLM 利用全新的注意力算法「PagedAttention」,对注意力键和值进行高效管理,其核心理念在于通过优化内存管理和资源调度,提升大语言模型部署和执行的效率。

m0_59235945的博客 5960

jre9下载 jdk9

(Java SE Runtime Environment 9.0) 9.0u175 官方最新版 【x86|x64】

SIMATIC S7-1200, S7-1500 PID 控制[手册]

SIMATIC S7-1200, S7-1500 PID 控制[手册]

DeepSeek 部署指南 (使用 vLLM 本地部署)

vLLM (https://github.com/vllm-project/vllm) 是一个快速且易于使用的库,用于进行大型语言模型的推理和部署。

m0_48891301的博客 4万+

什么是vLLM

vLLM是由加州大学伯克利团队开发的高性能大语言模型推理引擎,通过创新的PagedAttention机制实现了高效的显存管理。该技术借鉴操作系统分页内存思想,将注意力缓存分割为可动态分配的小块,显著提升显存利用率并支持更长的上下文。vLLM还采用智能请求调度和连续批处理技术,支持多GPU并行计算和流式响应,使模型推理吞吐量提升5-10倍。这种"虚拟内存+智能调度"的架构使vLLM成为当前大模型推理的主流方案之一,广泛应用于Qwen、LLaMA等模型的高并发服务场景。

keyboard专栏 2438

使用vllm部署自己的大模型

使用vllm能够部署一个非常方便使用的大模型的后端,供webui前端使用,它不仅能够加速模型的推理过程,包括支持fast-attn库,而且还具有很友好的openai风格的api调用功能。

刘炫320的博客 3万+

什么是VLLM

VLLM 是一种强大的工具,专注于优化 LLM 的推理过程,尤其适用于需要高效推理和有限硬件资源的场景。通过使用动态张量并行、异步推理和高效的内存管理等技术,VLLM 能够显著提高推理性能,为大型语言模型的实际应用提供了更加灵活和可扩展的解决方案。

keyboard专栏 4774
上一篇: 从零构建企业级 Agent 编排框架:基于 FastAPI 的 LangGraph 风格框架设计与实现
下一篇: 长尾数据挖掘闭环:利用 Embedding 聚类与 Label Studio 构建智能情绪标注体系
玄同765
玄同765 新星创作者: 人工智能技术领域 新星创作者: 人工智能技术领域
博客等级 码龄6年 1万+粉丝 262原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值