> ## Documentation Index
> Fetch the complete documentation index at: https://felimet-hub.jmcores.com/llms.txt
> Use this file to discover all available pages before exploring further.

# 05-4 OpenClaw、Hermes-Agent 與 NemoClaw：三條路線的差異、能力與該學的事

> 三個開源 agent 框架的全維度對照：定位、Skill 設計、記憶機制、企業就緒程度，以及從它們能學到什麼、該反問自己什麼。

export const FrameworkMap = ({lang = "zh", title, layers = []}) => {
  const t = lang === "en" ? {
    hint: "Click a module to see details",
    layer: "Layer",
    module: "Module",
    responsibilities: "Responsibilities",
    path: "Key path",
    relations: "Connected to",
    noSelect: "Select a module above"
  } : {
    hint: "點模組卡片查看職責與關係",
    layer: "層",
    module: "模組",
    responsibilities: "職責",
    path: "關鍵路徑",
    relations: "連結到",
    noSelect: "點上方模組查看詳細說明"
  };
  const [selected, setSelected] = useState(null);
  const safe = Array.isArray(layers) ? layers : [];
  const PALETTE = {
    blue: {
      base: "#5b6c8f",
      bg: "#5b6c8f18",
      border: "#5b6c8f55",
      label: "#46587a"
    },
    purple: {
      base: "#8a6f8e",
      bg: "#8a6f8e18",
      border: "#8a6f8e55",
      label: "#6f5673"
    },
    yellow: {
      base: "#c9a35b",
      bg: "#c9a35b18",
      border: "#c9a35b55",
      label: "#9a7a3c"
    },
    green: {
      base: "#8a9a7b",
      bg: "#8a9a7b18",
      border: "#8a9a7b55",
      label: "#6a7a5c"
    },
    teal: {
      base: "#6f9290",
      bg: "#6f929018",
      border: "#6f929055",
      label: "#527472"
    },
    rose: {
      base: "#ab7269",
      bg: "#ab726918",
      border: "#ab726955",
      label: "#8e564e"
    },
    amber: {
      base: "#b9835c",
      bg: "#b9835c18",
      border: "#b9835c55",
      label: "#96683f"
    }
  };
  const ROTATION = ["blue", "purple", "yellow", "green", "teal", "rose"];
  const getColor = (layer, index) => {
    const key = layer.color && PALETTE[layer.color] ? layer.color : ROTATION[index % ROTATION.length];
    return PALETTE[key];
  };
  const sel = (() => {
    if (!selected) return null;
    for (let li = 0; li < safe.length; li++) {
      const layer = safe[li];
      const m = layer.modules.find(m => m.id === selected);
      if (m) return {
        module: m,
        layerLabel: layer.label,
        color: getColor(layer, li)
      };
    }
    return null;
  })();
  const css = `
.fm-root{--fm-bg:#FAF8F3;--fm-surface2:rgba(0,0,0,0.022);
  --fm-border:rgba(0,0,0,0.08);--fm-text:#2b2722;--fm-dim:#6f6a62;
  --fm-faint:#9a9490;--fm-accent:#bf7551;--fm-card-bg:#fff;
  border:1px solid var(--fm-border);border-radius:14px;background:var(--fm-bg);
  color:var(--fm-text);overflow:hidden;font-family:inherit;}
.dark .fm-root{--fm-bg:#1b1a18;--fm-surface2:rgba(255,255,255,0.025);
  --fm-border:rgba(255,255,255,0.07);--fm-text:#e7e3da;
  --fm-dim:#a8a299;--fm-faint:#6a6560;--fm-card-bg:rgba(255,255,255,0.04);}
.fm-head{padding:13px 18px 11px;display:flex;align-items:center;gap:10px;border-bottom:1px solid var(--fm-border);flex-wrap:wrap;}
.fm-head-ic{color:var(--fm-accent);flex-shrink:0;}
.fm-head-title{font-size:14px;font-weight:700;letter-spacing:0.01em;flex:1;}
.fm-head-hint{font-size:11.5px;color:var(--fm-faint);}
.fm-layers{padding:14px 16px 10px;display:flex;flex-direction:column;gap:12px;}
.fm-layer{}
.fm-layer-label{font-size:10.5px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;
  margin-bottom:7px;display:flex;align-items:center;gap:6px;}
.fm-layer-label::after{content:"";flex:1;height:1px;opacity:0.4;}
.fm-modules{display:flex;flex-wrap:wrap;gap:8px;}
.fm-module{display:flex;flex-direction:column;gap:3px;padding:9px 13px;border-radius:9px;
  border:1px solid var(--fm-border);background:var(--fm-card-bg);cursor:pointer;
  transition:border-color .15s,background .15s,box-shadow .15s;text-align:left;
  min-width:120px;max-width:220px;flex:1 1 120px;}
.fm-mod-name{font-size:13px;font-weight:600;line-height:1.3;color:var(--fm-text);}
.fm-mod-summary{font-size:11.5px;line-height:1.5;color:var(--fm-dim);}
.fm-mod-path{font-size:10.5px;font-family:ui-monospace,"Cascadia Code","Consolas",monospace;
  color:var(--fm-faint);margin-top:2px;word-break:break-all;}
.fm-arrow{display:flex;justify-content:center;padding:2px 0;}
.fm-panel{border-top:1px solid var(--fm-border);padding:14px 18px;background:var(--fm-surface2);min-height:80px;}
.fm-panel-empty{display:flex;align-items:center;justify-content:center;height:60px;
  font-size:12.5px;color:var(--fm-faint);}
.fm-panel-layer{font-size:10.5px;font-weight:700;letter-spacing:0.06em;text-transform:uppercase;
  margin-bottom:5px;}
.fm-panel-name{font-size:16px;font-weight:700;margin-bottom:6px;line-height:1.3;}
.fm-panel-path{font-size:11px;font-family:ui-monospace,"Cascadia Code","Consolas",monospace;
  color:var(--fm-faint);margin-bottom:10px;word-break:break-all;}
.fm-panel-sec{font-size:10.5px;font-weight:700;letter-spacing:0.05em;text-transform:uppercase;
  color:var(--fm-dim);margin-bottom:5px;margin-top:10px;}
.fm-panel-detail{font-size:13.5px;line-height:1.65;color:var(--fm-dim);}
.fm-rels{display:flex;flex-wrap:wrap;gap:6px;margin-top:4px;}
.fm-rel{font-size:11.5px;padding:3px 9px;border-radius:20px;}
@media (max-width:600px){
  .fm-modules{flex-direction:column;}
  .fm-module{max-width:100%;}
  .fm-head-hint{display:none;}
}
`;
  return <div className="fm-root">
      <style>{css}</style>
      <div className="fm-head">
        <svg className="fm-head-ic" xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
          <rect x="2" y="3" width="20" height="5" rx="2" />
          <rect x="2" y="10" width="20" height="5" rx="2" />
          <rect x="2" y="17" width="20" height="5" rx="2" />
        </svg>
        {title && <span className="fm-head-title">{title}</span>}
        <span className="fm-head-hint">{t.hint}</span>
      </div>

      <div className="fm-layers">
        {safe.map((layer, li) => {
    const c = getColor(layer, li);
    return <div className="fm-layer" key={layer.id || li}>
              <div className="fm-layer-label" style={{
      color: c.label
    }}>
                <span>{layer.label}</span>
                <span style={{
      flex: 1,
      height: "1px",
      background: c.border,
      display: "block"
    }} />
              </div>
              <div className="fm-modules">
                {(layer.modules || []).map(mod => {
      const isActive = selected === mod.id;
      return <button key={mod.id} type="button" className="fm-module" style={isActive ? {
        borderColor: c.base,
        background: c.bg,
        boxShadow: "0 0 0 3px " + c.base + "22"
      } : {}} onMouseEnter={e => {
        if (!isActive) {
          e.currentTarget.style.borderColor = c.base;
          e.currentTarget.style.background = c.bg;
        }
      }} onMouseLeave={e => {
        if (!isActive) {
          e.currentTarget.style.borderColor = "";
          e.currentTarget.style.background = "";
        }
      }} onClick={() => setSelected(selected === mod.id ? null : mod.id)}>
                      <div className="fm-mod-name" style={isActive ? {
        color: c.label
      } : {}}>{mod.name}</div>
                      {mod.summary && <div className="fm-mod-summary">{mod.summary}</div>}
                      {mod.path && <div className="fm-mod-path">{mod.path}</div>}
                    </button>;
    })}
              </div>
              {li < safe.length - 1 && <div className="fm-arrow">
                  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke={getColor(safe[li + 1] || layer, li + 1).base} strokeWidth="2.2" strokeLinecap="round" strokeLinejoin="round" style={{
      opacity: 0.5
    }}>
                    <path d="M12 5v14M5 15l7 7 7-7" />
                  </svg>
                </div>}
            </div>;
  })}
      </div>

      <div className="fm-panel">
        {sel ? <div>
            <div className="fm-panel-layer" style={{
    color: sel.color.label
  }}>{t.layer}: {sel.layerLabel}</div>
            <div className="fm-panel-name">{sel.module.name}</div>
            {sel.module.path && <div className="fm-panel-path">{sel.module.path}</div>}
            {sel.module.detail && <div>
                <div className="fm-panel-sec">{t.responsibilities}</div>
                <div className="fm-panel-detail">{sel.module.detail}</div>
              </div>}
            {sel.module.relations && sel.module.relations.length > 0 && <div>
                <div className="fm-panel-sec">{t.relations}</div>
                <div className="fm-rels">
                  {sel.module.relations.map((r, i) => <span className="fm-rel" key={i} style={{
    background: sel.color.bg,
    color: sel.color.label,
    border: "1px solid " + sel.color.border
  }}>{r.to}{r.label ? " — " + r.label : ""}</span>)}
                </div>
              </div>}
          </div> : <div className="fm-panel-empty">{t.noSelect}</div>}
      </div>
    </div>;
};

export const OptionPicker = ({options = [], lang = "zh", label}) => {
  const t = lang === "en" ? {
    pick: label || "Pick an option to compare",
    rec: "Recommended"
  } : {
    pick: label || "點選項目比較",
    rec: "推薦"
  };
  const opts = Array.isArray(options) ? options : [];
  const [sel, setSel] = useState(0);
  if (opts.length === 0) return null;
  const idx = Math.min(sel, opts.length - 1);
  const cur = opts[idx];
  const css = `
  .op-root{--op-bg:#FAF8F3;--op-surface:rgba(0,0,0,0.025);--op-border:rgba(0,0,0,0.09);--op-text:#2b2722;--op-dim:#6f6a62;--op-faint:#8a8378;--op-accent:#bf7551;border:1px solid var(--op-border);border-radius:14px;background:var(--op-bg);color:var(--op-text);overflow:hidden;}
  .dark .op-root{--op-bg:#1b1a18;--op-surface:rgba(255,255,255,0.03);--op-border:rgba(255,255,255,0.08);--op-text:#e7e3da;--op-dim:#a8a299;--op-faint:#8a8378;--op-accent:#cf8a68;}
  .op-head{padding:12px 16px 11px;border-bottom:1px solid var(--op-border);font-size:12.5px;color:var(--op-dim);display:flex;align-items:center;gap:8px;}
  .op-head-ic{color:var(--op-accent);flex-shrink:0;}
  .op-tabs{display:flex;gap:8px;padding:14px 16px 4px;flex-wrap:wrap;}
  .op-tab{position:relative;flex:1 1 130px;min-width:120px;text-align:left;background:transparent;border:1px solid var(--op-border);border-radius:11px;padding:11px 13px;cursor:pointer;color:inherit;font:inherit;transition:border-color .15s,background .15s,box-shadow .15s;}
  .op-tab:hover{background:var(--op-surface);}
  .op-tab-on{border-color:rgba(191,117,81,.5);background:rgba(191,117,81,.07);box-shadow:0 0 0 3px rgba(191,117,81,.1);}
  .op-tab-name{font-size:14.5px;font-weight:600;line-height:1.3;}
  .op-tab-on .op-tab-name{color:var(--op-accent);}
  .op-badge{display:inline-block;margin-top:6px;font-size:10.5px;font-weight:700;letter-spacing:.4px;text-transform:uppercase;padding:2px 7px;border-radius:20px;background:rgba(191,117,81,.14);color:var(--op-accent);}
  .op-rec{position:absolute;top:10px;right:11px;font-size:10px;font-weight:700;letter-spacing:.3px;text-transform:uppercase;color:var(--op-accent);}
  .op-body{padding:6px 16px 16px;}
  .op-specs{border:1px solid var(--op-border);border-radius:10px;overflow:hidden;margin-bottom:11px;}
  .op-row{display:flex;border-top:1px solid var(--op-border);font-size:13px;}
  .op-row:first-child{border-top:none;}
  .op-k{width:42%;flex-shrink:0;padding:8px 12px;color:var(--op-dim);background:var(--op-surface);}
  .op-v{flex:1 1 0;min-width:0;padding:8px 12px;font-weight:500;}
  .op-note{font-size:13px;line-height:1.6;color:var(--op-dim);padding:0 2px;}
  `;
  return <div className="op-root">
      <style>{css}</style>
      <div className="op-head"><svg className="op-head-ic" xmlns="http://www.w3.org/2000/svg" width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><path d="m12.83 2.18a2 2 0 0 0-1.66 0L2.6 6.08a1 1 0 0 0 0 1.83l8.58 3.91a2 2 0 0 0 1.66 0l8.58-3.9a1 1 0 0 0 0-1.83Z" /><path d="m22 17.65-9.17 4.16a2 2 0 0 1-1.66 0L2 17.65" /><path d="m22 12.65-9.17 4.16a2 2 0 0 1-1.66 0L2 12.65" /></svg>{t.pick}</div>
      <div className="op-tabs">
        {opts.map((o, i) => <button key={i} type="button" className={"op-tab" + (i === idx ? " op-tab-on" : "")} onClick={() => setSel(i)}>
            {o.recommend && <span className="op-rec">★ {t.rec}</span>}
            <div className="op-tab-name">{o.name}</div>
            {o.badge && <span className="op-badge">{o.badge}</span>}
          </button>)}
      </div>
      <div className="op-body">
        <div className="op-specs">
          {(cur.specs || []).map((row, i) => <div className="op-row" key={i}>
              <div className="op-k">{row[0]}</div>
              <div className="op-v">{row[1]}</div>
            </div>)}
        </div>
        {cur.note && <div className="op-note">{cur.note}</div>}
      </div>
    </div>;
};

export const LearnerPrimer = ({items = [], lang = "zh"}) => {
  const t = lang === "en" ? {
    title: "Before this unit, be honest with yourself",
    sub: "If you can't answer these, that gap is exactly what this unit closes."
  } : {
    title: "讀這個單元前，先誠實面對",
    sub: "這幾題答不出來，正是這個單元要替你補的洞。"
  };
  const css = `
  .lp-root{--lp-bg:#FAF7F1;--lp-surface:rgba(191,117,81,0.05);--lp-border:rgba(0,0,0,0.09);--lp-edge:rgba(191,117,81,0.42);--lp-text:#2b2722;--lp-dim:#6f6a62;--lp-accent:#bf7551;border:1px solid var(--lp-border);border-left:3px solid var(--lp-edge);border-radius:13px;background:var(--lp-bg);color:var(--lp-text);overflow:hidden;margin:1.25rem 0;}
  .dark .lp-root{--lp-bg:#1b1a18;--lp-surface:rgba(207,138,104,0.07);--lp-border:rgba(255,255,255,0.08);--lp-edge:rgba(207,138,104,0.5);--lp-text:#e7e3da;--lp-dim:#a8a299;--lp-accent:#cf8a68;}
  .lp-head{display:flex;align-items:center;gap:9px;padding:13px 18px 11px;border-bottom:1px solid var(--lp-border);background:var(--lp-surface);}
  .lp-ic{color:var(--lp-accent);flex-shrink:0;}
  .lp-htx{display:flex;flex-direction:column;gap:1px;min-width:0;}
  .lp-title{font-size:14px;font-weight:650;line-height:1.3;letter-spacing:.01em;}
  .lp-sub{font-size:12px;color:var(--lp-dim);line-height:1.4;}
  .lp-list{list-style:none;margin:0;padding:10px 18px 14px;display:flex;flex-direction:column;gap:0;}
  .lp-item{display:flex;align-items:baseline;gap:11px;padding:7px 0;font-size:14px;line-height:1.6;border-top:1px solid var(--lp-border);}
  .lp-item:first-child{border-top:none;}
  .lp-mark{flex-shrink:0;color:var(--lp-accent);font-size:13px;font-weight:700;line-height:1.55;font-variant-numeric:tabular-nums;opacity:.85;}
  .lp-q{flex:1 1 0;min-width:0;color:var(--lp-text);}
  @media (max-width:620px){.lp-head{padding:12px 14px 10px;}.lp-list{padding:8px 14px 12px;}}
  `;
  return <div className="lp-root">
      <style>{css}</style>
      <div className="lp-head">
        <svg className="lp-ic" xmlns="http://www.w3.org/2000/svg" width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round"><circle cx="12" cy="12" r="10" /><circle cx="12" cy="12" r="6" /><circle cx="12" cy="12" r="2" /></svg>
        <span className="lp-htx">
          <span className="lp-title">{t.title}</span>
          <span className="lp-sub">{t.sub}</span>
        </span>
      </div>
      <ul className="lp-list">
        {items.map((q, i) => <li className="lp-item" key={i}>
            <span className="lp-mark">{String(i + 1).padStart(2, "0")}</span>
            <span className="lp-q">{q}</span>
          </li>)}
      </ul>
    </div>;
};

<LearnerPrimer
  lang="zh"
  items={[
"三個框架共同點：把人格外顯成可版控 md 檔",
"NemoClaw 是硬化外殼，沒內部 agent 跑就沒意義",
"沒進版控的 md 跟 system prompt 字串沒差別",
"照抄別人 SOUL.md，引入你不需要的約束",
"個人實驗選 OpenClaw 或 Hermes，企業環境疊 NemoClaw",
"你能在 PR diff 看到 agent 學到什麼嗎？答否就是技術債",
"企業政策跟 agent 邏輯分開管理",
]}
/>

<Info>
  **這個單元解決什麼問題**

  三個專案問同一個問題：「這個 agent 學到的東西，我能看見、能版控嗎？」但切入角度不同。OpenClaw 是一套以 TypeScript 寫成的個人 AI 助理框架，把每一個 Skill 寫成 `skills/<name>/SKILL.md`，讓 agent harness 可讀、可版控、可審查；Hermes-Agent 是 Nous Research 以 Python 打造的自我改進 agent，用 `skills/` 目錄存放可累積的程序性知識，並透過 `prompt_builder` 的三層 system prompt 組裝（stable, context, volatile）把人格、指令、記憶有紀律地注入；NemoClaw 是 NVIDIA 在 2026-03 開源的 reference stack，包覆 OpenClaw 或 Hermes，在 OpenShell 沙箱內提供 YAML 藍圖、網路政策、SSRF 驗證、managed inference 等硬化層，把個人 agent 變成可被企業環境接受的常駐服務。本單元對照三者的差異、能力與特點，談該從中學什麼、該反問自己什麼、日後趨勢。NemoClaw 的深度解析見 [05-3](/code-agent/case-studies/nemoclaw)。
</Info>

## 學習目標

* [ ] 能用一張對照表說出三者在定位、核心語言、Skill/harness 設計、記憶機制與企業就緒程度上的關鍵差異
* [ ] 能說出三者共同的設計取向：把 agent 的「規劃與人格」外顯成可讀、可版控的 `.md` 檔
* [ ] 能具體講出從這三個框架可以學什麼，以及落地時該問自己哪些反問
* [ ] 能評估這類 agent 外殼標準化趨勢與自己目前的落點
* [ ] 能說出 NemoClaw 與 OpenClaw / Hermes 的角色差異：agent 本體 vs 硬化外殼

***

## 1. 對照表：定位、技術、Skill 設計、記憶與社群

### 1.1 三條路線的一句話定位

| 專案           | 一句話定位                              | 角色           |
| ------------ | ---------------------------------- | ------------ |
| OpenClaw     | 「在 20+ 個通訊管道上替你做事的個人 daemon」       | agent 本體（個人） |
| Hermes-Agent | 「每天早上重組一次人格的自我改進 agent」            | agent 本體（個人） |
| NemoClaw     | 「把上述任一 agent 包進 OpenShell 沙箱的硬化外殼」 | 硬化外殼（企業）     |

OpenClaw 與 Hermes-Agent 解決「agent 怎麼做事」，NemoClaw 解決「企業怎麼接受 always-on agent」。三者不是競爭關係，是疊加關係：NemoClaw 包 OpenClaw 或 Hermes，提供硬化層。

### 1.2 全維度對照

| 維度       | OpenClaw ([05-1](/code-agent/case-studies/openclaw)) | Hermes-Agent ([05-2](/code-agent/case-studies/hermes-agent)) | NemoClaw ([05-3](/code-agent/case-studies/nemoclaw)) |
| -------- | ---------------------------------------------------- | ------------------------------------------------------------ | ---------------------------------------------------- |
| 角色       | agent 本體（個人）                                         | agent 本體（個人）                                                 | 硬化外殼（企業）                                             |
| 核心語言     | TypeScript (Node.js)                                 | Python（`uv` 管理）                                              | TypeScript + YAML + Bash                             |
| 授權       | MIT                                                  | MIT                                                          | Apache 2.0                                           |
| 推論後端     | 多家（Anthropic、OpenAI 等）                               | 任何 OpenAI-compatible API                                     | managed inference + `model-specific-setup` registry  |
| 沙箱       | 可選（`agents.defaults.sandbox`）                        | 6 種 terminal 後端                                              | 強制（OpenShell）                                        |
| 網路政策     | 工具層級                                                 | 工具層級                                                         | blueprint + policy engine                            |
| Skill 存放 | `skills/<name>/SKILL.md`                             | `skills/<name>/SKILL.md`                                     | `.agents/skills/`（三層分眾）                              |
| Skill 載入 | 整體載入                                                 | 漸進揭露（Level 0 / Level 1）                                      | 透過 `nemoclaw-skills-guide` skill 取得決策樹               |
| 記憶精煉層    | `MEMORY.md`                                          | `MEMORY.md`（凍結快照）                                            | 沿用內部 agent                                           |
| 詳細日誌     | `memory/YYYY-MM-DD.md`                               | SQLite + FTS5                                                | 沿用內部 agent                                           |
| 心跳       | `HEARTBEAT.md` + Gateway 排程                          | 官方文件未明確說明                                                    | 沿用內部 agent + blueprint 控制                            |
| 人格檔      | `SOUL.md` + `IDENTITY.md`                            | `SOUL.md`                                                    | 沿用內部 agent                                           |
| 使用者檔     | `USER.md`                                            | `USER.md`（volatile）                                          | 沿用內部 agent                                           |
| 工具慣例     | `TOOLS.md`                                           | 併入 `AGENTS.md`                                               | blueprint 宣告                                         |
| 多管道路由    | 20+ 平台                                               | Telegram、Discord 等（adapter 形式）                               | 透過 OpenClaw 內部 agent 繼承                              |
| 子代理委派    | 透過 cron / ACP                                        | `delegate_task`（預設 3 個並發）                                    | 沿用內部 agent                                           |
| 企業就緒     | 否（local-first 個人）                                    | 否（local-first 個人）                                            | 是（policy-as-code + 漏洞揭露流程）                           |
| 政策表達     | md 檔                                                 | md 檔                                                         | YAML blueprint + policies/                           |
| 升級上游     | 自託管                                                  | 自託管                                                          | plugin 而非 fork，無需 rebase                             |

<FrameworkMap
  lang="zh"
  title="OpenClaw 分層架構（精簡版）"
  layers={[
{
  id: "gateway",
  color: "blue",
  label: "入口層 — Gateway + Heartbeat",
  modules: [
    { id: "gw", name: "Gateway", path: "src/gateway/", summary: "多管道路由，session 隔離，事件匯流排", detail: "常駐控制平面。20+ 管道入站訊息路由到各自隔離的 agent 實例；heartbeat 排程週期性喚醒 agent（預設 30 分鐘）。" },
    { id: "hb", name: "Heartbeat", path: "HEARTBEAT.md", summary: "週期性 turn，留空跳過模型呼叫", detail: "心跳任務清單；留空時連 LLM 推論都不發起，零成本開關。" },
  ],
},
{
  id: "workspace",
  color: "purple",
  label: "工作區層 — md 檔組裝人格",
  modules: [
    { id: "soul", name: "SOUL.md", summary: "人格層", detail: "高優先指令層，決定語氣與行為邊界。每次 session 啟動載入。" },
    { id: "memory", name: "MEMORY.md", summary: "長期記憶精煉層", detail: "精選事實；詳細日誌另存 memory/YYYY-MM-DD.md。" },
    { id: "agents", name: "AGENTS.md", summary: "操作政策", detail: "管「做什麼」的操作指令；同時是根層級 policy。" },
    { id: "tools", name: "TOOLS.md + skills/", summary: "工具慣例 + 可重用技能", detail: "TOOLS.md 記環境備注；skills/ 存可版控的可分享程序（SKILL.md 格式）。" },
  ],
},
{
  id: "execution",
  color: "green",
  label: "執行層 — 工具與沙箱",
  modules: [
    { id: "cfg", name: "openclaw.json", summary: "沙箱選項、模型、路徑設定", detail: "main session 預設全主機權限；非 main session 可設 Docker/SSH/OpenShell 沙箱。" },
  ],
},
]}
/>

<FrameworkMap
  lang="zh"
  title="Hermes-Agent 分層架構（精簡版）"
  layers={[
{
  id: "stable",
  color: "teal",
  label: "stable 層 — 人格（prefix cache 最高）",
  modules: [
    { id: "soul", name: "SOUL.md", path: "$HERMES_HOME/SOUL.md", summary: "跨專案固定人格", detail: "從 $HERMES_HOME 載入，不隨 CWD 改變。幾乎不動，prefix cache 命中率最高。" },
  ],
},
{
  id: "context",
  color: "blue",
  label: "context 層 — 專案指令（隨 CWD）",
  modules: [
    { id: "agents", name: "AGENTS.md", path: "CWD → git root", summary: "專案指令，也接受 .hermes.md / CLAUDE.md", detail: "從 CWD 往上找，首個符合優先。讓同一 daemon 跨專案有不同操作指令。" },
  ],
},
{
  id: "volatile",
  color: "purple",
  label: "volatile 層 — session 起始凍結快照",
  modules: [
    { id: "memory", name: "MEMORY.md", path: "$HERMES_HOME/memories/", summary: "上限 2,200 字元，agent 主動策展", detail: "session 起始凍結為快照；session 中寫入只落磁碟，下次才生效。字元上限強迫 consolidation。" },
    { id: "user", name: "USER.md", path: "$HERMES_HOME/memories/", summary: "上限 1,375 字元，使用者偏好", detail: "記錄使用者溝通風格與期待，同樣凍結快照。" },
  ],
},
{
  id: "infra",
  color: "amber",
  label: "基礎設施層 — SQLite + curator",
  modules: [
    { id: "db", name: "state.db", summary: "SQLite + FTS5 完整 session 歷史", detail: "可全文搜尋的詳盡日誌，與 MEMORY.md 是兩套獨立系統，不自動互相蒸餾。" },
    { id: "curator", name: "curator.py", summary: "agent 自建 skill lifecycle 維護", detail: "30 天未用轉 stale，90 天封存，永不自動刪除。" },
  ],
},
]}
/>

<FrameworkMap
  lang="zh"
  title="NemoClaw 分層架構（精簡版）"
  layers={[
{
  id: "hardening",
  color: "rose",
  label: "硬化外殼層 — CLI + Blueprint + Policies",
  modules: [
    { id: "cli", name: "CLI 核心", path: "src/lib/", summary: "onboard / credentials / inference / policies / preflight / runner", detail: "TypeScript 核心，負責安裝引導、憑證淨化、managed inference 路由與政策套用。" },
    { id: "bp", name: "blueprint.yaml", path: "nemoclaw-blueprint/", summary: "ops 期望狀態：sandbox / 政策 / 推論路由", detail: "整個堆疊的單一事實來源（ops 視角），進版控，改後需 preflight 重啟。" },
    { id: "pol", name: "policies/", summary: "egress 白名單 + SSRF 阻擋", detail: "always-on agent 最後一道離開主機的關卡；阻擋 loopback / link-local / RFC1918。" },
  ],
},
{
  id: "sandbox",
  color: "amber",
  label: "沙箱層 — OpenShell 強制隔離",
  modules: [
    { id: "shell", name: "OpenShell", summary: "強制隔離執行環境，沙箱邊界即審計單位", detail: "Docker capability drops + process limits；每個 sandbox 是獨立審計單位，事件可獨立追溯。" },
  ],
},
{
  id: "agent",
  color: "green",
  label: "代理層 — OpenClaw 或 Hermes（原封不動）",
  modules: [
    { id: "oc", name: "OpenClaw（預設）", summary: "plugin 接入，skills / heartbeat 完整保留", detail: "透過 nemoclaw/ Commander plugin 注入命令，不 fork；升級上游時硬化層不必 rebase。" },
    { id: "hm", name: "Hermes（可選）", summary: "NEMOCLAW_AGENT=hermes，alias nemohermes", detail: "兩者共用同一 NemoClaw package，切換不需重裝。" },
  ],
},
]}
/>

<Tip>
  **為什麼 NemoClaw 列「沿用內部 agent」很多欄**

  NemoClaw 不是另起爐灶，是把 OpenClaw 或 Hermes 整包放進 OpenShell 沙箱。所以 OpenClaw 的 `SOUL.md` 機制、Hermes 的 SQLite 記憶、心跳排程這些都「繼承」自內部 agent；NemoClaw 自己的強項在「外殼」：藍圖、政策、推論路由、漏洞揭露流程。
</Tip>

## 2. 共同點：把規劃與編排外顯成可讀、可版控的 md 檔

三個專案在技術選擇上看起來南轅北轍（TypeScript vs Python vs TypeScript+YAML），但底層設計哲學高度一致：**把 agent 的「人格、規劃、編排」從隱性字串或閉源二進位拉出來，放進人類可讀、可 diff、可版控的 `.md` 檔**。

具體表現：

* 三者都以 `SKILL.md` 作為技能的主要表達媒介（前綴 YAML frontmatter，內文 Markdown 指令）
* 三者都把人格（`SOUL.md`）獨立成檔，不混在 system prompt 字串裡
* 三者都把使用者側（`USER.md`）與專案側（`AGENTS.md`）分開管理
* NemoClaw 進一步把「企業政策」也外顯成 YAML（blueprint、policies、model-specific-setup），讓硬化層也走版控與審查流程

這個共同點的工程意義：把通常埋在 system prompt 或程式碼裡的設定層拉到 repo 後，agent 行為就可以被 PR review、可以被 diff、可以移交給下一個人。**人不是固定的，設定檔是固定的；設定檔可版控，agent 行為就可在不同 session、不同機器、不同人之間可重現**。

<Note>
  **同一段「如何使用新工具」在三個框架的形狀**

  假設你要給 agent 一條「讀 PDF 報告並摘要」的技能：

  ```markdown theme={null}
  # SKILL.md（OpenClaw / Hermes 通用）
  ---
  name: pdf-summarize
  description: 讀 PDF 報告並產生 5 點摘要
  ---

  ## 何時用
  使用者給了 PDF 檔案路徑且要求摘要時。

  ## 怎麼做
  1. 用 pdfplumber 把每頁文字抽出
  2. 用 tiktoken 計算 token 數
  3. 超過 8k tokens 時先 chunk 再摘要
  4. 用條列格式輸出 5 點

  ## 不要做
  - 不要把 PDF 內容上傳到任何外部 API
  - 不要假設使用者的研究領域
  ```

  這份檔在三個框架都能讀懂、都能版控、都能在 PR review 裡被討論。差別在於 NemoClaw 還會多一份 blueprint 宣告「這個 skill 允許呼叫哪些內網 API、不能呼叫哪些外網 host」。
</Note>

## 3. 該學什麼：harness 外顯化、可重現、可審查

從這三個框架可以提煉出五個值得內化的設計：

* **Skill as code**：把 agent 的操作程序寫成 `.md` 並加入版控，就像把測試寫成 `.spec` 一樣，是工程化 agent 行為的最小可行實踐。`SKILL.md` 是「agent 的 unit test」這層次的東西
* **記憶有 lifecycle**：Hermes-Agent 的 `curator` 只 archive 不刪除、pinned Skill 豁免自動 transition，這套設計防止 agent 靜默遺忘關鍵知識。OpenClaw 的 `MEMORY.md` + `memory/YYYY-MM-DD.md` 分層也是同樣的 lifecycle 思路
* **工具白名單隔離背景任務**：Hermes-Agent 的 `background_review.py`（官方文件以此描述背景評估任務的隔離機制）fork 出的評估 agent 只能呼叫記憶與 Skill 管理工具；NemoClaw 的 SSRF validation + network policies 是同樣的隔離哲學：把「能動什麼」與「在動什麼」分開審計
* **`.plans/` 或 blueprint 作為決策日誌**：功能規劃文件留在倉庫裡，後人（包括 agent）可以看見「為什麼這樣設計」而不只是「實作了什麼」。NemoClaw 的 `nemoclaw-blueprint/blueprint.yaml` 是這個思路的硬化版
* **外殼即政策**：NemoClaw 把「能不能打外網」這類安全決策從 agent 程式碼搬到外殼（YAML blueprint）強制執行，agent 邏輯與安全政策徹底解耦。這是給「agent 邏輯頻繁升級、但企業政策不能動」的場景的最乾淨解法

<Warning>
  **不要照抄**

  別人的 `.md` 反映別人的工作流。OpenClaw 的 `SOUL.md` 是 OpenClaw 團隊的，Hermes-Agent 的 `MEMORY.md` 凍結快照假設是 Hermes-Agent 的。**照抄常常引入你不需要的約束，甚至安全風險**（見 [03-3 安全、隱私與供應鏈風險](/code-agent/judgment/security-privacy-supply-chain)）。先理解「為什麼這樣設計」再決定要不要學這個設計。
</Warning>

## 4. 該反問自己什麼：你的 agent 設定是隱性散落還是外顯成檔

把這三個框架的共同設計翻成自我檢核問題：

* 你目前的 agent 行為，是散落在 system prompt 字串裡，還是有版本化的 `SKILL.md`？
* 你能在 Pull Request diff 裡看到「agent 學到了什麼新行為」嗎？
* 背景記憶更新是否有可稽核的邊界，還是隱性吃掉了主流程的上下文？
* 你的 agent 設定如果移交給另一個人，他能在五分鐘內看懂整套 harness 嗎？
* 你的 agent 升級上游時，硬化層或客製化設定會被原樣保留還是必須 rebase？
* 你的「能不能打外網」「能不能讀某個目錄」是程式碼層級還是政策層級（policy-as-code）？

如果前三題有兩題以上答「否」，你正處在「agent 行為不可重現、不可審查」的狀態，這正是 OpenClaw / Hermes / NemoClaw 想解決的問題。

## 5. 日後趨勢：agent 外殼標準化、md 檔作為人格與編排載體

幾個值得觀察的趨勢（截至 2026-05 / 2026-06）：

* **`SKILL.md` + YAML frontmatter 正在成為事實格式**。OpenClaw、Hermes-Agent、Claude Code skills、agentskills.io 規範都在收斂這個方向（agentskills.io specification 截至 2026-05；正式跨框架標準化組織或 RFC 是否存在，以 agentskills.io 官方頁面為準）。短期看起來會出現「跨框架相容的最小 `SKILL.md` 子集」
* **決策文件成為 agent 可讀的 RAG 輸入**。`.plans/` 類的文件讓 agent 可以被問「為什麼這樣設計」，這是給 agent 自我解釋能力的最簡單來源
* **NemoClaw 把趨勢往前推一步**。把「企業政策」也外顯成可版控的 YAML（blueprint、policies、model-specific-setup），硬化層從「每個 agent 各自為政」推向「reference stack + policy-as-code」。**NemoClaw 採 plugin 而非 fork 的方式升級上游 OpenClaw，這個模式（升級上游時硬化層不必 rebase）很可能成為其他個人 agent 框架進入企業環境的範本**
* **未來的基礎建設方向**：agent 行為的 CI 測試（像 OpenClaw 已有 `.github/codeql/` 做邊界品質掃描）、Skill lifecycle 自動化（curator 模式）、政策測試（NemoClaw 的 preflight）、安全審計單位（OpenShell 沙箱邊界）

<Note>
  **這是觀察，不是預言**

  「事實格式收斂」與「升級時硬化層不必 rebase」是目前能驗證的兩件事；其他趨勢仍在演進。把它們當作你設計自己的 agent 設定時的方向感，不要當作不可違背的命運。
</Note>

## 6. 一句生活比喻

三個框架像三種養寵物的風格：

* **OpenClaw** 像一本隨身帶的工作手冊：你把每套 SOP 寫進去，它按照上面做。**手冊是你的（可版控），它只是執行者**
* **Hermes-Agent** 像一個帶著學習日記的實習生：每天早上從家裡的三份文件重組自己（`SOUL.md` + `MEMORY.md` + `USER.md`），做完事會寫日記，明天才看得到。**日記在寫但不是即時生效，紀律換成本**
* **NemoClaw** 像把上述兩種機器人放進強化玻璃展示間：機器人本身沒換（OpenClaw 還是 OpenClaw、Hermes 還是 Hermes），但展示間決定了它能碰什麼、不能碰什麼、看得到誰、誰看得到它。**展示間的政策（YAML blueprint）是可版控的，機器人的人格（SOUL.md）也是可版控的，兩層分開管理**

你選哪一種取決於你的場景：要 SOP 紀律、要可成長性、還是要企業可接受性。多數人會從 OpenClaw 或 Hermes 開始，撞到企業或 always-on 的需求時再疊 NemoClaw。

***

## 選型指引

<OptionPicker
  lang="zh"
  label="選你的場景，看推薦組合"
  options={[
{
  name: "個人實驗",
  badge: "最快上手",
  specs: [
    ["推薦組合", "裸 OpenClaw 或裸 Hermes"],
    ["核心語言", "TypeScript（OpenClaw）或 Python（Hermes）"],
    ["沙箱", "可選，不強制"],
    ["記憶", "MEMORY.md + 日誌分層"],
    ["政策表達", "md 檔，版控即可"],
    ["上手成本", "低，直接 clone 開跑"],
  ],
  note: "個人助理、側專案、快速驗證 agent 行為。不需要 NemoClaw，企業政策層反而是負擔。",
},
{
  name: "多模型實驗",
  badge: "彈性最高",
  specs: [
    ["推薦組合", "Hermes-Agent（任何 OpenAI-compatible API）"],
    ["核心語言", "Python（uv 管理）"],
    ["沙箱", "6 種 terminal 後端可選"],
    ["記憶", "SQLite + FTS5，curator 管理 lifecycle"],
    ["Skill 載入", "漸進揭露（Level 0 / Level 1）"],
    ["切換後端", "換 API endpoint 即可，無需改框架"],
  ],
  note: "需要橫跨 Anthropic、OpenAI、本地模型做 A/B 的場景。Hermes 的後端無關性讓你專注在 Skill 設計而不是適配層。",
},
{
  name: "企業 always-on",
  recommend: true,
  specs: [
    ["推薦組合", "OpenClaw 或 Hermes + NemoClaw 外殼"],
    ["沙箱", "強制（OpenShell）"],
    ["網路政策", "YAML blueprint + policy engine"],
    ["升級方式", "plugin，硬化層不必 rebase"],
    ["安全審計", "SSRF validation + 漏洞揭露流程"],
    ["企業就緒", "policy-as-code，版控稽核"],
  ],
  note: "需要 always-on、IT 可稽核、安全邊界明確的生產環境。agent 邏輯（SOUL.md）與企業政策（blueprint）分層管理，兩邊各自升級互不干擾。",
},
]}
/>

***

## 動手做

* **跨框架比較 Skill**：分別 clone OpenClaw 與 Hermes-Agent，找出各自 Skill 目錄，比較一個現有 `SKILL.md` 的 frontmatter 結構與主體風格，回答：「這兩個框架的 Skill，能互相移植嗎？」實驗：在 OpenClaw 的 `skills/` 與 Hermes 的 `skills/` 互放一份最簡單的 `SKILL.md`，看啟動時會不會讀到。
* **外顯化你自己的 agent 設定**：挑出三個你目前用 system prompt 字串表達的操作程序，嘗試改寫成一份 `SKILL.md` 草稿（YAML frontmatter + Markdown 主體）。把它加入版控，送進 PR 給自己 review，看 diff 是不是你預期的那種「小而明確的變更」。
* **讀 NemoClaw 的 blueprint**：直接 clone NemoClaw 倉庫，讀 `nemoclaw-blueprint/blueprint.yaml` 與 `nemoclaw-blueprint/policies/` 一個範例，體會 policy-as-code 的形狀。問自己：你的企業（或研究室）有沒有等效的「政策外顯成檔」實踐？

## 常見誤區

* **以為「有 `.md` 就等於有版控」**：寫了 `SKILL.md` 但沒有加入 Git，或者每次覆蓋不留 diff，等於沒有外顯化。**沒進版控的 `.md` 跟 system prompt 字串沒有差別**
* **混淆「記憶（個人偏好與事實）」與「Skill（可重現的操作程序）」**：Hermes-Agent 的記憶管理明確分開評估這兩類，不應合併。個人偏好寫進 `MEMORY.md` / `USER.md`，操作程序寫進 `SKILL.md`
* **把 Hermes-Agent 的自我改進能力誤解為「agent 會自主改變自己的行為而難以預測」**：實際上 `SOUL.md` 凍結快照、curator 只 archive 不刪除、工具白名單隔離，這些設計是主動限制意外突變的護欄。**自我改進 ≠ 不可預測**
* **以為 NemoClaw 取代了 OpenClaw 或 Hermes**：NemoClaw 是硬化外殼，沒有自己的 `AIAgent` 主迴圈。沒有 OpenClaw 或 Hermes 跑在裡面，NemoClaw 沒有意義
* **照搬別人的 `SOUL.md` 或 `SKILL.md`**：見第 3 節 warning，別人的設定反映別人的工作流與約束

## 自我檢核

<Check>
  **通過本單元的標準**

  1. 不看文件，能否用一張三欄表（OpenClaw、Hermes-Agent、自己目前的 agent）填出：Skill 存放位置、記憶 lifecycle 設計、背景評估機制？
  2. 能說出 OpenClaw、Hermes-Agent、NemoClaw 三者的角色差異嗎？（提示：哪幾個是 agent 本體，哪個是硬化外殼）
  3. 你能在 Pull Request diff 裡看到「agent 學到了什麼新行為」嗎？答「否」的話，最不透明的那塊是技術債還是有意為之？
  4. 你的工作流是「個人實驗」「always-on daemon」「企業 production」中的哪一種？這個判斷會導向哪個組合（裸 OpenClaw / 裸 Hermes / OpenClaw + NemoClaw / Hermes + NemoClaw）？
</Check>

## 來源與延伸閱讀

事實主張依官方文件，快變動項標註截至 2026-05。

<div className="references">
  * \[1] OpenClaw, "openclaw/openclaw," GitHub. [https://github.com/openclaw/openclaw](https://github.com/openclaw/openclaw) （截至 2026-05）

  * \[2] OpenClaw, "openclaw\.ai," Official website. [https://openclaw.ai](https://openclaw.ai) （截至 2026-05）

  * \[3] Nous Research, "NousResearch/hermes-agent," GitHub. [https://github.com/NousResearch/hermes-agent](https://github.com/NousResearch/hermes-agent) （截至 2026-05）

  * \[4] Nous Research, "Hermes-Agent Documentation," hermes-agent.nousresearch.com. [https://hermes-agent.nousresearch.com/docs/](https://hermes-agent.nousresearch.com/docs/) （截至 2026-05）

  * \[5] NVIDIA, "NVIDIA/NemoClaw," GitHub. [https://github.com/NVIDIA/NemoClaw](https://github.com/NVIDIA/NemoClaw) （截至 2026-06）

  * \[6] NVIDIA, "NemoClaw Documentation," docs.nvidia.com. [https://docs.nvidia.com/nemoclaw/latest/](https://docs.nvidia.com/nemoclaw/latest/) （截至 2026-06）

  * \[7] agentskills.io, "Open Specification," agentskills.io. [https://agentskills.io/specification](https://agentskills.io/specification) （截至 2026-05）
</div>

* 深度解析：[05-1 OpenClaw 解剖](/code-agent/case-studies/openclaw)、[05-2 Hermes-Agent 解剖](/code-agent/case-studies/hermes-agent)、[05-3 NemoClaw 解剖](/code-agent/case-studies/nemoclaw)
* 安全邊界背後的威脅模型：[03-3 安全、隱私與供應鏈風險](/code-agent/judgment/security-privacy-supply-chain)
