跳转到内容

架构

系统级约束与长期设计原则。

User-facing product terms (Chinese in i18n/glossary.md):

RoleEnglishChineseMeaning
Long-running process / connect targetHabitat栖息地One process hosts multiple digital lives (agent subjects) and human assets (user); connect / token / restart target
External connectors (class)Portal入口Class of ways into Habitat; four forms: application / browser / mcp / cli
Portal form入口形态Realization kind of a Portal
Application-form PortalShell / 应用形态Form application — windowed SPA(desktop / mobile / web)。Not app frame
Browser-form Portal浏览器形态入口Form browser — browser extension(MV3);src/portal/extensionnot Web Shell
MCP-form PortalMCPMCP 形态入口Form mcp — Habitat /mcpmcp-server)。Inbound mcp-client is not a Portal
CLI-form PortalCLICLI 形态入口Form clianima CLI;src/portal/cli
Remote-tool registrantOutpost前哨Unreachable local app that remote_tools.attach (Portal-embedded companion or standalone tool); not a Portal
ShellShellApplication-form Portal(desktop / mobile / web)。Not Habitat; not app frame; not browser Portal
app frameapp frame应用布局SPA chrome in src/client/app-frame (AppFrame); viewport-driven; orthogonal to Shell
Admin / inspect UI (legacy Habitat)Habitat (UI)栖息地Area under /habitat/*; “Open Habitat” vs “Connect to Habitat”
Admin home pageDashboard仪表盘/habitat/dashboard only; other Habitat routes keep their own labels
Message bridgesGatewayGatewayDiscord / WeChat — not a Portal
Protocol / code identifiers协议/代码标识/rpc/v1HabitatRPC/1.0habitat_*habitat_runtime_configdev:habitat

Verbs: connect to Habitat (URL + token); open Habitat (admin UI); reach via a Portal (Shell / browser extension / MCP / CLI).

Code layout: src/portal/{app,extension,cli};MCP-form implementation stays in src/host/capabilities/mcp-server. See docs/modules/portal.md.

Estate Body (VM / OS / network under the four-layer model) is the cognitive “what I run on” for a subject — not the Habitat process name.

User copy says Habitat. Storage/RPC identifiers use habitat_* / HabitatRPC/1.0 / /rpc/v1.

LayerStorageWho reads/writes
Bootstrap~/.anima/config.yaml (database, http, redis)platform/boot only; install/ops edit YAML
RuntimePostgreSQL habitat_runtime_configEngine, tools, Shell Habitat settings, Habitat UI config.*

Legacy hub_* / console protocol aliases and dual-write keys have been removed; use Habitat identifiers only.

  • 记忆体系内部可以分层,但 LLM 只看到一个统一入口
  • 记忆编排内建于运行时;LLM 不控制记忆流水线
  • 凭证管理是一等系统关切
  • Habitat runtime configuration (LLM, compression, integrations) is persisted in PostgreSQL (habitat_runtime_config); ~/.anima/config.yaml holds bootstrap only (database, http, redis) for cold start — not editable via Shell or Habitat UI API
  • Habitat may start without LLM configured; first-time setup happens in Shell Settings → Habitat (persist to PG). Saving runtime config hot-applies in memory (no Habitat restart). Missing llm must not block cold start.
  • Habitat hosts browser /web/* whenever Web dist is present (no config switch; run just pack web for source deploy). Source just dev habitat skips hosting so Vite serves the UI.
  • Asset management is a first-class system concern

Bootstrap and runtime are not merged into a single config object (no AnimaConfig / animaConfigSchema supersets). Types: bootstrapConfigSchema vs runtimeConfigSchema / Config.data: RuntimeConfig. CLI cold paths connect via internal withPlatformDb and receive runtime only. Legacy runtime keys in config.yaml are ignored (optional startup warn).

KindSections (examples)After UI save
Livecompression, memory, fts, cjk, clarify, browser, firecrawl, models, tts, auto_llm, gateway tool_displayConsumers read Config.data each use; snapshot update is enough
Transferredllm, i18n, embedding, mcp_servers, acp_agents, discord / weixin / gateway platforms, worldsSnapshot update plus section apply (re-init registries / reconnect / re-bind)
Bootstrapdatabase, http, redisEdit YAML; process restart required
Hard / rareeventbus (queue already built)Prefer process restart if changing key_prefix etc.
  • 系统提示词是架构的一部分,而非临时字符串拼接

数字生命由内而外分层。每一层回答一个不同的核心问题:

┌───────────────────────────────────────────────┐
│ ① Consciousness │
│ "What am I aware of right now?" │
│ The LLM runtime stream — the innermost now. │
│ Not persisted; flows and fades. │
├───────────────────────────────────────────────┤
│ ② Self │
│ "Who am I?" │
│ └── existence_anchor (nearly immutable) │
│ └── self_model (updatable) │
│ └── personality_baseline (semi-stable) │
│ └── direction │
│ └── metacognition │
│ └── autobiography_summary (append-only) │
│ See [`self-layer.md`](../cognition/self-layer.md) │
├───────────────────────────────────────────────┤
│ ③ Memory │
│ "What do I know / remember?" │
│ └── Semantic (incl. `procedural` type) │
│ └── Episodic │
│ └── Limbic / Imprint │
│ See [`memory.md`](../cognition/memory.md) │
├───────────────────────────────────────────────┤
│ ④ Estate │
│ "What do I have / rely on?" │
│ ├── Body: VM / OS / network / toolchains │
│ │ (Estate cognitive body — not Habitat) │
│ ├── Internal assets: notes, projects, code │
│ └── External assets: email, accounts, creds│
│ Credentials: see "Credential System" below │
└───────────────────────────────────────────────┘
  • 自上而下依赖:感知层内容沉淀为自我层;自我层决定什么进入记忆层;记忆层与运行需求驱动资源层需求。
  • 自我与感知:感知是流动的觉察;自我是从中沉淀下来的稳定「我」。
  • 自我与记忆:自我回答「我是谁」;记忆回答「我知道什么」。二者同级、性质不同。
  • 资源层在最外层——不是「我是谁」,而是「我拥有什么、依托什么运行」。身体与资产在此交汇,作为边界的延伸。

Structured business data (tasks, notes, email accounts/messages, future memory migrations) converges on a single entities PostgreSQL table with component tags (task_list, task_item, email_account, …). Self layer self_blocks stays physically separate. See entity-model.md(remove / deleteComponent / deleteEntity 软删与回收站;Shell Entity 模块见 ../modules/entity.md)。

Shell UI /tasks and /email are primary module entries (entity-backed); legacy Habitat email route removed.

Entity deep links / overlay / clipboard use Anima URI (anima:{id}?component=…&present=…). Structured persistence still uses numeric entity ids. See anima-uri.md.

Repository layout (Phase 1 — host/client)

Section titled “Repository layout (Phase 1 — host/client)”

Target layout is feature modules under src/features/<slug>/ (UI + protocol + Habitat adapter + domain + plugin.ts). Habitat 管理台 uses the same module shape as chat/task — not a separate admin-* stack. src/features/companion/ is legacy naming; do not add new products there.

End state: Habitat RPC per feature; business methods use POST|WS /rpc/v1 (same envelope). Public health/TLS probes and binary methods (e.g. tts.synthesize) are Habitat RPC REST with auth: optional or Bearer as declared in registry.

Authoritative spec: repository-topology.md.

Host stack: src/host/{kernel,core,engine,capabilities,platform}。Client: src/client/{portal-sdk,app-frame}。Design system: src/ui-kit/(与 shared/ 并列)。Portal Shell: src/portal/app/{tauri,web}

UI/UX design system (dimensions, visual foundations, components, interaction patterns) → docs/ui/. Agent hard bans / API → .agent/rules/ui-dimensions.md, .agent/rules/frontend-ui.md.

LayerPlatform-native?LocationData path
Shell(壳子维)Yessrc/portal/app/tauri, companion, Habitat bindingTauri IPC / commands
app frameLayout follows viewport; settings chrome follows layout bandsrc/client/app-frameAppFrameHabitat RPC(Feature RPC)
Habitat UIShell embed(ordinary feature)src/features/habitat(UI + plugin.habitat.rpcHabitat RPC(WS + HTTP POST /rpc/v1
Companion hostOverlay WebView-host(first-party)src/features/companion/companion(spa attach;thin shell IPC/FS)Habitat RPC + remote_tools.attach

Navigation and main layout must use useLayoutMode() / viewport breakpoints (layout dimension). Do not lock app layout with getShellKind(). Interaction (context menu / long-press / Enter-to-send) uses portal-sdk interaction APIs. Visual, component, and pattern norms all adapt through the same three dimensions.

Boundaries: app-frame and src/features/*/ui reach Habitat via portal-sdk + Feature RPC. Remote tool registration (remote_tools.attach + tool.*) is only for local apps Habitat cannot dial (today: companion first-party overlay / embedded-overlay; shell provides window/IPC/FS only; no Node sidecar). Product surfaces (Chat, etc.) do not attach. Dialable peers use MCP. See .agent/rules/frontend-features.md, docs/ops/habitat-rpc.md.

Habitat sidebar is grouped (not flat storage tables). Map new features onto these user-visible concepts:

GroupCognitive layerRoutes (representative)
RuntimeEstate + opsdashboard, config, cron
MemoryMemorymemory browse sub-routes, sleep, auto-llm-runs
SelfSelfself-layer, system-prompt
EstateEstatesubjects, worlds
CapabilitiesEstate (tools)tools, commands, mcp, acp, remote-tool instances

FTS 索引维护在 Memory 下(非顶级)。勿新增未映射到上述分组的扁平导航项。

四层模型借鉴认知心理学与 Hindsight 的四网络记忆架构,并有两项根本扩展:感性(情绪)记忆与资源层(资产作为一等公民),以及将自我层从记忆层中独立出来。

数字生命在何处存在、如何存在、能做什么——由两个独立但协作的子系统共同约束。

问题:此刻是什么样的场景?

场景感知是约束——调节语气、距离、记忆召回偏好与主动性。它不是权限系统,而是调节在场感。

示例维度(非穷尽):

  • 话题:情感 / 职业 / 技术 / 哲学 / 历史 / 文学 / 日常
  • 活动:角色扮演 / 游戏 / 创作 / 编程 / 阅读
  • 氛围:放松 / 专注 / 深夜 / 亲密 / 紧急

Operation: Runs continuously without explicit switch commands. Inferred from dialogue, time, frequency, etc. See time-perception.md.

Question: what is my host / runtime quiet state?

Distinct from scene awareness: banded host and process markers (disk, RSS, deps, MCP/ACP) live as a session-static system-prompt copy, with event-level Inbox notifications on change. See environment-awareness.md.

问题:此刻我能使用哪些工具与数据?

能力面具是约束——绑定工具集、数据范围与凭证权限。同一数字生命可在不同会话或任务中佩戴不同面具,以防权限泄漏与工具污染。

示例面具:

  • 开发者:终端、代码读写、ACP Cursor
  • 维护者:逸灵风配置、部署、数据库
  • 创作者:文件、笔记、媒体生成
  • 研究:网页搜索、论文检索
  • 角色扮演:仅对话上下文,无外部工具
  • 默认:基础聊天 + 有限查询

运行方式: 在会话边界、显式命令或场景感知触发时切换。每个面具是声明的工具/数据范围,而非独立身份。

Scene awareness (soft tuning)
│ tone, distance, recall bias
Capability mask (hard constraints)
│ tools, permissions, data scope
Agent behavior
  • 场景感知推断「我们在做什么」→ 可能建议切换面具并调整在场感
  • 能力面具约束「我能做什么」→ 防止跨场景工具误用
  • 二者在最终行为中汇合,但各自独立演化

设计草案请开 GitHub Issue(docs 中不设 design-doc 目录)。

认知类型说明
Episodic(情景)对话归档;保留完整历史
Semantic(语义)跨 sessions 的事实、偏好、经历;“how-to” 知识用 procedural 类型
Limbic(感性)情绪锚点与印记——“感受到什么”
Autobiographical(自传)重要经历的意义;按需召回

Pipeline: nightly sleep-cycle pipeline (in-process Bun.cron builtin-sleep-cycle) extracts and maintains memory; memory_recall retrieves on demand during chat. Details: memory.md, sleep.md.

  • Vault(User 与 Agent 库中的 ECS vault_item)为权威密钥存储;遗留的 ~/.password-store(pass)不会从磁盘删除,但运行时不再读取
  • LLM 永远看不到密钥值——仅见保险库条目元数据与 config 引用
  • Bootstrap config.yaml (cold start, before PG): plaintext or env("KEY") only — not vault(). Runtime PG config: vault("item_id", "field") and env("KEY"); Shell /vault for management
  • 密钥值不会写入会话归档或日志

See ops/security.md.

Production (standalone install CLI): anima service (systemd —user). Auto-restarts after crashes; only systemctl stop stops the service. Source-tree anima does not register service — use just dev habitat for local Habitat.

  • Habitat / service: long-running — Habitat HTTP (/rpc/v1), Discord / WeChat Gateway, cron
  • UI: src/portal/app/tauri + web/dist-* bundled SPA (Chat + Habitat); Habitat does not host /habitat
Terminal window
# standalone install
anima service start # default: systemd --user (does not auto-build Web)
anima service start --foreground # foreground (logs to stdout; systemd unit uses this)
anima service status
# monorepo / worktree
just dev # Habitat (≥10000) + Vite Web (≥5000)
just dev habitat # Habitat foreground (default random ≥10000; not 2658)
just dev web # browser shell Vite HMR from :5000 (set FREEANIMA_URL)
just pack web # source deploy / Habitat /web: build dist before start

工具分三层注册,但对 LLM 暴露为统一的扁平工具列表。LLM 只关心名称与参数,不关心来源。

LLM view — flat tool list:
file_read_file(path) ← local
file_write_file(path, content) ← local
code_execute(code) ← local
query_database(sql) ← MCP server
send_email(to, subject) ← MCP server
acp_cursor(goal, context) ← ACP agent
  • 在逸灵风进程内执行;延迟最低
  • 服务启动时自动注册

第二层:MCP 工具(Model Context Protocol)

Section titled “第二层:MCP 工具(Model Context Protocol)”
  • 连接外部 MCP 服务器(独立进程)
  • 每个服务器可注册多个细粒度工具(单次函数调用)
  • Configure under Habitat runtime mcp_servers (PG habitat_runtime_config); manage in Habitat UI /habitat/mcp (config
    • start/stop + tools). Shell Settings no longer edits this section.
mcp_servers:
database:
command: npx
args: ["@modelcontextprotocol/server-postgres", "postgresql://..."]
transport: stdio
env:
PGOPTIONS: "-c statement_timeout=5s"
remote_habitat:
transport: http # Streamable HTTP — use for FreeAnima Habitat /mcp
url: http://127.0.0.1:2658/mcp
headers:
Authorization: "Bearer fa_at_…"

第三层:ACP 工具(Agent Client Protocol)

Section titled “第三层:ACP 工具(Agent Client Protocol)”
  • 每个外部代理实例注册为一个任务级工具:acp_{name}(goal, context)
  • 用于完整任务委托(编码、分析、预订等);延迟为秒到分钟级
  • Configure under Habitat runtime acp_agents (PG habitat_runtime_config); edit in Shell Settings → Habitat 服务 → 服务配置
# habitat_runtime_config fragment (not config.yaml)
acp_agents:
cursor:
command: ~/.local/bin/agent
args: ["--force", "acp"]
name: cursor
description: "Delegate coding, refactoring, and code review"
维度本地MCPACP
运行位置进程内外部服务器外部代理
粒度函数函数完整任务
延迟毫秒毫秒–秒秒–分钟
配置内置mcp_serversacp_agents

三层可混用;LLM 决定调用顺序;逸灵风负责注册与路由。

轴: 执行期间是否有 user 轮次(与谁触发无关)。

类型User 轮次PG 持久化过程追踪睡眠流水线
Conversationsessions + messages(代码仍称 session消息归档参与(浅睡、梦境输入)
AutoLlmRunauto_llm_runs(经 runAutoLlm()审计行、TTL排除
Delegation (ACP)否(外部)父 conversation + acp_tasks可选经父 conversation 返回结果
Script croncron_logstdout 文件排除

AutoLlmRun 涵盖:cron agent 分支、睡眠 LLM 阶段、未来内部 subagent。工具上下文使用 contextKind: auto_llm,故 memory_remember 不附加 source_conversations。Cron no_agent shell 脚本不是 AutoLlmRun。

问题:此 conversation 是否应继续朝既定结果推进?

Session Goal 是资源层/编排层的进程内自治循环——区别于 ACP 异步委托:

维度Session GoalACP
范围单个 conversation外部 agent 任务
触发/goal slash + 回合后 judge工具调用 + 回调回合
持久化conversations.goal JSONBconversations.acp_tasks
延续同一 SSE 流、回合预算任务更新后的独立消息

Judge uses optional llm.profiles.goal_judge; on judge call/parse failure the goal is paused (warn logged + status line in chat). User messages preempt the loop; /goal pause / /goal resume control auto-continue without clearing state. See goal.md.

Client UI(web/dist SSOT + 原生壳打包)

Section titled “Client UI(web/dist SSOT + 原生壳打包)”

Portal Shell 运行时Tauri(Rust 主进程 + 系统 WebView;桌面与 Android 统一壳层)。壳规则:.agent/rules/tauri-shell.md禁止为 companion 再打 Node sidecar;remote_tools.attach 在第一方伴侣浮层(见 Desktop companion)。

UI 源码产物src/portal/app/web/distbase: /web/)。

客户端UI 加载更新方式
浏览器 / PWAHabitat 托管 /web/*(有 dist 时始终托管)Service Worker 提示新版本后手动重载(不自动刷新);跟 GitHub 包通道
Desktop安装包内 ui/webprepare-tauri-ui);调试可用 Tauri dev按 bake channel 查 GitHub(release=stable latest + semver;canary=tag canary + commit);用户确认后 NSIS 覆盖;可切换 releasecanary;About 可选公共 gh-proxy(默认直连)
Mobile APK安装包内 ui/web(本地同源);Habitat 仅 API同上轨语义;有 APK asset 才提示;确认后系统安装器覆盖;可切换轨;同上代理选择
Standalone嵌入 Web UI 的单文件 animaanima upgrade / --channel release|canary / --proxy …dev / 源码安装不可换轨;curl 安装脚本可用 PROXY=…

壳层保留原生能力(Tauri commands / prefs / 通知等)。无壳内 UI OTA:原生端不从 Habitat 热替换 SPA。允许用户确认后的安装包级覆盖(Desktop 安装包 / Mobile APK / Standalone anima upgrade → 独立前缀如 ~/.anima/standalone)。分发轨 SSOT 为安装包 bake 的 build-meta.channelrelease / canary / dev)。Habitat 配置统一走 settings「连接」(/settings);无独立 bootstrap Habitat 页。

ConceptWhatCode
ShellPortal host runtime(browser / Tauri;形态 web / desktop / mobile)src/portal/app/*portal-sdkgetShellKind / ShellApi / buildTarget
app frameSPA chrome:模块左栏 Rail / 底栏 Tabs、设置页 chromesrc/client/app-frameAppFrame);跟视口,由壳类型锁定

三维度模型(壳子 / 布局 / 交互)

Section titled “三维度模型(壳子 / 布局 / 交互)”
维度驱动职责
壳子getShellKind()web/tauri)+ getShellBuildTarget()存储、IPC、Habitat 连接、settings 内容字段、原生能力
布局仅视口断点(壳不锁底栏/左栏)compact / expanded;列表 drawer / 并列 / 三栏;settings chrome(tabs vs 侧栏)
交互primaryInput(touch / pointer)长按 vs 右键、Enter 发送等

手机端通常只有窄档,但 手机端 ≠ 窄布局;Portal / 浏览器窗口可以是窄或宽任意档。标准 → docs/ui/dimensions.md(Agent API → .agent/rules/ui-dimensions.md)。

档位视口布局粗档Nav IA页内
< 768px(Tailwind md)compact底栏 + Moredrawer
768–1027pxexpanded左侧 Rail两栏(清单 drawer)
≥ 1028pxexpanded左侧 Rail三栏并列

resolveLayoutMode():窄 → compact,中宽 → expanded(URL / config.json 可覆盖)。detectSettingsChromePlatform() 跟布局粗档(设置页 chrome);settings 字段差异由壳子维 resolveShellBindings() / getShellKind() / getShellBuildTarget() 决定。

客户端UI 加载壳发版
浏览器 / PWAHabitat /web/*随 Habitat / anima upgrade
Desktop安装包内本地 /web/*(默认)Tauri 安装包
Mobile APK安装包内本地 /web/*Tauri Android
ModuleConnectionNotes
ChatHabitat RPC /rpc/v1 (shared WS, no remote-tool attach)/web/chat
HabitatHabitat RPC /rpc/v1 (WS + HTTP POST, same envelope)/web/habitat/dashboard

/web/config.json 提供 habitat_urlhabitat_ws_urlui_versionmin_shell_version(浏览器/PWA 与壳调试用;原生壳 UI 版本随安装包)。

  • EventBus:异步通知传输(Redis 队列);生产代码目前会发出如 session:updated 等主题,但无已注册处理器——ACP 回调改用直接 onSessionUpdated用于睡眠编排。
  • Pipeline Runner: explicit DAG for background cycles (sleep-cycle: light → deep → cross-domain maintenance steps). State in ~/.anima/runtime/pipeline_*_run.json; Habitat API for diagnostics.
  • Hooks:同步拦截器——在消息入口、回合结束、工具返回等节点做校验或澄清

互补关系:Pipeline Runner 负责定时的多步后台任务;Hooks 处理「进行前/进行中是否允许」;EventBus 保留供未来跨进程扇出,但不在睡眠路径上。

The desktop companion(桌面伴侣)is an unreachable local app that actively connects to Habitat and registers remote tools in the first-party companion overlay(伴侣浮层 / embedded-overlay;shell provides window/IPC/FS only — not a Node sidecar), with a split boundary:

ConcernHabitat (src/features/companion/)Local install
Behavior, slots, active modelcompanion_profile entity + Habitat RPCCache in ~/.anima/companion/config.json
VRM / VRMA libraryFiles on Habitat host + content-hash metadataLazy download to desktop cache
FBX → VRMAHabitat service onlyNot bundled in desktop installer
Settings UIHabitat RPC + /rpc/v1/companion/* uploadDesktop Settings section (not Habitat)
VRM render, float window, patrolTauri Portal shell + overlay SPA
Agent tools (bubble, play_slot)Habitat RPC tool.* after remote_tools.attachOverlay WebView-host 执行(本地 runtime)

Remote tools ≠ Portal / MCP: Portal shells and Chat/Settings use Habitat RPC for UI only. Dialable peers expose tools via MCP. Remote-tool attach exists solely when Habitat cannot dial the app (companion overlay today; future independent local apps). Routing uses instance_id (same machine may have multiple instances). See companion.mdhabitat-rpc.md.

能力愿景与讨论:GitHub Issues(标签 enhancementdiscussionsecurity)。本文不跟踪待办。

  • 原则与结构写在这里;不含具体任务清单
  • 快速变化的行为以运行中的服务为准,而非过时的文字
  • 可执行工作放在 GitHub Issues;完成后关闭