Files
penguin-harness/packages/docs/content/architecture.zh.md
T
Yaowei Zheng 45bfae6e94 Initialize repository with harness code and assets
Initial import of all source code, config, and README assets: the
packages workspace (cli, core, server, web, docs, landing, skills),
build scripts, tooling config, and CI workflows.

Includes the data-layout revision made on this branch: the local data
root defaults to ~/.penguin/data (PENGUIN_HOME still overrides; the
installer keeps its binaries in ~/.penguin), and every Agent lives
under <project>/agents/<agent>/ — path helpers, the three
agent-enumeration scans, the system prompt, built-in Skills, tests
and docs all follow the new layout.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018ihk8iQuo3kv2aPjAYEPuR
2026-07-19 14:06:53 +08:00

10 KiB

title, description
title description
架构总览 三接口边界、context_engine 与 OmniMessage 如何把 SDK、CLI、Server、Web 组织成一个系统。

PenguinHarness 是一个 pnpm monorepo,核心是 @prismshadow/penguin-core 中的执行引擎;CLI、Server 与 Web App 都只是这同一个引擎的不同「Human 实现」。

分层结构

┌─────────────┐  ┌─────────────────────────────┐
│   CLI       │  │  Web App (React SPA)        │
│  (penguin)  │  │    ↑ OmniMessage over SSE   │
│             │  │  Server (Hono + SQLite)     │
└──────┬──────┘  └──────────────┬──────────────┘
       │      session.run(...)  │        ← Human 边界
┌──────┴────────────────────────┴──────────────┐
│  core: context_engine(ReAct 循环)           │
│    ├── LLMInterface ──→ AgentHub ──→ 各模型  │
│    ├── EnvironmentInterface ──→ 内置工具      │
│    ├── Agent State(可编辑文件)              │
│    └── Trace(追加式 JSONL)                  │
└──────────────────────────────────────────────┘
包 角色
packages/core SDK 与执行引擎:context_engine、OmniMessage、LLM/Environment 接口、State 与 Trace
packages/cli 终端 Human 实现:REPL 与单次运行,直接内嵌 core
packages/server Web Human 实现:HTTP 承接输入与审批,SSE 推送输出流
packages/web 渲染层 SPA:按 OmniMessage 协议流式渲染,不含业务引擎
packages/skills 内置技能库(SKILL.md 文件集合)

职责划分

判断一个设计归属哪一层,只看它的事实来源在哪里。四层的分工:

层 承担 不承担
SDK(core) 协议与执行:让消息流动起来的一切 持久化用户态、多用户、任何渲染
Server 常驻进程与多用户运行时 引擎逻辑(全部委托给 SDK)
文件层(~/.penguin/data) 一切可编辑的定义与一切被记录的历史 任何计算
CLI / Web 渲染与交互 业务状态

逐项对应(设计 → 归属 → 承载文件或模块):

设计 归属 承载
OmniMessage 协议、消息解析与分片聚合 SDK core/src/omnimessage/,见 OmniMessage 协议
ReAct 循环、补发、重连、压缩 SDK core/src/engine/context-engine.ts,见 Agent 运行循环
审批机制(每个 tool_call 一次决策) SDK ApproveFn(core/src/interfaces.ts);具体模式由 CLI/Server 注入
工具执行与统一收尾 SDK core/src/environment/,见工具与审批
模型接入(Provider 协议适配) SDK → AgentHub core/src/llm/ + @prismshadow/agenthub,见模型与 Provider
Trace 写入与 Session 恢复逻辑 SDK core/src/trace/(记录本体在文件层)
Subagent 派生与消息回流 SDK run_subagent 工具 + SubagentRunner 注入
多用户认证与 Project 授权 Server server/src/auth/、server/src/services/project-service.ts
Session 索引、并发互斥、SSE 转发 Server server/src/runtime/,见 Server API
定时任务(Schedule 执行) Server server/src/runtime/scheduler.ts;任务定义在文件层 agent_state/schedule/*.toml
审批模式持久化与人工决策 Server server/src/runtime/approvals.ts + SQLite
用量落库与成本统计 Server server/src/runtime/usage-recorder.ts、services/usage-service.ts
Agent 行为定义(Prompt、运行参数) 文件层 agent_state/system_config.yaml、AGENTS.md,见配置参考
Skill 文件层 agent_state/skills/<name>/SKILL.md,见技能系统
密钥 文件层 Vault:agent_state/.vault.toml;模型凭据:.project_config.toml(均 0600)
模型表与默认模型 文件层 <project>/.project_config.toml
运行历史(恢复的唯一事实来源) 文件层 traces/<date>/<session>_<index>.jsonl,见 Session 与 Trace
Benchmark 题库与评分 文件层 benchmarks/<id>/,见自我进化
快照 文件层 snapshots/v<version>.tar.gz;导入导出服务在 Server
流式渲染、审批 UI、统计图表 CLI / Web cli/src、web/src(纯渲染,不含引擎逻辑)

一句话判定:**能编辑的与被记录的在文件层;让消息流动起来的在 SDK;需要常驻进程与多用户的在 Server;其余是渲染。**Server 的 SQLite 只存索引与聚合,从不与文件层争当事实来源。

源码结构

各包的目录设计(职责单一、按层拆分;文件头注释即该文件的设计说明):

packages/
├── core/src
│   ├── agent.ts / session.ts       # createAgent 组装层与 Session(run / compact / generateTitle)
│   ├── session-title.ts            # 一次性标题生成(旁路 LLM 调用,不入 Trace)
│   ├── engine/context-engine.ts    # ReAct 循环编排:轮生命周期、审批、补发、重连、压缩
│   ├── omnimessage/                # types.ts 协议类型 · builders.ts 构造函数 · aggregate.ts 分片聚合
│   ├── llm/                        # generative-model.ts AgentHub 适配 · tool-call-ids.ts id 唯一化
│   ├── environment/                # environment.ts 执行与收尾 · tools/ 注册表、6 个内置工具、后台会话
│   ├── state/                      # paths · default-config · project-config · model-catalog
│   │                               # agent-state(Skill 安装、提示词装配)· agent-vault · builtin-agents
│   ├── trace/                      # writer.ts 追加式 JSONL · resume.ts 回放恢复
│   └── internal/                   # 日期与 Session 辅助
├── cli/src                         # commander 入口 + run / chat / config / serve 命令与审批交互
├── server/src                      # app 组装 · db(node:sqlite)· auth · http/routes · runtime · services
├── web/src                         # api 客户端 · state · lib/omni 流渲染 · components · features 各页面
├── skills/                         # 加载器 + skills/<name>/SKILL.md 技能库
├── landing/                        # 产品落地页(含博客)
└── docs/                           # 本文档站

server 与 web 的内部结构分别见 Server API 与 Web App 指南。

三接口边界

context_engine 是整个系统的核心,它只做两件事:维护线性消息历史,以及在三个接口之间编排事件流。它只认识 OmniMessage,不做任何协议转换:

  • Human——用户侧边界。它不是一个接口类:SDK 的唯一入口 session.run(newMessages, { approve, signal }) 就是 Human 边界本身。输入是新增的 OmniMessage 列表与审批回调,输出是流式 OmniMessage。CLI 与 Server 是它的两种实现形态。
  • LLM——模型侧接口(LLMInterface)。把 OmniMessage 翻译为模型网关 AgentHub 的请求,把流式事件翻译回 OmniMessage。所有 Provider 协议适配都在 AgentHub 内完成,core 不直接依赖任何模型厂商 SDK。
  • Environment——工具执行接口(EnvironmentInterface)。执行通过审批的工具调用,把结果以流式 OmniMessage 送回。

这一边界设计的意义:引擎内核不含任何 Provider、工具或 UI 细节,三侧实现均可按配置替换(本地 shell、其他执行沙箱;CLI、Web、程序化调用),而互不影响。接口签名详见接口契约。

一个 Task 的数据流

  1. Human 把 Prompt(OmniMessage 列表)交给 session.run;
  2. 引擎发起一次 Request:经 LLMInterface 流式产出 partial_* 与完整消息;
  3. 每个完整的 tool_call 触发一次 approve 审批;通过后交 Environment 并发执行;
  4. 工具输出按原始顺序回填,进入下一轮 Request;
  5. 某轮不再产生 tool_call(最终答复)时 Task 结束。

全程的每条消息与事件同时流向两个去处:实时输出给 Human,以及追加写入 Trace。运行循环的细节(中断、重连、压缩)见 Agent 运行循环。

状态层

引擎之下是纯文件的状态层,数据根目录为 ~/.penguin/data(PENGUIN_HOME 可改),按 <project>/agents/<agent>/ 组织:

  • Agent State——agent_state/ 目录:system_config.yaml、AGENTS.md、Skills、Vault。Agent 的全部行为定义都是可编辑文件。
  • Project 配置——.project_config.toml:模型表与凭据,模型身份恒为 (provider, model_id) 二元组。
  • Trace——traces/ 目录:追加式 JSONL,恢复 Session 的唯一事实来源。

Server 额外维护一个 SQLite 索引库(用户、授权、用量统计),但从不复制文件层的事实——CLI、SDK 与 Web 共用同一份数据目录,可以混用。

关键设计决策

  • 一个协议,三种职责:OmniMessage 同时是 SDK 对外接口、Trace 落盘格式与引擎内部通货——「流出去的」「存下来的」「模型看到的」是同一种东西。
  • 错误收敛为消息:LLM 与 Environment 从不向引擎抛异常;结果携带五值 stop_reason(completed | failed | aborted | timeout | malformed),仅 LLM 侧的 timeout / malformed 触发引擎内重连。
  • 薄模型层:core 只定义 LLMInterface,Provider 适配全部下沉到 AgentHub(@prismshadow/agenthub),因此支持任意 OpenAI 兼容端点,见模型与 Provider。

源码入口:packages/core/src/engine/context-engine.ts、packages/core/src/interfaces.ts。