news 2026/10/21 1:20:28

Kun 会话轨迹紧凑存储:始终在线的请求生命周期元数据与内容寻址提示清单设计

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Kun 会话轨迹紧凑存储:始终在线的请求生命周期元数据与内容寻址提示清单设计
  • 人工智能
  • AI Agent
  • 自主智能体
  • 桌面应用
  • MCP Clients

【免费下载链接】Kun

Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.

项目地址:https://gitcode.com/gh_mirrors/de/Kun
点击查看免费下载

Kun 是一款 Local-first 的 AI Agent 工作区,其模型请求诊断此前局限于右侧检查器,并以原始请求/响应载荷独立持久化,导致长会话存储膨胀且难以理解。本文基于 Kun 仓库中add-conversation-trajectory-view变更包的紧凑轨迹存储规范,完整讲解其“始终在线的请求生命周期元数据 + 可选的、内容寻址的提示清单”存储模型,并结合 紧凑轨迹存储规范、方案提案、设计文档 与对应源码实现,说明 Kun 如何在低存储成本下持久化每一次模型调用的身份、时序、用量与失败信息,并在重启、预算回收、遗留数据兼容等场景下保持可靠性与可查询性。读完本文,你将掌握该存储模型的九大需求约束、核心数据结构(轨迹记录、提示清单、内容寻址 Blob)、预算与恢复策略,以及对应查询 API 的契约与底层实现。

背景:为什么模型请求诊断需要“紧凑”存储

Kun 此前通过LlmDebugRecorder记录精确的模型传输过程,并把完成的记录按线程写入 JSONL 文件,渲染在 workbench 的 Agent Perspective 右面板中。会话正文(assistant 文本/推理、工具调用与结果、压缩条目、用量事件、附件)已经存在于 Session store 中,并通过线程时间线与 SSE 路径到达。问题在于:把完整请求历史与原始响应再次写入 trace JSONL,对长会话而言存储扩展性差(见 设计文档)。

因此该变更包的目标是:

  • 让每一次对话都有紧凑的请求生命周期元数据,无论是否开启完整内容捕获;
  • 用稳定 ID 关联 Provider 尝试、逻辑模型步骤、Session 条目与工具生命周期;
  • 通过提示清单与去重压缩 Blob 重建可选的精确提示细节,而不复制响应/工具/附件正文;
  • 暴露有界的轨迹分页、摘要与详情 API,并提供以文件系统为权威的回退;
  • 将右面板的 Agent Perspective 入口迁移为居中会话视图,同时保留其语义解析与详情能力。

非目标包括:持久化 token/chunk 级增量、原始 HTTP 交换、附件字节或完整工具输出;提示 diff、轨迹导出、时间轴缩放、请求对比;以及把 SQLite 变成权威存储。

核心架构:把“生命周期元数据”与“内容捕获”解耦

设计文档明确了两条分离的持久化路径:

  1. 紧凑生命周期保留(Always-on):模型观察器总是为每个逻辑 round 创建记录,并为每次实际传输尝试创建一条记录。记录获得稳定的roundId、requestId、turnId、step、attempt值,并在开始、首个内容、终态三个时刻写入检查点。
  2. 可选内容保留:线程现有的捕获开关只控制提示清单/Blob 的创建与有界的内存线诊断,绝不抑制元数据。

源码中LlmDebugRecorder在每次beginAttempt时即创建ModelRequestTraceRecord,其captureMode字段区分full与metadata:当捕获关闭时,请求体与响应头被emptyHeaders()/emptyBody()占位(见 llm-debug-recorder-recorder.ts 与 llm-debug-recorder-trajectory.ts)。这取代了原先“要么全捕获要么什么都不存”的记录器策略,在保留时序/错误/用量信息的同时,让敏感提示的捕获保持显式可控。

需求一:始终在线的紧凑请求生命周期

规范第一条要求:对每个逻辑模型 round 与每次实际 Provider 尝试,无论是否捕获完整内容,Kun 都必须持久化紧凑生命周期元数据。

对应场景:

  • 捕获关闭的请求:线程禁用完整内容捕获并发送模型请求时,Kun 仍持久化稳定的 round/request/turn/step/attempt 身份、provider/model/purpose、状态、时序、用量、错误与条目关系,只是不含提示与原始响应正文。
  • Provider 重试:一个逻辑模型步骤执行多次 Provider 尝试时,每次尝试拥有唯一 request ID 与尝试序号,同时共享逻辑 round 与 step 身份。

源码侧,LlmDebugRecorder.beginAttempt生成的记录包含roundId、step、purpose、captureMode、attempt、attemptReason、transport、endpointFormat、provider、model等字段;finish阶段对所有pending记录依据响应捕获错误、输出错误、stopReason 依次归一为capture_error/failed/completed/cancelled,并逐条调用persistentMetadataRecord写入持久层。persistentMetadataRecord专门把 request/response/decoded 中的正文与头全部清空,只保留方法、URL(含脱敏标记)、状态码与错误信息——这正是“紧凑”二字在实现层面的直接体现。

需求二:有界生命周期写入与重启恢复

Kun 只持久化生命周期开始、首个内容、终态,以及每两秒至多一次轻量进度检查点,绝不把普通流增量写成轨迹记录。

源码中captureChunk在收到首个内容块(isFirstContentChunk覆盖文本、推理、工具调用、图像生成完成等)时写入firstTokenAt检查点;此后每次捕获块时判断now - state.lastCheckpointAt >= 2_000才再次持久化(见 llm-debug-recorder-recorder.ts)。规范中“Runtime exits mid-request”场景要求:重启后若存在从未到达终态的记录,则该请求被暴露为interrupted,且超过 24 小时的检查点被移除。实现上interruptedRecord()会为这类记录补写interrupted状态、finishedAt、durationMs与默认错误文案;查询侧listThread会把持久化中pending且不在活跃集合内的记录自动投影为interruptedRecord。

需求三:规范会话引用——以 Session 条目为响应与工具的唯一真源

轨迹元数据引用规范的 user、assistant、reasoning、tool、compaction、attachment 记录,而不是复制其完整内容。工具调用与结果共享 call ID 时,轨迹投影只暴露一条工具生命周期记录,包含条目引用、开始/结束时序、状态与有界预览。

在 trajectory-query-service.ts 的projectItems中可以看到具体实现:tool_call条目通过resultByCall(以callId为键)关联对应的tool_result,生成kind: 'tool'的记录,只保存argumentsItemId、resultItemId、argumentPreview(2 KiB 内)、resultPreview、isError与attachmentIds;assistant_text/assistant_reasoning被聚合为assistant记录并携带itemIds数组;user_message、model_context、runtime_context_source、compaction分别投影为user/context/compacted等记录。设计文档明确指出,把响应与工具正文复制进轨迹存储的方案被拒绝,因为它会在长会话中重复最大体积的数据,并造成删除/压缩行为不一致。

需求四:内容寻址提示清单(Prompt Manifest)

当开启完整内容捕获时,Kun 会为每个请求创建一个提示清单,包含有序的条目/消息引用、Session 边界、附件元数据,以及 System Prompt、工具 schema、请求配置、回退片段经过脱敏后的SHA-256 Blob 引用。

关键场景:

  • 重复提示组件:多个请求使用相同的 System Prompt 或工具 schema 字节时,它们共享一个不可变的 Brotli 压缩 Blob,而不是重复存储内容。
  • 超大内容:单个脱敏 Blob 超过 8 MiB 时,Kun 存储有界的头/尾表示,附带原始大小、内容哈希与显式截断状态。

实现位于 trajectory-content-store.ts:captureRequest先对正文做脱敏(redactModelTraceValues+redactBrowserUseDebugContent),再由promptParts把请求拆分为system、tools、config、message四类片段;每个片段经putBlob计算 SHA-256(createHash('sha256')),以blobId.br为文件名按wx模式写入(文件已存在即视为去重命中),用 Node 内置brotliCompress(质量参数 5)压缩。超限内容保留头 512 KiB 与尾 64 KiB,中间插入显式截断标记(TRAJECTORY_BLOB_HEAD_BYTES/TRAJECTORY_BLOB_TAIL_BYTES/TRAJECTORY_MAX_BLOB_BYTES均在源码顶部定义)。清单本身(trajectory.ts 的PromptManifestSchema)记录blobs数组、messageItemIds、attachmentIds、retainedBytes,Blob 引用含blobId、kind、codec: 'br'、rawSize、compressedSize、truncated。选择 Node Brotli 而非引入 Zstd 原生依赖,是为了让打包版 Kun 与独立 TUI 共用同一内置编解码器,清单记录 codec 以支持未来扩展。

查询投影时(projectRequests),若清单存在,则从system/tools/config三类非 message Blob 拼接出promptFingerprint,据此生成“Initial System Prompt / System Prompt Updated”系统记录,实现提示变更的可视化;optionsAvailable、systemBlobId、toolsBlobId、configBlobId等字段也随之填充。

需求五:敏感与二进制内容排除

轨迹持久化在哈希或写入之前移除凭据,且不得保留原始 HTTP 头/帧、授权值、cookies、附件正文、图片/Base64 载荷或完整工具输出。

场景:当配置的凭据出现在 URL、头、请求选项或回退片段中时,元数据日志、清单、Blob、索引、DTO 与警告日志中都不能出现该凭据值。

源码中实现了多层防线:

  • URL 脱敏:sanitizeModelTraceUrl在写入记录前同步执行;
  • 头脱敏:sanitizeModelTraceHeaders移除/标记含敏感值的头名,持久化记录中headers一律为空;
  • 值脱敏:redactModelTraceValues在捕获正文前替换secretValues;
  • 浏览器动作脱敏:redactBrowserUseDebugContent/redactBrowserUseActionForPersistence专门处理browser_use工具;
  • 清单侧:sanitizePromptValue对authorization、api_key、access_token、refresh_token、password、cookie、secret、private_key、credential等键名(正则sensitiveKey)直接替换为[REDACTED],对data:...;base64,前缀或疑似大 Base64 字符串替换为[BINARY OMITTED]。

需求六:明细保留预算(Retention Budgets)

Kun 将保留的详细内容上限设为每线程 64 MiB、全局 512 MiB,内联预览 16 KiB、搜索预览 2 KiB;当明细被回收时,生命周期元数据必须保留。

场景:添加内容将超过全局预算时,逐出最近最少使用的非活跃明细,元数据保持可查询,受影响记录报告evicted明细状态。

对应常量在 trajectory-content-store.ts 顶部:TRAJECTORY_MAX_THREAD_DETAIL_BYTES = 64 * 1024 * 1024、TRAJECTORY_MAX_TOTAL_DETAIL_BYTES = 512 * 1024 * 1024、TRAJECTORY_INLINE_PREVIEW_BYTES = 16 * 1024、TRAJECTORY_SEARCH_PREVIEW_BYTES = 2 * 1024。实现上enforceThreadBudget按清单createdAt从旧到新删除清单文件直至低于线程预算,enforceGlobalBudget类似地在全局层面回收,回收后统一执行collectUnreferencedBlobs做标记-清除(mark-and-sweep)式的 Blob 垃圾回收。设计文档特别强调:Blob GC 以权威清单为参照,而不是信任崩溃后可能失真的可变引用计数。被删除清单对应的记录在查询时以evicted明细状态呈现,而时序、用量、错误与 Session 引用仍完整可查。

需求七:权威文件系统与可重建索引

轨迹日志、清单与 Blob 是权威的文件系统数据;任何 SQLite 轨迹索引都必须是可重建的,查询 API 在无索引时通过有界文件系统回退继续工作。

场景:混合 SQLite 绑定或轨迹表不可用时,轨迹分页、摘要、过滤与有界搜索继续基于紧凑文件系统数据运行,并给出降级警告。

当前仓库实现中,ModelRequestTraceStore承担按线程的 JSONL 持久化与分页游标读取,TrajectoryContentStore负责清单与 Blob 文件,二者共同构成权威文件系统层;TrajectoryQueryService直接通过recorder.listThread(内部走ModelRequestTraceStore)与sessions.loadItems读取数据投影轨迹记录,不依赖任何索引表。设计文档把“索引可重建 + 文件系统回退”列为显式决策,并将 SQLite 原生绑定失败列为风险项,要求在实现与测试中验证扫描回退路径。

需求八:轨迹查询 API 契约

Kun 暴露带认证、线程隔离的轨迹分页、摘要与分段详情路由,使用版本化 DTO、不透明游标,并安全处理未知或缺失数据。

场景:客户端请求的详情 ID 不属于路由线程时,Kun 返回 not found 且不泄露该记录。

路由实现位于 trajectory.ts:

  • GET /v1/threads/{id}/trajectory:解析limit(默认 100,最大 200)、cursor(不透明游标)、filter(all/llm/tool/error)、q(查询串,最长 512 字符);
  • 摘要路由返回TrajectorySummary,包含 requestCount、toolCount、runningCount、failedCount、输入/输出/推理 token、缓存命中率、平均首 token 延迟、平均 token/秒、总时长、成本(USD/CNY)与价值估算等聚合指标;
  • 详情路由按section参数(overview/input/output/usage/timing/raw/arguments/result/system-prompt/tools/diff/options/rendered/source/schema)解析指定分段。

所有 DTO 由 contracts/trajectory.ts 中的 Zod schema(TrajectoryPageSchema、TrajectorySummarySchema、TrajectoryDetailSchema等)版本化校验。分页游标编码为{ v: 1, startedAt, id }的 base64url 形式,按“时间倒序 + ID 字典序”确定下一页边界;过滤器语义为llm(请求+assistant)、tool、error(failed/cancelled/interrupted)。serviceFor先校验线程存在并检查轨迹记录器可用性,线程归属校验在TrajectoryQueryService.detail中通过在线程记录集合内查找实现——找不到即返回notFound,实现“不泄露跨线程记录”的要求。

需求九:遗留兼容与删除语义

Kun无破坏性迁移地读取遗留的 schema-v1 模型请求 trace,并在线程删除时移除该线程的轨迹元数据、清单、遗留 trace 与无引用 Blob。

场景:会话只有 schema-v1 trace 记录时,轨迹 API 投影出可恢复的遗留元数据,并把新字段标记为不可用,而不会让会话失败。

实现层面,ModelRequestTraceStore兼容读取既有 schema-v1 文件;projectRequests中当记录无manifestId且captureMode非 metadata、请求体仍有原始字节时,detailState判定为legacy(其余无捕获的为not_captured);trajectoryStatus将transport_error、capture_error、failed、not_started统一归并为failed。删除路径上,LlmDebugRecorder.deleteThread同步清理活跃 round、内存记录,并并行调用ModelRequestTraceStore.deleteThread与TrajectoryContentStore.deleteThread;后者递归删除线程清单目录后执行collectUnreferencedBlobs回收不再被任何清单引用的.br文件。设计文档的迁移计划六步与此一致:先引入新契约与兼容读取器 → 开始写紧凑记录与可选清单(遗留文件不动)→ 添加查询 API 与渲染端客户端并切换入口 → 停止持久化原始载荷追加(保留有界实时诊断)→ 线程删除时清理新旧产物并尽力回收 Blob → 因 Session 数据未变、遗留设置与记录仍可解析,回滚始终可行。

从存储到视图:轨迹数据如何驱动 UI

紧凑存储之上是 会话轨迹视图规范:标题栏提供chat | trajectory居中视图切换,切换时保持 composer 与聊天组件挂载,不改变活动线程、草稿、模型、权限模式与聊天滚动位置。轨迹视图把请求元数据与 Session 条目投影为稳定的 Turn/Step 分组,按时间顺序渲染并支持向后分页,十万条记录时仅挂载可见 overscan 窗口(使用@tanstack/react-virtual处理变高行)。三车道时间线(输入/模型/工具)、all/llm/tool/error过滤、有界搜索、折叠控制、实时/等宽显示模式与全会话摘要指标均由查询 API 的filter/q/summary支撑;记录检查器区分 unavailable、truncated、evicted、failed、cancelled、interrupted 六种明细状态,并在捕获关闭时明确提示“内容未捕获”。每线程的过滤、查询、选择、折叠、滚动、检查器宽度与时间线模式状态被隔离(由有界 Zustand store 承载),仅在账本位于实时边缘时自动跟随新记录,离开实时边缘则保持滚动位置并提供返回实时边缘的新记录计数。原先的 Agent Perspective 右面板贡献被移除,持久化的遗留布局标识归一化为无面板,确保旧布局恢复时不会出现重复检查器。

总结:一份“元数据权威、内容可选、索引可弃”的轨迹存储设计

综合规范、设计与源码,Kun 的紧凑轨迹存储形成了如下闭环:始终在线的生命周期元数据保证每次请求尝试都可追溯;规范 Session 引用避免复制会话正文这一最大数据源;内容寻址的提示清单 + Brotli 去重 Blob让精确提示重建按需且去重;多层脱敏与二进制排除守住凭据与隐私边界;线程 64 MiB / 全局 512 MiB 预算与 mark-and-sweep GC约束存储增长且永不驱逐元数据;文件系统权威 + 可重建索引 + 有界回退保证可靠性;版本化 DTO 与不透明游标保证查询契约稳定;遗留 schema-v1 兼容与线程删除清理保证平滑迁移。若要继续深入,可依次阅读 紧凑轨迹存储规范、设计文档、轨迹契约、内容存储实现、查询服务实现 以及对应的 路由实现 与测试用例(如 trajectory.test.ts、trajectory-content-store.test.ts、trajectory-query-service.test.ts)。

  • 人工智能
  • AI Agent
  • 自主智能体
  • 桌面应用
  • MCP Clients

【免费下载链接】Kun

Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.

项目地址:https://gitcode.com/gh_mirrors/de/Kun
点击查看免费下载

相关推荐

上一篇:窗口放大工具终极对决:Magpie凭什么成为Windows首选?
下一篇:obfuscator插件开发指南:如何为你的二进制混淆工具扩展自定义变换

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/10/21 1:19:35

CSDN第一篇技术博客写作指南:选题、排版与搜索优化

第一次在CSDN上按下“发布文章”那个按钮时,心里其实挺没底的:“我的第一个CSDN - First (小白 求指点)”这个标题,几乎是每个技术写作者的缩影。我刚入行时也发过类似的文章——内容就几百字,讲一个命令行怎么敲,排版…

作者头像 李华
网站建设 2026/10/17 7:17:11

如何安全管理 OpenFlux 共享密钥:传输、存储与轮换实战指南

如何安全管理 OpenFlux 共享密钥:传输、存储与轮换实战指南 OpenFlux 是一款网络栈研究工具,通过可插拔的传输层构建 TCP 隧道。当启用传输加密时,客户端与出口节点共用的**共享密钥(shared secret)**就是整条隧道的安…

作者头像 李华
网站建设 2026/10/15 7:14:06

Spring AI 实战:从配置到对话,ChatClient 链式调用与上下文管理

1. 从配置文件到对话窗口:Spring AI 到底简化了什么第一次接触 Spring AI 的时候,我脑子里其实带着一个很具体的疑问:过去在 Java 项目里接一个大模型对话能力,光是 HTTP 客户端封装、请求体拼装、响应解析、异常重试这些杂活&…

作者头像 李华
网站建设 2026/10/18 0:08:52

金属风速传感器选型与部署:从原理到实战的实时数据驱动指南

我做现场仪表十几年,风速传感器这东西看着不起眼,但一到关键时刻——比如起重设备高空作业、风电场的偏航控制、港口码头的气象预警——它就是整条安全链的第一道闸门。这些年经手过塑料壳的、铝合金的、不锈钢的,也踩过不少坑。今天想认真聊…

作者头像 李华