- 人工智能
- AI Agent
- 自主智能体
- 桌面应用
- MCP Clients
【免费下载链接】Kun
Local-first AI agent workspace for coding, writing, design, research, and automation — one runtime for desktop GUI and TUI.
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 变成权威存储。
核心架构:把“生命周期元数据”与“内容捕获”解耦
设计文档明确了两条分离的持久化路径:
- 紧凑生命周期保留(Always-on):模型观察器总是为每个逻辑 round 创建记录,并为每次实际传输尝试创建一条记录。记录获得稳定的
roundId、requestId、turnId、step、attempt值,并在开始、首个内容、终态三个时刻写入检查点。 - 可选内容保留:线程现有的捕获开关只控制提示清单/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.
相关推荐
douyin-downloader:一条链接跑通抖音批量下载的 Python 工具
douyin downloader:一条链接跑通抖音批量下载的 Python 工具 假设你要存档某位创作者的全部作品:手动打开主页,一条条等视频加载、点保存,拿
人工智能AI Agent自主智能体桌面应用MCP ClientsWin11Debloat 免费精简 Windows 11:一次跑通的系统去臃肿完整教程
Win11Debloat 免费精简 Windows 11:一次跑通的系统去臃肿完整教程 开机少等十几秒、右键菜单秒开、后台不再往外卖数据——这些都能靠免费开源的
人工智能AI Agent自主智能体桌面应用MCP ClientsUnicity Astrid OS 发行版分发机制:Distro 清单、内容寻址存储与 Capsule 全生命周期管理
Unicity Astrid OS 发行版分发机制:Distro 清单、内容寻址存储与 Capsule 全生命周期管理 本篇以 Astrid 官方参考书《Dis
文档教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考