# 时态 Story Graph 内置 MCP 设计 日期:2026-09-28。状态:D1 只读工具及本地、远程接线已实现,验证记录见[实施进度](./progress.md#2026-09-28-story-graph-只读工具)。D2 跨范围复用、D3 记忆读取切换及历史重建仍未实现。 本文负责 FR-S11 的 MCP 工具契约、时间语义、范围复用和记忆过渡设计。产品要求见[监督者产品设计](./supervisor-prd.md),用户验收见 [US-S28 至 US-S31](./user-stories.md#us-s28-依据决定演变继续工作)。既有图谱对象与交互仍由[图谱 PRD](../story-graph/prd.md)定义。可执行输入以[共享工具合同](../../../src/shared/story-graph-tools.ts)为准,数据投影见 [Story Graph Reader](../../../src/main/assistant/story-graph-reader.ts);本文件同时保留后续阶段目标。 ## 1. 目标与现状 监督者的主要产出是带时间、演变关系和来源的 Story Graph。工作回顾、侧栏和 Agent 上下文从同一组事实中组织内容。扁平记忆条目无法独立表达“当时决定了什么、后来为何修订、现在应采用什么”,因此不再作为目标架构的主要产物。 用户侧统一为监督者维护的工作记忆:包含长期偏好、决定、事件、未解决事项及其依据。“替代记忆”指统一概念与后续读取入口,保留已确认偏好、用户修订和已有数据。它不授权删除 `memory_items`、抹掉历史或给旧内容补造事件。 | 能力 | 当前代码事实 | 本设计目标 | | --- | --- | --- | | 图谱与来源 | 已有结果、事件、实体、关系、结果图谱快照及成功叶子;历史归属存在缺失边界 | 按对象、时间和相关性读取事实与演变,并返回来源版本和覆盖信息 | | MCP | [内置目录](../../../src/shared/builtin-mcp-servers.ts)和[能力合同](../../../src/shared/capability-contracts.ts)已有 `story-graph`,包含三个只读工具 | 按需读取,沿用应用启停及 Runtime 分配 | | 时间与关系 | [监督合同](../../../src/shared/supervision-contracts.ts)已有 `occurredAt`、确认状态和 `revised`;关系枚举没有明确替代/矛盾类型 | 分离发生、生成、记录时间;用有证据的关系表达替代和矛盾 | | 范围与身份 | checkpoint 按阶段、精确 scope、来源区分;候选实体只取同 scope | 同一来源版本的有效叶子事实可跨 scope 复用,范围仍控制可见性与解释 | | 记忆 | `reviewBackground` 仍读已确认记忆;最多四条、每条 500 字符,作为当前背景 | 逐步由统一查询承接,保留确认状态与适用范围 | | 历史重建 | 有结果快照,不具备完整来源版本及人工修改时间序列 | 首期只提供 `current`、`timeline`;`as_of` 留待历史重建能力具备后设计交付 | 现状依据还包括 [AssistantDatabase](../../../src/main/assistant/assistant-database.ts) 的 `saveReviewCheckpoints`、`listSupervisionCandidates`、`reviewBackground`,以及[生产分块调度](./review-scheduling-design.md#0-生产接线与剩余边界)。现有结果快照只证明某次回顾保存过哪些内容,不能据此声称能还原任意时刻的认识。 ## 2. 对象与稳定身份 复用现有 SQLite、领域对象和来源定位。设计不要求图数据库、独立向量库、完整正文副本、新持久化日志或通用历史快照层。所需读取视图先从已有结果、叶子、关系和 locator 推导;无法推导的内容明确返回未知。 | 对象 | 概念合同 | 身份与关联规则 | | --- | --- | --- | | 故事线 `story_line` | `id`、主题、范围、事件与实体引用 | 沿用故事线 ID;不把项目名当 ID | | 事件 `event` | `id`、类型、描述、发生时间、实体及来源引用 | 决定、讨论、变化、里程碑是事件类型;一次回顾运行不是工作事件 | | 实体 `entity` | `id`、描述、确认状态、有效性、演变引用 | 对象 ID 与内容修订分离;同名或模型局部 ID 不证明同一实体 | | 变化 `entity_change` | 实体、事件、变化类型、依据、适用时间 | 指向具体对象版本;无独立主键时以已有所属对象和稳定关联键定位,不以页内下标造身份 | | 关系 `relation` | `id`、两端引用、类型、理由、来源、确认状态 | 替代/矛盾关系必须能定位两端主张及支持该关系的证据 | | 阶段总结 `summary` | 结果 ID、范围、区间、正文、事实引用 | 用于导航;检索与上下文仍能访问完整叶子事实 | | 来源引用 `source_reference` | 引用 ID、来源类型与原 ID、版本、locator、可用性 | 引用 ID 与原消息/任务 ID 分开;跨结果的引用 UUID 不等于新的原始来源 | 偏好、方案、决定涉及的概念沿用实体及事件表达,不为每个业务名词另建基础设施。业务上的“已决定”(`decided`)与“已完成”(`completed`)是不同状态,这里不声明新增任务状态枚举;运行 `completed` 仅表示整理发布成功。决定采用方案、计划执行、模型写出“已决定”均不能推出任务完成,完成须有任务记录或明确执行证据。 返回对象携带 `object_ref`(类型和稳定 ID)、`object_revision`、`result_id` 及适用 scope。版本可使用已有结果身份与可用修订信息表达;没有可靠对象版本时返回 `unknown`,不把 `updated_at` 伪装成完整版本链。旧文本主键及 Main 精确别名映射继续保留,`candidateRef` 只是一次模型输入内的候选引用,不作为 MCP 持久 ID。跨 scope 身份合并须有明确映射依据,不能因复用叶子事实而自动合并同名实体。 ## 3. 时间、确认与有效性 | 维度 | 含义 | 禁止的推断 | | --- | --- | --- | | `event_time` | 原记录描述的事件发生时刻或区间;带精度、来源依据,未知可空 | 不以回顾时间填补未知发生时间;消息发送时间只有在确实表示该事件时才可采用 | | `review_generated_at` | 产生该归纳的回顾时间 | 较晚生成不表示事件较晚发生或旧决定已被替代 | | `recorded_at` | 对象或版本实际写入系统的时间 | 迟录、补录不改变事件先后,也不证明当时已知 | | `validity` | 该主张适用的区间及当前有效、被替代、存在争议或未知状态 | 不用确认时间、记录时间直接推导有效起止 | | `confirmation_state` | 自动归纳、已确认、用户修订、已撤销等内容审核状态 | 已确认不等于永久有效;未确认不等于已失效 | `replaces` 表示有明确证据的新主张替代旧主张,记录方向、两端版本、适用范围、理由和来源。`contradicts` 表示两项主张存在证据支持的冲突,不自动决定谁正确。它们是目标关系语义,不能将当前 `contrasts` 或单纯 `revised` 无条件映射为这两类关系。新增选项、日期较新、措辞更肯定都不足以建立替代关系;没有证据时保留并列主张或待核对差异。 时间区间输入使用带时区的 ISO 8601 时间,查询归一化为 UTC。概念 `time_range` 使用闭区间 `[from, to]`,与现有监督范围保持一致;区间事件按相交判定。检索支持明确的 `time_basis`:默认 `event_time`,也可指定 `review_generated_at` 或 `recorded_at`。响应回显所用维度。未知时间不冒充命中区间,返回可计算的排除数量或 `unknown`,允许通过不限定时间的查询另行查找。 `timeline` 按发生时间和稳定 ID 排序,未知时间单列,展示目前保存的演变及缺口。它可以包含后来补录的旧事件,因而不等于“当时已知”。`current` 给出截至读取时、在已覆盖证据内可支持的当前理解;有冲突或缺口时保留并说明,不能仅挑最新摘要作为答案。 `as_of` 是未来能力,首期输入 schema 不接受该模式或参数,调用必须明确返回不支持,不能悄悄降级为 `current`。只有具备所需对象版本、人工修订记录以及可用来源版本/历史重建,并能验证不会泄漏当时未知的信息后,才另行定义其语义和验收。本文不批准为它新建持久化日志或快照体系。 ## 4. 公共读取合同 三个工具都只读已有数据,不调用模型、不自动发起回顾、不更新 checkpoint、不确认或修订对象、不创建任务,也不写入记忆或知识库。检索采用已有数据的确定性过滤与相关性排序,不能在调用中借模型补齐缺失事实。生成面向用户的建议由调用方 Agent 完成,必须引用返回证据并遵守其范围与限制。 默认 scope 为调用会话的当前项目,由 Main 解析并冻结;没有当前项目时要求显式选择 scope,不默认为 Global。调用方可显式请求 Global 或指定项目集合,沿用已有范围与能力授权校验。分页继续使用首请求范围,切换 UI 当前项目不改变旧游标。对象与来源读取也要重新校验归属,稳定 ID 不能绕过 scope。 首期沿用现有精确 scope 结果归属,不把 Global 摘要冒充单项目结论,也不隐式把 Global 结果投影为项目上下文。项目查询缺少该 scope 数据时明确返回空结果及覆盖状态。未来跨 scope 复用只接纳能按来源项目和事实依据正确筛选的叶子,跨项目归纳仍保留真实范围。 | 响应部分 | 必须表达的信息 | | --- | --- | | 范围 | `requested_scope`、`resolved_scope`、每项原始 scope、所选时间区间和时间维度 | | 修订 | 对象/结果修订引用、来源版本、读取时间;区分已知版本与 `unknown` | | 覆盖 `coverage` | 已知处理范围、来源版本和片段区间、缺失或未处理部分、未纳入来源类型;缺少数据时为 `unknown`,不能用零代替未知 | | 分页 `page` | `returned_count`、`has_more`、`next_cursor`、本页是否完整及限制原因;集合数量不能计算时为 `unknown` | | 证据 | 来源引用与 locator、关系依据、确认与有效性、已知冲突和历史缺口 | 覆盖与分页独立:最后一页只证明匹配结果读完,不能证明所有原始消息已被整理;已处理全部入选正文也不证明模型识别了全部事实。旧 `coverage_json` 只有条数时,不能推导完整的时间、版本或语义覆盖。 列表及长字段都需有界分页,超过响应容量时提供可续读的位置,无法分页的响应明确失败。不得静默裁剪后标完整。游标绑定过滤条件、排序和可用结果修订;分页期间内容变化、无法维持同一读取版本时返回 `stale_cursor` 并要求重新查询,不混入新旧页。该规则可基于现有修订检查完成,不要求持久化查询快照。取消、读取失败与真实空页分别返回。 ## 5. 工具定义 ### `story_graph_search` 输入:`query`、可选 `scope`、`object_types`、`time_range`、`time_basis`、`page_size` 和 `cursor`。`object_types` 对应第 2 节的对象类型;省略时检索全部支持类型。默认 scope 及时间规则按第 3、4 节执行。对象页默认 10 项,上限 50 项。匹配为不区分大小写的连续文本匹配,标题、实体标签和摘要字段权重高于其他字段;同分按类型、稳定 ID 和结果 ID 排序。 输出相关对象页,包含匹配原因或命中字段、明确标为摘录的预览、稳定引用、修订、相关演变和来源入口,以及公共响应信息。过滤须先于分页,按相关性排序并以稳定 ID 消除同分歧义。不能只在最近若干摘要中筛选后声称查完历史,也不能将摘要中的命中替代叶子事实。 ### `story_graph_get_context` 输入:`object_ref`、可选 `scope`、`mode: current | timeline`(默认 `current`)、可选时间范围与维度、页大小及游标。`current` 表达当前理解;时间过滤只限定证据集合,响应需标明“在所选证据范围内”,不能称作该区间终点时的历史状态。 输出对象当前描述、明确的决定/工作状态、偏好与适用范围、未解决事项、相关实体及关系、演变和证据引用。`timeline` 分页列出提出、补充、验证、修订、替代和冲突证据,保留旧决定及新修订;多组集合或长正文须各自提供续读位置,不能只对顶层对象分页。返回的是存储事实及其组织视图,不在工具内生成新建议。 D1 返回各结果快照、当前投影、事件、变化、关系、来源及成功叶子的独立描述。既有存储没有可靠的有效期和替代关系枚举,因此 `validity` 返回 `unknown`,保留 `revised`、`contrasts` 及原始理由供调用方核对。当前实体投影不会遮掉旧结果或叶子描述。`content` 是可拼接的 JSON 文本片段,每项附零基码点位置;按对象引用和结果 ID 拼接后解析。`content_page_size` 默认 4,000、上限 8,000 码点,游标可停在对象内部。完整响应超过 100,000 字节时明确失败,要求缩小页大小。 对象不存在、scope 不匹配、版本缺失或只有部分历史时分别说明。已存在的某个结果快照可作为具体版本读取依据,但不能作为任意 `as_of` 查询的替代品。 ### `story_graph_read_source` 输入:`source_reference_id`、可选 `scope`、明确的读取种类 `snapshot | version | current`、所需 `source_version`(读取指定版本时必填)及片段页位置。默认 `snapshot`,读取该引用实际保存的证据片段;不默认换成当前原文。 输出正文页、`content_kind`、请求版本与实际返回版本、`source_location`、原来源 ID、已保存片段与整份来源的范围和长度、可用时间元数据,以及公共覆盖和分页信息。位置用零基、左闭右开的 Unicode 码点 `[start, end)`;消息 locator 尽量保留项目、会话、消息、顺序和角色,本地资料保留文档/分块,外部片段保留已有 locator。缺少位置时明确标记,不伪造链接。 `snapshot` 只声称返回当次实际保存的片段;`version` 仅在该来源支持并保留指定版本时成功;`current` 显式读取当前内容并报告与引用版本是否不同。历史版本不可用时返回 `historical_version_unavailable` 及可用读取选项,由调用方决定是否另读当前版本。来源删除或不可访问时返回对应可用性;已保存片段仍可读时准确标注其范围。读取完一个片段不能称整份原文已读完,超长内容必须可续页,无静默截断。 D1 来源页默认 4,000、上限 8,000 码点。`current` 通过既有 locator 精确读取消息、任务状态正文或保存的知识引用片段;不拉取外部知识库原文。`version` 仅在当前可读来源的修订恰好等于请求版本时返回该版正文。旧版正文没有保留时明确不可用;无 locator 的旧引用仍可读 snapshot。当前来源迁出所选项目时拒绝读取。已保存引用的范围与当前位置分别校验。 ## 6. 内置 MCP 与 Runtime 接入 内置 MCP 列表提供 `Story Graph`,能力 ID 为 `story-graph`,默认分配给 Model、OpenCode、Continue 和 DeepSeek Harness;监督者应用默认关闭,开启应用后才可发现工具。工具描述说明适用于“继续上次工作”“此前如何决定”“方案为何改变”等请求。普通聊天不强制查询,也不把整份图谱自动注入每轮上下文;Agent 先搜索、再取相关对象上下文,在需要核对原话、处理冲突或详细引用时读取来源。 | 配置或目录 | 当前状态 | 目标映射 | | --- | --- | --- | | `heartbeatEnabled` | 监督者应用开关,新设置默认关闭 | 为 Story Graph 暴露和每次调用的必要条件;关闭后不列出三个工具,旧会话直接调用也拒绝 | | 会话 `storyGraphEnabled` | 每个会话默认开启,缺省字段按开启处理 | 关闭后不向该会话签发图谱工具,并拒绝已有能力会话的发现、调用和在途结果交付;其他会话不受影响 | | 内置能力 `enabled` 与 `assignments` | [CapabilityService](../../../src/main/capabilities/capability-service.ts)管理现有 MCP 开关与 Runtime 分配 | `story-graph` 沿用同一管理方式;应用关闭时不得由内置能力开关绕过,保留用户原分配 | | 内置工具目录 | 已有 Story Graph 条目 | 映射 `story_graph_search`、`story_graph_get_context`、`story_graph_read_source`,全部 `access: read`、`readOnlyHint: true` | | 监督整理参数 | 超时、并发、来源页与批次设置已存在 | 控制后台整理;不让一次 MCP 查询启动整理,也不以正文批次预算截断检索结果 | | 计划 `enabled` | 决定自动回顾是否到期执行 | 不决定已有图谱能否读取;应用开启且能力可用时,无计划也能查已有结果 | Main 在工具发现和调用分发时共同检查应用启用、会话开关、能力启用、Runtime 分配和 scope;只隐藏菜单不足以阻止旧会话调用。关闭后拒绝新的读取请求,已在途响应交付前复核状态,避免旧绑定继续返回数据。启用工具本身不产生回顾或图谱写入。工具本身只读,不依赖产品工作模式,也不增加写工具。 会话开关只决定 Agent 能否按需使用这三个 MCP 工具,不自动查询或注入图谱上下文,也不触发回顾。监督者应用关闭时,输入区完全隐藏开关并保留会话选择;再次启用应用后恢复该选择。后台回顾和计划仍由各自设置控制。 开关复用已发布的 `conversations.context_state_json`,字段为可选布尔值,不增加数据库版本或迁移。Renderer 经串行会话保存队列与 `conversations:set-story-graph` IPC 保存,成功后更新显示,失败保留原值并通知。Main 按会话主键只读设置,不加载消息历史;显式设置写入后,旧的自动保存快照不能覆盖它。新会话及新建分支使用默认开启。 本地 OpenCode、Continue 和原生客户端复用 [KnowledgeMcpGateway](../../../src/main/agent/knowledge-mcp-gateway.ts) 的 MCP 注册和分发;直连 Model 与 DeepSeek Harness Main 代理调用同一服务。远程 OpenCode/Continue 复用现有图像工具的受管 HTTP MCP 和二进制通道,`storyGraph` 描述独立于图像生成描述,可只携带图谱读取。请求仍由桌面 Main 解析范围和读取 SQLite,不把数据库、正文库或凭据复制到 Host。 远程发现每次向 Main 复核启用状态;调用复用同一个范围、分页及版本读取入口。OpenCode 根会话和子会话精确接入当前受管端点的图谱工具;Continue 保留 Main 绑定的会话端点,不按产品模式过滤工具。DeepSeek Harness 的 Main 目录、参数转换和 Host 执行路径均接入这三个工具;其他普通内置 MCP 的 Harness 限制保留。当前远程 Runtime 支持 OpenCode/Continue,不新增远程 Harness。桥接不可用时明确失败。 远程使用须配套部署含此接线的 Desktop 与 Agent;旧 Agent 不识别新增的工具描述字段。本次没有发布或更新受管包,源码验证不代表已发布客户端具备该能力。 ## 7. Global 与项目复用 当前 Global、单项目和不同项目集合分别维护 checkpoint 和故事线候选身份,项目集合排序归一化后才比较。项目 A 已处理某消息,不会使 Global 的相同消息自动成为已处理;Global 当前会按自身范围重新收集原始来源,不能描述为“只聚合已有项目图谱”。报告与监督也是独立阶段,不能互相推进位置。当前手动重看还会重新整理所选历史。 目标是同一来源版本的事实提取成功一次后,在 Global 和项目查询/整理中复用可用叶子事实,包括事件、实体变化、关系证据及来源位置,不只复用项目摘要。Global 可在已提取事实之上组织跨项目关系和摘要;范围变化本身不应造成相同正文再次提取。范围控制查询归属和综合判断,不再充当重复提取的唯一分界。 复用须核对原来源身份、版本、已覆盖片段及提取合同是否仍适用。仅部分成功时继续缺失部分;来源改版或用户明确要求重新分析时可产生新的提取结果,不能覆盖已确认内容。任务的当前状态、偏好背景和既有摘要仍须标明时间角色,不能作为跨 scope 复用时新发生的事件。共享事实的可见范围不能被 Global 摘要扩大。 这一目标尚未实现。本轮不批准具体数据迁移、跨 scope 主键重写、历史实体合并、checkpoint 转换或缓存表方案。实施前应从已有成功叶子和来源版本定位验证最小复用方案,列出旧数据的可复用程度与缺口;保留现有精确 scope 行为直到替代方案完成验证,不提前修改现有 checkpoint 含义。 ## 8. 记忆过渡与分阶段交付 已确认的长期偏好必须继续可用,并保留 Global/项目适用范围。旧记忆只有更新时间时,只能显示为当前偏好背景和实际更新时间,不能补造“首次决定”“当时生效”或替代关系。新的图谱归纳与已确认偏好冲突时返回待核对差异,不以较新生成时间覆盖用户选择。 | 阶段 | 交付内容与退出条件 | 当前状态 | | --- | --- | --- | | D0 设计 | 记录工具、时间、范围、过渡与验收合同 | 已记录 | | D1 只读查询 | 基于现有数据实现三个工具及本地/远程接线、配置目录、关闭拦截和分页;缺失版本与覆盖返回未知,对应 US-S28 至 US-S30 | 已实现;验证范围见实施进度,当前 memory 读取继续保留 | | D2 事实复用 | 验证同一来源版本跨 scope 复用完整叶子;明确身份、版本变化与已有数据处理方案,达到 US-S31 的范围验收 | 待设计具体存储调整并实施;无已批准迁移方案 | | D3 概念与读取统一 | 让统一入口承接已确认偏好及图谱上下文;核对引用、范围、冲突和旧数据可读性后,才切换现有 memory 消费路径 | 待实现;不以删除旧记录作为退出条件 | | 未来历史重建 | 单独定义并验证 `as_of` 所需版本与当时可知性 | 不属于首期,不承诺交付时间 | D1 可将现有已确认偏好作为明确标记的背景投影,原读取路径在过渡期仍工作。重复背景按原记忆身份识别,图谱未知历史不能由记忆更新时间补齐。具体迁移与旧入口退出时点须根据实现证据确定,本文没有要求立即删除旧 memory API、设置或 UI。 ## 9. 验收映射 核心场景使用可核对的旧决定 A、后来明确修订为 B 的证据,以及“现在继续做应采用什么”的请求。工具返回 A、修订事件、替代依据与 B;Agent 基于它们推荐 B,并引用旧决定和修订来源。新增选项 C 但没有替代依据时,不得把 B 判为失效;决定 B 也不能被回答为 B 已执行完成。 | 验收 | 对应故事 | 通过条件 | | --- | --- | --- | | 决定演变 | US-S28 | 完成上述旧决定 → 有证据修订 → 当前建议及引用链;冲突没有证据解决时保持待核对 | | 时间 | US-S28、US-S29 | 迟录旧事件不变成新事件;确认与有效性分开;timeline 不声称当时已知;`as_of` 明确不支持 | | 分页与来源 | US-S29 | 匹配项、演变和长原文均可读到末页;覆盖未知与零命中可区分;游标失效不混页;旧版不可用不返回伪历史 | | 启停与只读 | US-S30 | 应用关闭后发现与直接调用均不可用;开启但无计划可读已有数据;工具自身模型调用与业务写入均为零 | | Runtime 一致性 | US-S30 | 本地及 GoodBuddy Agent 远程实际链路具有相同 scope、版本、分页、引用与关闭行为,不能以共享 schema 代替接线验收 | | 记忆及范围复用 | US-S31 | 确认偏好与已有引用保留;同版本成功叶子跨 scope 复用且能检索事实,不只返回摘要;历史缺口不补造 | 2026-09-28 的 D1 验证命令和证据记录在[实施进度](./progress.md#2026-09-28-story-graph-只读工具),当时运行定向测试、类型检查、限定文件 lint 和确定性 Host 探针,没有付费模型请求。2026-10-03 会话开关的 Electron、Host 及真实模型调用证据见[本轮记录](./progress.md#2026-10-03-会话故事图谱开关)。真实模型如何根据冲突证据形成最终建议,仍需与工具读取正确性分开验收。