先说结论:你要的不是“把模型跑起来”,而是“把模型变成可被流程调用的能力”

关键字是 “qmd openclaw”。它背后往往不是好奇心,而是一个很现实的工程目标:

  • 你希望用 QMD 这一类“本地推理/模型调度层”(它可能封装 llama.cpp / vLLM / Ollama,也可能是你自建的统一推理网关)把本地模型跑稳、跑便宜。
  • 你希望用 OpenClaw 把“推理”升级为“产出”:能接任务、能调工具、能发布、能自检、能失败恢复。

我的判断是:QMD + OpenClaw 的最佳组合,是“推理层的确定性 + 编排层的状态机”。

如果你只把它当作“换个模型 provider”,那你迟早会遇到:长文生成卡死、输出结构崩坏、重复发布、任务状态对不上、出了问题只能肉眼翻日志。

本文按“能落地”的顺序写:先给最小可用,再给生产化路线。


1) 正确分层:QMD 在哪,OpenClaw 在哪

把系统拆成三层,你会立刻清醒:

  1. 推理层(Inference Plane)= QMD

    • 负责模型进程生命周期(启动/停止/热加载)
    • 负责并发、队列、超时、缓存(至少要有超时和错误码)
    • 对外暴露稳定 API(强烈建议兼容 OpenAI 形态)
  2. 编排层(Orchestration Plane)= OpenClaw / OpenClaw OS

    • 负责“何时推理、推理后做什么”
    • 负责工具调用(读写文件、跑脚本、抓网页、发消息)
    • 负责任务状态机(queued/running/done/failed)与事件审计(events/artifacts)
  3. 交付层(Delivery Plane)= 站点/业务系统

    • Astro 构建发布、WordPress 发布、DB 写入
    • 产物 URL、版本记录、回滚

一句话:QMD 让模型“可用”,OpenClaw 让系统“可运营”。


2) 最小可用(MVP):让 OpenClaw 能稳定调用 QMD

2.1 你需要一个“QMD 适配器”(薄到不能再薄)

为什么要适配器?因为你需要把“不稳定的推理服务”包成“稳定的函数调用”。

适配器要做四件事(缺一不可):

  1. 统一协议:把 QMD 的 API 固定成一个调用入口(最好 OpenAI-like)。
  2. 超时与重试:长文很容易卡死;没有超时你会把任务状态机拖死。
  3. 输出护栏:JSON 解析失败就重试一次;再失败就降级。
  4. 日志与 request_id:方便你定位“坏在 QMD 还是坏在编排”。

示意(Python,假设 QMD 兼容 /v1/chat/completions):

import os, time, json
import requests

QMD_BASE = os.getenv('QMD_BASE', 'http://127.0.0.1:8000')
QMD_MODEL = os.getenv('QMD_MODEL', 'writer-zh')

def qmd_chat(messages, max_tokens=2048, temperature=0.4, timeout=120):
    payload = {
        "model": QMD_MODEL,
        "messages": messages,
        "temperature": temperature,
        "max_tokens": max_tokens,
    }
    last_err = None
    for attempt in range(2):
        try:
            r = requests.post(f"{QMD_BASE}/v1/chat/completions", json=payload, timeout=timeout)
            r.raise_for_status()
            data = r.json()
            return data["choices"][0]["message"]["content"]
        except Exception as e:
            last_err = e
            time.sleep(1.0)
    raise RuntimeError(f"QMD call failed: {last_err}")

2.2 输出护栏:先大纲、后正文(否则你会被长文逼疯)

长文生成最稳的方式不是“一次生成 3000 字”,而是:

  • 步骤 A:让模型输出严格 JSON 大纲(可 parse)
  • 步骤 B:按章节逐段生成(每段 600~900 字)
  • 步骤 C:合并 + 最终润色(可选)

你会得到两个好处:

  • 失败可重跑某一段,不会整篇重来
  • 可控长度,可控结构

3) 工程化关键:为什么“本地模型 + Agent”最容易翻车

3.1 坑一:输出不可控(结构崩坏、跑题、重复)

本地模型常见问题:

  • JSON 断裂、字段缺失
  • 章节反复复读
  • 结尾突然开始写别的主题

治理方法(建议当成硬规则):

  • 低温度:教程/工程文 0.2~0.5 更稳。
  • 硬上限:每章限制最大字数;用“章节编号 + 目标字数”约束。
  • 校验:结构化输出必须能 parse;失败重试一次;再失败就降级纯文本。
  • 分段写作:把不确定性切小。

3.2 坑二:延迟、并发与资源调度

当 QMD 底层是 llama.cpp 或 vLLM,你会遇到:

  • 并发一高就排队
  • 显存不够就 OOM
  • CPU 模式慢到不可用

正确做法:

  • 编排层限制并发:比如 SEO 长文一次只跑 1 个任务(对产线是最划算的)。
  • 推理层做队列:至少要返回“排队中/拥塞”的状态。
  • 任务拆分:章节化生成可以把一次推理的峰值压下去。

3.3 坑三:错误不可观测(你不知道坏在哪)

如果你没有 request_id + 事件日志,你会处在一种很原始的状态: “它坏了,但我不知道坏在哪里”。

最低观测标准:

  • QMD:记录每次请求的耗时、tokens、错误码、异常栈
  • OpenClaw OS:记录 task 的 state_transition(queued→running→done/failed)
  • artifacts:保存最终 URL(或文件路径)

4) 生产化路线:把 QMD 变成“模型路由表”,而不是单一模型

你迟早会有多个模型:

  • 一个偏写作(中文更稳)
  • 一个偏代码(补全更强)
  • 一个偏分类/审计(小模型便宜快)

所以建议你在 QMD 做“能力标签”,OpenClaw 只认标签。

4.1 QMD 侧:能力标签(capability alias)

例如:

  • writer-zh → qwen2.5-14b
  • coder → deepseek-coder
  • router → 7b 小模型

OpenClaw 配置里就写 writer-zh,换模型不改业务逻辑。

4.2 OpenClaw 侧:任务 → 模型策略

  • 写长文(SEO、教程):writer-zh
  • 写代码/脚本:coder
  • 质量审计(长度、重复、分类):router

这一步的价值是:你把“模型选择”从工程里抽离成策略。


5) 回到关键字:用 QMD + OpenClaw 跑一条“SEO 文章工厂”

你现在做的是典型的关键词驱动内容工厂:

  • 从队列里拿关键词
  • 生成 3000+ 字
  • 发布到 Astro
  • 落库记录

这类任务非常适合本地推理:

  • tokens 大、成本敏感
  • 不需要强实时
  • 容忍一定波动,但必须“结构稳定、可重复运行”

5.1 一条靠谱的流水线应该长这样(强烈建议照抄)

  1. 从 OS DB 拉 1 个 queued 任务
  2. 先写 state=running(并写 event)
  3. 生成文章(大纲→分段→合并)
  4. 本地去重(slug/title/dedupe_key)
  5. 写入 astro-site/src/content/blog/*.md
  6. 构建 + 部署
  7. curl 验证 URL 可访问
  8. 写 state=done + output_ref=URL
  9. 写 artifacts + events

注意顺序:状态先写,再做动作,这是为了可恢复。

5.2 去重策略:别只靠“我记得没写过”

最低去重标准:

  • dedupe_key(例如 keyword:qmd openclaw)唯一
  • slug 唯一(文件名/路由)
  • title 不重复(至少在 repo 内全文检索)

更强去重(可选):

  • embedding 相似度(>0.9 直接阻断)

5.2 一个更具体的落地示例:把 QMD 变成 OpenClaw 的“内网模型提供商”

上面讲的是思想,下面给你一个可以直接照着抄的工程切片。

5.2.1 QMD 侧:统一成 OpenAI-like 接口(推荐)

不管你底层是 llama.cpp、vLLM、Ollama,最重要的一点是:对外接口尽量统一。 原因很简单:当你把接口统一成 /v1/chat/completions 这种形态,后面你换模型、换后端、加缓存、加队列,都不会影响编排层。

你至少要保证这些字段:

  • model:支持“能力标签”而不是死模型名(writer-zh / coder / router)
  • messages:system/user/assistant
  • max_tokens:硬上限
  • temperature:可控
  • 返回结构里能拿到 choices[0].message.content

做不到这点,你就会在一堆脚本里写“如果是 QMD-A 就用字段 x,如果是 QMD-B 就用字段 y”,最后把自己写死。

5.2.2 OpenClaw 侧:给 QMD 一层“工具封装”

很多人第一反应是:直接在任务脚本里 curl QMD。 能跑,但不可维护。

正确姿势是:把“调用 QMD”封装成一个明确的工具(tool),让上层任务只关心:

  • 我要哪个能力(writer-zh / coder)
  • 我给什么输入(prompt/messages)
  • 我需要什么输出形态(纯文本 / JSON)

这样你未来要加:

  • 重试
  • 降级到云模型
  • 速率限制
  • 统计 tokens 都只改工具层,不动业务逻辑。

5.2.3 章节化生成的真实好处:可观测、可恢复、可并行

“先大纲后正文”不是写作技巧,是可靠性设计。

  • 可观测:你能知道卡在哪一章(比如第 3 章卡死,而不是整篇卡死)
  • 可恢复:失败时只重跑一章,不用整篇重来
  • 可并行:资源允许时,甚至可以并行生成章节(但 SEO 长文通常不需要并行,稳定更重要)

一个非常务实的做法是:

  1. outline.json(章节列表、每章目标字数、关键要点)
  2. section-01.md … section-08.md
  3. final.md(合并并补上开头/结尾/FAQ/总结)

在 OpenClaw OS 里,这些都可以作为 artifacts 记录(文件路径也算 artifact),你就能把“写作过程”可追踪化。


6) 生产化必做:监控、熔断、降级(不做就是赌命)

只要你把本地模型接进产线,你就会遇到两类事故:

  • 推理层事故:模型卡死、OOM、显存碎片、性能突然掉、队列堆积
  • 编排层事故:任务跑一半、状态不一致、重复发布、产物丢失

6.1 监控:先别上 Prometheus,先把“必要字段”写进日志

你不一定需要复杂监控系统,但你必须能回答三个问题:

  1. 这次推理用了多久?
  2. 这次推理大概用了多少 tokens?
  3. 失败时的错误码/异常栈是什么?

最低成本做法:QMD 返回一个 request_id,OpenClaw 把这个 id 写到 task 的 event payload 里。 出了问题你就能一键串起来排查。

6.2 熔断:别让坏服务拖死整个队列

当 QMD 连续失败时(比如 5 分钟内失败 3 次),你要做两件事:

  • 暂停新任务(或把任务标记为 blocked)
  • 发出告警(Telegram/邮件)

否则你会得到一个很经典的灾难: 队列被失败任务塞满,系统看似在跑,其实一直在原地打转。

6.3 降级:只在必要时用云模型(成本可控、体验稳定)

降级策略是“工程理性”的体现:

  • 绝大多数时候用本地模型(便宜)
  • 只有在关键步骤失败时(比如 JSON 大纲生成失败两次),才切到云模型

这能把成本控制在一个很舒服的范围,同时把可用性拉上去。


7) 安全与边界:别让本地模型变成你的“数据泄露出口”

很多人以为“本地 = 安全”,这是错觉。 风险在于:

  • 你的 prompt 里可能带了 token、cookie、内部链接
  • 你的工具链可能会把敏感文件读出来喂给模型
  • 你的日志可能把这些东西原样落盘

最低限度的安全边界建议:

  1. 敏感信息红线:任何 API Key/Token 不进入 prompt(用工具调用时从环境变量读取)。
  2. 文件读取白名单:工具层限制可读目录,别给 agent 整个文件系统。
  3. 日志脱敏:日志里不记录完整 prompt,只记录 hash/摘要。

你做的是内容工厂,更容易不小心把“配置片段”写进文章里,务必小心。

6) “能跑”到“能稳定跑”:可靠性治理清单

6.1 超时 + 失败策略

  • 每次推理:timeout(比如 120s)
  • 每次章节:失败重试 1 次
  • 整篇:失败 → task=failed,并记录 error

6.2 幂等与回滚

发布链路很容易出现:构建成功、复制失败;或复制成功、URL 404。

建议:

  • 文章文件落地后不覆盖旧文件(timestamp slug)
  • 发布成功后才写 task done
  • 如果构建失败,不要写 artifacts

6.3 质量门槛(你可以后面再严格,但至少要有)

  • 字数/长度:不足阈值直接失败或补写
  • 标题/描述:必须存在
  • 段落结构:至少 8 个二级标题
  • FAQ:至少 2~4 条

7) 实战:一个可复制的“3000+ 字工程文结构”

为了 SEO 友好,同时不灌水,我建议你用这个结构写(每节都能写出实质内容):

  1. 结论与适用场景(你要解决什么)
  2. 三层架构与职责边界(推理/编排/交付)
  3. 推理接口契约(请求/响应/错误码/超时)
  4. 输出控制(JSON 大纲、分段生成、校验)
  5. 并发与资源(队列、限流、GPU/CPU 策略)
  6. 任务状态机与审计(events/artifacts)
  7. 发布链路(构建、部署、校验、回滚)
  8. 常见坑与排错(卡死、OOM、重复、404)
  9. 结语与下一步(质量审计、降级云模型)

8) 常见问题与排错(你很可能今天就会用到)

8.1 生成卡死怎么办?

  • 首先确认是 QMD 超时还是 OpenClaw 编排卡住。
  • QMD 侧加 timeout;OpenClaw 侧对每个阶段加 deadline。
  • 把长文拆成章节:任何单次推理超过 2 分钟都应当被视为异常。

8.2 OOM(显存爆了)怎么办?

  • 降 batch / 降上下文 / 降并发。
  • 用更小模型负责“规划”,用大模型负责“写作”。
  • 章节化生成比一次生成更省峰值。

8.3 文章发布了但 URL 404?

  • 先看 Astro build 是否成功。
  • 再看 dist 是否复制到 Nginx 根目录。
  • 最后 curl 站点页面,确认路由是否按预期。

8.4 任务状态不一致(running 卡死)?

  • 需要一个 watchdog:超过 N 分钟没有 updated_at 更新就标记 failed 或 requeue。
  • OpenClaw OS 的状态机不是装饰品,它是你的“恢复按钮”。

9) 写在最后:QMD 是成本杠杆,OpenClaw 是运营杠杆

“qmd openclaw”不是一个炫技组合,而是一个很实际的路线:

  • 用本地推理把成本打下来
  • 用 Agent OS 把产出变成流水线
  • 用治理把不确定性关进笼子里

你要的不是“能推理”,而是“能稳定产出”。

—— 完 ——