LOCAL MODEL LAB / 学习入口

用 ComfyUI 完成同一个训练实验

这是到手价助手的第二条操作路线,复用原模型、数据规则、奖励和评估标准。使用 ComfyUI 组织实验,MLX 执行真正的 SFT/GRPO,最终模型由 MLX-LM HTTP 服务提供。

本次交付是完整的课程与桥接接口设计。PriceLab* 节点和 worker API 尚未实现/安装,附带 blueprint 不是可导入或可提交的 ComfyUI workflow。 实现并验证节点后,才能按下述路线在画布上执行。原 命令行手册 仍可作为后端实现依据。

本机能力核实

2026-10-10 只读探测:ComfyUI 0.35.0 在 127.0.0.1:8188 运行,使用 MPS,Python 3.12.10;探测时队列为空。当前注册的 TrainLoraNode 来自 comfy_extras.nodes_train,输入包含 MODEL/LATENT/CONDITIONING,损失选择是 MSE 等,不能直接拿来训练本课的 Qwen 文本模型。未发现对应的 Qwen SFT/GRPO 桥接节点。

这只描述当前安装状态;comfy-cli 的更新检查超时,未据此断言最新生态中没有其他方案。现有实例未升级、重启、安装节点或提交训练任务。

学习顺序与阶段门槛

课 本课只练一个能力 实作前置条件
0002 · 画布与训练的边界 读懂三种任务状态 可以立即阅读,不需要安装节点
0003 · 画布上的 SFT(后续实作) 判断一次 SFT 产物能否进入下一阶段 基础课理解、数据检查通过、桥接节点与环境冒烟完成
0004 · 画布上的 GRPO(后续实作) 确认 RL 从正确的 SFT 产物继续 SFT artifact 可重载、奖励反例检查通过
0005 · 训后模型服务(后续实作) 证明请求经过正确版本的模型 最终模型 artifact 已保存并可重载

课程预制不代表学习者已完成。实际教学根据答题与训练证据调整;没有新增 learning record。

三个进程,各有自己的工作

进程 建议地址/环境 职责 完成证据
ComfyUI 现有 127.0.0.1:8188,现有 .venv 画布、参数、提交/查询/预览节点 prompt_id、history、节点 ui 回读
PriceLab worker(待实现) 拟定 127.0.0.1:8091,课程 .venv 持久化任务、调用 MLX 子进程、收集训练产物 job_id、日志、更新证据、artifact manifest
MLX-LM 模型服务 127.0.0.1:8088,课程 .venv 加载最终模型,响应推理请求 加载路径/hash、健康、模型 ID、完整响应

桥接节点只需常规 Python HTTP/JSON 能力,不导入 MLX,不把 PyTorch tensor 传到 MLX。不同 Python 进程可以用不同版本;同机进程仍共享物理内存。训练期间避免同时运行重的生成或其他模型服务。

worker 用操作枚举构造 argv,调用固定的课程 Python/CLI;训练依赖装在课程环境。MLX 是否使用 GPU,以该进程自己的设备输出为准,不由 ComfyUI 的设备标签证明。Python 子进程是 MLX 的执行载体,不是另一套学习算法。

把大图拆成四个阶段

工作流图传递 artifact/job 引用,不传递大型模型权重。下面的节点都是本课程拟定的名称。

A. 数据与原始模型基线

PriceLabExperiment ──→ PriceLabPrepareData ──→ 数据 manifest
        │
        └────────────→ PriceLabPrepareModel ─→ 原始模型 artifact

数据 artifact + 原始模型 artifact
        └────────────→ PriceLabSubmit(operation=eval)
                            └→ JobReadback → Preview

先执行 lab.py self-check,再生成带 seed/hash 的数据,确认分区不交叉。模型转换使用原有 mlx_lm.convert。准备数据/模型也属于可能较长的 worker 任务,不在节点里阻塞下载。

上图是阶段关系,不是一次 Queue 就跑完的图。Prepare 返回 job;任务成功后,在另一张读取图中分别用 PriceLabDataRead 和 PriceLabArtifactRead 得到数据与模型引用,再提交 eval。

B. SFT

Experiment + DataArtifact + OriginalModelArtifact
        → PriceLabSubmit(operation=sft, iters=5)
        → JobReadback → Preview

完成后另一次 Queue:job_id → PriceLabArtifactRead
        → SFT adapter artifact → fuse job → 融合 SFT artifact → 验证集评估

先 5 步冒烟。通过后用新的 experiment/request_id 做正式 300 步起点实验。后端复用 assets/sft.yaml:batch 1、rank 8、最后 8 层、mask prompt、序列上限 512。配置数值是实验起点,不是最佳参数。

SFT job 收集有限 loss、真实 optimizer update、可训练参数变化、adapter/config 与正确原模型组合的重载证据;另一个 fuse job 产生融合模型并核验重载。只产生一个文件或退出码 0 不能证明发生了学习;独立正确率提升还需另评估。

C. GRPO

Experiment + RLDataArtifact + 已验证的 SFTArtifact
        → PriceLabSubmit(operation=grpo, iters=5)
        → JobReadback → Preview

完成后:PriceLabArtifactRead → GRPO adapter artifact → fuse job
        → 融合 GRPO artifact → 验证/三阶段评估

parent artifact 必须是融合并核验的 SFT 模型;固定参考模型也指向同一 SFT artifact。后端复用社区 mlx_lm_lora.train、assets/grpo.yaml 与 assets/rewards.py,并显式提供原 system prompt。正式起点是 100 步,先用 5 步检查。

同题生成 4 份答案、逐条严格评分、记录组内奖励和优势,然后真实更新 LoRA。worker 的 GRPO adapter 与融合产物分别保存。节点里展示 reward 数值不等于完成 GRPO。

实施时必须增加 rollout 留证: 原 CLI 起步配置不自动保证保存全部采样明细。worker/callback 需要保存 prompt、completion、reward、组 ID、advantage、update step 和 KL 摘要。缺少这一步时只报告“训练命令执行”,不能声称已验证完整 RL 链路。

D. 训后服务与 ComfyUI 调用

FinalModelArtifact → PriceLabServiceStart → ServiceReadback → Preview

ReadyService + 订单输入 → PriceLabPriceRequest
                         → 原始回包 + 严格评分 + Receipt → Preview

服务启动在独立 worker 子进程中进行,不把永久运行的 server 放进一个永不结束的 ComfyUI 节点。Start 返回 service_id 和 starting;后续 Readback 查健康、版本和加载记录,并完成一次固定 artifact 路径的真实暖机推理,才得到 ready。

训练图与推理图分开。普通订单查询只调用已有模型服务,不能回溯触发 SFT/GRPO。API 失败时保存错误,不用 oracle 悄悄替代模型答案。

节点输入输出契约

拟定节点 关键输入 返回类型 阻塞/缓存约定
PriceLabExperiment experiment_id、工作区、模型、seed、配置引用 PRICE_EXPERIMENT 纯配置;相同输入可缓存
PriceLabPrepareData / PrepareModel experiment、request_id PRICE_JOB 异步提交;worker 按请求幂等
PriceLabSubmit operation、parent/data artifact、request_id、参数 PRICE_JOB 返回已接受任务,不等待全部训练
PriceLabJobReadback job_id、refresh PRICE_JOB_REPORT 每次 Queue 读取真实状态,不能读旧缓存
PriceLabDataRead completed prepare_data job_id、artifact_id PRICE_DATA_ARTIFACT 检查数据 manifest、分区与 hash;缺失时失败
PriceLabArtifactRead completed job_id、artifact_id PRICE_MODEL_ARTIFACT 核验 manifest、文件 hash、parent 与重载证据;缺失时失败
PriceLabServiceStart final artifact、request_id、port PRICE_SERVICE_REF 返回 starting,不代表可推理
PriceLabServiceReadback service_id、refresh PRICE_SERVICE_REPORT 健康、版本与实际暖机推理通过后才 ready
PriceLabPriceRequest ready service、订单、request_id PRICE_RECEIPT 真实调用模型服务,保留完整原始输出
PriceLabServiceStop / JobCancel 已归属的 service_id/job_id PRICE_CONTROL_RESULT 控制自己创建的进程;记录结果
PriceLabPreview JSON 文本(STRING) 无,OUTPUT_NODE ui + history 输出,不修改状态

这些是自定义类型中的小 JSON 引用,不是 ComfyUI 的 MODEL、LATENT 或 LORA_MODEL。参考 官方节点属性 和 数据类型。实现时可用 V1 schema,官方当前仍支持。

JobReadback、DataRead、ArtifactRead、ServiceReadback 和 PriceRequest 均另返回一个 STRING JSON 显示输出,供 Preview 连接;各自的专用类型输出供下游逻辑连接。字段校验仍由节点与 worker 执行,类型名本身不证明内容有效。

MLX-LM 0.32.0 的 /health 检查生成线程可用性,/v1/models 列出可用模型;二者单独不能证明目标权重已加载。Readback 使用 artifact 的绝对路径作为请求 model,保存一次暖机原始回包与启动记录的关联,再标 ready。暖机只检查推理可用,不以答案正确作为服务 ready 的条件。PriceRequest 固定使用这一 artifact 对应的 model,不让订单请求切换到其他模型。依据:官方服务源码,已对照所选发布版。

长任务、缓存与重复 Queue

行为来源:ComfyUI 缓存文档、Server Routes 与 WebSocket Messages。

worker API 与持久化格式(待实现)

拟定 worker API 作用
POST /jobs 提交 prepare_data/prepare_model/sft/grpo/eval/fuse;返回 job_id
GET /jobs/{job_id} 返回状态、日志位置、错误、结果 artifact 引用
POST /jobs/{job_id}/cancel 取消本 worker 创建的任务,回读终止状态
GET /artifacts/{artifact_id} 返回不可变 manifest 与 hash 校验结果
POST /services 启动指定最终 artifact 的 MLX-LM 服务
GET /services/{service_id} 健康、加载模型、实际监听端口与错误
POST /services/{service_id}/stop 停止对应服务进程并确认不再监听

这些不是 ComfyUI 原生路由,也不是已存在的 MLX-LM 路由。原生端口 8188 的 /prompt、/history/{prompt_id} 用于提交/回读 ComfyUI 图;端口 8088 的 /v1/chat/completions 用于调用训后模型。

拟定的 POST /jobs 请求示例(仅接口样本,ID 为占位符):

{
  "experiment_id": "price-grpo-smoke-01",
  "request_id": "grpo-submit-01",
  "operation": "grpo",
  "parent_artifact_id": "sft-fused-placeholder",
  "reference_artifact_id": "sft-fused-placeholder",
  "data_artifact_id": "price-data-placeholder",
  "parameters": {"config": "assets/grpo.yaml", "iters": 5}
}

worker 返回 job_id/state/experiment_id/operation/request_id;JobReadback 增加 progress/error/log_path/artifact_ids/evidence_summary。runtime 摘要记录真正的 Python 路径、包版本、设备、PID、耗时与峰值内存;未知字段留空,不根据 ComfyUI 的 MPS 标签填训练设备。请求参数只接受固定操作与允许的配置,后端自己构造 argv,不接受任意 shell 命令。

runs/comfy/{experiment_id}/
  experiment.json                 # 原始模型、数据与配置 hash
  jobs/{job_id}/
    request.json                  # 命令 argv、输入引用、幂等键
    status.json                   # 原子更新,状态和错误
    stdout.log
    training-evidence.json        # 实际更新与重载证据
    rollouts.jsonl                # GRPO 时必需
    artifacts.json                # 只在核验成功后发布引用
  services/{service_id}/
    launch.json                   # 模型 artifact、路径/hash、端口
    server.log
    readiness.json
    receipts/{request_id}.json    # 完整请求与回包

model artifact manifest 最少包含 artifact_id/stage/representation/parent_artifact_id/base_artifact_id/model_path/weights_sha256/tokenizer_sha256/config_sha256/data_sha256/producer_job_id。stage 为 original/sft/grpo;representation 为 full/adapter/fused。adapter 引用必须带 base_artifact_id,并把 adapter 与对应 base 组合重载;fuse 产物保留 adapter parent 与训练 base 血缘。哈希包含真正的权重、tokenizer 与配置;manifest 的 ID 不等于训练完成证明。建立不可变产物后再发布引用。

Submit 的 sft 输入须是 original/full;grpo 的训练 parent 和固定参考模型输入须是 sft/fused;ServiceStart 输入须是所选最终阶段的 fused。worker 遇到 stage、representation 或 base/hash 不匹配时拒绝提交,不能只凭文件名判断。

data manifest 最少包含 artifact_id/stage=data/seed/oracle_version/partition_paths/partition_counts/partition_sha256/disjoint_check/producer_job_id。模型 artifact 中的 data_sha256 指向这一份冻结数据摘要;原始未训练模型的该字段为空。

实现与运行顺序

  1. 保留现有 ComfyUI 实例;把桥接包源码先准备为独立 custom_nodes/price_lab_bridge,不改核心或现有业务节点。
  2. 在课程 .venv 安装原手册的依赖,确认 MLX 设备;运行 oracle/奖励检查和数据 hash 检查。
  3. 实现 worker 与只依赖 HTTP 的桥接节点,先验证假任务状态转换、幂等、失败和重载检查。假任务只证明编排。
  4. 单独跑真实 SFT/GRPO 小步,确认训练命令、回读与 callback 证据,再用 ComfyUI 触发同一 runner。
  5. 节点安装后,在原实例任务允许时再正常重启;读取 /object_info 确认 PriceLab 类名出现,验证图依赖后再 Queue。
  6. 按 A/B/C/D 分阶段创建图。blueprint 仅用于按规格建图;导出真正 UI workflow 与 API workflow 后,才能调用原生 /prompt。
  7. 训练、质量与服务按独立门槛验收。新实验选 checkpoint 用验证集,冻结测试集规则仍沿用原课程。

本轮设计不自动执行上面步骤;它给后续节点实现和实作课程提供可审查的规格。

验收表

声明 足够的证据 不足的证据
画布提交成功 prompt_id、history、worker job_id 训练完成
SFT 更新发生 新参数、有限更新、产物与重载 单独 loss 曲线截图
GRPO 完成真实闭环 SFT parent + 真实采样/奖励/优势 + 更新/重载 单独 reward 分数或模拟器
模型有效 同一固定题/同解码的原始-SFT-GRPO 对照 合成数据检查通过
服务可调用 正确 artifact 实例 + 健康 + 原始推理 receipt service_id、端口打开或 HTTP 200
金额正确 receipt 的 content 通过原严格评分器 用 oracle 替换后的显示值

桥接速查 · 状态与证据速查 · 设计 blueprint · 学习入口