返回文章列表
    工程实践
    9 分钟

    一台 DGX Spark 的企业级 RAG 48 小时:从重排缺失到 v1.0.0

    2026-07-24 到 07-25 的 48 小时里,我们的生产 RAG 系统从"聊天路径从未真正重排过"走到了 v1.0.0:接入 cross-encoder 重排、把推理引擎从 llama.cpp 迁到 vLLM、黄金问题集从 1 条扩到 17 条。这篇文章老实地记录整个过程——包括一次 4.5 分钟就中止回滚的失败窗口。

    目录

    起点:聊天路径从未真正"重排"过

    故事的起点并不体面:聊天路径检索一直是纯向量召回 top-8 直接喂给 LLM,rerank_score 要么不存在要么恒为 0。第一步是把 cross-encoder 重排真正接进聊天链路:新增门控、候选池从 8 扩到 24 再精排回 8、REST 接口同步扩宽、写 SSE 并发压测脚本[1]。中途撞上第一个意外:原计划用的 HuggingFace TEI 在 DGX Spark 的 ARM64 架构上没有可用镜像,临时改用已经在跑的 llama.cpp 镜像的 --reranking 模式顶上(细节见另一篇文章)[1]

    引擎瓶颈浮出水面

    重排接好后测的单槽位 llama.cpp 并发基线:c=1 TTFT p50 12.3s、c=2 33.8s、c=4 51.2s,聚合吞吐平坦卡在 2.4–2.7 tok/s——引擎串行化就是瓶颈本身。止血方案是切到 4 槽位(-np 4):c=4 TTFT 降到约 21s,聚合吞吐从 2.7 提到 4.6 tok/s(+70%),单流解码没有回退[1]

    闭环测试逼出的 5 个隐性缺陷

    真正暴露问题的不是代码评审,是带真实认证、真实并发的闭环压测,一轮下来揪出 5 个此前从未被点亮过的缺陷[1]:MCP 子进程环境被剥光(stdio 子进程只继承 HOME/PATHRERANKER_HOST 静默丢失,透传全部环境后才修复);MCP 日志打到 stdout 污染了 JSONRPC 通信通道(392 次解析错误,改路由到 stderr);用户断连时 agent 请求上下文重置在跨上下文场景抛 ValueError(加守卫修复);第一次"全挂"基线其实是访客限流拦截了裸压测请求(换成正式会员 JWT 解决);reranker 对约 1K token 分片返回 500(默认批大小 512 装不下,调大 ubatch 到 4096 解决)。外加一个平行发现:新版 llama.cpp 已移除 --cache-prompt 标志,继续传参会崩溃重启循环,随即剔除[1]。这批修复合入主干后,完整测试套件 2743 个用例全绿。

    P1a:迁移到 vLLM,第一个窗口 4.5 分钟内中止

    -np 4 只是止血。第二阶段计划换成 vLLM,拿到自动前缀缓存 + chunked prefill 的优势。第一个维护窗口(2026-07-25 凌晨 01:31–01:35)在 4.5 分钟内主动中止并回滚——vLLM 启动报 SafetensorError,15 个权重分片里 4 个是被中断下载留下的残片。窗口前检查只核实了分片"是否存在",没核实"是否完整",教训明确:正式开窗前必须做头部完整性校验[2]。失败之前的信号是正面的:所有计划参数(mxfp4、Marlin 后端、fp8 KV、前缀缓存、chunked prefill 等)都被完整接受,只是权重坏了;回滚在 01:35:57 验证完成,llama.cpp 全程保持对外 200,没有真正的生产中断[2]。修复权重、加上双重完整性校验(头部解析 + 大小偏移交叉核对)后,才申请第二个窗口。

    第二个窗口:成功,但老实记录了两个未达标项

    第二窗口(02:39–02:58)冷启动仅约 10 分钟(预算 30–40)。B 侧真实聊天链路压测结果[2]

    并发TTFT p50(A→B)聚合吞吐(A→B)
    c=114.0s → 3.96s(−72%)3.1 → 3.7 tok/s
    c=228.0s → 11.27s(−60%)2.5 → 5.0 tok/s
    c=421.0s → 14.74s(−30%)4.6 → 5.5 tok/s

    18/18 请求成功、0 错误。但我们同时老实记录了两处没有达标的地方:单流解码 34.3 tok/s 未达 ≥45 目标线(社区宣称的 59 需要这次构建里没有的 FlashInfer 后端,未复现);黄金问题集单条问题忠实度从 83.5 掉到 70.9,但这是 n=1 且受查询缓存影响的样本,统计意义不足,真正修复留给黄金集扩容[2]。最终判定保留 vLLM——每个用户能感知的指标都在变好,这是权衡后的决定,不是回避不利数据。

    NGC 头对头 + 稳定性复查

    03:59–04:35 又把 NGC 官方镜像拉下来头对头:单流解码两边一致(34.3,都卡在 TRITON_ATTN),c=1 TTFT p50 自建镜像 3.96s、NGC 9.83s,c=4 基本打平,NGC 聚合吞吐略高 9%——继续保留自建镜像,2.5 倍的单用户 TTFT 优势比 9% 聚合吞吐更值钱[2]。05:47 复查确认 0 重启、0 错误,内存稳定 89–91GB;c=6/8 探测显示 c=6 聚合吞吐 8.6 tok/s(+56%),c=8 已饱和,据此把并发闸从 4 提到 6(全局)/4(单租户)[1][3]

    护栏与数字总览

    CI 阻断门槛的黄金问题集从 1 条扩到 17 条,覆盖源召回、关键词召回、反捏造负样本、引用正确性、忠实度五个维度,全部来自真实流量和知识库覆盖度;夜间 03:10 定时任务用同一套框架对线上跑一遍[3]。48 小时里我们在栈的各层共发现修复 13 个真实缺陷;收尾时后端测试 2,810 个、前端 842 个,全部绿灯[3]

    老实说的工程文化

    这篇文章值得写,不是因为"一路顺利",而是不顺利的部分也被完整记录:一次窗口中止回滚,两处未达标指标,五个闭环测试才暴露的缺陷。把 gate miss 写进记录,比悄悄调好看更有价值——后来的人才知道哪里还欠着账。

    常见问题

    Q: 48 小时里有没有出过真正的生产事故? A: 没有不可控宕机。唯一的"失败"是 P1a 第一个窗口——vLLM 因权重损坏启动失败,我们在 4.5 分钟内主动中止回滚,llama.cpp 全程保持服务,这是按预案执行的受控中止。

    Q: 为什么要老实公布"没达标"的指标? A: 两处 gate miss(单流解码未达标;n=1 忠实度分数因样本和缓存不具统计意义)都各有明确后续修复计划,保留 vLLM 是在看到它们之后做出的权衡,而不是隐藏它们。

    Q: 黄金问题集到底测什么? A: 17 道题覆盖源召回、关键词召回、反捏造负样本、引用正确性、忠实度,来自真实用户提问和知识库覆盖分析,既是 CI 门槛也是每日 03:10 生产体检。

    关于 HTZL.AI

    HTZL.AI 是四川慧途智联科技有限公司旗下的企业级私有 AI 品牌,专注于"数据不出企业、模型不进云端"的私有化部署路径,是 NVIDIA Inception 会员企业。本文所有数据均来自我们在真实 DGX Spark 生产环境上的工程记录。

    参考来源

    1. 1.HTZL.AI P0 重排激活工程记录(内部工程台账,2026-07-24,未公开发布)
    2. 2.P1a: vLLM Migration A/B Implementation Plan,docs/superpowers/plans/2026-07-24-p1a-vllm-migration.md,内部工程记录
    3. 3.HTZL.AI multi-agent-chatbot v1.0.0 Release Notes,内部工程记录