TT Lab
博客

한국어 English 日本語 中文

开始
博客

博客

vLLM production-stack · LMCache · llm-d: 引擎前端与 KV 缓存层的代码导览

状态:草案(2026-10-17),部分实测。 基准版本:production-stack vllm-stack-0.1.13(提交 e8cb4959ebfa)、vLLM v0.31.0(提交 db9527a46873)、LMCache v0.5.6(提交 3e11e10812b4)、llm-d v0.10.0(提交 f22bbaf3173a)、llm-d-router v0.11.0(提交 a5cbe600ebad)。由于小型集群中 vLLM 不常驻,今天没有启动新的 Pod,实测采用的是 2026-10-16 在单个 GPU 节点(8GB 级别笔记本 GPU)上通过临时 Pod 测得的原始数据,并在 2026-10-17 重新清点后引用。 编写规则:以实测数据为基础,未能测得的标为“未实测”,无法确认的标为“未确认”。

目录

1. 一句话总结:只有一个厨房,但大堂经理和冰箱可以随时更换

vLLM 是在一张(或几张)GPU 上快速运行模型的 厨房。如果开设多个厨房,就需要决定将订单送到哪个厨房的 大堂经理、共享各厨房各自独立进行的预备工作(KV 缓存)的 冰箱, 以及通报厨房繁忙程度的 订单板(指标) 。这三个项目保持厨房(vLLM)不变,而以各自不同的方式填充这些周边。

组件 职责 运行位置
production-stack 路由器(vllm_router) 接收兼容 OpenAI 的请求,挑选一个引擎 Pod 并原样转发 单个 Deployment,Python·FastAPI 进程
production-stack Helm Chart 一次性安装引擎 Deployment、路由器、ServiceMonitor 和 Grafana 仪表盘 helm install 一次
vLLM KV 连接器(KVConnectorBase_V1) 引擎调度器与工作进程调用外部 KV 存储的接口 引擎进程内部
LMCache 连接器实现。将 KV 切割为 256 token 的块,并在 CPU·磁盘·远程中存储与查询 引擎进程内部,或独立服务器(MP 模式)
llm-d Router(代理 + EPP) 代理仅负责连接,由 EPP 选择 Pod 并返回地址 Envoy 系列网关 + EPP Deployment(Go)
vLLM /metrics 以 Prometheus 格式输出队列长度、KV 使用率、前缀缓存命中等 引擎 API 服务器

为什么需要 KV 缓存以及 LMCache 节省了什么,请参见 LLM KV 缓存剖析 系列。本篇不讲原理,专注于 哪个文件的哪个函数在执行该任务。

2. 架构图

요청 경로: 앞단 → vLLM 엔진 ↔ LMCache 계층 앞단은 둘 중 하나 클라이언트 production-stack 라우터 Python · FastAPI 한 프로세스 roundrobin session prefixaware kvaware k8s 파드 감시 + /metrics 수집 llm-d Router 프록시(Envoy 등) + EPP(Go) 필터 점수 고르기 ext-proc 로 파드 주소만 돌려주고 전달은 프록시가 KV 이벤트(ZMQ) 구독 vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) vLLM 엔진 파드 스케줄러 워커(GPU) KV 커넥터: LMCache 구현 GPU 안 KV 블록(16토큰) LMCache: 256토큰 청크 단위로 저장·조회 LocalCPU LocalDisk 원격 백엔드 P2P·PD 앞 계층부터 찾고, 원격에서 찾으면 CPU 에 올려 둔다 MP 모드는 이 계층을 엔진 밖 별도 서버(ZMQ)로 뺀다
图:请求路径:客户端通过 production-stack 路由器或 llm-d Router 到达 vLLM 引擎 Pod,引擎通过 KV 连接器调用 LMCache 层(LocalCPU·LocalDisk·远程·P2P)

这是读图指南。

3. 代码导览

首先整理“代码在哪个仓库中”。 llm-d/llm-d 仓库中没有服务代码。 在 v0.10.0 标签的 1,315 个文件中,834 个是 YAML,249 个是 Markdown,没有 Go 文件,Python 文件也只有 33 个 CI、工具和示例脚本。它是一个收集了部署指南、Helm 值和设计文档的地方,实际的组件位于 COMPONENTS.md 的 Roster 表所指向的独立仓库中。EPP 是 llm-d-router 。旧名称 llm-d-inference-scheduler 的地址现在已跳转到 llm-d-router (2026-10-17 已确认 HTTP 301)。由于 llm-d v0.10.0 将 guides/env.sh 锁定为 EPP 镜像 v0.11.0 和 Gateway API Inference Extension (GAIE) v1.5.0 CRD,因此本文单独下载并阅读了 llm-d-router v0.11.0。诸如 InferencePool 之类的 API 类型取自 GAIE 模块。

路径 内容 首次阅读的文件
production-stack src/vllm_router/ 路由器主体。接收请求·服务发现·路由·统计 app.py 的 initialize_all
production-stack helm/ Chart。引擎 Deployment、路由器、ServiceMonitor、仪表盘 ConfigMap templates/deployment-vllm-multi.yaml
production-stack helm/dashboards/ 三套 Grafana 仪表盘 JSON(vLLM·按模型·LMCache) vllm-dashboard.json
vLLM vllm/v1/metrics/ Prometheus 指标定义与记录 loggers.py 的 PrometheusStatLogger
vLLM vllm/distributed/kv_transfer/kv_connector/v1/ KV 连接器接口与实现(LMCache·NIXL·Mooncake 等) base.py
LMCache lmcache/integration/vllm/ vLLM 连接器的实际实现 vllm_v1_adapter.py
LMCache lmcache/v1/ 缓存引擎·token 块·存储后端·MP 服务器 cache_engine.py, storage_backend/__init__.py
llm-d guides/, docs/architecture/ 部署配方与设计文档。无代码 docs/architecture/core/router/README.md
llm-d-router pkg/epp/ EPP:ext-proc 服务器、调度器、过滤器·打分器·挑选器插件 scheduling/scheduler.go
llm-d-router pkg/kvevents/, pkg/kvcache/, pkg/sidecar/ KV 事件订阅·块索引、P/D 路由边车 kvevents/README.md

4. 核心路径追踪

4.1 production-stack:单个请求到达引擎的过程

  1. 路由器在启动时,会依次将 initialize_all 中的服务发现、引擎统计收集器、请求统计和路由逻辑创建为单例。由于路由逻辑需要读取前三者创建的表格,因此必须按此顺序。
  2. 引擎列表由 K8sPodIPServiceDiscovery._watch_engines 创建。它通过标签选择器 watch Pod,并向每个就绪的 Pod 查询 _get_model_names 以获取 /v1/models 并记录“此 IP 提供哪个模型”。之所以直接通过 Pod IP 而非 Service 进行连接,是因为路由器必须选择具体的 Pod,而 Service 会将该选择交由 kube-proxy 处理。
  3. 请求通过 route_chat_completion 进入 route_general_request 。在此处,利用正文中的 model 筛选出候选引擎,调用路由逻辑的 route_request 获取一个 URL,随后通过 process_request 流式传输响应。如果失败,该函数内还包含一个重试循环,用于剔除该 URL 并重新选择。
  4. 路由逻辑有 RoutingLogic 枚举定义的八种。核心轴包括 RoundRobinRouter、将会话键放入一致性哈希环的 SessionRouter、将提示词字符串按 128 字符块哈希并放入字典树的 PrefixAwareRouter(HashTrie), 以及向 LMCache 控制器询问“拥有最长匹配 token 的实例”的 KvawareRouter 。

两种前缀路由器的差异很好地展示了该项目的设计考量。 PrefixAwareRouter 是指路由器仅根据 自己发送的记录进行推测。正如类注释中自行表明的“假设前缀缓存不会被逐出(Eviction)”一样,即使引擎删除了块,路由器也无从得知。但它的优点是没有外部依赖。而 KvawareRouter 则向 LMCache 控制器询问 实际存储的位置,因此非常准确,但代价是路由器镜像中必须包含 LMCache 软件包。默认的路由器镜像安装了 pyproject.toml 的可选依赖 lmcache==0.3.11 ,这与引擎侧的版本有所不同,不过双方的控制器消息是否匹配尚未得到确认(未确认)。

Helm Chart 将这些路由器参数原样展开。 deployment-router.yaml 将 routerSpec.routingLogic 传递给 --routing-logic ,而 deployment-vllm-multi.yaml 则为每个 modelSpec 组装 vllm serve 命令。如果启用了 LMCache,则将 --kv-transfer-config 传给 LMCacheConnectorV1 ,并将大小作为 LMCACHE_* 环境变量传入。由于引擎参数是以字符串形式拼接的,如果 vLLM 删除了某个标志,Chart 和教程就会在静默中陈旧,并在执行时崩溃。第 5 节的贡献正是针对这一痛点。

4.2 vLLM ↔ LMCache:名为连接器的洞

vLLM v1 将外部 KV 存储抽象为一个 KVConnectorBase_V1 。该类的核心在于同一个对象是由 两个角色构成的。

为什么要分为两部分?调度器不触碰 GPU 张量,仅负责编排批次,而工作进程直接处理 GPU 张量。两者甚至可能处于不同的进程中。“能加载多少”必须在编排批次前得知,而实际复制则需要与前向传播重叠执行,因此这是一种通过一块元数据将判断与执行连接起来的结构。按层调用的函数旨在计算当前层的同时加载并复制下一层的 KV,以掩盖复制时间。

vLLM 树内的 LMCacheConnectorV1 只是一个薄薄的外壳。

use_native = vllm_config.kv_transfer_config.get_from_extra_config(
    "use_native", False
)
if use_native:
    from vllm.distributed.kv_transfer.kv_connector.v1 import lmcache_integration
    _adapter = lmcache_integration.vllm_v1_adapter
    cls = _adapter.LMCacheConnectorV1Impl
else:
    from lmcache.integration.vllm.vllm_v1_adapter import (
        LMCacheConnectorV1Impl as LMCacheConnectorLatestImpl,
    )
    cls = LMCacheConnectorLatestImpl

self._lmcache_engine = cls(vllm_config, role, self)

来源:vllm v0.31.0 vllm/distributed/kv_transfer/kv_connector/v1/lmcache_connector.py(第 88–108 行,省略日志行),Apache-2.0

默认值为 已安装的 LMCache 软件包中的实现。如果把实现写死在 vLLM 里,每次 LMCache 修改时都得等待 vLLM 发布版本,因此 vLLM 负责接口,而实现则归 LMCache。多进程(MP)连接器也 lmcache_mp_connector.py 中优先查找 LMCache 侧的实现,只有在找不到时才使用树内的副本。

LMCache 侧的实际实现是 LMCacheConnectorV1Impl 。查询从 get_num_new_matched_tokens 开始,经由 lookup 客户端,而 Token 则由 ChunkedTokenDatabase 切分为 _chunk_tokens ,并通过 _prefix_hash 连续拼接前一个分块的哈希来生成键。分块的默认值是 config.py 的 256 个 Token,是 vLLM GPU 块(实测 16 个 Token)的 16 倍。这是因为为了减少远程存储的对象数量和往返开销,分块必须足够大;代价是比 256 个 Token 更短的重叠前缀无法在 LMCache 中命中。

存储层是 CreateStorageBackends 写入 OrderedDict 的 顺序即为查询顺序(在 PD、LocalCPU、P2P、NIXL、LocalDisk、GDS、Maru、远程中已启用的项)。 StorageManager.batched_get 按顺序从前往后查找,在远程找到的内容会重新写入 LocalCPU。远程侧则分化为 storage_backend/connector/ 的 17 个适配器(Redis、Valkey、S3、文件系统、Mooncake 等)。

MP 模式将整个这一层移到了引擎外部。 MPCacheServer 组装了多个引擎模块, MPServerConfig 的默认传输是 ZMQ(端口 5555),分块同样是 256,哈希则是 blake3。这样做的目的是即使引擎重启,缓存也能保留,并允许多个引擎共享使用。

4.3 llm-d:EPP 如何选择 Pod

llm-d Router 分为代理(Proxy)和 EPP。当代理拦截请求并通过 ext-proc 流向 EPP 询问时,EPP 的 StreamingServer.Process 接收并运行调度器,随后 generateHeaders 将选中的 Pod 地址写入 x-gateway-destination-endpoint 请求头中并返回。这种将请求体转发、TLS、重试交由经过验证的代理,而把 LLM 专属判定剥离出来的架构,与在一个 Python 进程中搞定一切的 production-stack 路由器正好相反。

调度器 Scheduler.Schedule 是一个“运行由配置文件处理器选定的配置文件,并根据结果再次进行选择”的循环。单个配置文件在 SchedulerProfile.Run 内部依次通过:过滤器 → 加权打分器(runScorerPlugins) → 挑选器。之所以允许多次运行配置文件,是因为采用了 P/D(Prefill/Decode)分离架构。 disagg 处理器的 Pick 通过解码配置文件首先选择 D Pod,并在需要时通过预填充配置文件选择 P Pod。实际的 P→D 流程由解码 Pod 旁边的路由边车(llm-d-router pkg/sidecar/proxy/)负责,它先将请求发送至 P max_tokens=1 ,并附带收到的 KV 传输信息传递给 D。P/D 分离本身的原理参见 NVIDIA Dynamo 篇。

打分器读取的数值通过两条途径传入。一条是抓取引擎 /metrics 的 datalayer 数据源,其中 parseMetrics 解析 Prometheus 文本,并决定不同引擎类型的标签应将哪些名称映射到哪些属性上。

var defaultEngineConfigs = []engineConfigParams{
	{
		Name:                "vllm",
		QueuedRequestsSpec:  "vllm:num_requests_waiting",
		RunningRequestsSpec: "vllm:num_requests_running",
		KVUsageSpec:         "vllm:kv_cache_usage_perc",
		LoRASpec:            "vllm:lora_requests_info",
		CacheInfoSpec:       "vllm:cache_config_info",
	},

来源:llm-d-router v0.11.0 pkg/epp/framework/plugins/datalayer/extractor/metrics/factories.go(第 91–99 行),Apache-2.0

同样的表格中并列列出了 SGLang、TensorRT-LLM、Triton 的名称,并通过 Pod 标签 llm-d.ai/engine-type 来选择对应的表格。这种设计使得即使引擎更改了名称,也只需要修改这一个表格即可。接收到该值后, KVCacheUtilizationScorer.Score 将 1 − KV 사용률 赋予相应的分数。

另一条途径是 KV 事件。当 vLLM 存储或逐出块时,会通过 BlockStored 发布诸如 ZmqEventPublisher 之类的事件,而 EPP 的 precise-prefix-cache 插件则通过 ensureSubscriber 为每个 Pod 打开订阅。接收到的消息由 VLLMAdapter.ParseMessage 解析并添加或删除块索引。这用引擎告知的事实取代了 production-stack PrefixAwareRouter 的“通过发送记录进行推测”,代价是必须在引擎中开启 --kv-events-config 并打开 ZMQ 端口。

4.4 指标路径:定义中的一行化作三处的查询

메트릭 경로: 정의 한 줄이 세 곳의 쿼리가 된다 vLLM 엔진 · vllm/v1/metrics/loggers.py Counter(name="vllm:prefix_cache_hits") GET /metrics 에서 _total · _created 가 붙어 나간다 ① PS 라우터 engine_stats.py 30초마다 직접 긁음 이름 정확 일치 ② Prometheus ServiceMonitor service-port 포트 PromQL 로 질의 ③ llm-d EPP datalayer 소스 factories.go 이름표 파드마다 폴링 라우터 /metrics 로 같은 접두사 재노출 Grafana 대시보드 JSON 3벌 KV 사용률 점수 = 1 − 사용률 이름이 바뀌면 오류 없이 0 이나 No data 가 된다 옛 이름 v0.31.0 의 이름 gpu_cache_usage_perc → kv_cache_usage_perc gpu_prefix_cache_hits/queries → prefix_cache_hits/queries time_per_output_token_seconds → inter_token_latency_seconds 실측 /metrics(v0.31.0)에 옛 이름은 하나도 없었다
图:指标路径:vLLM loggers.py 中的定义输出为 /metrics,并在 production-stack 路由器解析器、Prometheus/Grafana 仪表盘以及 llm-d EPP 标签这三处按原样读取,如果名称改变,数值就会在不出错的情况下变为空白

图中的 PS 即 production-stack。起点是 PrometheusStatLogger 构造函数,其中 vllm: 指标是一行行定义的。KV 使用率为 vllm:kv_cache_usage_perc,前缀缓存为 vllm:prefix_cache_queries·vllm:prefix_cache_hits,Token 间延迟为 vllm:inter_token_latency_seconds 。源码中的计数器名称中虽然没有 _total ,但由于 prometheus_client 在导出时会附加 _total 和 _created ,因此编写查询的一方必须以 vllm:prefix_cache_hits_total 来读取。这种差异正是导致名称混乱的原因之一。该注册表由 attach_router 挂载到 /metrics 路径上。

vLLM 不输出“命中率仪表盘(Gauge)”,而是输出“查询与命中两个计数器(Counter)”。这是为了让用户能够像 rate(hits[5m]) / rate(queries[5m]) 那样自行计算任意区间的比例(docs/design/metrics.md)。在废弃指标时,遵循 X.Y 废弃预告、X.Y+1 隐藏(通过--show-hidden-metrics-for-version 恢复)、X.Y+2 删除的规则(docs/usage/metrics.md)。引擎侧的规则虽然很明确,但问题在于 读取这些名称的一方位于其他代码库中。

第一个消费者,即 production-stack 路由器的采集器 EngineStatsScraper 默认每 30 秒向每个引擎的 /metrics 发起 GET 请求,并用 from_vllm_scrape 精确匹配名称来提取数据。

for family in text_string_to_metric_families(vllm_scrape):
    for sample in family.samples:
        if sample.name == "vllm:num_requests_running":
            num_running_reqs = sample.value
        elif sample.name == "vllm:num_requests_waiting":
            num_queuing_reqs = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hit_rate":
            gpu_prefix_cache_hit_rate = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_hits_total":
            gpu_prefix_cache_hits_total = sample.value
        elif sample.name == "vllm:gpu_prefix_cache_queries_total":
            gpu_prefix_cache_queries_total = sample.value
        elif sample.name == "vllm:gpu_cache_usage_perc":
            gpu_cache_usage_perc = sample.value

来源:production-stack vllm-stack-0.1.13 src/vllm_router/stats/engine_stats.py(第 63–76 行),Apache-2.0

如果没有匹配的名称,既不会报错也不会记录日志,只会留下 0。路由器会使用 metrics_router.py 的 /metrics 将该值重新输出为带有诸如 之类 vllm: 前缀的仪表盘(server 标签)。因此,相同的名称在引擎和路由器两个来源中仅凭标签不同而混杂在一起,而默认仪表盘的前缀缓存面板则通过 endpoint="service-port" 进行过滤。 service-port 是 图表附加到引擎 Service 端口上的名称,而路由器 Service 端口的名称则是 router-sport ,因此只保留来自引擎的时间序列。

第二个消费者是 Prometheus 和 Grafana。图表的 ServiceMonitor 负责抓取引擎和路由器,且仪表盘 JSON 中的 PromQL 直接硬编码了字符串形式的名称。第三个消费者则是 4.3 节中的 EPP 标签。这三者如果名称写错,都只会默默显示为 0 或“No data”,而不会导致失败。

5. 与用户贡献的关联

作者的四笔 PR 全部体现了“vLLM 删除了某个名称,但其他存储库仍在调用它”的相同模式(状态截至 2026-10-17)。本文重新打开了 v0.31.0 标签,确认了“现在已无此项,取而代之的是这个”。

PR 修改位置 对应的 vLLM v0.31.0 行 状态
vllm-project/production-stack#1147 helm/dashboards/vllm-dashboard.json:826 “GPU KV Cache Hit Rate”面板的 vllm:gpu_prefix_cache_* loggers.py:600 vllm:prefix_cache_queries, loggers.py:611 vllm:prefix_cache_hits 待审(自动审查为「无修改点」)
vllm-project/production-stack#1148 tutorials/23-whisper-api-transcription.md:24 的 --task transcription --task 不复存在,取而代之的是 arg_utils.py:930 的 --runner·--convert 接替 待审
LMCache/LMCache#5814 12 个文档、基准测试和示例文件(v0.5.6 标签中的 20 处)的 --disable-log-requests arg_utils.py:3141 中只有 --enable-log-requests ,而 BooleanOptionalAction 的反义词是 --no-enable-log-requests 待审(base dev)
llm-d/llm-d#2835 文档的 vllm:time_per_output_token_seconds, vllm:gpu_prefix_cache_hit_rate loggers.py:832 vllm:inter_token_latency_seconds,命中率是第 600 行和第 611 行两个计数器的比例 待审

#1147 仅修复了仪表板,而路由解析器(engine_stats.py:69–76) 的同类问题正如 PR 正文所述,由 #1140 和 #1146 分别处理。llm-d#2835 基于 main 分支,因此在 v0.10.0 标签中,相同的句子位于 docs/architecture/advanced/autoscaling/wva.md:144·210줄 和 DigitalOcean 仪表板 1544줄 附近,而第三个修复的指南(turn-priority-fairness)在 v0.10.0 中尚不存在。路径不同的原因未作确认(未确认)。

6. 实测

6.1 测量内容与方法

于 2026-10-16 在小型集群的一个 GPU 节点(8GB 级别笔记本 GPU)上运行一次性 Pod 进行了测量。测量脚本(pod.sh) 固定节点、仅使用现有镜像(imagePullPolicy: Never)、阻止访问 Hugging Face,并给予 3.5Gi 内存限制和 90 分钟超时。模型使用了仅创建了配置文件、具有 随机权重 Qwen2 结构(2 层,hidden 256, --load-format dummy)的模型。镜像是 vllm/vllm-openai v0.31.0(PyTorch 2.13,CUDA 13.0),在发送了前 160 个 token 相同的 5 个请求后,获取了 /metrics 。由于未启动路由器、LMCache 和 EPP,因此 6.3 节是将从各标签源码中提取的名称与此输出进行匹配的 静态对比。

6.2 /metrics 输出规模

项目 值
总行数 587
# TYPE 系列 88 (vllm 68, http 10, process 6, python 4)
vllm: 系列中的 _created 辅助 Gauge 31
vllm: 实际指标 37(Counter 15,Histogram 16,Gauge 6)
vllm:prefix_cache_queries_total / _hits_total 805 / 640 → 命中率 0.795
vllm:inter_token_latency_seconds_count 75
vllm:cache_config_info block_size="16", num_gpu_blocks="144272", gpu_memory_utilization="0.3"
旧名称(gpu_prefix_cache*, gpu_cache_usage_perc, time_per_output_token_seconds) 0 个

数字完全吻合。5 个请求 × 161 个 token = 805 次查询,第一个请求全部未命中,其余四个请求各命中 160 个 token(10 个块),共计 640。由 16 个 token 组成的 5 个请求,其 token 间的间隔为 15 × 5 = 75。144,272 个块 × 16 = 2,308,352 也与同行的 kv_cache_size_tokens 相同(因为模型非常小,所以块很多)。

6.3 消费者查找的名称是否存在于输出中

消费者(标签源码) 查找的 vllm: 名称 输出中是否存在 不存在及原因
production-stack 路由解析器 engine_stats.py 6 2 4: gpu_prefix_cache_hit_rate·_hits_total·_queries_total·gpu_cache_usage_perc,v0.31.0 源码中未定义
production-stack vllm-dashboard.json 13 9 4: current_qps·healthy_pods_total 是路由器自身的指标(正常), gpu_prefix_cache_* 这 2 个是已消失的名称(#1147)
production-stack vllm-model-metrics-dashboard.json 21 21 0
llm-d-router EPP 默认 vLLM 标签 5 4 1: lora_requests_info,启用 LoRA 才会出现的条件指标(loggers.py:993)
llm-d v0.10.0 文档与指南全集 71 40 31: 27 个是启用卸载(Offload)或 NIXL 连接器才会出现的条件指标,4 个在 v0.31.0 源码中未定义

llm-d 文档中「无定义」的 4 个是 time_per_output_token_seconds 系列 3 个(由 #2835 修复)和 vllm:kv_offload_tiering_block_hits_total 1 个。最后一个留作第 8 节的贡献候选。

6.4 消失的标志

命令 结果
vllm serve <작은 모델> --disable-log-requests unrecognized arguments,退出代码 2
vllm serve <작은 모델> --swap-space 0 unrecognized arguments,退出代码 2
vllm serve 从完整帮助信息中提取的标志列表(约 400 个) --task·--disable-log-requests·--swap-space 无, --enable-log-requests·--no-enable-log-requests·--runner·--kv-transfer-config·--kv-events-config·--show-hidden-metrics-for-version 有
--task 在没有 vllm serve 的情况下,以随机权重的 Whisper 结构运行 Supported tasks: ['transcription'], /v1/audio/transcriptions 日志

HTTP 200lmcache/vllm-openai:latest同一天读取的 Chart 默认引擎镜像( loggers.py )是 vLLM 0.29.0 和 LMCache 0.5.5,其中 gpu_prefix_cache 中也没有 。即使保持 Chart 默认值不变 ,路由解析器和前缀缓存面板也会显示 0 或 No data。并非实际安装 Chart 并查看了 Grafana(未实测)。

7. 从何处开始阅读

  1. vllm/v1/metrics/loggers.py 的 PrometheusStatLogger 构造函数自上而下浏览。引擎统计的内容是其余所有内容输入。
  2. vllm_router/app.py 的 initialize_all 开始,沿着 request.py 的 route_general_request 追踪单个请求到底。
  3. routing_logic.py 中对比 RoutingInterface 和两三个实现。结合测试(src/tests/test_*_router.py)一起看能更快理解预期行为。
  4. kv_connector/v1/base.py 读完最上方的注释(调度器侧和工作进程侧的函数列表)后,跳转到 LMCache vllm_v1_adapter.py 中同名函数。
  5. llm-d 使用 docs/architecture/core/router/README.md 统一术语,代码则查看 llm-d-router scheduler.go 和 factories.go 。

8. 适合进行首次贡献的地方

这是阅读源码时发现的。由于未确认是否已有开启的 PR 或 Issue,提交前必须先进行搜索。

  1. production-stack 各云厂商仪表盘副本。 Helm Chart 仪表盘正在通过 #989 和 #1147 进行修复,但 tutorials/terraform/{coreweave,eks,nebius}/config/ 的 vllm-dashboard*.json 四个版本中, vllm:gpu_cache_usage_perc 和 vllm:gpu_prefix_cache_* 均剩余 10 处(例如: eks/config/vllm-dashboard.json:732).
  2. production-stack 基准测试教程中的 --swap-space 0. 07-benchmark-multi-round-qa-single-gpu.md:51 等,07 和 08 号教程的四个地方会产生 extraArgs: ["--swap-space", 0] 。v0.31.0 会在此标志处以退出码 2 停止(第 6.4 节)。
  3. llm-d P2P 指南中的指标名称。 guides/p2p-kv-cache-sharing/benchmarking/README.md:129 使用了 vllm:kv_offload_tiering_block_hits_total ,但在 v0.31.0 中仅定义了 vllm:kv_offload_tiering_chunk_hits 。必须首先确认名称是否在指南依赖的 vLLM 版本(vllm#48798 之后)中发生了更改。
  4. KvawareRouter 的聊天请求。 route_request 仅对 request_json.get("prompt", "") 进行了分词,而将 TODO: Handle chat completions 留下了。 /v1/chat/completions 请求预计会被视为带有空提示词的查询,从而降级为会话和 QPS 路由。可以参考同一文件 PrefixAwareRouter 中的 messages 处理。
  5. 默认仪表盘中的混杂时间序列(未确认)。 「Number of Running Requests」面板绘制了不带标签的 vllm:num_requests_running 。由于路由器也会输出相同的名称(第 4.4 节),因此这两个时间序列可能会被绘制在一起。必须先查看实际屏幕。

项目指南:对于重大更改,production-stack 要求在 proposals/ 中首先提交提案,并且提交需要包含 DCO Signed-off-by 。LMCache 的默认分支是 dev,必须包含 DCO,并且包含新手友好 Issue 集合(LMCache#627),同时在 AGENTS.md 和编码规范中设立了「AI 生成代码信号」审查项。llm-d 规定以相同的标准接受使用 AI 的贡献,但提交者必须能够亲自解释内容。作者的所有 PR 都在正文末尾说明了 AI 的协助情况。

9. 局限性

Teety 的话。 厨房明明没变,仅仅是菜单名字变了,大堂经理就会端出空盘子,而不是说「没有这道菜」。因此,修复一个名称的 PR 能够堵住比想象中更大的漏洞!

参考

所有访问日期均为 2026-10-17。

引用的代码:vLLM、production-stack 和 llm-d-router 均采用 Apache License 2.0。

登录后即可点赞

评论

还没有评论。

登录后即可发表评论