arch-mapping.html 11.1 KB
<!doctype html>
<html lang="zh-CN">
<head>
<meta charset="utf-8" /><meta name="viewport" content="width=device-width,initial-scale=1" />
<title>对 LemonBoss 的映射 · LemonBoss PRD</title>
<link rel="preconnect" href="https://fonts.googleapis.com" />
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin />
<link href="https://fonts.googleapis.com/css2?family=Fraunces:opsz,wght@9..144,300;9..144,500;9..144,700&family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500;700&display=swap" rel="stylesheet" />
<link rel="stylesheet" href="assets/shell.css" />
</head>
<body data-page="arch-mapping.html">
<div class="shell">
  <aside id="sidebar"></aside>
  <main class="page">
    <header class="page-head">
      <div class="crumb"><span class="pill">04 · ARCHITECTURE</span><span class="sep">/</span><span>对 LemonBoss 的映射</span></div>
      <h1>对 LemonBoss 现有架构的映射</h1>
      <p class="deck">99% 是在现有抽象上加薄薄一层。本页列出每一项能力的对照:REUSE(沿用)/ NEW(新增)/ PATCH(小改)。</p>
    </header>
    <article class="body">

      <section>
        <h2>能力映射总表</h2>
        <table class="dense">
          <thead><tr><th>能力</th><th>新组件</th><th>对应现有模块</th><th>改动</th></tr></thead>
          <tbody>
            <tr><td>Agent 启动与会话复用</td><td>—</td><td><code>packages/acp-core/protocol/ClaudeCliProtocol.js</code> + <code>server/src/core/session-pool.js</code></td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>多 Agent 调用 facade</td><td>—</td><td><code>server/src/core/agent-manager.js</code> · <code>master-session.js</code></td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>任务 → 步骤拆分</td><td>—</td><td><code>server/src/tools/orchestrate-tool.js</code></td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>DAG 依赖解析</td><td>—</td><td><code>server/src/core/scheduler.js</code> (<code>getExecutionGroups</code>)</td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>Pipeline 描述结构</td><td>—</td><td><code>server/services/pipeline-service.js</code></td><td><span class="tag REUSE">REUSE</span> · units.json 复用 step 结构</td></tr>
            <tr><td>重试 + 反思</td><td><code>retryWithReflection()</code> 写在 loop-controller.js 内</td><td>参考 <code>server/services/evolution-loop.js</code> (PRAE) 思路,但<b>不抽公共</b></td><td><span class="tag NEW">NEW</span> · P5 再考虑合并</td></tr>
            <tr><td>ACP / Claude CLI 协议层</td><td>—</td><td><code>packages/acp-core/</code></td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>Agent 模板注册</td><td>6 个新模板</td><td><code>system/templates/_registry.json</code></td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>三层 Loop 编排</td><td><code>LoopController</code></td><td><code>server/src/core/loop-controller.js</code></td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>PRD/SDD 文档存储</td><td><code>DocStore</code></td><td><code>server/services/doc-store.js</code></td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>Unit 状态机</td><td><code>UnitRegistry</code></td><td><code>server/services/unit-registry.js</code></td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>HTTP / SSE 入口</td><td><code>/api/devloop/*</code></td><td><code>server/routes/devloop.js</code> → 挂在 <code>server/index.js</code></td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>Web 看板</td><td><code>DevLoopDashboard.vue</code></td><td>放在 <code>web/src/views/</code>,复用 <code>PipelineEditor</code> DAG 渲染</td><td><span class="tag NEW">NEW</span></td></tr>
            <tr><td>结果持久化根</td><td>—</td><td><code>data/companies/{cid}/projects/{pid}/</code></td><td><span class="tag REUSE">REUSE</span> · 沿用 company-scoped 文件根</td></tr>
            <tr><td>进程沙箱 / 端口管理</td><td>—</td><td><code>server/src/engine/process-guard.js</code></td><td><span class="tag REUSE">REUSE</span></td></tr>
            <tr><td>WS 流式</td><td>—</td><td><code>server/routes/claude-chat-ws.js</code></td><td><span class="tag REUSE">REUSE</span> · DevLoop SSE 走 HTTP 复用同套帧</td></tr>
          </tbody>
        </table>
      </section>

      <section>
        <h2>分层视图</h2>
        <p>三色块代表三种改动类型;越靠下越基础,越靠上越业务。</p>
        <svg viewBox="0 0 1100 360" width="100%" role="img" aria-label="分层视图">
          <defs><marker id="arrM" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="7" markerHeight="7" orient="auto"><path d="M0,0 L10,5 L0,10 Z" fill="#0E0E10"/></marker></defs>
          <g font-family="JetBrains Mono" font-size="12">
            <rect x="40"  y="30"  width="240" height="50" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="160" y="60" text-anchor="middle">routes/devloop.js · NEW</text>
            <rect x="40"  y="110" width="240" height="50" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="160" y="140" text-anchor="middle">core/loop-controller.js · NEW</text>
            <rect x="40"  y="190" width="110" height="50" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="95" y="220" text-anchor="middle">DocStore</text>
            <rect x="170" y="190" width="110" height="50" rx="4" fill="#FCE7E2" stroke="#C7341A"/><text x="225" y="220" text-anchor="middle">UnitRegistry</text>

            <rect x="360" y="30"  width="260" height="50" rx="4" fill="#EAEAEA" stroke="#666"/><text x="490" y="60" text-anchor="middle">core/master-session.js</text>
            <rect x="360" y="110" width="260" height="50" rx="4" fill="#EAEAEA" stroke="#666"/><text x="490" y="140" text-anchor="middle">core/agent-manager.js</text>
            <rect x="360" y="190" width="260" height="50" rx="4" fill="#EAEAEA" stroke="#666"/><text x="490" y="220" text-anchor="middle">core/scheduler.js · core/session-pool.js</text>
            <rect x="360" y="270" width="260" height="50" rx="4" fill="#EAEAEA" stroke="#666"/><text x="490" y="300" text-anchor="middle">tools/orchestrate · call-agent</text>

            <rect x="700" y="30"  width="320" height="50" rx="4" fill="#fff" stroke="#0E0E10"/><text x="860" y="60" text-anchor="middle">packages/acp-core (Protocol)</text>
            <rect x="700" y="110" width="320" height="50" rx="4" fill="#fff" stroke="#0E0E10"/><text x="860" y="140" text-anchor="middle">ClaudeCliProtocol (subprocess)</text>
            <rect x="700" y="190" width="320" height="50" rx="4" fill="#fff" stroke="#0E0E10"/><text x="860" y="220" text-anchor="middle">Claude CLI · local process</text>
            <rect x="700" y="270" width="320" height="50" rx="4" fill="#EAEAEA" stroke="#666"/><text x="860" y="300" text-anchor="middle">services/evolution-loop.js · 不动</text>

            <g stroke="#0E0E10" fill="none" marker-end="url(#arrM)">
              <line x1="280" y1="55" x2="355" y2="55"/>
              <line x1="280" y1="135" x2="355" y2="135"/>
              <line x1="280" y1="215" x2="355" y2="215"/>
              <line x1="620" y1="55" x2="695" y2="55"/>
              <line x1="620" y1="135" x2="695" y2="135"/>
              <line x1="620" y1="215" x2="695" y2="215"/>
              <line x1="620" y1="295" x2="695" y2="295"/>
              <line x1="160" y1="160" x2="160" y2="185"/>
              <line x1="225" y1="160" x2="225" y2="185"/>
            </g>
          </g>
        </svg>
        <p class="sig"><span style="color:#C7341A;font-weight:700">红</span> 新增 · <span style="color:#666">灰</span> 复用 · <span style="color:#8a6b00">黄</span> 补丁 · <span style="color:#0E0E10">黑</span> 底层依赖</p>
      </section>

      <section>
        <h2>为什么 P1~P4 不抽公共 retry 函数</h2>
        <p>EvolutionLoop 当前实现 PRAE (Perceive → Reason &amp; Plan → Execute → Evaluate → Reflect),里面有「失败 → 反思 → 重试」逻辑。直觉上 L1 可以共用,但<b>初版决定不抽</b>:</p>
        <ul>
          <li><b>避免回归风险:</b>EvolutionLoop 跑的是 Skill 进化,目前在生产里运行。动它意味着要把 Skill 进化的回归一起做。</li>
          <li><b>反思 prompt 差异大:</b>Skill 进化的反思是「Skill 评分低,怎么改 prompt」;L1 的反思是「单测失败,代码哪里错」。共用会让 prompt 模板变成「上帝模板」,反而难维护。</li>
          <li><b>80 行不算多:</b>P2 在 loop-controller.js 内重写一份 retryWithReflection 也就 80 行,DRY 不必这么早追。</li>
        </ul>
        <pre class="code"><span class="c">// P2 直接在 server/src/core/loop-controller.js 写一份新的</span>
<span class="k">async function</span> <span class="n">retryWithReflection</span>(<span class="v">unit</span>, <span class="v">budget</span>) <span class="p">{</span>
  <span class="k">let</span> failures = <span class="p">[]</span>;
  <span class="k">for</span> (<span class="k">let</span> i = <span class="v">0</span>; i &lt; budget; i++) <span class="p">{</span>
    <span class="k">await</span> <span class="n">agentManager</span>.<span class="n">callAgent</span>(<span class="s">'coder'</span>, <span class="p">{</span> spec: unit.spec, history: failures <span class="p">}</span>);
    <span class="k">const</span> r = <span class="k">await</span> <span class="n">agentManager</span>.<span class="n">callAgent</span>(<span class="s">'tester'</span>, <span class="p">{</span> unit <span class="p">}</span>);
    <span class="k">if</span> (r.<span class="n">ok</span>) <span class="k">return</span> <span class="p">{</span> ok: <span class="v">true</span>, retries: i <span class="p">}</span>;
    failures.<span class="n">push</span>(r.<span class="n">failures</span>);
  <span class="p">}</span>
  <span class="k">return</span> <span class="p">{</span> ok: <span class="v">false</span>, reason: <span class="s">"budget_exceeded"</span> <span class="p">}</span>;
<span class="p">}</span>
<span class="c">// P5 视实测情况再决定是否与 evolution-loop 合并到 packages/ 公共目录</span></pre>
        <p class="sig">这条改动同步影响 plan-migration.html 的 P2 阶段:retry 函数从「抽公共」改为「直接新写」。</p>
      </section>

      <section>
        <h2>不动的边界</h2>
        <div class="callout">
          <b>本次改造绝不触碰:</b>
          <ul>
            <li>ACP 协议本身(<code>packages/acp-core/</code>)</li>
            <li>Master / Orchestrator 已有行为(仅注册新的 6 个 Agent 模板)</li>
            <li>WebSocket 路由(<code>/ws/claude</code>)</li>
            <li>SkillsMarket / Arena 路径下任何文件</li>
            <li>认证 / 用户系统(<code>routes/auth.js</code>, <code>routes/users.js</code>)</li>
          </ul>
        </div>
      </section>

    </article>
    <nav class="prev-next"></nav>
    <footer class="site"><div><b>Section</b>04 · Architecture</div><div><b>Page</b>arch-mapping.html</div></footer>
  </main>
</div>
<script src="assets/shell.js"></script>
</body>
</html>