在 AMD Instinct GPU 上编译 vLLM,ROCm 7.x 抛出一个 Segmentation fault 却连 traceback 都不给,是很多人卡住的第一关。遇到这种段错误,先去 TaoToken(https://taotoken.net/?utm_source=taotoken_aicg_blog_end )创建一把 API Key,再把 Codex 接到统一通道上,让它陪你逐条对照 rocminfo 输出、pip 版本清单和编译日志。TaoToken 在这里只负责提供可用的模型通道,真正敲 rocminfo、跑 pip install、执行 python -c 的人始终是你自己——Codex 不会远程 SSH 到你的机器,也不会替你操作生产环境,它做的只有一件事:把你贴过去的文本和版本号排成一张对照表,指出 Triton、PyTorch ROCm 后端和架构代码这三者里谁先对不上。
经验上,vLLM 的段错误可以粗分成三类:编译期 hipcc/clang 段错误、导入期import vllm崩在 Triton、运行期非法指令。三类现象的排查顺序完全不同,混在一起看就会觉得「明明都装了还是崩」。下面按这个顺序展开:先抓现场证据,再让 Codex 进对话做版本对照,然后落到PYTORCH_ROCM_ARCH、HIP_PATH这些具体变量上重编,最后才是显存和多卡参数。原始那篇部署指南里的环境验证、源码编译、显存优化、性能监控四段,在排障场景下会被拆成「证据 → 对照 → 修复 → 复现」四步来走。
1. 段错误现场:vLLM 在 ROCm 7.x 上到底崩在哪一层
1.1 先分清编译期 segfault 和导入期 segfault
很多人一看到 Segmentation fault 就怀疑显卡、怀疑驱动,其实这一步最该做的是分清崩溃发生在哪个进程阶段。编译期的段错误,终端通常停在Building wheel for vllm或hipcc调用的那一行,前后会夹着大段 kernel 编译输出;导入期的段错误则出现在你敲完python -c "import vllm"之后,屏幕上干净得只剩一个 Segmentation fault,连 Python 层的异常都没抛出来;运行期的报错往往是Illegal instruction或者 HIP 返回hipErrorNoBinaryForGpu,这种才是典型的架构代码不匹配。
把这三类分开,直接决定了后面要不要重编。编译期崩溃要动编译器和环境变量,导入期崩溃优先查 Triton 与 torch 的二进制兼容,运行期崩溃基本锁定PYTORCH_ROCM_ARCH写错了型号。下面几条命令在你本地终端执行,把输出留着,后面要整体贴进对话:
python -c "import torch; print('torch', torch.__version__, 'hip', torch.version.hip)" python -c "import triton; print('triton', triton.__version__)" python -c "import vllm; print('vllm', vllm.__version__)" python -c "import torch; print(torch.cuda.is_available(), torch.cuda.get_device_name(0))"注意最后一条虽然写的是torch.cuda,在 ROCm 轮子里它映射到的就是 HIP 后端,返回 True 只能说明后端被识别到了,并不能证明算子全部可用,这个区别后面还会再提。
1.2 把 rocminfo、pip freeze、编译日志三份材料抓齐
Codex 拿不到你的机器状态,它能推理的全部依据就是你粘过去的文本,所以材料一定要抓全。第一份是硬件架构信息,用rocminfo找 Agent 段落里的 gfx 编号;第二份是版本清单;第三份是完整编译日志。三份都建议重定向到文件,方便一会整段复制:
rocminfo | grep -E "Name:|gfx" | head -40 rocm-smi pip freeze | grep -Ei "torch|triton|vllm|hip|rocm" pip install --no-build-isolation -v vllm 2>&1 | tee /tmp/vllm-build.log/tmp/vllm-build.log不要只截最后十行,Triton 相关的失败信息常常混在中段。如果日志超过几千行,可以先用grep -n -i "segmentation\|fault\|triton\|error" /tmp/vllm-build.log | head -50筛一遍,把命中行前后各二十行一起贴出去,比重贴整份日志更有效。
材料准备的同时把通道准备好:打开 TaoToken 注册账号,在控制台创建一把 API Key,记下模型广场里当前可用的模型 ID。Key 只用于 Codex 对话本身,跟你的 ROCm 编译环境没有任何耦合,别把 Key 写进PYTORCH_ROCM_ARCH这类变量里,两者是完全不同层面的东西。
2. Codex 通过 TaoToken 读日志:config.toml 的三行关键配置
2.1 API Key 与模型 ID 从哪里取
Key 的创建入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end ,进去之后按控制台提示新建,复制出来的字符串形如占位符YOUR_API_KEY。模型 ID 不要凭记忆写,也不要自己加日期后缀猜一个,一律以模型广场当时的列表为准——每家平台命名习惯不同,猜错一个字符,返回的就是 404 或者「模型不存在」,白白浪费一轮排查时间。
拿到这两样东西之后,先在网页侧的模型对话里发一条测试消息,确认这把 Key 是活的、模型 ID 是能命中的。这一步几十秒就能做完,却能排掉后面一大半「配了但没反应」的假故障。
2.2 在 ~/.codex/config.toml 里把 base_url 指向 TaoToken
Codex 的配置放在用户目录下的~/.codex/config.toml,核心是声明一个自定义 provider,把base_url填成统一入口,再通过环境变量把 Key 喂进去。填进工具的地址用https://taotoken.net/api,末尾不要带/v1,这一点和很多 OpenAI 兼容文档里的写法不同,多写一层路径会出现 404:
model = "YOUR_MODEL_ID" model_provider = "taotoken" [model_providers.taotoken] name = "TaoToken" base_url = "https://taotoken.net/api" env_key = "TAOTOKEN_API_KEY" wire_api = "chat"环境变量在同一个 shell 里导出,或者写进你的 shell 配置文件,注意这里只能写 Key 本身,不要写任何 URL:
export TAOTOKEN_API_KEY=YOUR_API_KEY保存后重新开一个终端,随便提一句「你能看到我这条消息吗」验证连通性。如果 Codex 报 401,先检查环境变量有没有在正确的 shell 里生效;报 404 则检查base_url是否被误写成了带/v1的形式,或者模型 ID 写错。
2.3 把三份材料贴进对话的提问模板
进对话之后不要只说「vLLM 段错误怎么办」,信息量太低,模型只能给你一堆通用建议。有效的提问是把证据铺开,让 Codex 做交叉比对,模板大致长这样:第一段贴rocminfo里 gfx 开头的行;第二段贴pip freeze中 torch、triton、vllm 三行;第三段贴编译日志里命中段错误的前后文;最后明确要求三列输出——「当前版本组合」「需要执行的核对命令」「可能的修复动作」。
这样提问的好处是结论可验证。Codex 给出的每一条修复动作,你都能在自己机器上跑一遍看结果,跑完把新输出再贴回去,形成一轮闭环。TaoToken 在这个闭环里承担的只是模型通道的角色,Token 消耗在每次对照与解释上,编译和验证依然在你本机完成。
3. Triton 与 PyTorch ROCm 后端的版本对照怎么做
3.1 用 torch 的源码 pin 锁定 Triton 版本
ROCm 版 PyTorch 对 Triton 的依赖比 CUDA 版更敏感,因为它背后是同一套 HIP 编译链。判断该装哪个 Triton,最可靠的做法不是看 PyPI 上最新版是多少,而是看你当前 torch 版本对应的源码 tag 里那份 pin 文件——PyTorch 仓库的.ci/docker/ci_commit_pins/目录下就放着 Triton 的提交锁定记录。把当前torch.__version__里的版本号对上 tag,翻到那个文件,就能知道官方测试时用的是哪一支 Triton。
拿到目标版本之后,先卸干净再装,避免旧编译产物残留:
pip uninstall -y triton vllm pip install "triton==目标版本" python -c "import triton; print(triton.__version__)"如果 Codex 在对话里判断「Triton 版本超前于 torch ROCm 后端」,它会建议你降级而不是升级。这时候别硬扛,段错误本身就说明二进制层面的 ABI 已经对不上了,继续往上装新版本只会让崩溃点从导入期挪到运行期,更难定位。
3.2 PYTORCH_ROCM_ARCH 与 rocminfo 的 gfx 代码对齐
第二层对照是架构代码。rocminfo输出里会有一行类似Name: gfx942或者gfx90a,而PYTORCH_ROCM_ARCH必须写成不带任何特性后缀的纯编号。有些机器会打印成gfx942:sramecc+:xnack-这种形式,冒号后面的部分属于运行时特性标记,填进环境变量时要去掉,只留gfx942。
如果是混插机器,可以用分号并列多个架构,但每个都要确认对应真实硬件:
export PYTORCH_ROCM_ARCH="gfx942;gfx90a" echo $PYTORCH_ROCM_ARCH写错架构的后果非常有辨识度:编译能过,一加载模型就崩,报错还常常是底层的非法指令而不是 Python 异常。原始部署文章里提到这一步能规避大部分架构不匹配问题,从排障角度看,它同时也是段错误和非法指令两类报错的分水岭——先确认PYTORCH_ROCM_ARCH和rocminfo一致,再去怀疑 Triton。
3.3 HIP_PATH、MAX_JOBS 与 no-build-isolation 的重编顺序
修完前两层,重编要按固定顺序来:先清旧包,再定变量,最后带日志装。HIP_PATH指向 ROCm 安装根目录,MAX_JOBS用满 CPU 核数缩短构建时间,--no-build-isolation让构建过程复用你当前环境里已经对齐好的 Triton,而不是另起一个隔离沙箱又装一份版本不一致的依赖:
export HIP_PATH=/opt/rocm export MAX_JOBS=$(nproc) pip install --no-build-isolation -v vllm 2>&1 | tee /tmp/vllm-build-2.logMAX_JOBS不要盲目开到核数两倍,内存不够时并行编译会触发 OOM Killer 把 hipcc 杀掉,现象看起来像「编译到一半进程消失」,很容易被误判成段错误。机器内存偏小的话,把MAX_JOBS压到核数的一半更稳。
4. 重编之后的最小验证:别急着上大模型
4.1 确认 HIP 后端与设备名
编译完第一件事不是起服务,而是确认后端真的被识别。跑一次设备和后端检查,把输出和之前那份对比,看torch.version.hip有没有变化、设备名是否列出你预期的 Instinct 型号:
python -c "import torch; print(torch.cuda.is_available()); print(torch.cuda.get_device_name(0)); print(torch.version.hip)" python -c "import triton; print(triton.__version__)" python -c "import vllm; print(vllm.__version__)"三条命令都能正常打印,说明导入期段错误已经被消掉。如果第二条仍然崩,说明 Triton 这一层还没对齐,回到 3.1 重新核对 pin 版本,不要靠反复重装碰运气。
4.2 用最小模型加载替代全量服务启动
真正跑通加载之前,不建议直接拉几十 GB 的大模型。先用参数量小的模型走一遍权重加载和一次前向,确认算子路径没有问题;服务入口能不能正常起来,也可以用参数帮助信息快速验证:
python -c "from vllm import LLM; llm = LLM(model='小模型路径', tensor_parallel_size=1); print('load ok')" python -m vllm.entrypoints.openai.api_server --help > /dev/null && echo "entrypoint ok"小模型能加载、入口能打印帮助,这两个信号同时拿到,才说明编译层面的段错误已经解决,接下来遇到的任何崩溃都属于显存或并行配置范畴,排查范围一下子缩小了。
4.3 段错误还在,把新的 traceback 再贴一轮
如果重编之后导入期仍然段错误,不要推翻前面的结论从头再来。正确做法是抓新的日志,和上一轮做差集:新日志里如果 Triton 相关的报错行消失了,说明方向对了,只是还有别的问题;如果崩在同一处,大概率是环境里同时存在两个版本的 Triton,可以pip list | grep -i triton确认有没有重复安装,再python -c "import triton; print(triton.__file__)"看实际加载的是哪一个路径。
把这轮的新日志、pip freeze里相关行、以及你实际执行的修复命令一起贴回对话,让 Codex 做前后对比。这一步比第一轮更有价值,因为此时版本组合已经变化,模型能基于差异给出更贴近的下一步建议。
5. 编译通过之后再谈显存与多卡:两个容易互相掩盖的参数
5.1 gpu-memory-utilization 与 OOM 的边界
段错误解决后最常见的下一个报错是 OOM,而 OOM 和非法指令的日志长得完全不一样:前者是显存申请失败,后者是二进制不兼容。参数上,--gpu-memory-utilization不要一上手就顶到 0.95,ROCm 栈本身有驱动缓冲和运行时开销,把比例留在合理区间更稳,具体到你的卡型和模型,可以用小模型先试出上限再往上调。
显存碎片化明显时,--block-size会影响管理开销:短序列偏多的业务用小 block 更省,长序列为主的场景把 block 调大能减少调度负担。这两个参数在编译没通过之前调是没有意义的,先解决崩溃,再优化利用率。
5.2 tensor-parallel-size 与 NUMA 绑核
多卡推理上--tensor-parallel-size会按卡数切分权重,卡间通信质量直接决定吞吐。同一 PCIe 根复合体下或走高速互联的卡,通信延迟差异会体现在长序列生成的首字延迟上。进程绑核这一层,可以用 numactl 把推理进程固定到对应 NUMA 节点,避免多张卡的计算进程挤在同一批 CPU 核心上互相抢资源,现象是并发一上来吞吐不升反降。
判断瓶颈时可以先用固定并发跑几组对比,把--tensor-parallel-size和绑核策略当成两个自变量,一次只动一个,记录吞吐变化,这样得到的结论才可复现。参数调优阶段的性能数据建议连同当时的版本清单一起存档,下次环境变化时能直接对照。
6. 把这套排查沉淀成清单,并接上下一次调用
6.1 一份可复用的环境清单
一轮段错误排查下来,真正值钱的产出是那份版本组合:torch 版本、triton 版本、PYTORCH_ROCM_ARCH取值、HIP_PATH、编译器版本、rocminfo 的 gfx 行。把它们写进一个env-check.sh,每次换机器或升级 ROCm 之前跑一遍,比出事之后再翻聊天记录快得多。驱动侧依旧建议先跑rocm-smi看温度功耗显存是否正常,再用rocminfo确认架构代码,这两步是原始部署流程里最值得保留的开场动作。
排查方法上也要留个心眼:Codex 给的是基于你贴过去文本的推理结论,编译、执行、验证这三件事必须由你在本地完成,任何一步都不要指望对话窗口替你跑命令,更不要把它指向生产库或线上机器。把「生成建议 → 本地执行 → 结果回贴」当成固定循环,效率和安全性都能兼顾。
6.2 下一步:从对话验证到长期写代码
配置通了之后,先在 TaoToken 模型对话 里用同一把 Key 发一条消息,确认这次改的config.toml和模型 ID 都生效;如果你打算把这类日志排查变成日常动作,可以到 Coding Plan 看套餐额度是否压得住长期使用;需要再建新 Key 或轮换旧 Key,在 控制台 API Keys 里操作,顺手也能看到这几轮排查消耗了多少 Token。
想把这套流程挪到更顺手的命令行工作流,可以对照 Claude Code 接入文档 里的环境变量写法,把同一套 Base URL 和 Key 换到另一个工具上。至于 ROCm 版本清单,建议每隔一段时间回 https://taotoken.net/?utm_source=taotoken_aicg_blog_end 的模型广场对一次当前可用模型,避免照着旧记录填了一个已经下线的 ID。