# 接入 vLLM 的 qwen3.8 模型:WorkBuddy 自定义模型配置教程、踩坑记录与心路历程

接入 vLLM 的 qwen3.8 模型:WorkBuddy 自定义模型配置教程、踩坑记录与心路历程

适用场景:把自建 / 第三方 OpenAI 兼容推理服务(vLLM、Ollama、LM Studio、云端推理 API 等)接入 WorkBuddy 作为「自定义模型」使用。

本文所有服务器地址、端口、密钥、容器名、路径均为占位符,请按实际环境替换;不涉密。

目录

  • 零、接入背景与模型说明
  • 一、我的心路历程:从一个 400 到三层根因
  • 二、WorkBuddy 自定义模型配置教程
  • 三、踩坑记录(坑 1 ~ 坑 5)
  • 四、关键结论与速查表
  • 五、一句话总结

零、接入背景与模型说明

1. 我们接入了什么

本次把一套自建推理服务接入了 WorkBuddy:

  • 模型qwen3.8-27b-FP8(Qwen3 系列,约 27B 参数规模),以 FP8 量化部署,兼顾显存占用与推理质量。
  • 推理框架 :vLLM,对外暴露 OpenAI 兼容接口(/v1/chat/completions/v1/models)。
  • 部署形态 :多卡张量并行(tensor-parallel-size=2)、启用 MTP 投机解码 加速、上下文窗口开到很大(支持超长输入)、开启 reasoning 推理解析 (thinking)与 auto tool choice 自动工具调用
  • 接入目的:让 WorkBuddy 的 Agent 直接调用这个模型做深度思考(reasoning)、普通对话与工具调用。

2. 为什么值得记一笔

Qwen3 这类「会思考」的新一代模型,和老一代纯对话模型在接入时差异很大:

  • 它会在请求里带 reasoning_effort 这类推理强度参数,而不同模型的 chat template 对参数取值的宽容度不同;
  • 它支持工具调用(function calling),而工具调用的流式输出格式正是很多客户端解析的暗礁;
  • WorkBuddy 又是默认走流式、且 UI 上没有开关关掉流式的客户端。

这三件事叠加在一起,让接入过程一路踩坑。下面既是教程,也是一份「踩坑复盘」。


一、我的心路历程:从一个 400 到三层根因

这部分是我(排查人)当时的思考路径,写出来是想说明:同一个报错,可能藏着完全不同的根因,千万别被第一眼现象骗了。

第一眼:以为是「流式续传 body 空了」

最开始看到报错:

复制代码
400 ... JSON decode error ... Expecting value ... input: {}

而且现象是「先正常吐出一段,然后报错」。我第一反应是:流式输出中途断了,客户端续传时发了个空 body,服务端解析失败。这是最常见的流式 bug 猜想,于是我先去查 vLLM 日志、版本、有没有 streaming + tool-call 的已知问题。

第二眼:发现是「服务端模板在渲染阶段就拒了」

查 vLLM 日志时,真正的报错露出来了:

复制代码
jinja2.exceptions.TemplateError: Unexpected reasoning effort high.
Supported types are xhigh (default), medium, and low.

原来不是流式续传的问题 ------ 是 WorkBuddy 默认给 Qwen3 发了 reasoning_effort: "high",但 Qwen3 的 chat template 只认 xhigh / medium / low (注意是 xhigh,不是 high)。模板里直接 raise_exception,vLLM 在 apply_chat_template 阶段就 400 了。

踩的第一个深坑 :我本能地去改 tokenizer_config.json 里的 chat_template 字段,怎么改都不生效。最后才发现,这台 vLLM 真正读取的是模型目录下的独立文件 chat_template.jinja,而不是 tokenizer_config 里的字段。改错文件 = 白忙活。

还踩了一个 Jinja2 作用域坑 :第一次补丁我写成在 {% if %} 块里 {% set %},结果这是块级局部变量,改不到外层变量,模板仍拒绝。必须改成顶层 set + 条件表达式重赋值。

修好这一层后,普通对话确实通了。我一度以为结束了。

第三眼:工具调用一触发,400 又回来了

普通对话 OK,但只要模型一触发工具调用(请求里带 tools),400 又来了,报错长得一模一样。这次我学乖了------先直接抓流式响应看 vLLM 到底吐了什么:

text 复制代码
chunk 1: tool_calls[0].function.arguments = ""
chunk 2: tool_calls[0].function.arguments = '{"a": '
chunk 3: tool_calls[0].function.arguments = '23, "b": '
chunk 4: tool_calls[0].function.arguments = '47}'

真相大白:vLLM 把工具调用的 arguments 拆成多片 SSE 流出来了 。WorkBuddy 网关每收到一片,就立刻拿 arguments 去做 json.loads() ------ 空串和半截 JSON 当然解析失败 → Expecting value → 400。这回是客户端网关解析 SSE 分片崩了,不是服务端拒绝。

验证也很干净:同一请求加 "stream":false,vLLM 一次性返回完整 JSON("arguments":"{\"a\": 23, \"b\": 47}"),完美解析。所以问题只在「流式 + 工具调用」组合。

第四眼:想关流式,发现关不掉

最直接的想法是「那就不走流式呗」。结果翻遍 WorkBuddy 自定义模型配置,根本没有「流式输出 / stream」这个开关 ------ 流式是客户端内置默认行为,UI 上改不了。这条路死了。

第五眼:加一层极薄代理,既保工具又适配客户端

既然客户端改不了、又必须流式,那就在服务端前面加一层转发代理

  • tools 的请求 → 代理强制 stream=false 调 vLLM → 拿到完整结果 → 包成一个 SSE chunk + [DONE] 回传给 WorkBuddy;
  • 不带 tools 的普通对话 → 代理真流式透传。

WorkBuddy 以为自己一直在收流式,工具调用也能拿到完整 JSON,400 消失,工具能力完好保留。

第六眼:改完 Base URL 报 502,防火墙忘了开

代理在服务器本地跑得好好的,把 WorkBuddy 的 Base URL 指向代理端口后,却报 502 Server error。本地 curl localhost:8900 一切正常,外部连不上 ------ 一查是 UFW 没放行代理端口。放行后立刻恢复。

三层根因,同一种报错表象:

  1. 服务端模板不认 reasoning_effort: "high" → 服务端 400
  2. 流式工具调用 arguments 分片 → 客户端网关 400
  3. 防火墙没开代理端口 → 网络 502

二、WorkBuddy 自定义模型配置教程

1. 入口

WorkBuddy 设置 → 模型 / 自定义模型 → 新建自定义模型 → 选择「OpenAI 兼容」类型。

2. 必填字段

字段 说明 示例(占位)
模型名称 / Model ID 在 WorkBuddy 里显示的模型名 qwen3-local
API Base URL 推理服务的 OpenAI 兼容地址,必须指向 /v1 http://<SERVER_IP>:<PORT>/v1
API Key 服务鉴权密钥;无鉴权时可填任意非空串或留空(取决于服务端) sk-xxxx
额外请求头(可选) 部分网关需要自定义 header Authorization

高级配置里的「工具调用」「图片输入」「推理模式」「思考强度」等开关,按需勾选即可;它们本质是让客户端在请求里加 toolsreasoning_effort 等参数。

3. 关键约定

  • Base URL 必须带 /v1 后缀 。WorkBuddy 会在后面拼 /chat/completions/models 等路径。
  • 模型名要对得上 :填的 Model ID 必须存在于服务端的 /v1/models 返回里,否则会报 model_not_found
  • 流式是默认行为 :WorkBuddy 调用自定义模型时默认走 SSE 流式(stream=true),UI 上没有关闭流式的开关
  • 「推理模式 / 思考强度」选项,本质是客户端下发 reasoning_effort 等参数,具体取值是否被模型接受,取决于模型 chat template(见坑 1)。

4. 连通性自检(在能访问服务端的机器上)

bash 复制代码
# 1) 鉴权 + 模型列表
curl -s -o /dev/null -w "%{http_code}\n" \
  -H "Authorization: Bearer <API_KEY>" \
  http://<SERVER_IP>:<PORT>/v1/models

# 2) 最小对话(非流式)
curl -s -H "Authorization: Bearer <API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"model":"<MODEL_ID>","messages":[{"role":"user","content":"hi"}],"max_tokens":32,"stream":false}' \
  http://<SERVER_IP>:<PORT>/v1/chat/completions

两个都返回 200 即代表服务层正常,问题才可能在 WorkBuddy 这一侧。


三、踩坑记录

坑 1:客户端发送 reasoning_effort: "high" 导致服务端 400

现象

对话先正常吐出一段,随后报错:

复制代码
400 ... validation error ... JSON decode error ... Expecting value

表面像是「流式续传 body 为空」,实际是服务端在渲染模板时就拒了

根因

部分推理客户端(含 WorkBuddy 的某些模型预设)会默认下发 reasoning_effort: "high"。而 qwen3.8-27b-FP8 等 Qwen3 系列 chat template 只接受 xhigh / medium / low (注意是 xhigh 不是 high),模板里直接 raise_exception("Unexpected reasoning effort high"),于是 vLLM 在 apply_chat_template 阶段返回 400。

定位技巧

  • 直接对 /v1/chat/completions 发带 "reasoning_effort":"high" 的请求,能稳定复现即坐实。
  • 检查模型目录下的模板文件:真正生效的往往是独立的 chat_template.jinja不是 tokenizer_config.json 里的 chat_template 字段(vLLM 优先读独立 .jinja 文件)。改错文件只会白忙。
  • Jinja2 的 {% set %}{% if %} 块内是块级作用域,补丁如果用块级 set 重赋值会失效,必须用顶层 set + 条件表达式

服务端修复(保留思考能力、不改客户端)

在模板顶部,把 high 映射成 xhigh

jinja 复制代码
{# 原来 #}
{%- set resolved_reasoning_effort = reasoning_effort|default('xhigh') %}

{# 改成 #}
{%- set _re = reasoning_effort|default('xhigh') %}
{%- set resolved_reasoning_effort = 'xhigh' if _re == 'high' else _re %}

改完需重启推理服务使模板重新加载;操作前先备份原文件。

附:在 WorkBuddy 端也可以把「支持的思考强度」里只勾选 medium / low / xhigh(如果模型支持),避免客户端主动发 high。但某些预设仍会带 high,所以服务端模板兜底更稳。


坑 2:流式工具调用 arguments 分片,导致 WorkBuddy 网关 400

本节中"工具请求强制非流式"的方案是早期临时方案,已被后文的流式聚合方案替代。

现象

同上 400 报错(JSON decode error / Expecting value / input: {}),但这次不是服务端模板问题,而是 WorkBuddy 网关自己在解析流式响应时崩了。

根因

WorkBuddy 走 SSE 流式 且请求里带 tools 时,vLLM 会把 tool_calls.function.arguments 拆成多片通过 SSE 流出:

text 复制代码
chunk 1: data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":""}}]}}]}
chunk 2: data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"{\"a\": "}}]}}]}
chunk 3: data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"23, \"b\": "}}]}}]}
chunk 4: data: {"choices":[{"delta":{"tool_calls":[{"index":0,"function":{"arguments":"47}"}}]}}]}

WorkBuddy 网关收到每一片时,直接拿 arguments 去做 json.loads()。空串或半截 JSON 就会 Expecting value → 400。这是客户端网关解析 SSE 分片失败,不是 vLLM 拒绝请求。

验证

  • 同一请求加 "stream":false 后再发,工具调用返回完整 JSON("arguments":"{\"a\": 23, \"b\": 47}"),可正常解析。
  • 这就证明问题只在「流式 + 工具调用」组合。

为什么不在 WorkBuddy 关流式?

WorkBuddy 自定义模型配置 UI 没有 stream 开关,无法客户端关闭。

解决方案:加一个极薄转发代理

在 vLLM 前面加一层转发代理:

  • tools 的请求 → 强制 stream=false 调 vLLM → 拿到完整结果 → 包成单个 SSE chunk + [DONE] 回传。
  • 不带 tools 的普通对话 → 真流式透传,不影响体验。

这样 WorkBuddy 仍以为是流式,工具调用也能拿到完整 JSON,不再 400。

代理脚本示例(FastAPI + httpx,通用版)

python 复制代码
# proxy_custom_model.py
import os, json, uvicorn
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse, Response
import httpx

UPSTREAM = os.environ.get('PROXY_UPSTREAM', 'http://localhost:8000')
API_KEY  = os.environ.get('PROXY_API_KEY', '')
PORT     = int(os.environ.get('PROXY_PORT', '8900'))

app = FastAPI()

def make_chunk(cid, model, created, delta, finish, usage=None):
    return {
        'id': cid, 'object': 'chat.completion.chunk',
        'created': created, 'model': model,
        'choices': [{'index': 0, 'delta': delta, 'finish_reason': finish}],
        'usage': usage,
    }

def build_tool_chunks(full):
    ch0 = (full.get('choices') or [{}])[0]
    msg = ch0.get('message', {}) or {}
    cid, model, created = full.get('id'), full.get('model'), full.get('created')
    finish, usage = ch0.get('finish_reason'), full.get('usage')
    d1 = {'role': 'assistant'}
    if msg.get('reasoning'):
        d1['reasoning'] = msg['reasoning']
    if msg.get('tool_calls'):
        d1['tool_calls'] = msg['tool_calls']
    out = [make_chunk(cid, model, created, d1, None)]
    if msg.get('content'):
        out.append(make_chunk(cid, model, created, {'content': msg['content']}, None))
    out.append(make_chunk(cid, model, created, {}, finish, usage))
    return out

def iter_sse(chunks):
    for c in chunks:
        yield 'data: ' + json.dumps(c, ensure_ascii=False) + '\n\n'
    yield 'data: [DONE]\n\n'

@app.api_route('/v1/{path:path}', methods=['GET','POST','OPTIONS','PUT','DELETE'])
async def proxy(path: str, request: Request):
    body = await request.body()
    try:
        payload = json.loads(body) if body else {}
    except Exception:
        payload = {}
    has_tools = isinstance(payload, dict) and bool(payload.get('tools'))

    headers = {k: v for k, v in request.headers.items()
               if k.lower() not in ('host','content-length','content-type','accept','accept-encoding')}
    if API_KEY:
        headers['Authorization'] = 'Bearer ' + API_KEY
    url = f'{UPSTREAM}/v1/{path}'

    # 工具调用:强制非流式,拿到完整结果后包成 SSE
    if has_tools:
        payload['stream'] = False
        async with httpx.AsyncClient(timeout=600) as c:
            r = await c.post(url, json=payload, headers=headers)
        if r.status_code != 200:
            return Response(content=r.content, status_code=r.status_code,
                            headers={'content-type': r.headers.get('content-type','application/json')})
        return StreamingResponse(iter_sse(build_tool_chunks(r.json())), media_type='text/event-stream')

    # 普通 GET(如 /v1/models)
    if request.method == 'GET':
        async with httpx.AsyncClient(timeout=600) as c:
            r = await c.get(url, headers=headers)
        return Response(content=r.content, status_code=r.status_code,
                        headers={'content-type': r.headers.get('content-type','application/json')})

    # 普通对话:真流式透传
    async def _stream():
        async with httpx.AsyncClient(timeout=600) as c:
            async with c.stream(request.method, url,
                                json=payload if payload else None,
                                headers=headers) as r:
                async for chunk in r.aiter_raw():
                    yield chunk

    return StreamingResponse(_stream(), media_type='text/event-stream')

if __name__ == '__main__':
    uvicorn.run(app, host='0.0.0.0', port=PORT, log_level='info')

启动:

bash 复制代码
export PROXY_UPSTREAM=http://<VLLM_IP>:<VLLM_PORT>
export PROXY_API_KEY=<API_KEY>
export PROXY_PORT=8900
setsid nohup python3 proxy_custom_model.py > proxy.log 2>&1 &

WorkBuddy 里把 Base URL 改成 http://<SERVER_IP>:8900/v1 即可。


坑 3:Base URL 切到代理端口后报 502

现象

WorkBuddy 报错:

复制代码
Network error: 502 Server error (proxy: undefined -> http://<IP>:8900)

根因

代理进程虽然在服务器本地正常监听,但服务器防火墙 / 安全组没放行 8900 端口,WorkBuddy 从公网连不进来。

排查

  • 在服务器本地:curl http://localhost:8900/v1/models 返回 200(代理正常)。
  • 从外部机器:curl http://<SERVER_IP>:8900/v1/models 超时或拒绝连接(防火墙/安全组问题)。

解决

放行端口。以 Ubuntu UFW 为例:

bash 复制代码
ufw allow 8900/tcp

云服务器还要在控制台安全组里放行 TCP 8900。


坑 4(后续新发现):工具请求带 stream_options,被代理强制非流式后触发 400

现象

配置都正常、对话也通了,但只要请求里带 tools(工具调用),立刻又报 400:

复制代码
400 Stream options can only be defined when `stream=True`. (parameter=stream_options)

根因

WorkBuddy 在请求里会附带 stream_options(例如 {"include_usage": true},用于让流式响应末尾带上 token 用量统计)。而我们的转发代理在「坑 2」的解法里,对 tools 请求强制把 stream 设成 False 去拿完整结果------但这时 stream_options 还留在 body 里。vLLM 的硬性校验是:stream_options 只有在 stream=True 时才允许存在,于是直接 400。

注意这个 400 和「坑 1」的报错表象不同(这里明确点名 stream_options),定位很快。

解决方案

在代理的「工具请求」分支里,强制非流式的同一时间,stream_options 一并摘掉

python 复制代码
if has_tools:
    payload['stream'] = False
    payload.pop('stream_options', None)  # vLLM: stream_options only valid when stream=True
    ...

普通对话(stream=True)路径不受影响,stream_options 原样透传。改完重启代理即可。


坑 5(最隐蔽,也是「不回复」的真凶):代理整段缓冲工具请求,客户端等不到首字节超时

现象

对话 / 工具调用的配置全对、curl 实测也 200,但在 WorkBuddy 里用模型提问,界面一直空白、像没回复;而且「直连 vLLM 能等、走代理就等不到回复」。

根因

问题出在转发代理的「工具请求」分支。该分支对带 tools 的请求强制 stream=False ,等 vLLM 把整轮(思考 15~25 秒 + 正文)全部生成完,才一次性吐出 SSE。在这十几二十秒里,WorkBuddy 一个字节都收不到 → 触发首 token 超时 / 判定无响应,直接白屏或断开。

直连 vLLM 是原生流式,思考片段立刻往外冒,客户端一直有数据,所以「能等」;代理整段缓冲把这条数据流掐断了,所以「不能等」。这也解释了为什么「之前直连能等、加代理后不能等」。

解决方案

「工具分支」也改成从上游流式取,边收边转发 reasoning / content ,只把 tool_calls.arguments(这个字段在流式里会被拆成多片)攒齐后一次性发。这样既保证客户端全程有数据、不会超时,又避免当初为修分片 bug 而强制非流式带来的副作用。核心代码:

python 复制代码
if has_tools:
    payload['stream'] = True                       # 改回流式:不让客户端干等
    payload['stream_options'] = {'include_usage': True}
    async def _tool_stream():
        tool_acc = {}
        async with httpx.AsyncClient(timeout=600) as c:
            async with c.stream('POST', url, json=payload, headers=headers) as r:
                async for raw in r.aiter_lines():
                    ...            # 逐行解析,content / reasoning 立即 yield 转发
                    # tool_calls 的 arguments 累加进 tool_acc,遇到 [DONE] 时整段 yield 一次
        ...
    return StreamingResponse(_tool_stream(), media_type='text/event-stream')

验证效果(同一工具请求):

  • 改前(整段缓冲):首字节要等 15~25 秒;
  • 改后(流式透传 + 工具参数整合):TTFB ≈ 4ms ,且 tool_calls.arguments 为完整合法 JSON(如 {"city": "北京"}),不再分片。

关键结论:代理永远不要对慢思考模型做「整段缓冲 + 末尾一次性返回」;要流式透传,只对确实会分片的字段做服务端整合。

坑 6(当前代理已修复):finish_reason 提前发送,导致 WorkBuddy 误判结束

现象

早期的流式聚合代理虽然已经把 arguments 拼成完整 JSON,但实际 SSE 顺序是:

text 复制代码
finish_reason: "tool_calls"
usage
tool_calls(完整参数)
[DONE]

WorkBuddy 收到 finish_reason 后会认为本轮已经结束,随后可能发出空请求,最终表现为"不回复"或 400 JSON decode error

当前修复

代理文件:/data/lost+found/sjh/VLLM/proxy_qwen3.8.py

代理端口:8900

上游 vLLM:http://localhost:8998

当前工具调用分支的正确顺序是:

text 复制代码
reasoning/content(实时转发)
tool_calls(arguments 已完整拼接,并包含 index)
finish_reason: "tool_calls"
[DONE]

具体修复包括:

  • 暂缓转发上游的 finish_reason,直到完整工具调用已经发送;
  • 聚合 tool_calls.function.arguments 后一次性发送;
  • 补充 tool_calls[].index
  • 延后 usage-only SSE chunk,避免严格客户端提前结束;
  • 不再对工具请求做整段 stream=false 缓冲,思考内容仍实时输出。

修复后使用带 toolsreasoning_effortstream_optionsstream=true 的请求实测,输出顺序正确,arguments 是完整合法 JSON。


四、关键结论与速查表

报错 真实位置 根因 解法
400 JSON decode error / Expecting value / input: {} 服务端 vLLM reasoning_effort: "high" 不被 Qwen3 模板接受 chat_template.jinjahigh 映射为 xhigh
400 JSON decode error / Expecting value / input: {} WorkBuddy 网关或中间代理 流式工具调用 arguments 分片,或代理先发 finish_reason 后发工具调用 代理实时转发 reasoning/content,聚合完整 arguments,先发 tool_calls 再发 finish_reason
502 Server error (proxy: undefined -> ...:8900) 网络层 防火墙/安全组未放行 8900 ufw allow 8900/tcp + 安全组放行
400 ... Stream options can only be defined when stream=True (parameter=stream_options) 服务端 vLLM 工具请求被代理强制非流式,但 stream_options 未摘掉 代理「工具分支」里 payload.pop('stream_options', None) 后重启
界面一直空白 /「不回复」(错误码可能无,仅超时或白屏) 客户端 WorkBuddy 代理「工具分支」对 tools 请求整段缓冲(stream=False),慢思考模型 15~25 秒无字节 → 客户端首 token 超时 代理工具分支改回 stream=True 流式透传,仅攒齐 tool_calls.arguments 后一次性发(见坑 5)

WorkBuddy 配置检查清单

  • Base URL 以 /v1 结尾
  • API Key 与服务端 --api-key 一致
  • 若用了代理,Base URL 指向代理端口(如 :8900),而非裸 vLLM 端口
  • 代理所在端口已在防火墙/安全组放行
  • 模型名称与服务端 /v1/models 返回的一致

五、一句话总结

WorkBuddy 自定义模型走 OpenAI 兼容协议时,流式是默认且不可关的 ;对 Qwen3 系模型要特别注意 reasoning_effort 参数映射,对工具调用要处理 SSE 分片和事件顺序。当前推荐的接入方式是在推理服务前加一个极薄代理:普通内容实时透传,只聚合 tool_calls.arguments,并保证先发完整工具调用、再发 finish_reason。同一个 400 表象下可能藏着多层不同根因,定位时务必先抓真实日志、再直接抓流式响应看事件顺序

相关推荐
thesky1234565 小时前
27届大模型面试准备(五十九):大模型推理引擎内核深度剖析——PagedAttention、调度器与显存管理
大模型·vllm·flashattention·推理引擎·pagedattention·连续批处理·显存管理
谢白羽5 小时前
SGLang模型加载过程笔记
笔记·llm·论文·agent·vllm·大模型部署·sglang
zhangfeng113319 小时前
AMD Instinct MI50(gfx906)上为 Qwen 系列模型优化并可用的 vLLM 相关仓库、Docker 镜像与实践指南。
人工智能·docker·ai编程·qwen·算子开发·vllm·mi50
缘友一世1 天前
MiniMax-M3 on A800:部署、Bug 修复与压测完整复盘
开源项目·vllm·大模型部署·项目复盘·a800·minimax-m3
缘友一世2 天前
GLM-5.2-NVFP4 在 8×A800 上部署实战(下)
vllm·大模型部署·a800·glm5.2 nvfp4
缘友一世2 天前
GLM-5.2-NVFP4 在 8×A800 上部署实战(上)
vllm·大模型部署·a800·glm5.2 nvfp4
苏子寒2 天前
Nano-VLLM全代码解析笔记(2)-block_manager
人工智能·笔记·python·机器学习·nlp·vllm
苏子寒3 天前
Nano-VLLM全代码解析笔记(8)-qwen3与qwen3_moe
笔记·python·深度学习·ai·性能优化·vllm
Albart5753 天前
多卡张量并行显存分配不均终极解决:单卡跑满、其余显卡闲置彻底根治
大模型·vllm·张量并行·分布式推理·多卡部署·显存oom