第 21-3 篇:内嵌 HTML——gen_embedded_web.py 怎么把页面塞进二进制
上一篇21-2《对话模板与角色注入》 下一篇22-1《chat.html前端如何消费 SSE》本地级验证主体 板端补充生成器确定性在本地 Windows 复跑通过板端仅页面 GET 直测口径见文内一句话导读内嵌 HTMLgen_embedded_web.py 怎么把三个网页编译成 C 字节数组塞进单一可执行文件本篇要解决生成器确定性问题——改一句 HTML 重新生成产物 hash 必须逐字节不变。关键词内嵌 HTML、gen_embedded_web、C 字节数组、确定性、RK35881. 知识点内嵌资源的两种姿势管理页、对话页、转换页都是网页——但引擎的交付物是一个可执行文件单文件发布、零额外部署。网页从哪来答案tools/gen_embedded_web.py把三个 HTML 编译成 C 字节数组塞进二进制运行时由内置 HTTP 直接吐出。这篇讲生成器的确定性与体积账并实测改一句 HTML 再重新生成产物 hash 变不变——不变才是安全的构建。把网页塞进 C 程序常见两种文件旁挂exe 旁边放 admin.html运行时读文件——部署目录要同步多个文件单文件发布就破了字节数组内嵌构建期把 HTML 转成0xXX,0xXX,…的 C 数组运行时按需取出用Content-Type与长度字段回 HTTP——exe 自包含但每次改 HTML 都要重新生成 重新编译。引擎走的是 2gen_embedded_web.py第 9–11 行注释把话说透引擎在 HTML 未随 exe 部署时使用内嵌副本交付物是单一自包含文件。这个生成器必须有两条纪律确定性同一组 HTML 输入永远产出逐字节相同的.c/.h——否则每次构建都在抖二进制 hash 无法审计Day 17 的位级一致思想在这里复用可追溯生成文件头部写明generated by tools/gen_embedded_web.py — DO NOT EDIT BY HAND防止有人手改生成物后源码与产物失联。2. 对应代码生成器长什么样tools/gen_embedded_web.py全流程就 40 来行核心逻辑FILES [(admin, admin.html), (chat, chat.html), (convert, convert.html)] for name, fname in FILES: # 读 HTML data open(os.path.join(SRC, fname), rb).read() arrays.append((name, fname, data)) # 写 include/serve/embedded_web.h声明 embedded_name_html(size_t*) # 写 src/serve/embedded_web.c字节数组 长度函数输出.c就是长这样的一行行0xXX,0xXX,…to_c_bytes每 16 字节折行。生成的embedded_web.c参与 CMake 构建HTTP 层按 URL/admin/、/chat、/convert取对应数组配Content-Type: text/html; charsetutf-8返回。改页面流程 改 HTML → 跑生成器 → 重编三步缺一不可。3. 改动后果再生成的确定性实测实测口径本地 Windows 复跑生成器不动板端对src/serve/embedded_web.c与include/serve/embedded_web.h取 sha256 前后对照2026-09-07。[gen] before: embedded_web.c 615481abfc8f52bc embedded_web.h 8246a8a1dc1a6cc4 [gen] rc0 wrote embedded_web.h and embedded_web.c (81668 bytes total) [gen] after : embedded_web.c 615481abfc8f52bc embedded_web.h 8246a8a1dc1a6cc4 [gen] deterministic: YES同输入 → 同产物hash 逐位不变。这与 Day 17 的权重转换是同一个思想构建工具必须是纯函数产物才能进审计账本。反向推论也成立如果你改了 admin.html 却忘记跑生成器二进制里跑的还是旧页面——这是生成物 vs 源码失联的经典事故DO NOT EDIT BY HAND头注释就是防这个的。板端 serve 实际吐出的三个页面GET直测/admin/ → 200 text/html 44,683 B /chat → 200 text/html 26,168 B /convert → 200 text/html 10,774 B三个页面合计约 81KB相对引擎 818KB 的可执行体是零头——内嵌网页的代价就是这几万字节换来的是单文件分发scp 一个文件就部署完连 HTML 都不用带。4. 学员调试任务A 档本地动手在src/serve/admin.html里加一句可见改动如页面标题加个版本号跑生成器确认.c的 sha256 变了、且只变了与那段 HTML 对应的字节区域用 diff 定位把生成器改坏比如to_c_bytes从 16 字节折行改成 8 字节跑两次确认产物 hash 仍稳定——确定性来自算法本身不来自折行参数对照 21-3 §3 的页面尺寸在板端 serve 上 GET 三个页面并核对字节数是否与内嵌一致/admin/的 44,683 应该正好等于 admin.html 长度。B 档纯读源码读gen_embedded_web.py与 HTTP 侧取内嵌页的代码回答① 三个页面为什么用名字 size_t*出参的函数签名而不是一个大数组提示每个 URL 只要自己那份Content-Length要对② 若有人手工改了embedded_web.c而没改 HTML 源构建会不会报错怎么在设计上防止这种漂移③ 把 HTML 压成 gzip 内嵌、运行时解压能省多少体积、会引入什么新依赖对照零依赖红线想预期输出一次改 HTML → 再生 → hash 变化点定位的完整记录并能回答为什么确定性生成器是可审计构建的地基。收尾本篇源码点名gen_embedded_web.py确定性生成、embedded_web.c生成物、vllm_server.c页面路由开源仓库Kestrel-LLM (Gitee)AGPL-3.0-or-later 或商业许可二选一下篇预告页面会出了页面背后的管理 API 呢Day 22 讲/admin/api/*状态、配置、加载、转换四组接口以及vllm_mgr.py怎么把引擎当进程看起来。