<?xml version="1.0" encoding="utf-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" version="2.0"><channel><title>系统PLUS博客</title><link>https://www.sysplus.top/</link><description>系统PLUS博客</description><item><title>推理服务该盯哪些指标：一张诊断映射表</title><link>https://www.sysplus.top/?id=23</link><description>&lt;p&gt;推理服务上线后最难的不是没监控，是有一堆监控但故障发生时不知道看哪个。这篇给一张诊断映射表：指标怎么动，问题就在哪儿。&lt;/p&gt;
&lt;blockquote&gt;验证截至 2026-10-21。指标名取自 vLLM 原生 Prometheus 端点；阈值与诊断映射综合自公开运维实践文章，文中已区分&amp;quot;原生指标&amp;quot;与&amp;quot;经验阈值&amp;quot;，后者需按自身 SLO 调整。&lt;/blockquote&gt;
&lt;h2&gt;环境与版本&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;适用对象：vLLM（其他引擎指标名不同，但诊断逻辑通用）&lt;/li&gt;&lt;li&gt;数据源：引擎自带 &lt;code&gt;/metrics&lt;/code&gt; 端点 + &lt;code&gt;dcgm-exporter&lt;/code&gt;（GPU 层）+ &lt;code&gt;node-exporter&lt;/code&gt;（宿主机层）+ 网关层&lt;/li&gt;&lt;li&gt;前提：&lt;strong&gt;引擎必须先能吐指标&lt;/strong&gt;。纯黑盒跑推理、不打日志不暴露端点的，装什么监控都救不了&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;为什么传统监控在这里失灵&lt;/h2&gt;
&lt;p&gt;传统微服务看 CPU、内存、网络 I/O、HTTP 状态码四件套。在 GPU 集群上你会遇到三种解释不了的现象：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;GPU 利用率长期 100%，但服务其实在空转等待&lt;/li&gt;&lt;li&gt;显存显示占满 95%，但你不知道这是健康还是马上要 OOM&lt;/li&gt;&lt;li&gt;QPS 才 5，显存突然爆掉，容器被内核杀掉&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;根因是 LLM 的计算模式不同：&lt;strong&gt;prefill 吃算力，decode 吃显存带宽，KV cache 动态伸缩&lt;/strong&gt;。所以指标集必须换一套。&lt;/p&gt;
&lt;p&gt;另外，看 GPU 利用率建议用 DCGM 的 Duty Cycle 而不是 &lt;code&gt;nvidia-smi&lt;/code&gt; 那个瞬时值——后者波动大，容易误判。&lt;/p&gt;
&lt;h2&gt;四层立体结构&lt;/h2&gt;
&lt;p&gt;从上到下要打通四层，缺一层就定位不到底：&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;层&lt;/th&gt;&lt;th&gt;数据源&lt;/th&gt;&lt;th&gt;关键指标&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;业务网关&lt;/td&gt;&lt;td&gt;网关 / 代理&lt;/td&gt;&lt;td&gt;租户配额、RPM/TPM 限流（429）、上游熔断率&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;推理引擎&lt;/td&gt;&lt;td&gt;vLLM &lt;code&gt;/metrics&lt;/code&gt;&lt;/td&gt;&lt;td&gt;KV cache 水位、排队数、TTFT、TPOT&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;GPU 硬件&lt;/td&gt;&lt;td&gt;dcgm-exporter&lt;/td&gt;&lt;td&gt;SM 利用率、温度功耗、XID 错误&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;宿主机&lt;/td&gt;&lt;td&gt;node-exporter&lt;/td&gt;&lt;td&gt;CPU 软中断、PCIe 带宽、共享内存&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;日志和链路追踪是另两层，不能省：日志用来查&amp;quot;哪次请求用了多少 KV cache、哪个长上下文触发了批处理等待&amp;quot;；链路追踪用来回答&amp;quot;一次慢响应到底是卡在排队、模型计算还是后处理&amp;quot;。网关 + 多模型路由 + 后处理这种链式结构，没有 trace 就是黑盒。&lt;/p&gt;
&lt;h2&gt;五个最该盯的原生指标&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. &lt;code&gt;vllm:kv_cache_usage_perc&lt;/code&gt;——显存的真实生死线&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;vLLM 启动时按 &lt;code&gt;gpu_memory_utilization&lt;/code&gt; 把物理显存几乎整块划走，所以系统层面看到的显存占用永远是满的，没有参考价值。真正反映压力的是 KV cache 的实际占用百分比。&lt;/p&gt;
&lt;p&gt;经验阈值（非官方标准，按自己的并发与上下文长度调）：持续高于 75% 是黄色预警，说明并发或长文本在激增；高于 85% 就该紧急处理，此时极易触发请求抢占（preemption）或直接 CUDA OOM。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. &lt;code&gt;vllm:num_requests_waiting&lt;/code&gt;——容量指示灯&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;健康状态下应该持续为 0。大于 0 且持续上升，说明算力已到物理上限，后续请求全在排队。它同时也是做弹性扩缩容最合适的驱动信号——比 GPU 利用率更能反映&amp;quot;要不要加副本&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. TTFT（首 token 延迟）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用户点发送到屏幕上出现第一个字的时间。PromQL 取 P95：&lt;/p&gt;
&lt;pre&gt;&lt;code class=&quot;language-text&quot;&gt;histogram_quantile(0.95, sum(rate(vllm:time_to_first_token_seconds_bucket[5m])) by (le))&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一定要在面向用户的边界上测——排队、检索、路由、工具调用、重试都会计入用户的体感，即使模型执行本身很快。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. TPOT（每输出 token 耗时）&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;首字之后每个 token 的平均生成耗时。取倒数就是单请求的输出流速。参考口径：成年人阅读速度约 15–25 tokens/s，流速低于 10 tokens/s 时用户会明显感到&amp;quot;打字机卡壳&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;5. 投机解码的接受率&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;如果开了投机解码（MTP、草稿小模型等），必须同时看 &lt;code&gt;spec_decode_num_accepted_tokens_total&lt;/code&gt; 和 &lt;code&gt;spec_decode_num_draft_tokens_total&lt;/code&gt;，算接受率 = accepted / (accepted + draft)。&lt;strong&gt;接受率长期低于 50% 说明草稿模型猜得很差，此时投机解码不是在加速，是在白烧显存带宽和算力。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;诊断映射表&lt;/h2&gt;
&lt;p&gt;这张表是监控大盘的操作手册，建议直接贴在图表旁边——凌晨三点被叫醒的人不需要重新推一遍排队论。&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;现象&lt;/th&gt;&lt;th&gt;指向&lt;/th&gt;&lt;th&gt;下一步查什么&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;TTFT 高、TPOT 正常&lt;/td&gt;&lt;td&gt;上游问题&lt;/td&gt;&lt;td&gt;排队深度、prefill 压力、准入控制设置&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TTFT 正常、TPOT 高&lt;/td&gt;&lt;td&gt;decode 侧受限&lt;/td&gt;&lt;td&gt;batch 占用率、KV cache 水位、是否降频&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;两者同时升高&lt;/td&gt;&lt;td&gt;容量耗尽&lt;/td&gt;&lt;td&gt;加显存或加副本，&lt;strong&gt;调参已经没用&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;吞吐持平、延迟上升&lt;/td&gt;&lt;td&gt;已越过饱和拐点&lt;/td&gt;&lt;td&gt;在尾延迟崩溃前降载或扩容&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;DCGM_FI_DEV_XID_ERRORS&lt;/code&gt; ≠ 0&lt;/td&gt;&lt;td&gt;驱动或硬件级掉卡&lt;/td&gt;&lt;td&gt;立即摘除节点&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;只采延迟指标不采容量信号，你得到的看板只会告诉你&amp;quot;慢&amp;quot;，说不出&amp;quot;为什么慢&amp;quot;；反过来只采容量信号，则什么都解释得了但什么都证明不了。&lt;/p&gt;
&lt;h2&gt;告警怎么设&lt;/h2&gt;
&lt;p&gt;两条原则：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;按 SLO 预算的消耗速度告警，而不是按裸阈值穿越告警。&lt;/strong&gt; 这样 p99 的一次抖动只在该报警的时候报警，避免告警疲劳——见过最糟的情况是告警群 99+ 未读，真故障没人理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;技术告警用硬阈值立即触发，质量告警用软阈值转人工确认。&lt;/strong&gt; 质量类指标噪声大、误报代价高，比如负反馈率环比涨 20%、LLM-as-judge 评分掉 0.1 以上，这类更适合转人工确认而不是自动触发。&lt;/p&gt;
&lt;p&gt;一些常见参考阈值（来自公开实践，务必按自身 SLO 改）：TTFT P99 &amp;gt; 2 秒、TPOT P99 &amp;gt; 100ms、队列等待 P99 &amp;gt; 5 秒、KV cache &amp;gt; 85%、显存 &amp;gt; 90%、错误率 &amp;gt; 1%。&lt;/p&gt;
&lt;p&gt;成本侧建议单独设一路：单租户或单接口的 token 消耗按小时环比突增数倍就告警，这可能是滥用，也可能是 Agent 死循环在烧钱。&lt;/p&gt;
&lt;h2&gt;两个容易漏掉的坑&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;GPU hang。&lt;/strong&gt; 进程活着、端口通、但 CUDA 死锁，健康检查全绿。解法是在探针里定时发一次极小推理，超时未响应就摘除实例重启。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;&amp;quot;不报错但变笨&amp;quot;。&lt;/strong&gt; 模型更新后技术指标完全正常，输出质量却退化了。这类只能靠质量侧监控发现：golden case 定期回归 + LLM-as-judge 抽样评分 + 输出长度分布突变检测。所以日志里每一条请求都要带上模型版本、prompt 版本和 trace id——没有版本标记的日志，出事时无法归因。&lt;/p&gt;
&lt;h2&gt;上线前必须演练的三件事&lt;/h2&gt;
&lt;p&gt;1. &lt;strong&gt;过载行为&lt;/strong&gt;：压到超过容量，看返回的是干净的 429 带重试提示、超时、还是半个响应；同时确认多个服务同时重试不会形成更大的流量尖峰&lt;/p&gt;
&lt;p&gt;2. &lt;strong&gt;故障转移&lt;/strong&gt;：压测中途停掉一个 worker 或摘掉一张卡，确认控制面能检测到、停止向故障副本导流、并按设计恢复；同时定义哪些故障允许降级到小模型&lt;/p&gt;
&lt;p&gt;3. &lt;strong&gt;更新与回滚&lt;/strong&gt;：即使模型版本没变也要完整演练一次——权重审批、兼容性检查、预热、健康检查、流量切换、回滚。&lt;strong&gt;固定模型与运行时版本&lt;/strong&gt;，可变名会让事故无法复现；重要服务用金丝雀或蓝绿&lt;/p&gt;
&lt;h2&gt;边界与坑&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;不要把 prompt 原文默认写进日志，用可配置的保留期、脱敏和访问控制&lt;/li&gt;&lt;li&gt;每次部署记录模型 digest、引擎版本、量化方式、tokenizer、配置和硬件，缺一项就复现不了&lt;/li&gt;&lt;li&gt;有人看才有意义：告警接收人、恢复时间目标、升级路径要事先定好&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：显存一直显示占满，正常吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;正常。vLLM 启动时就按比例把显存划走了，要看 KV cache 的实际占用百分比而不是系统显存。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：GPU 利用率高是不是好事？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不一定。高利用率配高延迟没有意义；严格交互 SLO 下低利用率也可能是合理的。要把利用率和延迟、吞吐放在一起看。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：开了投机解码一定更快吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;看接受率。低于 50% 就是在浪费显存带宽。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：进程在、端口通，为什么还是不通？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;可能是 CUDA 死锁。用小推理探针检测，别只做端口健康检查。&lt;/p&gt;
&lt;h2&gt;参考与延伸&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Xinference《Production LLM inference checklist: 8 tests before launch》——上线前八项检查的主要来源&lt;/li&gt;&lt;li&gt;vLLM 官方 Prometheus 指标文档与社区 Grafana 看板模板&lt;/li&gt;&lt;li&gt;公开运维实践：vLLM 核心指标解读、四层监控架构与告警阈值速查表（阈值为经验值，非官方标准）&lt;/li&gt;&lt;li&gt;指标定义与诊断映射：多家推理服务监控实践整理，与 NVIDIA 推理基准的 TTFT/TPOT 口径一致&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:55:10 +0800</pubDate></item><item><title>压测推理服务：只报 tokens/s 是不够的</title><link>https://www.sysplus.top/?id=22</link><description>&lt;p&gt;&amp;quot;我们的推理服务能跑到 3000 tokens/s&amp;quot;——这句话基本没有信息量，因为它没说是用什么 prompt 长度、什么并发、什么输出长度跑出来的。这篇讲怎么做一个能拿来做容量规划的压测。&lt;/p&gt;
&lt;blockquote&gt;验证截至 2026-10-21。方法论综合自多家公开工程实践文章（彼此结论一致），指标定义与 NVIDIA 的推理性能基准口径对齐。&lt;/blockquote&gt;
&lt;h2&gt;环境与版本&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;适用对象：自建推理服务（vLLM / SGLang / TensorRT-LLM 等）或托管推理端点，上线前的容量评估&lt;/li&gt;&lt;li&gt;压测工具不限，但&lt;strong&gt;计时必须在客户端做&lt;/strong&gt;，原因见下文&lt;/li&gt;&lt;li&gt;需要的前置数据：生产日志里的 prompt 长度分布、输出长度分布、典型与峰值并发&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;为什么传统 RPS 压测在这里是错的&lt;/h2&gt;
&lt;p&gt;REST 接口的服务时间基本恒定，所以按每秒请求数容量规划是成立的。LLM 端点在四个地方打破了这个模型：&lt;/p&gt;
&lt;p&gt;1. &lt;strong&gt;输出长度是随机变量。&lt;/strong&gt; 端到端延迟 ≈ TTFT + 输出 token 数 × 单 token 耗时，同一个 prompt 每次跑出来都不一样。拿一个固定 prompt 采样一次，说明不了 p95。&lt;/p&gt;
&lt;p&gt;2. &lt;strong&gt;流式会长时间占着连接。&lt;/strong&gt; 一次生成占用一个在途槽位 4–20 秒，这里的&amp;quot;并发&amp;quot;指的是并发生成数。按利特尔法则：在途数 = 到达率 × 平均服务时间。&lt;/p&gt;
&lt;p&gt;3. &lt;strong&gt;排队可能发生在别人的机器上。&lt;/strong&gt; 用托管端点时，你自己的进程可能 CPU 才 5%，而 p95 TTFT 已经翻了三倍——队列在服务商那边。此时本地资源指标毫无参考价值。&lt;/p&gt;
&lt;p&gt;4. &lt;strong&gt;压测本身有账单。&lt;/strong&gt; 成本随生成 token 数线性增长，输出长度失控会让一次测试贵十倍；托管服务的限流通常是按 key 的请求数 + token 数双限，拿服务真实用户的 key 去压测会直接把线上打限流。&lt;/p&gt;
&lt;p&gt;结论：&lt;strong&gt;从单 prompt、非流式、低并发的测试外推出来的容量，偏差方向是乐观的——也就是危险的方向。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;必测的四个指标，一个都不能少&lt;/h2&gt;
&lt;p&gt;推理是两阶段过程：prefill 读完全部 prompt 产出第一个 token，decode 逐 token 生成剩余部分。两个阶段受限于不同资源、失败方式也不同，所以必须分开测。&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;指标&lt;/th&gt;&lt;th&gt;定义&lt;/th&gt;&lt;th&gt;用户感知&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;TTFT&lt;/td&gt;&lt;td&gt;发出请求到收到第一个 token&lt;/td&gt;&lt;td&gt;&amp;quot;有没有反应&amp;quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;TPOT / ITL&lt;/td&gt;&lt;td&gt;decode 阶段相邻 token 的间隔&lt;/td&gt;&lt;td&gt;&amp;quot;阅读速度顺不顺&amp;quot;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;吞吐&lt;/td&gt;&lt;td&gt;全部并发请求合计的 tokens/s&lt;/td&gt;&lt;td&gt;容量，用于规划&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;尾延迟&lt;/td&gt;&lt;td&gt;TTFT 与端到端的 p95 / p99&lt;/td&gt;&lt;td&gt;决定会不会超时&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;两个纪律：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;报百分位，不报均值。&lt;/strong&gt; 延迟分布是重尾的，少数卡在队列里的请求会把均值拖得到处跑。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;不要把 TTFT 并进端到端里看。&lt;/strong&gt; TTFT 主要由排队 + prefill 决定，剩下的由 decode 决定。把系统提示词砍一半会改善 TTFT 但完全不动 TPOT；服务端调大批次则正好相反。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;另外有个比吞吐更有用的量：&lt;strong&gt;goodput&lt;/strong&gt;——同时满足 TTFT 和端到端 SLO 的请求数。吞吐很高但 TTFT 全部超时的配置是不能上线的，goodput 才是可用容量。&lt;/p&gt;
&lt;h2&gt;并发要扫，不要选一个点&lt;/h2&gt;
&lt;p&gt;用同一份请求集，在并发 1、4、16、64 逐级往上跑，直到服务饱和或开始报错。每一级记录全部四个指标。你要找两个点：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;吞吐曲线的拐点&lt;/strong&gt;：再往上加并发，吞吐不再增长。这是你的有效容量。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;延迟预算被突破的位置&lt;/strong&gt;：TTFT 或 TPOT 超过产品能容忍的值。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;在多数栈上，&lt;strong&gt;第二个点出现在第一个点之前&lt;/strong&gt;。拐点前一级的并发才是你真正能跑的负载，通常远低于理论最大并发。这两个点之间的距离就是你的余量——距离宽的栈降级平缓，距离窄的栈从&amp;quot;正常&amp;quot;到&amp;quot;不可用&amp;quot;可能只差一点点负载。&lt;/p&gt;
&lt;h2&gt;让测试长得像真实流量&lt;/h2&gt;
&lt;p&gt;最常见也最致命的错误，是用错误形状的请求在做测试。100 token prompt、100 token 输出的吞吐数字，对&amp;quot;3 万 token prompt、300 token 输出&amp;quot;的负载毫无参考价值。&lt;/p&gt;
&lt;p&gt;开工前从生产日志里拉出这五项：prompt 长度分布（不只是均值）、输出长度分布、prompt 与输出的比例、典型与峰值并发、是否流式以及是否使用工具调用或结构化输出。然后按这个分布造请求集，能用真实 prompt 就别用合成的。&lt;/p&gt;
&lt;p&gt;几个具体坑：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;不要重复发同一个 prompt 几千次&lt;/strong&gt;——会命中 prefix caching，测出一个&amp;quot;又快又便宜但真实用户根本享受不到&amp;quot;的系统。每个长度桶至少准备几十条不同 prompt，或加随机 nonce。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;&lt;code&gt;max_tokens&lt;/code&gt; 按生产的上限设。&lt;/strong&gt; 不设上限会把 60 秒的测试变成几千 token 的测试，预算直接爆掉。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;想清楚你是闭环还是开环。&lt;/strong&gt; 固定 worker 数的闭环压测会自我限流：延迟一上升，每个 worker 完成得少，实际施加的负载悄悄下降，于是你低估了排队。要找到真正的拐点，需要固定到达率的开环压测。&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;跑多久算够&lt;/h2&gt;
&lt;p&gt;参考口径：每个配置至少 1000 次请求或 5–10 分钟，且&lt;strong&gt;必须先做 5–10 次预热再开始计时&lt;/strong&gt;。生产签收再加一轮一小时左右的 soak test，用来抓短测跑不出来的东西——显存缓慢增长、KV cache 碎片、以及降频。&lt;/p&gt;
&lt;p&gt;冷启动延迟（模型加载 + CUDA graph 捕获 + 缓存填充）要单独记录，大模型可以长达几十秒。压测报告里建议报热态延迟，但容量规划和自动扩缩容必须单独把冷启动时间算进去。&lt;/p&gt;
&lt;h2&gt;依赖什么功能，就测什么功能&lt;/h2&gt;
&lt;p&gt;兼容性是格式声明，行为得验证。路由生产流量之前，至少过一遍：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;流式&lt;/strong&gt;：确认 token 是增量到达的，长生成不会中途断连&lt;/li&gt;&lt;li&gt;&lt;strong&gt;工具调用&lt;/strong&gt;：确认返回结构是你的解析器能吃的，包括并行调用、空参数对象这类边界&lt;/li&gt;&lt;li&gt;&lt;strong&gt;结构化输出&lt;/strong&gt;：用你真实的 schema 测，尤其是大的 schema&lt;/li&gt;&lt;li&gt;&lt;strong&gt;长上下文&lt;/strong&gt;：发送长度分布最右端的 prompt，有些栈能正确处理但 TTFT 比均值暗示的差得多&lt;/li&gt;&lt;li&gt;&lt;strong&gt;采样参数&lt;/strong&gt;：确认 stop、temperature 等真的生效，而不是被静默忽略&lt;/li&gt;&lt;li&gt;&lt;strong&gt;错误语义&lt;/strong&gt;：故意打过载，看返回的是干净的 429 带重试提示、超时、还是半个响应——你的重试逻辑依赖这个&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;边界与坑&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;用独立的 API key 做压测，别污染线上配额&lt;/li&gt;&lt;li&gt;客户端打点，别看服务端资源利用率当饱和信号&lt;/li&gt;&lt;li&gt;引用他人 benchmark 数字时，先找它的 prompt 长度、输出长度和并发；&lt;strong&gt;口径不同的数字不要放进同一张表比较&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;对比两个候选配置时，只变硬件或只变一个变量，其余全部固定；没测到的维度写&amp;quot;未测&amp;quot;，不要推断&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：只报一个 tokens/s 够吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不够。没有 prompt 长度、输出长度、并发这三个前提，这个数字无法用于规划。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：为什么我的压测结果比厂商宣传的高？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;多半是命中了 prefix caching，或者请求形状比真实负载短得多。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：闭环和开环要选哪个？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;闭环上手快，但找拐点必须用开环——闭环会在延迟上升时自动降低施加的负载。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：并发加到多少停？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;加到 p95/p99 TTFT 或 TPOT 突破你的 SLO 为止，那个点就是上限，不是加到报错为止。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：需要跑多久？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;单配置 1000 次请求或 5–10 分钟起步，预热之后才开始计时；上线前加一轮小时级 soak。&lt;/p&gt;
&lt;h2&gt;参考与延伸&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Freestyle《How to benchmark LLM inference before you commit to capacity》——四指标定义、并发扫描与依赖功能测试清单&lt;/li&gt;&lt;li&gt;QoraAPI《Load Testing LLM Apps: Throughput, TTFT, and Concurrency》——闭环/开环差异、goodput、成本随 token 线性增长&lt;/li&gt;&lt;li&gt;HinterBuild《Benchmark LLM Serving: TTFT, TPOT, and Throughput Guide》——样本量与 soak test 的时长口径&lt;/li&gt;&lt;li&gt;Sekin《How to Size GPU, CPU, Memory, and Storage for AI Inference Workloads》——对比测试需固定的变量清单&lt;/li&gt;&lt;li&gt;指标定义对齐 NVIDIA 推理性能基准的 TTFT / TPOT / ITL / throughput 口径&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:55:10 +0800</pubDate></item><item><title>RAG 上线后效果不好，多半不是模型的问题</title><link>https://www.sysplus.top/?id=21</link><description>&lt;p&gt;RAG 的 demo 和线上是两回事。多数团队遇到的&amp;quot;模型答错了&amp;quot;，查下去其实是&amp;quot;模型根本没看到正确的文档&amp;quot;。这篇把生产环境里真正会出问题的几个环节拆开，给出对应的评估方法。&lt;/p&gt;
&lt;blockquote&gt;验证截至 2026-10-21。内容依据多家公开工程实践文章与向量数据库选型资料综合整理，文中标注了哪些结论属于团队个案报告、哪些属于通用实践。&lt;/blockquote&gt;
&lt;h2&gt;环境与版本&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;适用对象：已经跑通原型、准备上生产或上线后效果不达标的检索增强问答系统&lt;/li&gt;&lt;li&gt;不涉及具体框架选型（LangChain / LlamaIndex / Haystack / 自研均可），讨论的是与框架无关的环节&lt;/li&gt;&lt;li&gt;需要的前置条件：一批真实的查询样本，以及&amp;quot;哪些文档应该被检索到&amp;quot;的人工标注&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;第一条：检索和生成必须分开测&lt;/h2&gt;
&lt;p&gt;这是整个 RAG 工程里最该先立起来的规矩。&lt;strong&gt;在评判答案质量之前，先评判有没有把正确的上下文找出来。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;理由很实际：如果检索这一步就没召回正确文档，那么后面换什么模型、怎么改 prompt 都没用——模型没看见，就答不对。把两层混在一起评估，你得到的信号是噪声。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;检索侧该看的指标：&lt;/strong&gt;&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;指标&lt;/th&gt;&lt;th&gt;含义&lt;/th&gt;&lt;th&gt;用途&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Recall@k&lt;/td&gt;&lt;td&gt;前 k 条结果里包含了多少相关文档&lt;/td&gt;&lt;td&gt;判断召回是否够&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Precision@k&lt;/td&gt;&lt;td&gt;前 k 条里有多少是相关的&lt;/td&gt;&lt;td&gt;判断噪声是否多&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;MRR&lt;/td&gt;&lt;td&gt;第一条相关结果排在第几位&lt;/td&gt;&lt;td&gt;判断排序质量&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;nDCG&lt;/td&gt;&lt;td&gt;对排序位置加权的综合分&lt;/td&gt;&lt;td&gt;整体对比用&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;&lt;strong&gt;生成侧该看的指标：&lt;/strong&gt; faithfulness（每个论断是否被检索到的上下文支撑）、answer relevance（是否回答了用户的问题）、context precision/recall（送进上下文的片段是不是既相关又必要）、citation accuracy（引用是否指向正确的来源和片段）。&lt;/p&gt;
&lt;p&gt;生成侧用 LLM-as-judge 可以加快迭代，但&lt;strong&gt;必须拿人工标注校准过&lt;/strong&gt;——评审模型天然偏向长答案和自己风格的答案，这个偏差不校准就会把优化方向带偏。&lt;/p&gt;
&lt;h2&gt;第二条：golden dataset 是唯一的验收依据&lt;/h2&gt;
&lt;p&gt;公开实践里反复出现的一个数字区间是：&lt;strong&gt;起步 100 条真实查询，成熟期扩到 300–500 条，每条配专家确认过的参考答案；另一些团队的起点是 50–100 条。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;关键不在数量在多样性：要覆盖长尾、歧义、多跳、以及&amp;quot;文档里根本没有答案、应该拒答&amp;quot;的那一类。漏掉拒答类，你的系统会学会胡编。&lt;/p&gt;
&lt;p&gt;配套的纪律是：&lt;strong&gt;prompt、分块策略、检索配置全部进版本管理，每次改动跑一遍评测集。&lt;/strong&gt; 模型升级也要重跑——回归是常态，不是意外。&lt;/p&gt;
&lt;h2&gt;第三条：失败模式对照表&lt;/h2&gt;
&lt;p&gt;下面这些是生产环境里反复出现的形态，以及对应的处理方向。&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;现象&lt;/th&gt;&lt;th&gt;根因&lt;/th&gt;&lt;th&gt;处理方向&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;有上下文仍然胡编&lt;/td&gt;&lt;td&gt;模型忽略证据或混入了参数知识&lt;/td&gt;&lt;td&gt;收紧 prompt 约束、明确拒答、校验引用&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;证据埋在长上下文中间被忽略&lt;/td&gt;&lt;td&gt;lost-in-the-middle&lt;/td&gt;&lt;td&gt;重排、上下文压缩、把高相关片段放到两端&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;答案正好跨两个块，两边都没被完整召回&lt;/td&gt;&lt;td&gt;分块边界&lt;/td&gt;&lt;td&gt;加 overlap、父子块检索、句子窗口检索&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;文档更新了但答案还是旧的&lt;/td&gt;&lt;td&gt;索引陈旧&lt;/td&gt;&lt;td&gt;增量入库、索引版本化、新鲜度过滤&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;换了 embedding 模型后效果整体下滑&lt;/td&gt;&lt;td&gt;向量空间漂移&lt;/td&gt;&lt;td&gt;索引版本化，先在影子管线重嵌再切&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;用户用词和文档用词对不上&lt;/td&gt;&lt;td&gt;查询失配&lt;/td&gt;&lt;td&gt;查询扩展、混合检索、领域同义词表&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;用户看到了他没权限看的文档&lt;/td&gt;&lt;td&gt;权限泄漏&lt;/td&gt;&lt;td&gt;&lt;strong&gt;在检索时做 ACL 过滤，不是检索完再过滤&lt;/strong&gt;&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;索引里混进了恶意或错误内容&lt;/td&gt;&lt;td&gt;上下文投毒&lt;/td&gt;&lt;td&gt;来源白名单、入库清洗、溯源记录&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;权限那条值得单独强调：事后过滤看似简单，但它在分页、重排、top-k 截断之后才生效，既可能漏，也可能让本该返回的 k 条变得不足 k 条。&lt;strong&gt;过滤必须发生在检索阶段。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;第四条：检索出来的内容是不可信输入&lt;/h2&gt;
&lt;p&gt;这一点在接了 Agent 之后尤其致命。检索到的文本里可能藏着指令，试图覆盖系统提示词或让模型去调用工具。&lt;/p&gt;
&lt;p&gt;处理原则是：&lt;strong&gt;检索内容一律当作不可信输入&lt;/strong&gt;——用分隔符隔离、建立指令层级、对输出做校验；并且&lt;strong&gt;不要让检索到的文本直接决定工具调用或权限判断&lt;/strong&gt;。入库前做敏感信息脱敏，传输和存储加密，同时保留&amp;quot;哪次回答引用了哪些文档&amp;quot;的审计线索。&lt;/p&gt;
&lt;h2&gt;第五条：延迟是几段加出来的，从最划算的那段优化&lt;/h2&gt;
&lt;p&gt;一次问答的耗时 = 查询改写 + embedding + 向量检索 + 重排 + 生成。常见的优化顺序：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;缓存高频查询与其 embedding（相同问题反复付费是最常见的浪费）&lt;/li&gt;&lt;li&gt;生成用流式，让用户早点看到字&lt;/li&gt;&lt;li&gt;检索与重排能并行就并行&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;重排这一步要算账：&lt;strong&gt;top_k 较小时重排带来的延迟可能超过它的精度收益&lt;/strong&gt;，有团队的做法是只在 top_k 较大时才启用重排。这笔账要按你自己的 k 值和重排模型的延迟来算，不能照抄。&lt;/p&gt;
&lt;p&gt;公开资料里给出的经验阈值（属于参考值不是标准）：向量检索延迟控制在 100ms 内、端到端 3 秒内。是否适用取决于你的场景，别直接拿去当 SLO。&lt;/p&gt;
&lt;h2&gt;边界与坑&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;混合检索 + 重排的效果提升幅度是团队个案报告&lt;/strong&gt;，不同语料差异很大。有团队报告幻觉减少一半，这个数字不能外推，要在自己的评测集上重新量&lt;/li&gt;&lt;li&gt;中文场景要用中文语料微调过的 embedding 模型，通用多语模型在垂直领域的表现常常明显低于预期&lt;/li&gt;&lt;li&gt;向量库选型跟着数据量走：百万条以内 pgvector 或 Chroma 起步；百万到千万级考虑引入混合检索与重排；千万级以上再评估分布式方案。每次迁移前用真实数据和真实查询做基准，别只看文档参数&lt;/li&gt;&lt;li&gt;成本侧可做的：向量量化（float32 → int8 / binary）、用带稀疏输出的 embedding 模型替代独立维护的 BM25、冷热数据分级存储&lt;/li&gt;&lt;li&gt;&amp;quot;更多上下文&amp;quot;不等于更好：塞进无关文本既增加成本也干扰模型&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：效果不好是不是该换更大的模型？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先跑一遍检索侧指标。如果 Recall@k 上不去，换模型是浪费钱——多数&amp;quot;模型不行&amp;quot;其实是&amp;quot;检索没找到&amp;quot;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：评测集要多大才够？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;100 条能起步，300–500 条是成熟期。多样性比数量重要，务必包含应该拒答的样本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：什么时候该考虑微调而不是 RAG？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;RAG 解决事实性、时效性、需要溯源的知识；微调解决风格、格式、以及难以用 prompt 表达的行为模式。需要动态更新和可审计的知识，先用 RAG。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：多轮对话怎么处理？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;先把历史对话压缩成独立的查询再送检索；已经引用过的历史片段不要重复注入，否则既浪费 token 又干扰上下文。压缩失败时加 few-shot 示例引导。&lt;/p&gt;
&lt;h2&gt;参考与延伸&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Habsi Tech《RAG in Production: Architecture, Evaluation, and Failure Modes》与《A Practical Architecture for RAG》——失败模式与评估指标的主要来源&lt;/li&gt;&lt;li&gt;FreshCodes《RAG in Production: 7 Lessons》——golden dataset 规模与分层评估口径（其中&amp;quot;幻觉减半&amp;quot;为该团队个案报告）&lt;/li&gt;&lt;li&gt;向量数据库选型与分阶段迁移路径：国内技术团队公开实践整理&lt;/li&gt;&lt;li&gt;中文 RAG 实战资料：查询压缩、重排与 top_k 取舍的工程经验&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:55:09 +0800</pubDate></item><item><title>vLLM 显存装不下：参数该按什么顺序调</title><link>https://www.sysplus.top/?id=20</link><description>&lt;p&gt;vLLM 部署最常见的崩溃场景不是模型跑得慢，是启动时或跑着跑着 OOM。这篇不讲原理，只讲参数该按什么顺序调、每个参数动下去代价是什么。&lt;/p&gt;
&lt;blockquote&gt;验证截至 2026-10-21。依据 vLLM 官方文档（Conserving Memory / Optimization / FP8 三章），并与公开技术分析交叉核对；括号内标注了确认时点的数值，请以你所运行版本的官方文档为准。&lt;/blockquote&gt;
&lt;h2&gt;环境与版本&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;适用对象：vLLM 的 &lt;code&gt;vllm serve&lt;/code&gt; 与 &lt;code&gt;LLM&lt;/code&gt; 两类接口，单卡到多机多卡均适用&lt;/li&gt;&lt;li&gt;版本敏感项：&lt;code&gt;gpu_memory_utilization&lt;/code&gt; 的默认值在历史上变过，下文标注的 0.92 是 2026-08 在文档与 &lt;code&gt;CacheConfig&lt;/code&gt; 源码中确认的值，旧教程里常见的另一个数字已经不适用&lt;/li&gt;&lt;li&gt;硬件相关：量化那一节的收益严格依赖显卡计算能力，见下文边界部分&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;先说结论：一次只改一个参数&lt;/h2&gt;
&lt;p&gt;调参最常见的失败不是选错了值，而是同时改了五六个值，最后既不知道哪个起作用，也没法回滚。&lt;/p&gt;
&lt;p&gt;推荐的固定顺序是四步：&lt;/p&gt;
&lt;p&gt;1. &lt;strong&gt;先让模型装进显存&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;2. &lt;strong&gt;再把长度上限对齐真实负载&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;3. &lt;strong&gt;然后给 KV cache 留出余量&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;4. &lt;strong&gt;最后才动 batch 和延迟&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;前三步没定下来就去调 batching 相关的参数，通常什么也不会发生——因为限制根本不在那儿。&lt;/p&gt;
&lt;h2&gt;第一步：gpu_memory_utilization 是第一个该动的值&lt;/h2&gt;
&lt;p&gt;官方定义：模型执行器可使用的显存比例，取值 0 到 1，默认 0.92。&lt;/p&gt;
&lt;p&gt;它的机制决定了它为什么排第一：vLLM 先按这个比例把显存整块划走，扣掉模型权重和运行时开销之后，&lt;strong&gt;剩下的全部给 KV cache&lt;/strong&gt;。所以调高它，KV cache 立刻变大，能同时接住更长和更多的请求。&lt;/p&gt;
&lt;p&gt;但不要往 1 推，有两个理由：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;它按整张卡计算。&lt;/strong&gt; 卡上还跑着别的进程时（Kubernetes 里共享 GPU 是常态），这个比例算出来就是错的。&lt;/li&gt;&lt;li&gt;&lt;strong&gt;实际占用会比计算值高一截&lt;/strong&gt;，来自显存碎片和瞬时激活张量。把余量压成 0 的典型后果是&amp;quot;大部分时候一切正常，某些请求组合下突然炸掉&amp;quot;。&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;一个有用的判据：&lt;strong&gt;如果调高它吞吐没有涨，说明瓶颈不在显存&lt;/strong&gt;，卡住你的是 &lt;code&gt;max_num_seqs&lt;/code&gt; 或 &lt;code&gt;max_num_batched_tokens&lt;/code&gt;，继续拧这个旋钮没有意义。&lt;/p&gt;
&lt;h2&gt;第二步：长度上限和并发上限，直接对应 KV cache 的两个乘数&lt;/h2&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;参数&lt;/th&gt;&lt;th&gt;作用&lt;/th&gt;&lt;th&gt;降下去的代价&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;max_model_len&lt;/code&gt;&lt;/td&gt;&lt;td&gt;单条序列的最大 token 数&lt;/td&gt;&lt;td&gt;长文本会被截断或拒绝&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;max_num_seqs&lt;/code&gt;&lt;/td&gt;&lt;td&gt;一批里的并发请求上限&lt;/td&gt;&lt;td&gt;并发上不去，请求开始排队&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&lt;code&gt;max_num_batched_tokens&lt;/code&gt;&lt;/td&gt;&lt;td&gt;单批最大 token 数，影响 prefill 与 decode 的平衡&lt;/td&gt;&lt;td&gt;官方提到大于 8192 有利于吞吐，压太低会牺牲吞吐&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;KV cache 的需求大致就是&amp;quot;序列长度 × 并发数&amp;quot;的函数，所以装不下的时候不是玄学——&lt;strong&gt;降这两个值，需求按比例下来&lt;/strong&gt;。选降哪个取决于你的负载：长文本为主就压并发上限，高并发短请求为主就压长度上限。&lt;/p&gt;
&lt;h2&gt;第三步：TP 是容量选项，不是性能选项&lt;/h2&gt;
&lt;p&gt;这是最普遍的一个误解。官方文档的判据很直接：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;单卡放不下、单机多卡放得下&lt;/strong&gt; → 用 tensor parallelism&lt;/li&gt;&lt;li&gt;&lt;strong&gt;单机都放不下&lt;/strong&gt; → TP 加 PP（流水线并行）&lt;/li&gt;&lt;li&gt;取值：&lt;code&gt;tensor_parallel_size&lt;/code&gt; = 每节点 GPU 数，&lt;code&gt;pipeline_parallel_size&lt;/code&gt; = 节点数。文档给的例子是 2 节点 × 8 卡 → TP=8、PP=2&lt;/li&gt;&lt;li&gt;例外：单节点内 GPU 无法均分时，可以设 TP=1、PP=GPU 数，让模型层做不均分切分&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;tensor_parallel_size&lt;/code&gt; 的&lt;strong&gt;默认值是 1&lt;/strong&gt;。也就是说什么都不设，引擎就只用一张卡，哪怕机器上插了八张。多卡机器性能不及预期，第一个该查的就是这个值。&lt;/p&gt;
&lt;p&gt;模型本来就能塞进一张卡时开 TP 会怎样：每张卡分到自己的 KV cache，总容量确实变大——这部分是真实收益；但每一层都多了卡间通信，而且是同步的。所以这不能算无条件的赢，得实测过再定。&lt;/p&gt;
&lt;h2&gt;官方给出的 OOM 缓解顺序&lt;/h2&gt;
&lt;p&gt;按顺序试，不要跳步，因为越往后代价越大：&lt;/p&gt;
&lt;p&gt;1. 提高 &lt;code&gt;gpu_memory_utilization&lt;/code&gt;（代价：留给别人的显存变少）&lt;/p&gt;
&lt;p&gt;2. 降低 &lt;code&gt;max_num_seqs&lt;/code&gt; 或 &lt;code&gt;max_num_batched_tokens&lt;/code&gt;（代价：并发或吞吐下降）&lt;/p&gt;
&lt;p&gt;3. 提高 &lt;code&gt;tensor_parallel_size&lt;/code&gt;（代价：同步开销）&lt;/p&gt;
&lt;p&gt;4. 提高 &lt;code&gt;pipeline_parallel_size&lt;/code&gt;（代价：延迟）&lt;/p&gt;
&lt;p&gt;另外有个容易被忽略的参数：&lt;code&gt;kv_cache_memory&lt;/code&gt; 可以直接指定 KV cache 大小，&lt;strong&gt;跳过启动时的显存 profiling 阶段&lt;/strong&gt;。profiling 结果不稳定、或者启动耗时太长时，这是个绕过去的办法。&lt;/p&gt;
&lt;h2&gt;量化这笔账要按硬件算&lt;/h2&gt;
&lt;p&gt;官方 FP8 文档的说法是：显存减半，吞吐最高提升 1.6 倍，精度影响很小。两个前提容易被跳过：&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;一是硬件门槛。&lt;/strong&gt; FP8 计算需要 NVIDIA 计算能力 ≥ 8.9（Ada Lovelace / Hopper / Blackwell）。Turing / Ampere 上会退化成仅权重的 W8A16，走 Marlin kernel——显存收益还在，但拿不到完整的 W8A8 加速。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二是 kernel 选择。&lt;/strong&gt; vLLM 在加载时自动挑 FP8 GEMM kernel，启动日志里会打一行 Selected。如果推理挂住且不报错，官方建议试 &lt;code&gt;VLLM_USE_DEEP_GEMM=0&lt;/code&gt; 或 &lt;code&gt;--linear-backend cutlass&lt;/code&gt;。注意 &lt;code&gt;--linear-backend&lt;/code&gt; 只作用于量化线性层，MoE 专家走的是单独的 &lt;code&gt;--moe-backend&lt;/code&gt;，别设错了以为生效了。&lt;/p&gt;
&lt;p&gt;FP8 有两种表示，E4M3（范围小、精度高）和 E5M2（范围大、精度低），权重和激活的实际取值分布决定该用哪个。&lt;/p&gt;
&lt;h2&gt;边界与坑&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;显存要按&lt;strong&gt;权重 + KV cache + 余量&lt;/strong&gt;三块估，只算权重就会在&amp;quot;看起来够&amp;quot;的情况下 OOM&lt;/li&gt;&lt;li&gt;所有参数都与版本绑定。跨版本升级后必须重查文档，尤其是配置项重命名这类破坏性变更&lt;/li&gt;&lt;li&gt;共享 GPU 场景下 &lt;code&gt;gpu_memory_utilization&lt;/code&gt; 的语义会失真，此时不如直接用 &lt;code&gt;kv_cache_memory&lt;/code&gt; 锁死&lt;/li&gt;&lt;li&gt;调参期间一次只改一个值，否则无法归因&lt;/li&gt;&lt;/ul&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：&lt;code&gt;gpu_memory_utilization&lt;/code&gt; 是不是越高越好？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不是。它按整卡计算，共享 GPU 时会算错；且不留余量会在特定请求组合下突然 OOM。调到吞吐不再随它增长就够了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：多卡机器性能上不去，先查什么？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;&lt;code&gt;tensor_parallel_size&lt;/code&gt;，它默认是 1。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：模型装得下，还有必要开 TP 吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;KV cache 总量变大是真的收益，但每层多了同步通信。要测，不要默认开。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：量化一定能解决显存问题吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;显存层面基本能，加速层面看硬件——计算能力低于 8.9 的卡拿不到完整 FP8。&lt;/p&gt;
&lt;h2&gt;参考与延伸&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;vLLM 官方文档：Conserving Memory、Optimization、FP8 三章（&lt;code&gt;docs.vllm.ai&lt;/code&gt;，请以所运行版本为准）&lt;/li&gt;&lt;li&gt;交叉核对：TT Lab Blog《Inside vLLM (7) — Deployment Tuning, Common Pitfalls, and OOM Triage》，2026-08-12，该文明确声明其数值核对至该日期&lt;/li&gt;&lt;li&gt;显存估算方法：权重 / KV cache / 余量三块拆分法，可与上述参数一一对应&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:55:09 +0800</pubDate></item><item><title>装 skill 前先扫一遍：NVIDIA SkillSpector 和那组不好看的数字</title><link>https://www.sysplus.top/?id=18</link><description>&lt;p&gt;装 skill 这件事有个默认前提：&lt;strong&gt;你装的东西是可信的&lt;/strong&gt;。这个前提今年被一组数字打脸了，而 NVIDIA 干脆做了个扫描器来补这个洞。这篇把数字和工具都记一下，包括它扫不出来的那部分。&lt;/p&gt;
&lt;p&gt;信息验证截至 2026-10-20。&lt;/p&gt;
&lt;h2&gt;先看那组数字&lt;/h2&gt;
&lt;p&gt;最常被引用的是 Liu 等人 2026 年 1 月的论文（arXiv 2601.10338），扫了两个公开市场的 &lt;strong&gt;31,132 个 skill&lt;/strong&gt;：&lt;/p&gt;
&lt;ul&gt;&lt;li&gt;&lt;strong&gt;26.1%&lt;/strong&gt; 至少含一个漏洞（覆盖 14 种模式）&lt;/li&gt;&lt;li&gt;其中数据外传 &lt;strong&gt;13.3%&lt;/strong&gt;、提权 &lt;strong&gt;11.8%&lt;/strong&gt;&lt;/li&gt;&lt;li&gt;&lt;strong&gt;5.2%&lt;/strong&gt; 达到高危模式，可判断为有恶意意图&lt;/li&gt;&lt;li&gt;&lt;strong&gt;带可执行脚本的 skill，出问题的概率是不带脚本的 2.12 倍&lt;/strong&gt;&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;另一个更刺眼的是 Snyk 在 2026 年 2 月 3 日发的分析：ClawHub 注册表 2,857 个 skill 里查出 &lt;strong&gt;341 个恶意&lt;/strong&gt;，约占 12%，大部分来自同一波投放 Atomic Stealer 的活动，集中在 1 月 27–29 日三天。那个注册表服务的不是 Claude，但格式就是同一套 SKILL.md——&lt;strong&gt;换客户端读起来一模一样&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;顺带说个口径问题：有些二手报道写的是&amp;quot;扫描了 42,447 个 skill&amp;quot;，数字对不上。以论文口径为准，我采用 31,132。&lt;/p&gt;
&lt;h2&gt;SkillSpector 是什么&lt;/h2&gt;
&lt;p&gt;NVIDIA 出的开源扫描器，Apache-2.0，Python 3.12+，仓库 &lt;code&gt;NVIDIA/SkillSpector&lt;/code&gt;。它要回答的问题就一句：&lt;strong&gt;这个 skill 能不能装&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;工作方式分两段：&lt;/p&gt;
&lt;p&gt;1. &lt;strong&gt;静态分析&lt;/strong&gt;：正则匹配 + AST 走查（找 &lt;code&gt;exec&lt;/code&gt;、&lt;code&gt;eval&lt;/code&gt;、&lt;code&gt;subprocess&lt;/code&gt; 这类危险调用）+ 污点追踪 + YARA 规则，再查 OSV.dev 的实时 CVE（断网自动回退本地规则）。这段完全离线，加 &lt;code&gt;--no-llm&lt;/code&gt; 即可。&lt;/p&gt;
&lt;p&gt;2. &lt;strong&gt;LLM 语义评估&lt;/strong&gt;（可选）：把内容交给模型判断意图，抓那些被混淆过的指令。官方口径精度约 87%。&lt;/p&gt;
&lt;p&gt;覆盖面是 &lt;strong&gt;71 种漏洞模式、17 个类别&lt;/strong&gt;，除了常见的提示注入、数据外传、提权、供应链之外，还有几个只有 agent 场景才有的：&lt;strong&gt;记忆投毒、触发滥用、抗拒答、抗审查绕过、MCP 工具投毒、MCP 最小权限&lt;/strong&gt;。&lt;/p&gt;
&lt;p&gt;评分是 0–100，critical 50 分、high 25、medium 10、low 5，带脚本的再乘 1.3。档位：&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;分数&lt;/th&gt;&lt;th&gt;判定&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;0–20&lt;/td&gt;&lt;td&gt;LOW / SAFE&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;21–50&lt;/td&gt;&lt;td&gt;MEDIUM / CAUTION&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;&amp;gt;50&lt;/td&gt;&lt;td&gt;HIGH / CRITICAL，建议别装&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;h2&gt;装和用&lt;/h2&gt;
&lt;pre&gt;&lt;code class=&quot;language-bash&quot;&gt;# 安装
uv tool install git+https://github.com/NVIDIA/skillspector.git

# 扫一个本地目录 / 单个 SKILL.md / Git 仓库 / zip / URL
skillspector ./my-skill/
skillspector https://github.com/some-user/some-claude-skill

# 离线模式（跳过 LLM 阶段）
skillspector ./my-skill/ --no-llm

# 接 CI：输出 SARIF，分数 ≤50 时退出码为 0
skillspector ./my-skill/ --format sarif --output results.sarif&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;几个工程细节值得说。它对输入设了硬上限：单次摄入 100 MiB、zip 成员 1 万个，&lt;strong&gt;超了直接按失败处理（fail-closed）&lt;/strong&gt;，而不是硬着头皮扫一半——对一个&amp;quot;输入本身就是不可信内容&amp;quot;的扫描器，这是对的默认行为。还有 baseline 机制，把已知且接受的问题记进基线，重扫只冒出新问题，这样才能挂进 CI 当门禁，不至于每轮吐同一批旧警告。&lt;/p&gt;
&lt;h2&gt;它扫不出来的部分&lt;/h2&gt;
&lt;p&gt;这点必须写清楚。Cloud Security Alliance 2026 年 6 月的报告里提到，Trail of Bits 成功把恶意逻辑（窃取环境变量）藏进&lt;strong&gt;预编译的字节码&lt;/strong&gt;，跟看起来干净的源码放在一起，只扫源码的扫描器完全没发现。报告的结论是当前 skill 扫描器可以被系统性绕过。&lt;/p&gt;
&lt;p&gt;所以定位应该是：&lt;strong&gt;第一道闸，不是唯一一道闸&lt;/strong&gt;。配套还得做三件事——给 skill 最小权限、CAUTION 档的人工看一遍、优先选有透明源码历史和维护者身份的。它能拦住的，是那 26.1% 里绝大多数&amp;quot;不小心的、意外的、大规模分发的&amp;quot;问题。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：付费 API 的 skill 也要扫吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;要。扫描的对象是包本身，跟它从哪来无关。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：LLM 那段会不会把我的 skill 内容传出去？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;会。LLM 模式要把内容发到你配置的模型端点，属于一次数据出网。在意的话用 &lt;code&gt;--no-llm&lt;/code&gt;。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：只装 Anthropic 官方的就安全了吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;官方仓库 README 自己写了&amp;quot;仅供演示与教学，依赖前请自行测试&amp;quot;。风险低不等于零。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：能不能让 agent 自己扫？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;能，有 MCP server 模式，暴露一个 &lt;code&gt;scan_skill&lt;/code&gt; 工具，装完后可以在会话里直接调。&lt;/p&gt;
&lt;h2&gt;参考与出处&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Liu et al., &amp;quot;Agent Skills in the Wild&amp;quot;，arXiv 2601.10338，2026-01-15&lt;/li&gt;&lt;li&gt;Snyk, skill.md shell access 分析，2026-02-03&lt;/li&gt;&lt;li&gt;NVIDIA/SkillSpector 仓库：github.com/NVIDIA/SkillSpector（Apache-2.0）&lt;/li&gt;&lt;li&gt;CSA 关于预编译字节码绕过的研究报道，2026-06&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:32:27 +0800</pubDate></item><item><title>拆开 Agent Skills：SKILL.md 到底长什么样</title><link>https://www.sysplus.top/?id=17</link><description>&lt;p&gt;Agent Skills 这半年铺得很快，但网上讲概念的多，讲&amp;quot;一个 skill 拆开到底长什么样&amp;quot;的少。这篇从开放标准和官方公开仓库出发，把结构、加载机制和几个容易踩的坑捋一遍。&lt;/p&gt;
&lt;p&gt;信息验证截至 2026-10-20。素材来源：Agent Skills 开放标准站点、Anthropic 官方 skill 仓库与文档、公开的第三方整理。&lt;/p&gt;
&lt;h2&gt;一个 skill 就是一个文件夹&lt;/h2&gt;
&lt;p&gt;格式上，一个 skill 是&lt;strong&gt;一个包含 &lt;code&gt;SKILL.md&lt;/code&gt; 的文件夹&lt;/strong&gt;。文件结构分两部分：YAML frontmatter 写元信息（name、description，以及各客户端可选的扩展字段），后面跟正文指令，文件夹里还可以放脚本、参考文档和模板。&lt;/p&gt;
&lt;p&gt;这套格式是开放标准（agentskills.io），所以不只 Claude 能用——Codex、Gemini CLI、GitHub Copilot、Cursor、VS Code、Goose、OpenCode 都读 &lt;code&gt;SKILL.md&lt;/code&gt;。纯指令加脚本的 skill 通常能直接跨工具跑；用到某家特有能力的（比如 Claude Code 的 hooks 或它的特定 frontmatter 字段）需要改一点。&lt;/p&gt;
&lt;h2&gt;关键机制：description 驱动的渐进式披露&lt;/h2&gt;
&lt;p&gt;这是最容易理解错的一点。装了 skill 不等于它一直在消耗上下文，加载分三层：&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;层级&lt;/th&gt;&lt;th&gt;加载内容&lt;/th&gt;&lt;th&gt;何时加载&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;1&lt;/td&gt;&lt;td&gt;name + 一句话 description&lt;/td&gt;&lt;td&gt;每轮都加载，对所有已装 skill&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;2&lt;/td&gt;&lt;td&gt;SKILL.md 正文（指令、示例、边界）&lt;/td&gt;&lt;td&gt;模型判断相关时才加载&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;3&lt;/td&gt;&lt;td&gt;附带的脚本、参考文档、模板&lt;/td&gt;&lt;td&gt;正文指令指名调用时&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;第一层每个 skill 大约百来个 token 的量级；第二层视内容从几百到几千 token。&lt;strong&gt;只有 description 是每轮都在竞争注意力的&lt;/strong&gt;，所以它的写法直接决定这个 skill 会不会被用上。&lt;/p&gt;
&lt;p&gt;写&amp;quot;处理季度销售报表&amp;quot;太泛，模型要么在不该触发时触发，要么干脆不触发；写成触发条件才准——比如&amp;quot;把 CRM 导出的原始 CSV 整理成四段式季度报表模板，含预测偏差列&amp;quot;。有实践者提到另一个有效做法：在 description 里显式写&amp;quot;不适用于……&amp;quot;，用排除条件挡掉误触发。&lt;/p&gt;
&lt;h2&gt;装在哪里、谁能用&lt;/h2&gt;
&lt;p&gt;按 Anthropic 官方文档的划分：&lt;/p&gt;
&lt;table class=&quot;markdown-table&quot;&gt;&lt;thead&gt;&lt;tr&gt;&lt;th&gt;使用面&lt;/th&gt;&lt;th&gt;怎么装&lt;/th&gt;&lt;th&gt;谁能用&lt;/th&gt;&lt;th&gt;运行时&lt;/th&gt;&lt;/tr&gt;&lt;/thead&gt;&lt;tbody&gt;&lt;tr&gt;&lt;td&gt;Claude Code&lt;/td&gt;&lt;td&gt;复制文件夹到用户级或项目级的 skills 目录，或装插件&lt;/td&gt;&lt;td&gt;你自己、你的仓库，或拿到插件的人&lt;/td&gt;&lt;td&gt;完整网络访问&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;claude.ai / Cowork&lt;/td&gt;&lt;td&gt;设置里上传 zip（需开启代码执行）&lt;/td&gt;&lt;td&gt;只有你本人&lt;/td&gt;&lt;td&gt;取决于设置&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Claude API&lt;/td&gt;&lt;td&gt;通过 &lt;code&gt;/v1/skills&lt;/code&gt; 端点上传&lt;/td&gt;&lt;td&gt;整个工作区&lt;/td&gt;&lt;td&gt;无网络访问、不能装包&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;一个官方说明里写得很清楚、但常被忽略的点：&lt;strong&gt;自定义 skill 不在各个使用面之间同步&lt;/strong&gt;。上传到 claude.ai 的不会出现在 API 里，两边也都不会出现在 Claude Code 里。&lt;/p&gt;
&lt;h2&gt;官方仓库里有什么&lt;/h2&gt;
&lt;p&gt;Anthropic 的公开仓库 &lt;code&gt;anthropics/skills&lt;/code&gt; 是很多人第一站，2026 年 9 月底约 17.9 万 star，含 19 个 skill 文件夹；文档类四个（docx、xlsx、pptx、pdf）是 source-available 而非开源，其余多数 Apache 2.0。README 自称是演示与教学材料，要求使用者自行测试——这个态度对任何 skill 都适用。&lt;/p&gt;
&lt;p&gt;常被点名的几个：&lt;code&gt;skill-creator&lt;/code&gt;（写 skill、跑 A/B 评测、调 description）、&lt;code&gt;frontend-design&lt;/code&gt;（把 UI 从模板化默认样式里推出来）、&lt;code&gt;mcp-builder&lt;/code&gt;（指导用 FastMCP 或 TypeScript 写 MCP server）、&lt;code&gt;webapp-testing&lt;/code&gt;（用 Playwright 驱动本地 Web 应用做截图与 UI 检查）、&lt;code&gt;claude-api&lt;/code&gt;（加载当前的 API 与 SDK 参考，避免模型凭记忆写出过期的调用）。&lt;/p&gt;
&lt;h2&gt;两个成本项&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;一是上下文预算。&lt;/strong&gt; 在 Claude Code 里，已装 skill 的列表预算大约是上下文窗口的 1%。装一堆不用的 skill，会把有用的那些挤出预算——所以建议先装两三个对得上日常任务的，很少超过十个。有工具会列出每个 skill 的开销和从未被调用过的那些，方便定期清理。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二是重叠冲突。&lt;/strong&gt; 两个写作风格类 skill 同时装，会给出互相矛盾的指令。同类只留一个。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：skill 和 MCP 有什么区别？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;skill 是&amp;quot;教模型怎么做&amp;quot;，MCP 是&amp;quot;给模型接一个活数据源&amp;quot;。只让模型记住格式规范用 skill；真要连实时数据才上 MCP，后者要花一下午配鉴权，还得长期维护。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：热门就等于好用吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;不等于。star 数衡量的是关注度不是质量，更不是安全审查。装载之前读一遍 SKILL.md 和它附带的脚本，这个习惯比看排名有用。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：怎么判断一个 skill 到底有没有用？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;别看 README 里的示例，拿自己真实的任务跑。有工具会做 A/B——同一批 prompt 装与不装各跑一遍，对比通过率、耗时和 token 开销。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;ul&gt;&lt;li&gt;Agent Skills 开放标准：agentskills.io&lt;/li&gt;&lt;li&gt;Anthropic Agent Skills 文档：platform.claude.com/docs/en/agents-and-tools/agent-skills/overview&lt;/li&gt;&lt;li&gt;Anthropic 官方仓库：github.com/anthropics/skills&lt;/li&gt;&lt;li&gt;社区目录：ComposioHQ/awesome-claude-skills、VoltAgent/awesome-agent-skills&lt;/li&gt;&lt;/ul&gt;</description><pubDate>Wed, 21 Oct 2026 00:32:27 +0800</pubDate></item><item><title>读《Best Claude Skills》：我同意的三条和存疑的一条</title><link>https://www.sysplus.top/?id=16</link><description>&lt;p&gt;&amp;gt; 本文是学习笔记，不是翻译。原文：Farasat Ali《Best Claude Skills (September 2026)》，发布于 2026-09-30，https://www.farasat.me/blog/best-claude-skills 。以下提炼他的核心论点，加上我的理解和不同意见，观点归属已尽量标明。&lt;/p&gt;
&lt;p&gt;Farasat Ali 这篇有个地方跟别的盘点文章不一样：&lt;strong&gt;他的推荐清单短得离谱&lt;/strong&gt;，而且明说了是故意的。理由是他宁愿推荐八个能指出出处的，也不推荐四十个指不出的。这个态度本身比清单值钱，我读下来同意三条，有一条存疑，另外补一条他没覆盖到的情况。&lt;/p&gt;
&lt;h2&gt;他提炼的四条&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;1. 值得装的 skill 主要是 Anthropic 自家那几个。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;清单是：skill-creator、docx/xlsx/pptx/pdf 四个文档 skill、frontend-design、bundled 的 /design、mcp-builder、webapp-testing、claude-api，外加 knowledge-work-plugins 里的 marketing plugin（其中他最推 brand-review，因为它会筛查&amp;quot;无事实支撑的表述&amp;quot;）。anthropics/skills 仓库在 2026-09-30 约有 17.9 万 star；文档类四个是 source-available，其余多数 Apache 2.0；仓库 README 自称仅供演示与教学。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;2. 选 skill 更接近选依赖，而不是选 prompt。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;理由很实在：skill 常常带着脚本，在你的机器上以宿主进程同等的权限执行。所以清单之外的第三方 skill，要当依赖审。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;3. 装前五步自查。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;读 SKILL.md 和旁边每个文件（脚本优先）；检查 &lt;code&gt;allowed-tools&lt;/code&gt;（Claude Code 会应用项目 skill 的权限授予，即使目录未被信任）；搜脚本里的网络调用、对 &lt;code&gt;~/.ssh&lt;/code&gt;、&lt;code&gt;~/.aws&lt;/code&gt;、&lt;code&gt;.env&lt;/code&gt; 的读取和 base64 编码；pin 到具体 commit 而不是跟分支；能用纯指令实现就别带脚本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;4. 要评测、要清理。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用 skill-creator 做 A/B（同一批 prompt 装与不装对比通过率、耗时、token 开销）。Anthropic 2026 年 3 月加上这套评测后，光调 description 就让 6 个公开 skill 里 5 个的触发情况变好——说明很多 skill 表现差只是因为该触发时没触发。然后定期清理，因为已装 skill 的 description 常驻上下文，Claude Code 侧这份列表预算约占上下文窗口 1%。&lt;/p&gt;
&lt;h2&gt;我同意的三条&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;第 2 条是全文最该被记住的一句。&lt;/strong&gt; &amp;quot;skill = 提示词&amp;quot;这个直觉是错的。带脚本的 skill 和纯指令的 skill 完全是两个风险等级——这一点 NVIDIA 那边的扫描数据也印证了：带可执行脚本的 skill 出问题概率是不带脚本的 2.12 倍。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第 3 条里的 &lt;code&gt;allowed-tools&lt;/code&gt; 那条最容易被忽略。&lt;/strong&gt; 很多人审 skill 只看它在做什么，不看它申请了什么权限。申请了但没用到的权限就是纯粹的攻击面，这一条最该先查。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;第 4 条的&amp;quot;description 调优&amp;quot;成本极低、收益极高。&lt;/strong&gt; 公开讨论里反复出现的典型问题就是：描述写得泛，结果该出现时不出现。改法跟他建议的一致——写成触发条件而不是内容摘要。&lt;/p&gt;
&lt;h2&gt;我存疑的一条&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;他把 seo-audit 和 performance-report 定位成&amp;quot;分析的初稿，永远不是分析本身&amp;quot;，理由是两者完全依赖连接器能取到的数据。&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;这个判断我认同一半。确实，这两类 skill 的输出质量上限被数据口径锁死。但我认为问题不在 skill 本身，而在&lt;strong&gt;是否掌握了数据口径&lt;/strong&gt;——如果你清楚连接器能拿到什么、拿不到什么，输出就是可用的初稿加已知盲区清单；如果你不清楚，那才是&amp;quot;永远不能当分析&amp;quot;。所以判断标准应该是&amp;quot;我是否掌握这个 skill 的数据边界&amp;quot;，而不是&amp;quot;这类 skill 一律降档&amp;quot;。他这个一刀切的说法，会把本来可控的工具误杀。&lt;/p&gt;
&lt;h2&gt;他没有覆盖到的情况&lt;/h2&gt;
&lt;p&gt;他的清单基本面向有 Anthropic 账号、能联网的知识工作者。&lt;strong&gt;但公开仓库里还躺着另一半生态，他基本没提&lt;/strong&gt;：一类是明确面向本地、离线、数据不出机器场景的开源项目。比如做本地电脑操作自动化的 &lt;code&gt;typesafe-computer-use&lt;/code&gt;（MIT，把决策压到一个小模型上，README 里给了单步成本对比），以及做智能体记忆的 &lt;code&gt;MemOS&lt;/code&gt;（Apache-2.0，自托管方案依赖 Neo4j 加 Qdrant，也有把数据落在本机 SQLite 的本地插件路线）。它们解决的是&amp;quot;断网也能干&amp;quot;和&amp;quot;数据不出本机&amp;quot;，跟 Claude 生态没有绑定关系。&lt;/p&gt;
&lt;p&gt;这说明 skill 生态其实分成了两半：一半是云端市场里那些需要被扫描、被审计的第三方包（他文章的重心），另一半是个人或团队自己写的、只在本地跑的流程固化件。&lt;strong&gt;后半部分基本没有供应链风险，因为它就是使用者自己写的&lt;/strong&gt;，而它的价值恰恰呼应他文末那句——&amp;quot;多数团队最该装的 skill，是把他们已经在怎么干活写下来的那个&amp;quot;。&lt;/p&gt;
&lt;p&gt;对只想固化自己流程的人来说，后面这条路才是起点：把写作规范、审核清单、发布门槛这类重复规则写成自己的 skill，不依赖任何市场里的第三方包，也就绕开了他文章里那一整章安全风险。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：他的清单现在还适用吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;文章更新到 2026-09-30，本文写于 2026-10-20。skill 市场变动快，装之前建议回原文看最新版本。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：能不能直接照他的清单装？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;他的建议是先挑两三个跟你每周真在做的任务对得上的，别一次装一堆。理由见上文第 4 条。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：不装第三方 skill 是不是就安全了？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;风险低不等于零，官方仓库自己声明是演示材料。另外 skill 加载的外部网页内容本身可能携带指令，这类风险跟装不装第三方无关。&lt;/p&gt;
&lt;h2&gt;原文与延伸&lt;/h2&gt;
&lt;li&gt;&lt;strong&gt;原文&lt;/strong&gt;：Farasat Ali, Best Claude Skills (September 2026), 2026-09-30 — https://www.farasat.me/blog/best-claude-skills&lt;/li&gt;
&lt;li&gt;论文：Liu et al., Agent Skills in the Wild, arXiv 2601.10338&lt;/li&gt;
&lt;li&gt;Anthropic 官方仓库：github.com/anthropics/skills&lt;/li&gt;</description><pubDate>Tue, 20 Oct 2026 23:38:33 +0800</pubDate></item><item><title>Gemini 4 Argon 的 100 万输出 token，改变的到底是什么</title><link>https://www.sysplus.top/?id=15</link><description>&lt;p&gt;谷歌这代旗舰 Gemini 4 Argon 有个数字比榜单分数更值得注意：&lt;strong&gt;输出 token 上限从上一代的 6.4 万提到了 100 万&lt;/strong&gt;。输入长上下文这两年听得多了，输出长度一口气放大十五倍，改变的是另一类事情。&lt;/p&gt;
&lt;p&gt;信息验证截至 2026-10-20。数据来源为 2026-10-16 的公开月度科技汇总与 10 月模型发布追踪站，属二手信源；榜单分数与开放范围以谷歌官方为准。&lt;/p&gt;
&lt;h2&gt;已知信息&lt;/h2&gt;
&lt;li&gt;官方把这代描述为&amp;quot;前沿智能的新阶段&amp;quot;，主打&lt;strong&gt;复杂长周期工作流中的深度推理&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;输出 token 上限：6.4 万 → &lt;strong&gt;100 万&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;DeepSWE v1.1 得分 &lt;strong&gt;77.9%&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;面向场景：软件工程、企业知识工作、网络安全防御&lt;/li&gt;
&lt;li&gt;开放节奏：10 月 13 日起开发者可通过付费 Gemini API 与 Android Studio 使用；Google AI Studio 与 Antigravity 面向 AI Ultra 订阅者；Gemini app 与 Search AI Mode 面向符合条件的订阅者&lt;/li&gt;
&lt;li&gt;演示案例：在 Gemini app 内把一份密集的学术 PDF 转成全彩动画故事书（Argon 配合 Nano Banana 与 Veo）&lt;/li&gt;
&lt;h2&gt;输出变长，真正改变的是什么&lt;/h2&gt;
&lt;p&gt;输入变长只是&amp;quot;能喂更多&amp;quot;，输出变长是&amp;quot;能一次吐更多&amp;quot;，后者直接改变任务形态：&lt;/p&gt;
&lt;li&gt;以前要拆成十次调用、中间还要人工拼接的&lt;strong&gt;整包交付物&lt;/strong&gt;（一份完整技术文档、一次仓库级重构、一本带插图的书），现在可能一次成型；&lt;/li&gt;
&lt;li&gt;agent 的&amp;quot;多轮往返&amp;quot;可以压缩，中间状态丢失和上下文污染的机会变少；&lt;/li&gt;
&lt;li&gt;但代价也明确——&lt;strong&gt;一次输出越长，人越难审&lt;/strong&gt;。100 万 token 的产物，靠人从头读到尾已经不现实了，得改成&amp;quot;结构化分段 + 关键段人工核验&amp;quot;。&lt;/li&gt;
&lt;p&gt;对内容生产这类场景尤其明显：机器一次能产出的量变大，人审这一关的成本反而上升。可行的做法是把产出切成固定骨架（TL;DR、信源说明、核心内容、FAQ），人只重点核验信源和结论。&lt;strong&gt;长输出时代的审核，靠结构而不是靠通读。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;关于那个 77.9%&lt;/h2&gt;
&lt;p&gt;DeepSWE v1.1 得分 77.9% 这个数字被很多地方拿来跟别家横比，我不太建议这么做：&lt;/p&gt;
&lt;p&gt;1. DeepSWE v1.1 是特定版本的基准，别家公布的分数未必是同版本；&lt;/p&gt;
&lt;p&gt;2. 是否开深度推理、是否允许用工具、跑几轮取均值，这些条件不公开就无法对齐；&lt;/p&gt;
&lt;p&gt;3. 榜单分数和&amp;quot;我的任务能不能干好&amp;quot;之间本来就隔着一整套评测设计。&lt;/p&gt;
&lt;p&gt;拿它当&amp;quot;这代模型在长周期编码类任务上确实强&amp;quot;的定性信号可以，当排名用不行。&lt;/p&gt;
&lt;h2&gt;还没解决的疑问&lt;/h2&gt;
&lt;li&gt;100 万输出 token 是硬上限还是默认配置，超出后的截断行为是什么，公开信息里没看到；&lt;/li&gt;
&lt;li&gt;长输出的首字延迟和总耗时没找到可核对的数据，这直接决定它适不适合交互式场景；&lt;/li&gt;
&lt;li&gt;价格口径未见统一说法。&lt;/li&gt;
&lt;p&gt;这几项等官方发布页和一手文档出来再补。&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：输出 token 上限提高对我的 API 账单有什么影响？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;单次费用上限提高了。以前一次调用最多 6.4 万输出，现在理论上能到 100 万，需要在应用层自己设 &lt;code&gt;maxOutputTokens&lt;/code&gt; 兜底，别让一次异常请求把预算打穿。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：能通过 Android Studio 用意味着什么？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;说明面向的是长周期编码任务的实际工程环境，而不是只做聊天演示。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：国内能用吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;开放范围按地区与订阅等级划分，以官方说明为准，本文不猜测。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;li&gt;2026 年 10 月科技热点汇总（腾讯新闻，2026-10-16）：news.qq.com/rain/a/20261016A09I6K00&lt;/li&gt;
&lt;li&gt;AI 模型 10 月发布追踪：nocode.mba/ai-release-tracker/2026/10&lt;/li&gt;
&lt;li&gt;谷歌官方 Gemini API 文档（一手信息以官方为准）&lt;/li&gt;</description><pubDate>Tue, 20 Oct 2026 23:38:33 +0800</pubDate></item><item><title>MiniMax M3.1 Flash：1M 上下文、74% DeepSWE，权重 10 月 23 日开放</title><link>https://www.sysplus.top/?id=14</link><description>&lt;p&gt;10 月 19 日 MiniMax 发了 M3.1 Flash，几个数字挺扎眼：1M 上下文、DeepSWE 74%、价格是 M3 的三分之一，而且权重说 10 月 23 日放。这条我先记着，等权重出来再实际跑一遍。&lt;/p&gt;
&lt;p&gt;信息验证截至 2026-10-20。&lt;strong&gt;注意：本文数据来自公开聚合站的月度汇总与模型发布追踪站，尚未逐一核对 MiniMax 官方发布页，等权重开放后我会补一次实测。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;已知信息&lt;/h2&gt;
&lt;p&gt;| 项目 | 数值 |&lt;/p&gt;
&lt;p&gt;|------|------|&lt;/p&gt;
&lt;p&gt;| 发布时间 | 2026-10-19 |&lt;/p&gt;
&lt;p&gt;| 上下文窗口 | 1M token |&lt;/p&gt;
&lt;p&gt;| 多模态 | 是 |&lt;/p&gt;
&lt;p&gt;| 思考深度 | 可调 |&lt;/p&gt;
&lt;p&gt;| Automation-Bench | 62% |&lt;/p&gt;
&lt;p&gt;| DeepSWE | 74% |&lt;/p&gt;
&lt;p&gt;| API 价格 | 输入 $0.15 / 百万 token，输出 $0.60 / 百万 token |&lt;/p&gt;
&lt;p&gt;| 权重开放 | 2026-10-23（官方口径） |&lt;/p&gt;
&lt;p&gt;| 成本对比 | 约为 M3 的三分之一 |&lt;/p&gt;
&lt;p&gt;还有个背景：这个模型之前叫 Space Bunny，是改名后发布的。&lt;/p&gt;
&lt;h2&gt;我关心的三点&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;一是&amp;quot;1M 上下文 + 可调思考深度&amp;quot;这个组合。&lt;/strong&gt; 长上下文现在不稀奇了，稀奇的是思考深度能调——意味着同一个模型可以在&amp;quot;快而浅&amp;quot;和&amp;quot;慢而准&amp;quot;之间挪。对 agent 工作流来说这比单纯堆上下文有用：路由、分类这类活儿调浅，关键决策调深，成本可控。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;二是价格。&lt;/strong&gt; 输入 $0.15、输出 $0.60 这个档位，配 1M 上下文，明显是冲着&amp;quot;高频调用&amp;quot;去的。如果真能做到 M3 三分之一的成本而 benchmark 不掉太多，那 agent 循环里那些 repetitive step 的成本账就能重算了。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;三是权重。&lt;/strong&gt; 这是最值得盯的一项。API 再便宜也是跑在别人的机器上，权重放出来才能塞进自建环境、自己控制显存和并发。10 月 23 日是个关键日期。&lt;/p&gt;
&lt;h2&gt;存疑的地方&lt;/h2&gt;
&lt;li&gt;&lt;strong&gt;62% / 74% 这两个分是怎么测的&lt;/strong&gt;，官方没在聚合站里给出完整条件（是否开思考、是否用工具、跑几遍取均值）。在拿到一手评测配置之前，我只当参考值。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;74% 这个 DeepSWE 分数&lt;/strong&gt;，跟谷歌 Gemini 4 Argon 公布的 DeepSWE v1.1 77.9% 不是一个明确可比的口径（版本与测试条件都不确定），&lt;strong&gt;不要直接横比排名&lt;/strong&gt;。&lt;/li&gt;
&lt;li&gt;权重开放时间已经改过一次口径（不同来源写法略有出入），以官方为准。&lt;/li&gt;
&lt;h2&gt;下一步&lt;/h2&gt;
&lt;p&gt;权重放出后值得做的两件事：一是拉下来在自己的推理框架上跑一次，测真实吞吐和显存占用，别信纸面数字；二是拿自己手头的任务跑一遍，看榜单分数落到具体场景里还剩多少。&lt;strong&gt;聚合站的分数是别人的任务，不是你的。&lt;/strong&gt;&lt;/p&gt;
&lt;h2&gt;常见问题&lt;/h2&gt;
&lt;p&gt;&lt;strong&gt;Q：1M 上下文实际用得满吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;用得满不满取决于你的任务。长文档理解、仓库级代码改动这类用得上；日常问答塞满反而拖慢且更贵。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：可调思考深度怎么选？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;简单分类、路由这类选择题调最浅；需要多步推理或会触发实际操作（改文件、发请求）的调深。&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Q：权重开放后能在消费级显卡上跑吗？&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;取决于参数量和量化方案，官方尚未公布完整信息，等 10 月 23 日看。&lt;/p&gt;
&lt;h2&gt;参考&lt;/h2&gt;
&lt;li&gt;HeadsUpAI 2026 年 10 月 AI 发布汇总：headsupai.io/ai-news-and-updates/this-month&lt;/li&gt;
&lt;li&gt;LLM Gateway 模型发布时间线：llmgateway.io/timeline&lt;/li&gt;
&lt;li&gt;MiniMax 官方渠道（权重与评测一手信息以官方为准）&lt;/li&gt;</description><pubDate>Tue, 20 Oct 2026 23:38:33 +0800</pubDate></item><item><title>为VLLM运行的大模型API增加API KEY鉴权方法</title><link>https://www.sysplus.top/?id=10</link><description>&lt;p&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;前面在《[华为 Atlas 800I A2 部署 Qwen2.5-32B 全栈 AI 应用实践]》（*注：&lt;/span&gt;&lt;span md-inline=&quot;url&quot; class=&quot;md-link md-pair-s&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;a href=&quot;https://www.sysplus.top/?id=9&quot; style=&quot;box-sizing: border-box;cursor: pointer;color: rgb(65, 131, 196);-webkit-user-drag: none&quot;&gt;https://www.sysplus.top/?id=9&lt;/a&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;）中分享了如何通过 vLLM-Ascend部署大模型。虽然不鉴权方便测试，但在生产环境或者多人协作时，&lt;/span&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;API 鉴权（Authentication）&lt;/strong&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;是绝对不可或缺的一环。&lt;/span&gt;&lt;/p&gt;&lt;p cid=&quot;n4&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;今天，记录基于 FastAPI 搭建一个轻量级的 API Gateway（网关），为 vLLM 加上 Bearer Token 鉴权机制。这套方案不仅支持流式输出（Streaming），还能完美兼容 OpenAI SDK！&lt;/span&gt;&lt;/p&gt;&lt;p cid=&quot;n4&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;br/&gt;&lt;/p&gt;&lt;h2 cid=&quot;n17&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;🛠️ 核心代码实现&lt;/strong&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p cid=&quot;n18&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;首先，我们需要编写 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;main.py&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;。这里的核心是利用 FastAPI 的依赖注入功能来校验 Header 中的 API Key。&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-python&quot;&gt;import&amp;nbsp;os
from&amp;nbsp;fastapi&amp;nbsp;import&amp;nbsp;FastAPI,&amp;nbsp;Header,&amp;nbsp;HTTPException,&amp;nbsp;Depends
from&amp;nbsp;fastapi.responses&amp;nbsp;import&amp;nbsp;StreamingResponse
import&amp;nbsp;httpx
import&amp;nbsp;json

app&amp;nbsp;=&amp;nbsp;FastAPI()
VLLM_BASE_URL&amp;nbsp;=&amp;nbsp;os.getenv(&amp;quot;VLLM_BASE_URL&amp;quot;,&amp;nbsp;&amp;quot;http://127.0.0.1:8000&amp;quot;)
VALID_KEYS&amp;nbsp;=&amp;nbsp;[
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;7xK9mP2&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;B3jF8wY1c&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;eR5tU2iO9&amp;quot;
]

def&amp;nbsp;verify_api_key(authorization:&amp;nbsp;str&amp;nbsp;=&amp;nbsp;Header(None)):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;not&amp;nbsp;authorization&amp;nbsp;or&amp;nbsp;authorization.replace(&amp;quot;Bearer&amp;nbsp;&amp;quot;,&amp;nbsp;&amp;quot;&amp;quot;)&amp;nbsp;not&amp;nbsp;in&amp;nbsp;VALID_KEYS:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;raise&amp;nbsp;HTTPException(status_code=401,&amp;nbsp;detail=&amp;quot;Invalid&amp;nbsp;API&amp;nbsp;Key&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;True

async&amp;nbsp;def&amp;nbsp;stream_generator(request_data:&amp;nbsp;dict):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;try:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;async&amp;nbsp;with&amp;nbsp;httpx.AsyncClient(timeout=60.0)&amp;nbsp;as&amp;nbsp;client:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;async&amp;nbsp;with&amp;nbsp;client.stream(&amp;quot;POST&amp;quot;,&amp;nbsp;f&amp;quot;{VLLM_BASE_URL}/v1/chat/completions&amp;quot;,&amp;nbsp;json=request_data)&amp;nbsp;as&amp;nbsp;response:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;检查状态码
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;response.status_code&amp;nbsp;!=&amp;nbsp;200:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;error_msg&amp;nbsp;=&amp;nbsp;await&amp;nbsp;response.aread()
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield&amp;nbsp;f&amp;quot;data:&amp;nbsp;{json.dumps({&amp;#39;error&amp;#39;:&amp;nbsp;f&amp;#39;Upstream&amp;nbsp;Error:&amp;nbsp;{response.status_code}&amp;#39;,&amp;nbsp;&amp;#39;message&amp;#39;:&amp;nbsp;error_msg.decode()})}\n\n&amp;quot;.encode(&amp;quot;utf-8&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;正常转发流
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;async&amp;nbsp;for&amp;nbsp;chunk&amp;nbsp;in&amp;nbsp;response.aiter_bytes():
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield&amp;nbsp;chunk
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;httpx.ReadTimeout:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield&amp;nbsp;f&amp;quot;data:&amp;nbsp;{json.dumps({&amp;#39;error&amp;#39;:&amp;nbsp;&amp;#39;Gateway&amp;nbsp;Timeout&amp;#39;,&amp;nbsp;&amp;#39;message&amp;#39;:&amp;nbsp;&amp;#39;The&amp;nbsp;upstream&amp;nbsp;server&amp;nbsp;took&amp;nbsp;too&amp;nbsp;long&amp;nbsp;to&amp;nbsp;respond.&amp;#39;})}\n\n&amp;quot;.encode(&amp;quot;utf-8&amp;quot;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;except&amp;nbsp;Exception&amp;nbsp;as&amp;nbsp;e:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;#&amp;nbsp;捕获所有其他异常（如连接重置、断开等）
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;yield&amp;nbsp;f&amp;quot;data:&amp;nbsp;{json.dumps({&amp;#39;error&amp;#39;:&amp;nbsp;&amp;#39;Upstream&amp;nbsp;Connection&amp;nbsp;Failed&amp;#39;,&amp;nbsp;&amp;#39;message&amp;#39;:&amp;nbsp;str(e)})}\n\n&amp;quot;.encode(&amp;quot;utf-8&amp;quot;)

@app.post(&amp;quot;/v1/chat/completions&amp;quot;,&amp;nbsp;dependencies=[Depends(verify_api_key)])
async&amp;nbsp;def&amp;nbsp;chat_completions(request:&amp;nbsp;dict):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;if&amp;nbsp;request.get(&amp;quot;stream&amp;quot;,&amp;nbsp;False):
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;StreamingResponse(
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;stream_generator(request),
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;media_type=&amp;quot;text/event-stream&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;headers={
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Cache-Control&amp;quot;:&amp;nbsp;&amp;quot;no-cache&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;Connection&amp;quot;:&amp;nbsp;&amp;quot;keep-alive&amp;quot;,
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;quot;X-Accel-Buffering&amp;quot;:&amp;nbsp;&amp;quot;no&amp;quot;
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;}
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;else:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;async&amp;nbsp;with&amp;nbsp;httpx.AsyncClient()&amp;nbsp;as&amp;nbsp;client:
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;response&amp;nbsp;=&amp;nbsp;await&amp;nbsp;client.post(f&amp;quot;{VLLM_BASE_URL}/v1/chat/completions&amp;quot;,&amp;nbsp;json=request)
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;return&amp;nbsp;response.json()&lt;/pre&gt;&lt;p cid=&quot;n18&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;hr style=&quot;box-sizing: content-box;height: 2px;margin: 16px 0px;border: 0px;padding: 0px;background-color: rgb(231, 231, 231)&quot;/&gt;&lt;h2 cid=&quot;n26&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;🐳 Docker 容器化与网络配置&lt;/strong&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p cid=&quot;n27&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;为了方便一键部署，我们配合 Docker Compose 使用,将python脚本跑在docker容器中。这里有一个&lt;/span&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;非常容易踩坑的地方&lt;/strong&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;：因为网关需要访问宿主机上的 vLLM 服务，所以网络模式必须配置正确！总体的访问方向：客户端（dify/open webui等）&amp;lt;--&amp;gt;main.py脚本&amp;lt;--&amp;gt;大模型接口（8000端口），后续请求按照Open AI API的格式，请求头 Authorization：Bear XXXXXXXXXX 的格式携带API KEY。&lt;/span&gt;&lt;/p&gt;&lt;h3 cid=&quot;n28&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.5em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.43;cursor: text;white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;1. 编写 Dockerfile&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;&lt;pre class=&quot;prism-highlight prism-language-bash&quot;&gt;FROM&amp;nbsp;python:3.10-slim
WORKDIR&amp;nbsp;/app
COPY&amp;nbsp;requirements.txt&amp;nbsp;.
RUN&amp;nbsp;pip&amp;nbsp;install&amp;nbsp;--no-cache-dir&amp;nbsp;-r&amp;nbsp;requirements.txt
COPY&amp;nbsp;main.py&amp;nbsp;.
CMD&amp;nbsp;[&amp;quot;uvicorn&amp;quot;,&amp;nbsp;&amp;quot;main:app&amp;quot;,&amp;nbsp;&amp;quot;--host&amp;quot;,&amp;nbsp;&amp;quot;0.0.0.0&amp;quot;,&amp;nbsp;&amp;quot;--port&amp;quot;,&amp;nbsp;&amp;quot;8081&amp;quot;]&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;h3 cid=&quot;n35&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.5em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.43;cursor: text;white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;2. docker-compose.yml 关键配置&lt;/strong&gt;&lt;/span&gt;&lt;/h3&gt;&lt;pre class=&quot;prism-highlight prism-language-markup&quot;&gt;#这里增加到原vllm-ascend的配置文件
api-gateway:&amp;nbsp;build:&amp;nbsp;./gateway&amp;nbsp;
container_name:&amp;nbsp;vllm_ascend-api-gateway&amp;nbsp;
network_mode:&amp;nbsp;host&amp;nbsp;#&amp;nbsp;🔥&amp;nbsp;关键点：使用&amp;nbsp;host&amp;nbsp;模式，方便网关直接访问宿主机的&amp;nbsp;8000&amp;nbsp;端口&amp;nbsp;
environment:&amp;nbsp;-&amp;nbsp;VLLM_BASE_URL=http://127.0.0.1:8000&amp;nbsp;
ports:&amp;nbsp;-&amp;nbsp;&amp;quot;8081:8000&amp;quot;&amp;nbsp;#&amp;nbsp;对外暴露&amp;nbsp;8081&amp;nbsp;端口&amp;nbsp;
depends_on:&amp;nbsp;-&amp;nbsp;vllm-ascend&amp;nbsp;
restart:&amp;nbsp;unless-stopped&lt;/pre&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;&lt;blockquote cid=&quot;n42&quot; mdtype=&quot;blockquote&quot; style=&quot;box-sizing: border-box;margin: 0.8em 0px;border-left: 4px solid rgb(223, 226, 229);padding: 0px 15px;color: rgb(119, 119, 119);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px;white-space: normal&quot;&gt;&lt;p cid=&quot;n43&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;white-space: pre-wrap;position: relative&quot;&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;💡 避坑指南&lt;/strong&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;：如果你的 vLLM 跑在 Docker 里，而网关也在 Docker 里，建议使用自定义的 Docker Network（如 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;bridge&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;），并将 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;VLLM_BASE_URL&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt; 改为容器的内部 DNS 名称（如 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;http://vllm-ascend:8000&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;）。我这里用 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;network_mode: host&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt; 是为了简化本地开发环境的网络连通性。&lt;/span&gt;&lt;/p&gt;&lt;/blockquote&gt;&lt;hr style=&quot;box-sizing: content-box;height: 2px;margin: 16px 0px;border: 0px;padding: 0px;background-color: rgb(231, 231, 231)&quot;/&gt;&lt;h2 cid=&quot;n45&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;🧪 如何测试你的鉴权网关？&lt;/strong&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p cid=&quot;n46&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;启动服务后，我们可以简易测试（也可以直接在dify中设置测试）：&lt;/span&gt;&lt;/p&gt;&lt;pre class=&quot;prism-highlight prism-language-bash&quot;&gt;curl&amp;nbsp;-N&amp;nbsp;-X&amp;nbsp;POST&amp;nbsp;http://localhost:8081/v1/chat/completions&amp;nbsp;\&amp;nbsp;-H&amp;nbsp;&amp;quot;Authorization:&amp;nbsp;Bearer&amp;nbsp;7xK9mP2qL5nR8vs4&amp;quot;&amp;nbsp;\&amp;nbsp;-H&amp;nbsp;&amp;quot;Content-Type:&amp;nbsp;application/json&amp;quot;&amp;nbsp;\&amp;nbsp;-d&amp;nbsp;&amp;#39;{&amp;quot;model&amp;quot;:&amp;nbsp;&amp;quot;Qwen3-30B-A3B&amp;quot;,&amp;nbsp;&amp;quot;messages&amp;quot;:&amp;nbsp;[{&amp;quot;role&amp;quot;:&amp;nbsp;&amp;quot;user&amp;quot;,&amp;nbsp;&amp;quot;content&amp;quot;:&amp;nbsp;&amp;quot;你好&amp;quot;}],&amp;nbsp;&amp;quot;stream&amp;quot;:&amp;nbsp;true}&amp;#39;&lt;/pre&gt;&lt;p cid=&quot;n46&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;/span&gt;&lt;/p&gt;&lt;p cid=&quot;n53&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;如果你传入错误的 Key，FastAPI 会毫不留情地返回 &lt;/span&gt;&lt;span md-inline=&quot;code&quot; spellcheck=&quot;false&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;code style=&quot;box-sizing: border-box;font-family: var(--monospace);vertical-align: initial;border: 1px solid rgb(231, 234, 237);background-color: rgb(243, 244, 244);border-radius: 3px;padding: 0px 2px;font-size: 0.9em&quot;&gt;401 Unauthorized&lt;/code&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;，这就说明我们的网关已经成功拦截了非法请求！&lt;/span&gt;&lt;/p&gt;&lt;p cid=&quot;n53&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box; line-height: inherit; orphans: 4; margin: 0.8em 0px; white-space: pre-wrap; position: relative; color: rgb(51, 51, 51); font-family: &amp;quot;Open Sans&amp;quot;, &amp;quot;Clear Sans&amp;quot;, &amp;quot;Helvetica Neue&amp;quot;, Helvetica, Arial, &amp;quot;Segoe UI Emoji&amp;quot;, sans-serif; font-size: 16px; text-align: center;&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;img src=&quot;https://www.sysplus.top/zb_users/upload/2026/06/202606071780761830497944.png&quot; alt=&quot;image.png&quot;/&gt;&lt;/span&gt;&lt;/p&gt;&lt;hr style=&quot;box-sizing: content-box;height: 2px;margin: 16px 0px;border: 0px;padding: 0px;background-color: rgb(231, 231, 231)&quot;/&gt;&lt;h2 cid=&quot;n55&quot; mdtype=&quot;heading&quot; class=&quot;md-end-block md-heading&quot; style=&quot;box-sizing: border-box;break-after: avoid-page;break-inside: avoid;orphans: 4;font-size: 1.75em;margin-top: 1rem;margin-bottom: 1rem;position: relative;line-height: 1.225;cursor: text;border-bottom: 1px solid rgb(238, 238, 238);white-space: pre-wrap;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif&quot;&gt;&lt;span md-inline=&quot;strong&quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;📝 总结与后续优化方向&lt;/strong&gt;&lt;/span&gt;&lt;/h2&gt;&lt;p cid=&quot;n56&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;通过这几行简单的代码，我们就把“裸奔”的 vLLM 保护起来了。这种架构的好处在于&lt;/span&gt;&lt;span md-inline=&quot;strong&quot; class=&quot;md-pair-s &quot; style=&quot;box-sizing: border-box&quot;&gt;&lt;strong style=&quot;box-sizing: border-box&quot;&gt;解耦&lt;/strong&gt;&lt;/span&gt;&lt;span md-inline=&quot;plain&quot; style=&quot;box-sizing: border-box&quot;&gt;——无论底层的 vLLM 怎么升级，只要接口规范不变，我们的网关层完全不需要动。&lt;/span&gt;&lt;/p&gt;&lt;p cid=&quot;n64&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;br/&gt;&lt;/p&gt;&lt;p cid=&quot;n66&quot; mdtype=&quot;paragraph&quot; class=&quot;md-end-block md-p&quot; style=&quot;box-sizing: border-box;line-height: inherit;orphans: 4;margin: 0.8em 0px;white-space: pre-wrap;position: relative;color: rgb(51, 51, 51);font-family: &amp;#39;Open Sans&amp;#39;, &amp;#39;Clear Sans&amp;#39;, &amp;#39;Helvetica Neue&amp;#39;, Helvetica, Arial, &amp;#39;Segoe UI Emoji&amp;#39;, sans-serif;font-size: 16px&quot;&gt;&lt;span md-inline=&quot;plain&quot; class=&quot;md-plain md-expand&quot; style=&quot;box-sizing: border-box&quot;&gt;备注：文中的一些key，账号、地址等信息为脱敏数据。&lt;/span&gt;&lt;/p&gt;&lt;p&gt;&lt;br/&gt;&lt;/p&gt;</description><pubDate>Sat, 06 Jun 2026 23:42:34 +0800</pubDate></item></channel></rss>