免费获取学习方案
ARTICLE DETAIL

资讯详情

深耕编程基础知识与建站技术分享的一线实战洞察。

DeepSeek Harness 会话查询关系追踪:`traceSession` 与 `traceEvent` 的一次性视图设计

DeepSeek Harness 会话查询关系追踪:`traceSession` 与 `traceEvent` 的一次性视图设计 DeepSeek Harness 会话查询关系追踪traceSession与traceEvent的一次性视图设计【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness本文基于仓库中的设计笔记 .agents/notes/implemented/feature/2026-07-13-session-query-tracing.zh.md 展开并结合dsh-session-query包源码tracing.ts、types.ts、index.ts与其单元测试tracing.spec.ts进行纵深解读。导读在 DeepSeek Harness 中会话历史session history不是一份普通的消息列表而是一份追加式append-only事件日志会话之间的父子关系编码在不可变 header 的parentSession字段中模型可见的“当前表面”surface由位置式替换操作surfaceOp决定事件之间的派生关系则记录在sourceEventSeqs引用数组中。消费方若想重建这些关系就不得不重复实现语料优先级、表面折叠、格式错误日志处理、确定性谱系排序与克隆等整套逻辑——这正是ctx.sessionQuery新增的traceSession(sessionId)与traceEvent({ sessionId, seq })两个一次性视图要解决的问题。读完本文你将掌握这两个追踪 API 的语义、校验边界、状态分离原则、源码级实现路径与测试验证方式。背景关系分散在三处消费方无法低成本重建设计笔记开篇指出了核心痛点会话关系并非集中存储而是分散在三种载体上不可变 header会话之间的父子关系通过SessionHeader.parentSession表达header 一经创建不可变更位置式表面操作事件通过surfaceOp: append或surfaceOp: { op: replace, start, end }决定自己在模型表面中的位置替换操作会“遮蔽”shadow一段既有表面区间已记录的来源事件 seq 引用事件通过sourceEventSeqs数组显式声明它引用了哪些更早的事件作为构造输入。如果让每个消费方自己重建这些关系就必须重复实现四类易错逻辑语料优先级live 与持久化数据如何取舍、表面折叠如何把日志回放为当前表面、格式错误日志的处理损坏的约定如何报错、确定性的谱系顺序与克隆父子与后代树如何排序、返回的数据如何与内部状态隔离。更关键的是位置替换关系与来源事件引用关系语义不同一次位置替换可能遮蔽表面节点同时引用的来源事件可能根本不在表面上例如压缩产生的摘要替换了一段用户消息但摘要还引用了日志中更早的中间产物。如果把它们合并为一种通用边类型消费方将无法区分“谁在位置上取代了我”与“我引用了谁作为输入”。这正是后来“合并替换边与来源事件引用边”被否决的原因。决策在ctx.sessionQuery上公开两个一次性追踪视图设计决策非常克制在保留精确读取的基础上仅为ctx.sessionQuery增加两个方法traceSession(sessionId)追踪会话谱系traceEvent({ sessionId, seq })追踪单事件关系。两个方法都是基于现有「实时数据优先」live-preferred语料的一次性视图会话追踪读取一次完整语料列表源码中对应 corpus.ts 的listSessions见 index.ts#L300-L304事件追踪读取一份逻辑日志并执行一次规范表面折叠对应corpus.load与tracing.analyzeEventLog。服务在调用结束后不保留谱系、反向索引或替换状态——也就是说每次调用都是独立计算服务端零缓存、零状态。这保证了“真源”source of truth始终是日志本身任何一次调用得到的都是当时一致时刻的确定性快照。返回类型类型级契约两个方法的返回类型定义在 types.tsSessionLineageTrace由公共字段加一个完整性判别联合组成/** Known ancestry and descendants for one logical session. */ type SessionLineageTrace { /** Detached record for the session that was traced. */ target: SessionRecord /** Known parents from the immediate parent outward. */ ancestors: SessionRecord[] /** Complete known descendant trees rooted at the targets direct children. */ descendants: SessionLineageNode[] } ( | { /** The complete parent chain is present in the logical corpus. */ complete: true /** Detached record at the top of the complete lineage. */ root: SessionRecord } | { /** The parent chain leaves the visible logical corpus. */ complete: false /** First parent id that is not present in the logical corpus. */ unresolvedParentId: SessionId } )要点ancestors按从直接父级到外层父级的顺序排列descendants是递归的后代树根节点是目标的直接子级同级节点先按创建时间createdAt排序再按 session id 排序localeCompare保证结果确定性源码见 tracing.ts#L155-L157判别联合使“已知根节点”与“第一个无法解析的父级 id”互斥complete: true时携带rootcomplete: false时携带unresolvedParentId与目标相连的环ancestry 死循环以SESSION_QUERY_INVALID_LINEAGE失败源码见 tracing.ts#L130-L136。SessionEventTrace则把两类关系分开保留/** Direct surface replacements and relationships to cited source events for one event. */ interface SessionEventTrace { /** Lightweight target record. */ target: SessionEventRecord /** Immediate positional replacement event, when the target was shadowed. */ replacedBy?: number /** Positional replacers from the immediate replacement to the final replacement. */ replacementChain: number[] /** Surface nodes directly removed when the target itself performed a replacement. */ replacedEventSeqs: number[] /** Earlier events cited directly as sources, in their recorded order. */ sourceEventSeqs: number[] /** Later events that directly cite the target as a source, in log order. */ derivedEventSeqs: number[] }replacedBy直接的位置替换者目标被遮蔽时存在replacementChain沿替换者一路追踪至最终节点的完整链条replacedEventSeqs目标自身执行替换时直接移除的真实表面节点sourceEventSeqs日志中直接来源的顺序保持记录顺序derivedEventSeqs按日志顺序列出后续直接反向引用目标的更晚事件。需要特别强调的是查询不会沿被引用的来源事件继续向上追踪——所有 seq 列表除replacementChain外都只含直接链接。这一刻意选择在“考虑过的替代方案”中解释过如果返回所有传递引用的来源事件会掩盖日志中直接记录的证据、无谓增大结果并让一条遥远的格式错误边改变原本局部的输出。此外traceEvent在服务层返回的是SessionEventTraceObservation在 index.ts#L313-L320 中构造即SessionEventTrace附带与事件日志同一观测绑定的克隆 headersession供执行授权检查的消费方验证来源。校验边界先验目标再对整个日志接受或拒绝事件追踪的校验顺序在源码中有明确体现tracing.ts#L65-L105先检查目标是否存在events[seq]缺失或seq不匹配时抛出SESSION_QUERY_EVENT_NOT_FOUND不做任何表面或来源事件分析——避免用损坏的日志掩盖“目标不存在”这一更基础的错误随后事件列表与追踪共用dsh-session的单遍表面折叠foldSurface来自deepseek-ai/dsh-session对加载的整份日志整体接受或整体拒绝绝不产出部分结果。analyzeEventLogtracing.ts#L175-L214把折叠结果转成三类事件记录current当前表面节点、shadowed被替换遮蔽、log-only纯日志并构建replacedBy与replacedEventSeqs两个反向索引——但这两个索引只存在于单次调用内部调用结束后即被丢弃。foldSurface本身定义在 packages/core/session/src/surface.ts折叠过程中会对每条事件做局部校验surfaceOpOf见该文件 L184 起非表面事件不能携带surfaceOp或sourceEventSeqs表面事件必须携带surfaceOp标记。事件追踪把折叠抛出的任何错误统一包装为SESSION_QUERY_INVALID_SURFACE。设计笔记归纳的校验约定如下全部违例均以SESSION_QUERY_INVALID_SURFACE失败约定要求seq 连续性事件 seq从零开始且连续表面标记表面标记符合事件类型的适用范围仅user/message、assistant/message、tool/result三类可入表面来源引用权限只有表面事件类型可以引用来源事件 seq数组完整性存在的数组必须非空且没有重复项来源时序每个来源必须是更早的 seq替换完整性每次位置替换必须指明并引用它移除的全部表面节点替换与sourceEventSeqs联动校验测试 tracing.spec.ts#L419-L479 用一个it.each表格逐一覆盖了这些违例非表面来源引用、非法来源数组、空来源、稀疏来源、重复来源、缺失更早来源、未来来源、无来源的替换、替换缺少被遮蔽来源等全部断言抛SESSION_QUERY_INVALID_SURFACE。还有一条回归用例L481-L489证明listEvents应用同一套表面契约——设计笔记特别指出系统不存在只用于分类、要求更弱的表面标准追踪与精确读取必须看到同一个模型表面。状态分离所有返回记录都与内部状态隔离这是该功能可靠性的基石设计笔记明确两条规则已知的实时事件追踪绝不查询持久化只要目标 session 存在于ctx.sessions中就直接快照后端故障不会让当前内存历史变得不可读来自持久化数据的事件追踪保留精确读取所要求的列表/加载一致性检查——加载前列表与加载后观察之间的不可变 header 必须兼容否则以SESSION_QUERY_SOURCE_CONFLICT失败。会话谱系天然属于跨语料操作需要在全部会话中查找父级与后代因此保留跨语料的持久化失败语义SESSION_QUERY_PERSISTENCE_FAILED。源码层面有两处直接佐证SessionCorpus.listSessionscorpus.ts#L58-L77先列出持久化记录再用 live 记录覆盖同一 id并对重叠 id 断言 header 兼容最后按确定性顺序排序traceSession的实现正是「一次listSessions 一次确定性遍历」tracing.ts#L113-L173ancestors用循环沿parentSession上行并做环检测后代树用显式栈迭代构建buildDescendants而非递归测试专门验证了 3000 层深度不爆 JavaScript 调用栈tracing.spec.ts#L261-L280。所有返回的 header、事件与记录都是克隆cloneRecord对 header 做structuredClonetracing.ts#L246-L248事件记录均为新建对象。测试验证了即使调用方修改返回结果重复调用仍得到原始值tracing.spec.ts#L199-L206 与 L327-L344。考虑过的替代方案四个否决及其理由设计笔记记录了四个被否决的方向理解它们有助于把握当前 API 的边界替代方案否决理由公开独立的追踪辅助函数源优先级与状态分离边界属于ctx.sessionQuery公开辅助函数会诱使调用方绕过该边界自行拼接语料合并替换边与来源事件引用边为一种通用边位置替换可以遮蔽表面节点同时引用不在表面上的构造输入消费方必须能区分这两种含义返回所有传递引用的来源事件会掩盖日志中直接记录的证据、增大结果并让一条遥远的格式错误边改变原本局部的输出对格式错误的来源事件列表返回尽力而为的追踪结果结构上看似合理的局部结果会显得具有权威性当规范关系约定损坏时精确检查应明确报错后果与成本权衡设计笔记最后总结了该功能的后果消费方无需缓存也无需引入第二份语料即可获得确定性的关系视图成本透明事件追踪每次调用都会执行全日志校验与分配谱系追踪每次调用都会列出完整的逻辑语料——这些成本让真源保持明确并且与承载内容的全文搜索及过滤 API 相互独立搜索走挂载的 SQLite 后端追踪是内置具体行为测试边界该功能具备单元测试与服务层测试覆盖即 tracing.spec.ts但没有快照或端到端 fixture因为它没有引入面向模型的消费方、transcript文本记录变更或跨进程协议——这与dsh-session-query包“无模型体验”的定位一致见 README.zh.md 的“模型体验”一节。从服务层接线看index.ts#L300-L320traceSession与traceEvent都接受可选AbortSignal在语料解析完成后调用throwIfAborted()立即终止——因此在大语料下调用方可以安全地取消慢速追踪而不必担心返回过期数据。实战一个完整的追踪用例结合测试appendTraceEventstracing.spec.ts#L114-L168构造的日志seq 2 为用户消息、seq 3 为引用 seq 2 的用户消息、seq 4 为替换 3 并引用 [3,2] 的助手摘要、seq 8 为替换 4 并引用 [2,4] 的助手摘要traceEvent的语义一目了然目标 seqreplacedByreplacementChainreplacedEventSeqssourceEventSeqsderivedEventSeqs表面分类2—[][][][3, 4, 8]log-only34[4, 8][][2][4]shadowed48[8][3][3, 2][8]shadowed8—[][4][2, 4][]current注意 seq 2 是log-only它不在当前表面上也没有被直接替换被遮蔽的是 seq 3但它被 3、4、8 三个事件引用因此derivedEventSeqs记录了全部三个后续反向引用而 seq 3 被 seq 4 直接替换replacementChain沿替换链一路到 8。这个例子直观展示了“位置替换”与“来源引用”两条独立的关系维度。相关文档与进一步探索包级参考packages/session-query/session-query/README.zh.md——ctx.sessionQuery全部能力的操作清单、过滤器语义与错误码速查子系统参考docs/subsystems/session-query.zh.md——完整类型级约定记录、过滤器、搜索页、谱系、有界读取与错误其中「Cordis API」一节生成了traceSession/traceEvent的签名与 JSDoc底层表面折叠packages/core/session/src/surface.ts——foldSurface的折叠状态机与局部校验规则测试用例packages/session-query/session-query/tests/tracing.spec.ts——谱系排序、环检测、状态隔离、持久化失败语义与全部表面校验违例的完整断言设计决策上下文docs/subsystems/session-query.zh.md 中引用的统一服务决策笔记与 SQLite 提供方笔记。总体而言traceSession与traceEvent是对“会话历史可编程读取”的最小、克制而完备的补充它们把分散在 header、表面操作与来源引用中的关系收敛为两个一次性的确定性视图用整日志校验保证“坏日志绝不产生貌似权威的局部结果”用克隆与状态隔离保证消费方永远看到一致时刻的快照。【免费下载链接】deepseek-harnessDeepSeek Harness: Everything is a Plugin.项目地址: https://gitcode.com/gh_mirrors/de/deepseek-harness创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表