# 10. 运行时架构与并发模型 > 一个主进程 + N 个子进程。主进程是"中枢神经",子进程是独立运行的 Agent。 --- ## 10.1 并发模型 ``` ┌──────────────────────────────────────────────────────────────────┐ │ LemonBoss Server (主进程) │ │ Node.js 单进程 + EventEmitter │ │ │ │ ┌─────────────────────────────────────────────────────────┐ │ │ │ Agent 进程管理器 │ │ │ │ │ │ │ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │ │ │ │ ACP #1 │ │ ACP #2 │ │ ACP #3 │ ... │ │ │ │ │ Master │ │ CEO │ │ 编剧部 │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ │ 子进程 │ │ 子进程 │ │ 子进程 │ │ │ │ │ │ PID 1001 │ │ PID 1002 │ │ PID 1003 │ │ │ │ │ └────┬─────┘ └────┬─────┘ └────┬─────┘ │ │ │ │ │ │ │ │ │ │ │ └─────────────┼─────────────┘ │ │ │ │ stdin / stdout pipe │ │ │ │ (JSON-RPC 双向通讯) │ │ │ └─────────────────────┼────────────────────────────────────┘ │ │ │ │ │ ┌─────────────────────┼────────────────────────────────────┐ │ │ │ 工具拦截器 (Tool Interceptor) │ │ │ │ │ │ │ │ Agent stdout 输出工具调用 → 主进程拦截: │ │ │ │ │ │ │ │ task.assign → 路由消息到目标 Agent 的 stdin │ │ │ │ task.report → 路由消息到上级 Agent 的 stdin │ │ │ │ role.create → AgentScaffold 创建目录 → 启动子进程 │ │ │ │ pipeline.set → 存储 DAG 到 SQLite │ │ │ │ org.status → 查询 SQLite 返回全局状态 │ │ │ │ skill.reuse → 复制 Skill 引用 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ ┌──────────────────────────────────────────────────────────┐ │ │ │ SQLite (唯一共享存储) │ │ │ │ │ │ │ │ agents 表 ← Agent 注册信息和状态 │ │ │ │ messages 表 ← Agent 间所有通讯记录 │ │ │ │ tasks 表 ← 任务队列和进度 │ │ │ │ goals 表 ← 目标定义与追踪 │ │ │ │ loop_runs 表 ← Loop 运行记录 │ │ │ │ memories 表 ← Agent 共享记忆 │ │ │ └──────────────────────────────────────────────────────────┘ │ │ │ │ ┌─────────────┐ ┌─────────────┐ ┌──────────────────┐ │ │ │ Cron 调度器 │ │ Gateway Hub │ │ HTTP/WS API 层 │ │ │ │ node-cron │ │ 飞书/WA/钉钉│ │ Express + WS │ │ │ └─────────────┘ └─────────────┘ └──────────────────┘ │ │ │ └──────────────────────────────────────────────────────────────────┘ ``` ### 并发方式 | 维度 | 方案 | 说明 | |------|------|------| | **进程级并发** | 每个 Agent = 独立子进程 | 操作系统级别并行,互不阻塞 | | **通讯方式** | stdin/stdout pipe | JSON-RPC over stdio(ACP 标准协议) | | **共享存储** | SQLite (WAL 模式) | 主进程单写,子进程通过工具请求读取 | | **调度方式** | 异步事件驱动 | Agent A 发任务后不等待,Agent B 完成后异步回报 | ### 子进程的启动方式 ``` // 主进程启动一个 Agent 的 ACP 子进程 const child = spawn('claude', ['--agent', agentDir], { stdio: ['pipe', 'pipe', 'pipe'], // stdin, stdout, stderr 全部管道化 cwd: path.join(agentDir, 'workspace'), env: { ...process.env, ...agentEnv }, }); // 注入 System Prompt (agent.md + soul.md + ... 拼接) child.stdin.write(systemPrompt); // 监听 Agent 的输出 (工具调用) child.stdout.on('data', (data) => { const toolCall = parseJSON(data); toolInterceptor.handle(toolCall, agentId); }); ``` --- ## 10.2 Agent 生命周期 ``` ┌─── create ───┐ ▼ │ ┌─────────────────────────────────────┐ │ AgentRegistry │ │ │ │ ┌───────┐ ┌─────────┐ ┌───┐ │ │ │ idle │───→│ running │───→│stop│ │ │ │ │ │ │ │ │ │ │ │ 目录已│ │ ACP 子 │ │ACP│ │ │ │ 创建 │ │ 进程运行│ │关闭│ │ │ └───────┘ └─────────┘ └───┘ │ │ │ │ │ │ └──── error ───┘ │ │ │ │ destroy → 删除目录 + 注销 │ └─────────────────────────────────────┘ create: AgentScaffold 生成目录 → 注册到 SQLite → 注册到 Bus start: 加载 Bootstrap Files → 启动 ACP 子进程 → 注入 System Prompt stop: 关闭 ACP 子进程 → 状态改为 idle destroy: 停止 → 从 Bus 注销 → 删除目录 → 从 SQLite 删除 ``` --- ## 10.3 框架核心服务 | 服务 | 职责 | |------|------| | **AgentRegistry** | Agent CRUD + 生命周期管理 (create/list/get/update/destroy) | | **AgentBus** | 消息路由 + 持久化 + 广播 | | **AgentProcessManager** | ACP 子进程启动/停止/监控/重启 | | **ToolInterceptor** | 拦截 Agent 的工具调用,执行框架级操作 | | **CronScheduler** | 解析 heartbeat.md,按时触发 Agent | | **GatewayHub** | 外部 IM 消息接入 (飞书/WhatsApp/钉钉) | | **GoalTracker** | 目标进度追踪与评估 | | **PipelineRouter** | 按 DAG 自动流转 Pipeline 节点 | --- ## 10.4 技术选型 | 组件 | 技术方案 | 理由 | |------|----------|------| | **AI 内核** | Claude Code ACP | 已验证的四维架构 (Protocol + Transport + Launcher + Runtime) | | **后端主进程** | Node.js + Express | 事件驱动,天然适合管理多子进程 | | **子进程通讯** | stdio pipe (JSON-RPC) | ACP 标准协议,零依赖 | | **共享存储** | SQLite (better-sqlite3, WAL) | 轻量级,主进程单写多读,无外部依赖 | | **实时推送** | WebSocket + SSE | 前端仪表盘实时监控 | | **定时任务** | node-cron | 解析 heartbeat.md 的 cron 表达式 | | **进程管理** | Node.js child_process | 子进程启动/停止/信号管理 | | **前端** | Vue 3 + Vite | 控制面板和监控仪表盘 | | **日志** | Winston + JSONL | 结构化日志 + Agent 对话记录 | | **网关** | 适配器模式 | 统一不同 IM 的消息格式 | | **包管理** | pnpm workspace | Monorepo 管理 | --- ## 10.5 一个完整请求的生命周期 ``` 用户在飞书里说: "最近短剧市场怎么样?" │ ▼ [1] Gateway Hub (飞书适配器) │ 消息格式化为标准格式 ▼ [2] AgentProcessManager │ 找到 Master ACP 子进程 │ 通过 stdin 注入消息 ▼ [3] Master ACP 进程 (子进程 PID 1001) │ 读取 agent.md + soul.md → 推理 │ 决定: 让 CEO 调数据部去分析 │ stdout: task.assign({ to: "cxo-ceo-001", ... }) ▼ [4] ToolInterceptor (主进程) │ 拦截 task.assign │ 写入 SQLite messages 表 │ 注入到 CEO 的 stdin ▼ [5] CEO ACP 进程 (子进程 PID 1002) ← 与 Master 并行运行 │ 收到任务 → 推理 │ stdout: task.assign({ to: "role-data-001", ... }) ▼ [6] ToolInterceptor → 路由到数据部 ▼ [7] 数据部 ACP 进程 (子进程 PID 1005) ← 与 CEO 并行运行 │ 调用爬虫 Skill → 采集数据 │ 分析完成 │ stdout: task.report({ to: "cxo-ceo-001", ... }) ▼ [8] ToolInterceptor → 路由回 CEO ▼ [9] CEO 收到数据 → 汇总 │ stdout: task.report({ to: "master-001", ... }) ▼ [10] ToolInterceptor → 路由回 Master ▼ [11] Master 收到汇总 → 组织回复 │ stdout: reply({ content: "最近市场分析..." }) ▼ [12] Gateway Hub → 飞书 → 用户 整个过程中 Master / CEO / 数据部 可能在同时做其他事 (心跳、其他任务) 所有步骤全异步, 不阻塞 ```