首页
/
行业洞察
/
正文
INDUSTRY INSIGHT · 深度
SGLang Scripted Runtime 开发指南:面向调度器测试的 Harness API 设计规范
📅 2026/9/10 9:19:58
✍️ 爱科研究院
👁 阅读 3,247
SGLang Scripted Runtime 开发指南面向调度器测试的 Harness API 设计规范【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang导读本文基于 SGLang 仓库中.claude/skills/scripted-runtime-notes/SKILL.md展开系统讲解 SGLang Scripted Runtime脚本化运行时测试基础设施的设计哲学——什么情况下才应该为它新增 Harness API、什么情况下不应该。Scripted Runtime 允许测试以生成器脚本yield方式逐步驱动真实调度循环测试代码直接读取r.req.*与t._scheduler.*字段而非依赖封装层。读者将掌握三条 API 新增判据控制原语 / Hook 支撑 / 多结构派生、反模式清单削弱断言、探测实现细节、直接调用调度器私有方法以及如何通过yield推进引擎自身驱动的行为超时、空闲检测等。Scripted Runtime 是什么无封装边界的调度器测试通道Scripted Runtime 是 SGLang 测试体系中专为调度器scheduler行为验证设计的一套脚本化运行时。与常规的端到端 HTTP 测试不同它让测试脚本以生成器generator的形式编写脚本中每个yield让调度循环前进一个 step测试可以像操作真实引擎一样启动请求、暂停生成、中止请求、驱逐 radix 缓存、耗尽 KV 池。它的核心设计前提是Tests readr.req.*andt._scheduler.*directly — there is no encapsulation boundary. A thin wrapper buys zero isolation; it only grows the surface.即测试直接读取r.req.*请求句柄上的调度器 Req 对象字段和t._scheduler.*ScriptedContext 暴露的 Scheduler 内部结构刻意不设置封装边界。因为薄封装不带来任何隔离价值只会扩大 API 表面积、增加维护成本。从源码结构看Scripted Runtime 的完整实现分布在 python/sglang/test/scripted_runtime/ 目录下核心组件包括context/api.py—ScriptedContext脚本入口封装全部公开操作与查询scheduler_hook.py—ScriptedSchedulerHook挂载进真实调度器、驱动脚本生成器执行req_handle.py—ScriptedReqHandle指向某个请求的轻量句柄rid contexthttp_server.py—ScriptedHttpServer承载脚本执行会话tokenizer_recv_proxy.py— tokenizer 接收代理用于 Hook 支撑类 API 的数值累计。它通过scheduler.maybe_init_scripted_scheduler_hook()惰性挂载见 scheduler.py并在每个 batch 执行前回调scripted_scheduler_hook.on_run_batch(batch)见 scheduler.py从而让脚本能观测每一轮 forward 的 batch 构成。何时该新增 Harness API三条判据SKILL.md 给出了一个决策框架只有当 API doing real work 时才新增并列举了三种合规类型。判据一控制原语Control primitiveAPI 必须通过一条真实路径驱动引擎例如start_req— 发起请求pause_generation— 暂停生成abort/abort_all— 中止请求evict_radix— 驱逐 radix 缓存exhaust_kv— 耗尽 KV 池制造压力。判据要求复用真实路径绝不手工篡改状态never hand-mutate state。也就是说实现这些 API 时应当走真实的控制请求通道而不是直接修改调度器内部字段。这一点在 context/lifecycle.py 中得到印证pause_generation、continue_generation、abort_all、abort、flush_cache全部通过_await_control向真实 HTTP 端点/pause_generation、/continue_generation、/abort_request、/flush_cache发送请求并等待对应控制消息PauseGenerationReqInput、ContinueGenerationReqInput、AbortReq、FlushCacheReqInput到达后再返回确保控制指令确实被调度器消费而不是发完即忘。start_req的完整参数签名见 context/api.py支持prompt_len、max_new_tokens、rid显式请求 ID、ignore_eos、priority、dp_rank、prompt_token、return_logprob、logprob_start_len、top_logprobs_num、stop_token_ids、temperature、lora_path等参数。从 test_scripted_runtime_core.py 的用例可以看到这些参数的实际语义不传rid时自动生成以scripted-开头的 ridtest_start_req_auto_rid_and_finishespriority会真实传播到调度器 Req 的req.priority字段test_start_req_priority_is_propagatedignore_eosTrue时即使模型输出 EOS 也会解码满max_new_tokens长度test_start_req_ignore_eos_runs_full_length。判据二Hook 支撑Hook-backed如果某个值无法从快照中直接读取需要通过以下两种机制之一累计得到scheduler_hook.on_run_batch— 在每个 batch 执行时被真实调度器回调scheduler.py可用于记录 forward 轮次、batch 模式、rid 集合等recv proxychunks_done— 通过 tokenizer_recv_proxy.py 拦截 tokenizer 接收消息来累计数值。这类 API 必须是只读的绝不 monkey-patch绝不运行时篡改调度器行为绝不向srt/生产代码添加*_count之类的计数器字段。从scheduler_hook.py的实现看on_run_batch只做记录将forward_iter、forward_mode、rids、chunked rid 追加到_batch_logScriptedBatchRecord不修改任何调度器状态。chunks_done则用于回答某个请求的 prefill 被切成了几块这类无法从快照推导的问题——测试 test_scripted_runtime_core.py 中test_chunks_done_zero_for_unchunked_prompt、test_chunks_done_counts_two_chunks、test_chunks_done_scales_with_prompt验证了chunk_size与chunks_done的换算关系如5 * chunk_size的 prompt 恰好产生 5 块。判据三多结构派生、被广泛复用Multi-structure derivation, widely reusedAPI 需要同时扫描多个调度器内部结构才能回答查询并且会被大量测试复用例如is_idle/is_fully_idle— 判断引擎是否空闲status— 查询请求状态unknown/running/finished等batch_composition— 返回prefill/decode/chunked/running四类 rid 集合。以 context/queries.py 中的batch_composition为例它同时读取scheduler.chunked_req、scheduler.running_batch、scheduler.last_batch及其forward_mode才能把请求归入 prefill / decode / chunked / running 四个互不相交的子集。_get_all_reqs则横跨chunked_req、waiting_queue、running_batch含 PP 流水线下的mbs/last_mbs/running_mbs等多个结构。之所以要求被广泛复用是因为多结构扫描逻辑一旦内联进单个测试很容易写错且难以维护只有真正高频复用才值得提炼为公共 API。不满足任何判据别加如果 API 不符合上述三条判据那么不要新增——直接在测试里读取r.req.X/t._scheduler.X或者内联一次性的单用途 accessor。反模式清单两类绝不允许的行为SKILL.md 明确了两条Never铁律绝不为缺失的探针而削弱断言Weaken an assertion to fit a missing probe.如果某个断言由于缺少探针probe而无法通过正确做法是补一个能支撑断言的探针按上述判据而不是把断言改弱。例如在 test_scripted_runtime_core.py 中test_abort_single_handle_finishes_with_abort_reason断言被中止的请求最终携带FINISH_ABORT的finished_reason来自 schedule_batch.py 的FINISH_ABORT这就是断言结果而非过程的正面案例——它验证的是中止请求的可观察后果。绝不去探测实现细节Probe implementation details (field non-None, which branch ran) — assert the consequence.两类典型的实现细节探测断言某个字段非 None如field is not None——这只是在确认内部结构形状而非用户可观察行为断言走了哪个分支——这会把测试与实现强耦合实现一重构测试就碎。正确做法是断言行为的后果consequence。例如test_flush_cache_clears_radix_tree没有去探测flush_cache内部执行了什么分支而是断言flush_cache之后get_all_node_hit_counts()为空——验证radix 树被清空这一结果test_get_all_node_lock_refs_held_during_run_released_after断言请求运行期间 radix 节点lock_ref 1、完成后归零同样是验证锁引用的持有/释放这一可观察结果。其他要点引擎自身驱动的行为SKILL.md 最后一条提示针对引擎自身驱动engine-self-driven的行为这是最容易踩坑的地方Never synchronously call a scheduler private (e.g.scheduler._abort_on_waiting_timeout()) from the harness/test.即绝不要从 harness 或测试中同步调用调度器的私有方法如scheduler._abort_on_waiting_timeout()原因有三错误的循环相位私有方法往往被设计在调度循环的特定阶段调用同步调用会发生在错误的 loop phase绕过注入通道直接调用会绕过有序的recv_requests→process_input_requests注入链路这条链路定义在 scheduler.py 及 request_receiver.py 中导致控制消息与请求处理失序可能触发真实循环永远不会达到的状态例如在引擎已暂停paused期间调用超时处理会执行到真实运行中不可能出现的状态组合。对于引擎自身负责驱动的扫描行为如等待超时timeout、空闲检测idle正确姿势是启用对应的配置或环境变量然后通过yield推进调度循环让真实循环在正确的时机自行触发。这正体现在scheduler_hook.py的_drive_engine_through_warmup中它不断yield并轮询proxy.work_reqs_seen与scheduler.is_fully_idle()把服务端 warmup 请求驱动到完全排空且针对 PP流水线并行场景要求空闲状态连续保持2 * (pp_size pp_async_batch_depth)个 microbatch 轮转避免瞬时假空闲——全程没有调用任何调度器私有方法。实战示例一段完整的脚本化测试将上述规范落到实际一段典型的脚本化测试如下模式来自 test_scripted_runtime_core.pyfrom sglang.test.scripted_runtime.context import ScriptedContext from sglang.test.scripted_runtime.test_case import ScriptedTestCase from sglang.test.scripted_runtime_chunked_helpers import ( advance_to_decode_step, run_until_finished, base_engine_kwargs, ) class TestScriptedRuntimeDemo(ScriptedTestCase): ENGINE_KWARGS base_engine_kwargs(chunked_prefill_size64) def test_pause_retract_parks_in_waiting_queue(self): self.server.execute_script(self._script) staticmethod def _script(t: ScriptedContext): r t.start_req(prompt_len16, max_new_tokens8) yield from advance_to_decode_step(r, 1) # 推进到 decode 第 1 步 t.pause_generation(moderetract) # 控制原语真实路径 yield assert r.req in t.scheduler.waiting_queue # 断言可观察后果 t.continue_generation() yield from run_until_finished(r) assert r.finished关键点回顾测试类继承ScriptedTestCase必须设置非空的ENGINE_KWARGSsetUpClass会启动ScriptedHttpServer会话见 test_case.py脚本是一个静态生成器函数yield推进调度循环yield from复用辅助推进器断言的是行为后果请求被 park 到 waiting_queue、最终 finished而不是调度器内部字段是否非 None。会话管理方面ScriptedHttpServer保证shutdown()幂等可安全调用多次并在会话被标记为 dirty 时拒绝执行脚本——这些都是 http_server.py 提供的运行期保障。小结API 新增决策速查场景是否新增 API依据需要通过真实路径驱动引擎启停请求、中止、驱逐、耗尽 KV✅ 新增控制原语判据一复用真实路径、禁止手工改状态数值无法从快照读取需on_run_batch或 recv proxy 累计✅ 新增只读 Hook API判据二禁止 monkey-patch、禁止向srt/加*_count需同时扫描多结构且被广泛复用is_idle/status/batch_composition✅ 新增派生查询判据三单次使用则内联仅读取单个字段、一次性使用❌ 直接在测试中读r.req.X/t._scheduler.XElse: dont 原则断言无法通过❌ 绝不削弱断言补一个能支撑断言的探针想确认字段形状 / 分支走向❌ 绝不断言实现细节改为断言行为后果需要触发超时 / 空闲等引擎自驱动行为❌ 绝不调用调度器私有方法启用配置 yield推进真实循环这套规范的核心可以概括为一句话Scripted Runtime 的价值在于通过真实调度路径驱动引擎、并断言可观察后果任何绕过真实路径的私有调用、任何为迁就探针而削弱的断言都会让测试失去验证意义。按照三条判据取舍 API测试既能保持对调度器内部结构的直接可见性又不至于把 harness 变成第二个需要维护的影子调度器。【免费下载链接】sglangSGLang is a high-performance serving framework for large language models and multimodal models.项目地址: https://gitcode.com/GitHub_Trending/sg/sglang创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
📌 标签:
工业官网
设计趋势
AI 建站
SEO
获取完整报告 →
RELATED ARTICLES
推荐阅读
2026/9/10 9:19:58
Glycoengineering 糖基化工程实战指南:N/O-糖基化位点扫描、突变设计与治疗性抗体优化(scientific-agent-skills 仓库实践)
2026/9/10 9:19:58
5 分钟上手 Semgrep:新手从零跑通第一次静态代码扫描指南
2026/9/10 9:14:58
Lab Hardware CAD 制造极限指南:为科研硬件选对工艺、公差与材料
2026/9/10 9:55:08
毕业第一份工作是店群运营:00后的验证码启蒙课
2026/9/10 9:55:08
ToolJet Database Editor:可视化建表、CSV 批量导入与数据过滤的完整实践指南
2026/9/10 9:55:08
server status
2026/9/10 9:55:08
用户隐私协议与模型服务协议
2026/9/10 9:55:08
TiDB Plugin Framework 插件框架设计解析:从 Go Plugin 到可热升级的 SPI 插件体系
2026/9/10 9:50:08
在 Qwik 中使用 daisyUI:组件库接入指南与源码原理分析
2026/9/10 0:04:20
AI搜索的信任缺口:企业内容如何在答案时代自证可信
2026/9/10 0:04:20
Spring Boot+Vue+Node.js售后服务系统开发实战
2026/9/10 0:04:20
SpringBoot+Vue民宿预订管理系统开发实践:从架构设计到部署上线
2026/9/10 2:30:52
超人会飞不算本事:系统稳定依赖清晰规则与边界设计
2026/9/10 5:51:31
超人VS蜘蛛侠:拆解超级IP的影响力与传播方法论
2026/9/10 8:32:02
基于CNN的调制信号识别:MATLAB实现时频图分类实战