Skip to content

Latest commit

 

History

History
610 lines (464 loc) · 33.7 KB

File metadata and controls

610 lines (464 loc) · 33.7 KB

GroupX 存储与记忆设计

状态:Draft v0.1

1. 权威来源

SQLite/WAL 是 GroupX 唯一权威事实源。Broker 是唯一写入者。

存储继续接受 direct | structured 以读取旧记录;新产品与 release 路径使用 structureddirect 为 deprecated compatibility value。同一次 Broker 运行全部已配置 Agent 共用启动级 transport;请求不能覆盖,也不自动 fallback。access 恒为 unrestricted,不建立可变配置列、请求字段或数据库 policy 状态。

  • CLI 不直接写数据库;
  • Browser 不直接写数据库;
  • JSONL 仅用于人工可读的审计导出和备份交换;
  • UI、公共记忆视图、身份视图和健康摘要都是数据库事件/状态的投影;
  • 任何缓存都必须可以从数据库重建。

具体 SQLite Node 驱动在 M0 做安装与性能 smoke 后固定。架构不依赖本机仍提示 experimental 的 node:sqlite API。

2. 数据分类

数据 是否权威 是否默认持久化 说明
durable events 群聊和状态历史
turns 队列、执行和 terminal 状态
adapter runs / bindings / capabilities Direct invocation 或 Structured session、transport 快照及实测能力
final messages 完成或明确 partial 的正文
token delta 仅 live SSE,短时内存合并
aggregated reasoning record 每个已产生推理的 terminal Turn 最多一条;仅供时间线回放,不进入上下文
tool progress records terminal 时保存 Adapter 已投影的 started/completed;折叠回放,不进入上下文
public memory 显式记忆、来源可追溯
per-Agent core memory scope_type=agent, agent_memory_type=core;Agent 通过绑定工具主动写自己,Web 可维护
per-Agent dated memory scope_type=agent, agent_memory_type=dated;成功 Turn 先登记,所属 Agent 批量生成每日 rollup
identity memory 群组层身份记录,不替换 CLI 原生身份
configured Agent identity 配置 存在 groupx.json,每轮注入,不写入 memory 表
generated summary 派生 标记 summary,可重新生成
native interaction failure summary 只保存 request kind、错误码与有界 native reason;没有 pending/options/decision
supervision observation `supervision.paired
raw stderr/env/config 不属于 GroupX 数据模型,不主动采集

3. 初始逻辑 schema

以下是语义合同,不锁定最终 SQL 方言细节。

3.1 actors

actors(
  actor_id TEXT PRIMARY KEY,
  kind TEXT NOT NULL,
  display_name TEXT NOT NULL,
  enabled INTEGER NOT NULL,
  created_at TEXT NOT NULL,
  updated_at TEXT NOT NULL
)

预置:

user:web
user:assistant
agent:codex
agent:grok
agent:kimi
system:groupx

此外,runtime 启动时按配置名册 upsert 自定义或被改名的 agent actor(agent:<id>,kind=agent,display_name 取配置 name,缺省为 id 本身),使 durable 事件的 actor_display_name 与 Web UI 显示名始终来自配置;未改名的内置 agent 仍以上述种子为准。

3.2 agent_instances

agent_instances(
  instance_id TEXT PRIMARY KEY,
  actor_id TEXT NOT NULL,
  adapter_id TEXT NOT NULL,
  transport TEXT NOT NULL CHECK (transport IN ('direct','structured')),
  process_started_at TEXT NOT NULL,
  process_ended_at TEXT,
  status TEXT NOT NULL,
  FOREIGN KEY(actor_id) REFERENCES actors(actor_id)
)

不存 PID 作为稳定身份。PID 可以进入瞬时诊断,不作为恢复依据。

3.3 invocation/session bindings

session_bindings(
  binding_id TEXT PRIMARY KEY,
  instance_id TEXT NOT NULL,
  actor_id TEXT NOT NULL,
  transport TEXT CHECK (transport IN ('direct','structured')),
  native_session_id TEXT,
  protocol TEXT NOT NULL,
  protocol_version TEXT,
  status TEXT NOT NULL,
  capabilities_json TEXT NOT NULL,
  created_at TEXT NOT NULL,
  last_ready_at TEXT,
  closed_at TEXT
)

当前 binding 表示 Structured native session:Codex 使用 codex-app-server-stdio-jsonrpc-v2,Grok/Kimi/Hermes 使用 acp,Claude 使用 claude-cli-stream-json-v1。历史 Direct binding 仍按旧 invocation 语义可读,但当前 runtime 不创建或恢复它。M2 GroupX MCP 只关联各自的 Structured session binding,不为多个 Agent 共用匿名入口。

Agent binding 的 transport 必须非 null,且与所属 instance 及 Turn snapshot 相同;只有 Web source binding 例外为 null。v4 物理 migration 为了 SQLite ALTER TABLE 兼容性可先加 nullable 列,但必须回填所有旧 Agent 行为 structured,Store 在创建/claim 时拒绝任何 Agent null 值。

actor_id、binding 与 author 只表示 Broker 正常创建通道内的 provenance/correlation,不是认证结果或能力凭据;恶意本机进程修改数据库或仿造 binding 不在 GroupX 防御范围。

native_session_id 允许为 null,因为 Structured 路径可能在 native ID 返回前失败。历史 Direct 行可能包含 session.resume_hint 取得的 ID,但只供审计读取。API 默认不返回完整 native ID,但 bootstrap/Turn 投影可回显只读 transport/continuity 状态。

3.4 client_commands

client_commands(
  command_id TEXT PRIMARY KEY,
  source_binding_id TEXT NOT NULL,
  client_command_id TEXT NOT NULL,
  command_type TEXT NOT NULL,
  canonical_hash TEXT NOT NULL,
  result_json TEXT,
  accepted_at TEXT NOT NULL,
  UNIQUE(source_binding_id, client_command_id)
)

相同唯一键和相同 canonical_hash 返回原结果;hash 不同返回 conflict。

3.5 events

events(
  seq INTEGER PRIMARY KEY AUTOINCREMENT,
  event_id TEXT NOT NULL UNIQUE,
  schema_version TEXT NOT NULL,
  room_id TEXT NOT NULL,
  event_type TEXT NOT NULL,
  actor_id TEXT NOT NULL,
  instance_id TEXT,
  targets_json TEXT NOT NULL,
  reply_to_event_id TEXT,
  causation_id TEXT,
  correlation_id TEXT NOT NULL,
  idempotency_key TEXT,
  occurred_at TEXT NOT NULL,
  body_json TEXT NOT NULL,
  provenance_json TEXT,
  UNIQUE(room_id, event_type, idempotency_key)
)

idempotency_key 为 null,SQLite 唯一约束允许多行;非空时用于领域级去重。

为了让同一 Turn 的不同 terminal event type 也互斥,v0.1 还需要一个跨 event type 的 partial unique index:

CREATE UNIQUE INDEX events_room_idempotency_unique
ON events(room_id, idempotency_key)
WHERE idempotency_key IS NOT NULL;

terminal 统一使用 turn:<turnId>:terminal,response 使用 turn:<turnId>:response。对同一 terminal key 的重放返回已提交结果;不同 outcome/hash 为 STORE_CONFLICT

3.6 turns

turns(
  turn_id TEXT PRIMARY KEY,
  source_event_id TEXT NOT NULL,
  target_actor_id TEXT NOT NULL,
  adapter_id TEXT NOT NULL,
  transport TEXT NOT NULL CHECK (transport IN ('direct','structured')),
  binding_id TEXT,
  native_turn_id TEXT,
  parent_turn_id TEXT,
  root_correlation_id TEXT NOT NULL,
  hop_count INTEGER NOT NULL,
  queued_event_id TEXT NOT NULL,
  enqueue_seq INTEGER NOT NULL,
  status TEXT NOT NULL,
  partial_text TEXT,
  response_event_id TEXT,
  terminal_event_id TEXT,
  error_code TEXT,
  queued_at TEXT NOT NULL,
  started_at TEXT,
  terminal_at TEXT,
  UNIQUE(source_event_id, target_actor_id)
)

状态:

queued
dispatching
running
cancelling
completed
failed
cancelled
interrupted

transport 是命令接受时 Broker 启动选择的不可变快照,不接受 message 请求覆盖。Agent Turn、session binding 与 agent instance 的 transport 必须一致,创建后不可变;Web binding 的 transport 为 null。claim 使用 expectedTurnId + expectedTransport 做 CAS。streaming 是 running 期间的事件活动,不单列持久状态。重启后无法确认的已派发 Turn 进入 terminal interrupted,并用诊断/交付确定性字段表达 unknown,不让 terminal 状态回退。

任何 MCP child Turn 插入都必须在同一事务内验证:parent_turn_id 存在,root_correlation_id 等于父 Turn 的 root,hop_count = parent.hop_count + 1,父链无断裂或环。只有 command_type='mcp.ask' 且父 Turn 在 waitsForChildren 中时,target actor 命中祖先 actor chain 才返回 CAUSAL_CYCLEmcp.send 可异步回发祖先,但仍受上述完整性校验及 hop/root/actor/queue 限额。

最小 v4 migration 只为 agent_instancessession_bindingsturns 增加 transport:旧 v1-v3 的 Agent 行保守回填为 structured,Web binding 保持 null;不重命名 session_bindings,不增加 access 列,也不在 turn_attempts 重复 transport。

3.7 turn_attempts

turn_attempts(
  attempt_id TEXT PRIMARY KEY,
  turn_id TEXT NOT NULL,
  binding_id TEXT NOT NULL,
  instance_id TEXT NOT NULL,
  context_through_seq INTEGER NOT NULL,
  native_turn_id TEXT,
  dispatch_phase TEXT NOT NULL,
  delivery_certainty TEXT NOT NULL,
  claimed_at TEXT NOT NULL,
  prompt_invoked_at TEXT,
  started_at TEXT,
  terminal_at TEXT
)

dispatch_phase 至少区分 preparedprompt_invokednative_startedterminal。Structured prompt request 在越过可能交付边界前必须先持久化 prompt_invoked + delivery_certainty=unknown + prompt_invoked_at;因此只有仍为 prepared + not_delivered 的 Structured attempt 才能通过 CAS 重新排队。历史 Direct attempt 保留同一字段语义但不再被 runtime claim。

schema v5 为 attempt 增加可空 summary_through_seq。它只能指向该房间当前 active、且确实嵌入本次 Context Packet 的摘要边界,并且不得超过 context_through_seq

这不是 exactly-once 承诺,而是守住“可能已经执行就不自动重放”的持久证据。attempt 不重复存 transport;实际 transport 从不可变 Turn 读取,并校验其 binding/instance 与 expected transport 一致,不能用另一个 transport 的 capability 或恢复路径替代。

3.8 delivery_cursors

delivery_cursors(
  actor_id TEXT NOT NULL,
  room_id TEXT NOT NULL,
  last_delivered_seq INTEGER NOT NULL,
  last_summary_seq INTEGER,
  updated_at TEXT NOT NULL,
  PRIMARY KEY(actor_id, room_id)
)

该表用于构建增量 Context Packet,不表示用户已读回执,也不证明 native session 已持有这些历史。cursor 只能推进到成功交付 Structured attempt 的 context_through_seqlast_summary_seq 只能随含该持久摘要的 attempt 在 native start 被确认后推进。历史 Direct cursor 仅按旧事实读取。

3.9 memory_records

memory_records(
  memory_id TEXT PRIMARY KEY,
  scope_type TEXT NOT NULL,
  scope_id TEXT NOT NULL,
  agent_memory_type TEXT,
  kind TEXT NOT NULL,
  author_actor_id TEXT NOT NULL,
  subject_actor_id TEXT,
  content TEXT NOT NULL,
  source_event_id TEXT,
  source_kind TEXT NOT NULL,
  status TEXT NOT NULL,
  supersedes_memory_id TEXT,
  created_at TEXT NOT NULL,
  retracted_at TEXT
)

agent_memory_type 只允许 core | dated,仅在 scope_type=agent 时非空。schema v6 把升级前已有的 Agent 独立记忆迁移为 core,room/correlation memory 保持空值。调用方写入的 source_kindweb | mcp | operator;系统生成行使用 generated_summaryautomatic_turnautomatic_rollup

scope_type

room
agent
correlation

kind

fact
decision
preference
instruction
constraint
summary
note

3.10 identity_records

identity_records(
  identity_id TEXT PRIMARY KEY,
  subject_actor_id TEXT NOT NULL,
  author_actor_id TEXT NOT NULL,
  kind TEXT NOT NULL,
  content TEXT NOT NULL,
  source_event_id TEXT,
  source_kind TEXT NOT NULL,
  status TEXT NOT NULL,
  supersedes_identity_id TEXT,
  created_at TEXT NOT NULL,
  retracted_at TEXT
)

调用方写入的 source_kindweb | mcp | adapter | operatorlocal-operator 写入的作者是 user:assistant

物理上 identity 也可复用 memory 表,但逻辑和 API 必须保持独立,避免公共闲聊声明自动成为身份事实。

3.11 summaries

summaries(
  summary_id TEXT PRIMARY KEY,
  room_id TEXT NOT NULL,
  from_seq INTEGER NOT NULL,
  through_seq INTEGER NOT NULL,
  content TEXT NOT NULL,
  generator_actor_id TEXT NOT NULL,
  status TEXT NOT NULL,
  created_at TEXT NOT NULL
)

摘要永远是派生数据。每个房间最多一条 active 累计摘要;滚动更新以 compare-and-set 把旧摘要标为 superseded 后再写入新摘要。删除/失效摘要不能删除原事件。

3.12 agent_dated_memory_rollups / sources

schema v7 新增两张内部派生状态表:

  • agent_dated_memory_sources:每个 completed Turn 一行,只引用该 Turn 的 source message.created、最终 response、两者字符数和本地日期;reasoning/tool/stderr/native payload 没有列,也不参与查询;
  • agent_dated_memory_rollups:按 room_id + actor_id + local_date 保存 active memory id、已覆盖 response seq、待处理回合/字符数、最后活动时间和重试检查点。

这两张表不是第二份聊天正文。正文仍以 events 为事实源;sources 只保存外键和计数,rollup 可在崩溃后从外键重新构造。每个日期最多一条由引擎管理的 active dated MemoryRecord,同日后续批次通过 CAS/supersede 替换它。

3.13 supervision_pairs / pair_turns / steer_counts

schema v8 新增三张协作状态表:

  • supervision_pairs:一次用户命令的配对主键,引用用户任务 event、watch brief event 和 supervision.paired event;mode 只允许 live_steer
  • supervision_pair_turns:该 pair 上的 worker | observer 行;steer 产生的新 Worker Turn 必须挂回同一 pair 与同一 correlation_id
  • supervision_steer_counts:按被观察 subject_turn_id 计数,CAS 递增到配置上限后失败。

角色只存在于这些行,不进入 actors 枚举。观察快照写在 supervision.observed 的有界 body 里,不复制进 memory 表。Watch brief 是 message.createdbody.kind=supervision.watch,供 observer 当前消息使用,不作为房间 unread/压缩/dated-memory 源。

3.14 assistant_conversation_messages / context_resets

schema v9 新增:

  • assistant_conversation_messages:用户与房间助理的侧边对话。不进房间 transcript、Context Packet、压缩输入或 dated-memory。client_command_id 只对用户行做幂等。
  • context_resetscontext.reset 的下限游标。不删除 events。写入 reset 时同时 supersede throughSeq <= resetThroughSeq 的 active summary。后续 Context Packet / usage / compact 只使用越过该下限的检查点,否则从 resetThroughSeq 起读;仅有 context.reset 审计事件、没有新的 message.created / operator.dispatch 时,再次 reset 是 no-op。

operator.dispatch 是房间 durable event,不是侧边对话行。body 只含 contentoperationpromptLengthtargets;作者是 user:assistant。它可重放,并作为被叫醒 worker 的当前任务;UI 默认折叠,不当普通聊天气泡。监督 pair 在无 message.created 任务时引用该 event id。

4. 事务不变量

4.1 接受消息

一次用户或 Agent 发送必须在一个事务中完成:

  1. 插入或命中 client_commands
  2. 插入 source message.createdoperator.dispatch event;dispatch_event 可复用已有 message.created,不能复用 watch brief,也不能新造 operator.dispatch 来改原作者;
  3. 为每个目标插入唯一 turn.queued event,以其 durable seq 作为 enqueue_seq
  4. 为每个目标插入唯一 Turn,引用 queued_event_id/enqueue_seq,并快照当前 Broker transport;
  5. 若带 supervision:插入系统 watch brief(不复用用户正文)、observer Turns、supervision.paired 与 pair/turn 行;observer 不得与 worker 重叠;幂等 hash 包含 observer 集合,不含 watch brief 正文(其中有随机 turn id);
  6. 写入 command result;
  7. commit。

只有 commit 成功后才能向 Adapter 派发。这样不会出现“CLI 已收到但消息账本不存在”。

command_type='mcp.publish' 是同一接受事务的无 Turn 子集:必须 targets=[] 且不能带 supervision;事务只写 client_commands、source message.created 和 command result,然后 commit。它不写 turn.queued/turns,也不触发 Adapter。其他 command type 仍至少需要一个 target。publish、ask pending、collect 和队列可见性全部复用现有表:collect 以 turns.source_event_id(已有唯一索引前缀)精确找 child Turns,queue metadata 从 target_actor_id + enqueue_seq + status 计算,因此本阶段没有 schema/migration 变化。

4.2 结束 Turn

Turn terminal transaction 先对 terminal_event_id IS NULL 和当前 non-terminal status 执行 CAS,只有唯一胜者可以提交:

  1. 若本回合观察到推理增量,插入最多一条聚合 turn.reasoning.recorded;不逐 delta 写库;
  2. 为已观察到的工具 started/completed 写入有界 tool.progress.recorded 投影;不保存未建模 native payload;
  3. 成功时插入唯一 response message.created;失败时不伪造 response;
  4. 以统一 idempotency key turn:<turnId>:terminal 插入唯一 durable terminal event,不论 event type 为 completed/failed/cancelled/interrupted;
  5. 成功时只在 agent_dated_memory_sources/rollups 登记该 source/response 外键、日期和计数;不生成 MemoryRecord/event,不读取 reasoning/tool 记录;observer Watch Turn 不登记 dated-memory source;
  6. 更新 Turn terminal 状态、错误码及 event 引用;
  7. 更新当前 attempt 的 dispatch_phase=terminal、delivery certainty 和 terminal_at
  8. 仅在 confirmed delivered 时把 delivery cursor 最多推进到该 attempt 的 context_through_seq;若 attempt 带 summary_through_seq,同事务推进 last_summary_seq
  9. commit;
  10. 唤醒 SQLite-backed SSE cursor tail,再由它按 reasoning → tool progress → response(仅成功)→ terminal 的 durable seq 发布。

transient delta 可以在 commit 前实时广播,但 UI 必须把它显示为未完成状态。浏览器刷新时不会重放历史 delta;terminal commit 后由聚合 reasoning record 与 tool progress records 恢复推理和折叠工具展示。

4.3 Native interaction failure

schema 不包含 approval request/decision 表。若 Adapter 在 unrestricted 合同下收到 approval、permission、user-input、question 或 elicitation request,Turn terminal transaction 只持久化:

  • turn.failed,且关联 attempt 记录 delivery_certainty=delivereddispatch_phase=terminal
  • 错误码一律为 UNEXPECTED_NATIVE_INTERACTION
  • request kind、adapter/instance/turn correlation 与有界 native reason code。

不持久化 pending 状态、available decisions、用户选择或完整 native payload,也不产生可恢复的审批工作。Kimi ACP 为协议收尾返回 cancelled 只是 Turn 终止动作,不是审批记录。

NATIVE_POLICY_BLOCKED 只从独立外部策略 preflight 或 native 启动/session 拒绝记录产生,不得从 native interaction request/options 推断。

5. 崩溃恢复

5.1 queued

数据库中仍为 queued 且没有 dispatch attempt 的 Turn 可以重新加入 Agent lane。已经 claim 但 Turn 不是 cancelling、attempt 明确为 prepared + not_delivered 时,也可通过 CAS 回到 queued;cancelling + prepared + not_delivered 以 CAS 收敛为 cancelled,其他 attempt 不自动重放。

5.2 dispatching/running

Broker 启动时先枚举非终态 attempt,不先写 terminal:

  • prepared + not_delivered 只有 Turn 不是 cancelling、transport snapshot 与当前启动选择相同才可用 CAS 回到 queued;transport 不同以 TRANSPORT_MODE_MISMATCH 失败,cancelling 则收敛为 cancelled
  • prompt_invoked/native_started 先用记录的 binding/native process/session 做 reconciliation;对 cancelling 保留取消意图,关联成功后对同一 native Turn 重发 cancel;
  • 只有确实关联到同一个 native Turn 时才能继续 running、重发 cancel 或提交已知 terminal;native completion 在 cancel 前抢先成功时可收敛为 completed
  • 无法确认时一次性提交 terminal interrupted 并保留已合并 partial text;若已有 native event 证明 delivered,delivery_certainty 保留 delivered,否则保留 unknown,终态不明用独立 bounded reconciliation outcome 表达;
  • 不使用 unknown_after_restart 作为 Turn 状态,也不从 terminal 回到 running;
  • 不自动创建第二个模型 Turn;UI 提供显式“继续会话”或“新建重试”,并保留因果引用。

5.3 Structured session 与历史 Direct invocation

  • 历史 Direct attempt 保留原 terminal、delivery certainty 和 native session ID,但当前 runtime 不 claim、不 resume、不 replay;
  • Codex App Server 记录 thread ID;只有 capability verified 后才使用 thread/resume
  • Grok/Kimi/Hermes 只有在各自 capability evidence 支持时才使用 ACP session/load
  • Claude 的原生 session id 由 GroupX 自行分配(UUID,启动时经 --session-id 传入)并记录,后续新 Turn 用 --resume <id> 延续同一 native session;
  • Structured 原生 resume/load 不支持、未取得 session ID 或失败时,只为后续新 Turn 建立新 session,并注入 GroupX Context Packet;
  • prompt_invoked 或更晚阶段的当前 Turn 不因连接丢失、恢复失败或终态不明而自动重放;
  • 新 session 属于同一稳定 actor,但有新的 binding/instance lineage;
  • 不把“上下文重建”描述成“原生 session resume”。

5.4 groupx stop / groupx restart 的存储边界

runtime 停止/重载不增加锁表、PID 表、shutdown event 或第二个权威状态源。runtimeScopeKey 只由 canonical config path 确定,随 health/loopback 控制响应瞬时投影,不进入 SQLite,也不是 credential。

收到匹配的 shutdown 请求后,旧 runtime 先停止接收新产品工作并等待已开始的 REST 操作,再按既有顺序关闭 Broker、Agent session 与唯一 Store writer;在这些资源收敛前继续占用 HTTP 端口。CLI 只有连续观察到 listener 不可达后才启动新 runtime。因此替代进程仍使用本章 5.1-5.3 的正常打开、恢复和“不确定 Turn 不自动重放”合同,没有热替换数据库连接、跨进程 Store writer 重叠或额外 replay 许可。

6. 公共记忆

6.1 产生方式

公共记忆只能由以下方式产生:

  • 用户在 Web UI 显式固定;
  • Structured Agent 通过 GroupX MCP 显式调用 groupx.memory.remember
  • 系统生成滚动摘要,且 kind=summary/source_kind=generated_summary

普通聊天内容不会自动成为公共 MemoryRecord;成功 Agent Turn 只会成为第 6.3 节所属 Agent dated rollup 的候选来源。

6.3 Agent 核心记忆与日期记忆

  • core:长期、少量、显式维护。core_memory_remember 的 wire input 不含 scope、subject、author 或 binding,Broker 始终从当前 Structured binding 固定为调用 Agent 自己;Web Agent 设置也可追加、替换或撤回 core。
  • dated:completed Turn 在 terminal transaction 中只登记一个可恢复 source;失败、取消或 interrupted Turn 不登记。后台只读取这些 source 对应的当前 message.created 与最终 response,reasoning、tool progress、stderr 与 native payload 永不进入。
  • 每个 Agent、每个本地日期最多维护一条 active rollup。待处理批次达到 8 个成功 Turn、约 16K 原始字符、跨过日期边界,或房间压缩即将越过其 source seq 时触发;普通阈值触发需经过最后活动后的 5 分钟安静窗口。
  • rollup 使用独立、短生命周期、无 GroupX MCP 的所属 Agent session。其他 Agent 不代写;所属 Agent 不可用时保留 pending checkpoint 并有界退避重试,聊天 Turn 已完成状态不受影响。
  • 输出最多 8K 字符,只保留重要进展、决定、偏好、约束与未完成事项。若一批只有问候、确认或测试,Agent 可返回空语义结果;引擎只推进 source checkpoint,不创建 MemoryRecord。
  • 同日后续批次把旧 daily rollup 作为输入,并以 memory id CAS 在一个 immediate transaction 中 supersede 旧记录、写新记录和 memory.superseded event。首次生成使用 memory.remembered。原 transcript 永不删除。
  • 新 daily rollup 是语义摘要,不因最后一个 source response 仍在 unread transcript/reply chain 中而整体隐藏;Context Packet 仍先放近期 transcript,再在剩余预算内选择 dated。

6.2 冲突与纠正

不原地覆盖:

  • 新记录可以 supersedes 旧记录;
  • 撤回使用 tombstone/retracted status;
  • 相互冲突的记录可以并存;
  • 检索默认返回 active 版本,同时允许查看历史;
  • source event 和 author 必须保留。

M0-M2 不实现自动事实仲裁或“真相分数”。

7. 身份记忆

IdentityRecord 是 GroupX 群组层的身份叠加,不是 CLI 全局 persona 或配置文件。

允许的来源:

  • 用户对某 Agent 的群内角色/偏好说明;
  • Agent 对自己的显式 identity.remember;
  • 其他 Agent 对某 Agent 的观察,但必须保留 author != subject,且不自动转成 subject 的自我认定。

示例:

subject = agent:grok
author  = agent:codex
kind    = note
content = "Grok 在本轮更关注协议互操作"

这是一条 Codex 对 Grok 的观察,不是 Grok 的稳定身份事实。

IdentityRecord 没有以下专用字段:

  • API key、登录令牌;
  • CLI config 正文;
  • 本地私有密钥路径;
  • 未经显式选择的大段原生会话历史;
  • 其他 Agent 作为 subject 自我来源写入的记录。

普通文本内容按提交值保存;GroupX 不扫描 identity/memory 正文来识别 API key、令牌或其他秘密。上述边界表示 GroupX 不主动采集 CLI 运行态数据,并不构成内容安全承诺。

8. Context Packet

每次发往 Adapter 的 Context Packet 是独立结构,不篡改原始目标消息:

[groupx_protocol]
[configured_agent_identity]
[self_identity]
[agent_core_memory]
[agent_dated_memory]
[pinned_group_memory]
[relevant_memory]
[room_checkpoint_summary]
[room_delta_since_cursor]
[reply_chain]
[current_message]

Agent 设置中的稳定身份、滚动摘要、当前消息和完整 reply chain 是强制区段。滚动摘要是已省略旧 transcript 的覆盖证明,因此不能先推进 cursor 再丢掉摘要。其余可选区段优先保留 Agent core,再保留近期 room delta、Agent daily dated rollup、公共记忆和兼容身份记录。

turn.reasoning.recordedtool.progress.recorded 是可回放的 UI/审计记录,不是 Context Packet 区段。未读 transcript、reply chain 与压缩输入都只从房间业务 message.created 投影;watch brief(kind=supervision.watch)与 supervision.* 事件不得进入这些区段、摘要或自动记忆。监督快照是独立投影;Worker 业务包不自动塞进监督评语。Observer 的 Context Packet 使用同一 groupx.context/0.4 schema,另加 supervision_watch 分段与观察协议说明。

memory_records.scope_type=agentscope_id=agent:<id> 的记录只进入该目标 Agent 的 Context Packet。Web 在 Agent 设置中把 core 独立列出,把 dated 按 created_at 的本地日期分组;公共记忆仍使用 scope_type=room 并位于群聊左栏,三者不会相互提升或复制。Agent 稳定身份同样在 Agent 设置中写入配置,主界面不保留右侧记忆栏。

默认硬上限为 256,000 字符,可配置;该值不是 token 数。Room Context Engine 在约 75% 的软目标(默认 192,000 字符)将省略 unread transcript 时触发,并以“旧检查点 + 有界旧消息块”滚动生成下一检查点,为原生 instructions、工具和回复保留余量。不可压缩的强制区段可以使用到硬上限。生成者是配置顺序中第一个健康 Agent;不可用或返回无效摘要时尝试下一个健康 Agent。

Web 输入区域右上角的用量是 active checkpoint 加其后 message.created 及协议开销的保守字符估算,不是 token 计数。用户可通过 Broker 的幂等命令显式压缩;它复用同一累计摘要 CAS,并保留最近 12 条消息原文。该操作不删除 transcript,不读取 reasoning/tool 记录,也不直接推进任一 Agent 的 delivery cursor。

压缩 session 不挂载 GroupX MCP,不形成公开聊天 Turn,也不让压缩任务主动调用其他 Agent。若所有 Agent 都失败,当前业务 Turn 以 CONTEXT_BUDGET_EXCEEDED 结束;原文、旧摘要和 delivery cursor 保持不变。

压缩状态仅用 transient SSE 提示,不进入 events 表。只对生成阶段可确认的临时 session/transport 错误做有界退避重试;摘要校验或持久化失败不自动重写。只有完整生成、校验并通过摘要 CAS 后才发布 completed 并替换 active summary。会话启动重试同样不能放宽 Turn 的交付不确定边界:prompt 一旦可能送达便禁止自动 replay。

Context Packet 中每条记忆应带最小来源标签,例如:

source=user
source=self
source=agent:codex-about-agent:grok
source=generated-summary

9. 检索

M2 首版支持:

  • scope/kind/author/subject 过滤;
  • 最近性排序;
  • SQLite FTS 文本搜索;
  • source event 回链;
  • active/history 选择。

首版不引入向量数据库。只有真实检索质量证据证明 FTS 不足时再新增可重建的向量索引。

10. 数据保留、导出与备份

  • 数据库默认位于 .groupx/groupx.db,Git 忽略;
  • WAL/SHM 同样忽略;
  • 支持在线一致性 backup API 或 SQLite backup 机制,不直接复制活跃 WAL 组合;
  • JSONL 导出按 durable seq 排序,包含 schema version;
  • 导出默认保留 durable seq、Turn transport、公开 provenance 与 memory/identity 的 author/subject/source/supersedes;排除 native session ID、binding ID、native interaction payload、stderr 和诊断私有字段;
  • 恢复导入需要独立校验和新数据库,不覆盖原数据库;
  • 数据库迁移必须有 schema version 和可验证 rollback/backup 说明。

11. 诊断数据最小化

GroupX 诊断日志只记录实现合同需要的有界字段:

  • Adapter 名称、状态、耗时;
  • GroupX 实际追加的协议 argv 结构;
  • 协议 method/event type;
  • 关联 ID 的短前缀;
  • 限长错误摘要和错误码。

完整环境、CLI 配置、原始 stderr、未建模的 native interaction payload 和原生认证交换不属于 GroupX 诊断数据模型,因此不主动采集或导出。固定 unrestricted argv/mode 的结构摘要可以记录,以证明运行合同生效;限长或字段级替换只是可观测性卫生,不是秘密检测保证。

用户或模型提交到 transcript、memory 或 identity 正文的内容按产品语义持久化。GroupX 不负责识别、删除或阻止其中的凭据、秘密或其他敏感文本;用户决定是否写入和导出这些内容。

12. 存储验收

  1. 重复 client command 不新增 message 或 Turn。
  2. 相同幂等键、不同 payload 返回 conflict。
  3. message 与目标 Turns 原子提交。
  4. 三个 Adapter 不能直接打开数据库写入。
  5. 10,000 durable events 能按 cursor 分页,不全量重建发送。
  6. token delta 不导致数据库逐 token 增长。
  7. queued 在重启后恢复;running 不自动重放。
  8. public/identity memory 重启后保持 author/subject/source/supersedes。
  9. 摘要失效不影响原 transcript;滚动替换保持单 active 摘要和完整历史。
  10. 长房间触发压缩后,Context Packet 同时包含累计检查点与近期逐条原文;只有确认 native start 后 last_summary_seq 才推进。
  11. 压缩 Agent 失败时不更新摘要或 cursor,业务 Turn 产生明确 terminal failure;不得静默漏历史。
  12. 默认导出只包含合同定义的公共事件/记忆字段,不带 binding、native session、raw stderr 或未建模的原生 payload。
  13. Structured binding 按 active 合同持久化;历史 Direct invocation 仍可读取与恢复其已有语义,但状态明确为 deprecated,不形成新 Gate 或跨 transport fallback。
  14. binding 的 transport、协议、实测 capability 与 instance lineage 可查询;重建 process/session 不修改稳定 actor 或历史 Turn。
  15. schema 中不存在 approval table;native interaction request 只形成一次 terminal failure,重启后没有 pending approval 可恢复。
  16. 默认 transport 是 Structured;同一运行全部已配置 Agent 的 Turn、binding、instance transport snapshot 一致,attempt 从 Turn 推导,不重复存列;请求不能覆盖 transport/access。
  17. SQLite-backed SSE cursor tail 在历史补齐与 commit 唤醒之间不丢 durable event。
  18. 推理 delta 不逐条落库;terminal transaction 最多生成一条聚合 turn.reasoning.recorded,刷新后可回放。
  19. live tool.progress 不直接落库;terminal transaction 保存其 started/completed 有界投影,刷新后仍合并为折叠记录。
  20. turn.reasoning.recordedtool.progress.recorded 均不进入 Context Packet、reply chain、房间压缩或自动记忆。
  21. 监督配对与 worker/watch Turns 原子提交;观察快照与 steer 记录可回放,但不进入 memory/dated-memory/压缩输入。
  22. schema 中不存在 approval table;steer 不写 approval.*,不改 Turn transport 或 unrestricted 合同。
  23. groupx stop / groupx restart 不创建持久 shutdown/lock 状态;旧 listener 只在 Broker、Agent session 与 Store writer 已有界关闭后释放,stop 不再启动,restart 的新 runtime 再按既有恢复合同启动。