kshell:为散落一地的AI编程会话建一个本地中央车站 装了一堆 AI 编程工具之后真正让人抓狂的已经不是“哪个更好用”而是会话散落一地——今天这个问题是在工具 A 里问的那个报错是在工具 B 里解决的一周以后想翻记录手忙脚乱也找不到。我自己被这个状态折磨了快两个月最后写了一个叫 kshell 的小工具专门把散落的 AI 编程会话统一收口。这篇文章会讲它的设计思路、从零安装初始化的过程、日常使用最多的功能以及实现里踩过的坑。适合那些 AI 编程工具越装越多、已经开始觉得“查旧对话比写新代码还累”的人。我不会劝你卸载某个工具工具本身的生成质量很重要但记忆系统不应该继续散落下去。与其等各家厂商做统一出口不如自己给会话建一个中央车站。1. 会话散落一地的真实困境装工具一时爽找记录火葬场1.1 我的终端桌面几个工具、几套历史、谁也不认谁我有段时间的工作状态很典型右边屏幕开着某个聊天型 AI 工具的网页版左边终端里跑着另一个主打代码解释的 CLI 助手第三个窗口则时不时弹出一个补全插件的建议框。看起来效率很高实际上每个工具的会话历史都存在各自的目录里命名规则不同时间戳格式不同有的甚至只存内存不主动导出就彻底丢。最崩溃的一次我在某个终端工具里调试一个问题中午去吃饭前按了一下清空会话回来才发现按钮位置和回车键挨得很近手误点掉了。那一整段对话里包含了完整的报错栈和试过的修复路径全没了。那一刻我开始意识到工具装得多不是问题会话没有统一管理才是问题。类似的情况我猜不少人都遇过四个工具各有各的记忆相当于你雇了四个助理但每个助理都只记得自己经手的部分你问助理一“上次那个问题后来怎么解决的”助理一说“你问过吗我没记录”。1.2 找不到、接不上、算不清碎片化的三个代价会话碎片化带来的麻烦可以概括成三件事。第一是找不到。跨工具搜索很难做因为很多 AI 编程工具的历史记录不是普通文本文件有的用 JSONL有的塞进本地数据库还有的干脆加密存储。你想用 grep 全局搜一下根本无从下手。只能挨个工具打开翻到对应日期然后再手动定位。第二是接不上。同样一个问题我在工具 A 里问了一半换到工具 B 里重新描述一遍到工具 C 又得再解释一次。每个会话都像是第一次见面因上下文无法跨工具传递。最典型的是排查一个编译报错工具 A 给了一个方向工具 B 直接给了可运行补丁工具 C 指出根因。三个信息拼在一起才能解决但三者之间没有任何联系。第三是算不清。你每个月到底在哪些 AI 编程工具上花了多少时间、多少额度、命中率如何完全是一笔糊涂账。统计数据分散在各家平台后台没有一个统一视图。想复盘“今天为什么低效”都找不到数据支撑。1.3 等工具商统一出口不现实我决定自己做聚合层我一度期待某个大厂做一套协议让所有 AI 编程工具都能导出标准化会话。后来发现这不现实——各家把会话数据当作自己的护城河互相开放的意愿极低。与其干等不如自己做一个采集层。我的目标很朴素不接入任何模型的推理能力不做新的 AI 助手只做一件小事——把散落的会话记录统一采回来、转成同一种结构、存进本地数据库再通过一个统一入口去搜索、打标签、导出。这个工具就是 kshell。k 是 keep 的 kshell 指命令行外壳意思是用命令行帮我把所有 AI 会话都“留住”。2. kshell 的思路给散会话建一个本地中央车站2.1 为什么选“聚合层”而不是“再做一个新助手”一开始我也考虑过直接自己做一个整合版 AI 编程工具把会话、代码补全、代码解释全塞进去。后来想明白了这不是功能问题是生态问题。真正有价值的还是各工具自己积累的模型能力和产品体验我强行再造一个既没有算力优势也没有交互积累只会增加新的碎片。kshell 选择做聚合层相当于在火车站建了一个中央转乘大厅。各个方向的列车还是各自运行但乘客可以在这里统一查询车次、换乘、中转。对使用者来说我不用改变任何工具的日常使用习惯只需要让 kshell 在背后定期把新会话收进来。这个定位让我少写了几千行代码也让工具本身能长期稳定存在。聚合层还有一个容易被忽略的好处数据可迁移。今天我用工具 A明天觉得工具 B 更好kshell 里的会话不会因此失效。因为数据在本地格式是统一的换工具只是换了一个采集源而已。2.2 采集、规整、查询三个模块的分工kshell 的代码结构很清晰总共分三层。最底层是采集器collector。它负责定时扫描各个 AI 编程工具的日志目录、配置文件目录或数据库文件把原始记录增量拉出来。因为工具可能会升级路径所以每个工具对应一个单独的采集脚本互不影响。中间层是规整器normalizer。原始记录格式五花八门这一步会把它们统一成一种内部 schema每条会话至少包含时间、来源工具、项目目录、会话标题、消息列表、token 统计如果有。规整器还会做文本清洗去掉 ANSI 转义符、无关 HTML 标签和重复的时间戳。最上层是查询层CLI。用户真正接触到的只有kshell find、kshell tag、kshell export这些命令。查询层直接把 SQLite 里的数据拉出来格式化展示不碰底层细节。整个流程用一条流水线来理解收快递采集→ 拆箱登记规整→ 上货架存储→ 按需取件查询。每一层都只做一件事出问题时也容易定位比如我发现某条会话乱码会先查 normalizer 是不是漏了某种转义再看 collector 取到的是不是完整文件。2.3 技术选型Python 3.10、SQLite 和纯命令行kshell 没有用任何复杂框架核心是 Python 3.10 标准库加 SQLite 数据库查询入口是 argparse 命令。做这个选择有几个考虑。第一Python 做文本处理足够顺手。采集到的大多是 JSON、纯文本、Markdown用标准库的json、re、pathlib就能覆盖九成场景不需要引入重型依赖。第二SQLite 单文件数据库对本地工具极其友好备份就是复制一个文件不需要额外部署服务端。第三命令行天然契合 AI 编程工具的使用场景——写代码的人本来就泡在终端里几个短命令就能完成检索比开一个 GUI 更快。我也考虑过全部存成 JSON 文件但会话量上千之后JSON 的全文检索和过滤就会越来越痛苦。SQLite 自带的 FTS5 全文索引虽然不能和搜索巨头比但处理几千条到几万条会话绰绰有余。数据库里我还会存一份原始 JSON 字段规整器即使有 bug原始数据也不会丢。3. 装好并用起来从初始化到第一批会话入库3.1 安装和初始化命令kshell 的安装非常简单本地有 Python 3.10 或更高版本就行。一条命令装好再执行 init 初始化目录pip install kshell kshell init --dir ~/.kshellinit 会创建三个东西~/.kshell/kshell.db数据库文件、~/.kshell/config.toml配置文件、~/.kshell/exports/导出目录。数据库里自动建好sessions、messages、tags、source_meta四张表不需要手动迁移。我这人习惯先跑一次再读文档所以提醒一句init 前最好确认你当前用户对目标 AI 工具日志目录有读取权限尤其某些工具把日志放在系统临时目录下。权限不足时后面的 scan 命令不会直接报错而是安静地跳过那些目录很容易让人误以为“没有数据”。第一次初始化之后建议立刻跑一个带--verbose的扫描确认每个源是否真的读到了内容。3.2 第一次配置要回答的几个问题打开config.toml,核心就是声明数据源。我的配置大致长这样[storage] db_path ~/.kshell/kshell.db [sources.toolA] kind jsonl path ~/.toolA/logs poll_interval 60 [sources.toolB] kind sqlite path ~/.toolB/history.db poll_interval 300 [sources.toolC] kind html path ~/.toolC/conversations poll_interval 0配置时要回答四个问题这个工具的历史记录是什么格式JSONL、SQLite、纯文本、HTML它在本地落在哪个路径你希望多久自动扫一次以及这个源要不要默认启用。最后一个问题容易被忽略比如某个工具的聊天记录里包含大量和工作无关的闲聊我可能会把它单独放一个源默认不采集只在需要时手动扫。poll_interval 0表示关闭自动轮询只接受手动导入。我建议对使用频率低的工具都这么配减少无谓的磁盘扫描。3.3 自动采集与手动导入两条路径日常使用中kshell 提供两条路把会话收进来。自动采集走kshell scan。这个命令会按配置里的轮询间隔去扫所有poll_interval 0的源增量拉取新记录。kshell 自己维护一个“最后采集位置”游标避免每次全量扫描。如果你想让 kshell 常驻后台可以用系统自带的定时任务或进程管理器挂kshell daemon它会按配置时间自动反复扫描。手动导入走kshell import。这一条专门对付那些不支持自动读取的工具。比如某工具只能在网页端查看历史我就在网页上手动导出一份 JSON 或 Markdown 文件然后执行kshell import ~/Downloads/toolC-session-20250620.json --tool toolC注意手动导入时一定要通过--tool参数声明来源否则 kshell 没法把这条会话归类到对应工具后续按工具维度过滤就会漏掉它。这个参数最初设计成可选项我在实际使用中发现漏标的情况很多后来改成了强制项。3.4 用 stats 验证采集结果配置完源之后第一件事是看数据有没有真的进来。我每次配置新工具都会跑kshell stats输出大概是这样总会话数: 1,284 消息总数: 9,762 来源分布: toolA : 612 会话 (47.7%) toolB : 470 会话 (36.6%) toolC : 202 会话 (15.7%) 今日新增: 3 会话, 41 条消息 最近采集时间: 2025-06-20 12:04:18看到“今日新增”和“最近采集时间”都正确说明采集链路已经走通。如果显示的会话数明显低于预期可以先检查某个源是不是没扫到再确认是不是磁盘上历史文件已经很大但增量游标被误置到了末尾。我最初犯过的错误就是把游标初始化在了文件末尾导致前面的历史永远扫不到这个后面在踩坑部分会细说。4. 高频功能搜索、标签归档、导出备份4.1 全文搜索一条命令找回丢失的对话kshell 平时用得最多的命令是find。它走 SQLite FTS5 全文索引可以同时对会话标题、消息内容、代码片段做检索。基本用法kshell find TypeError --tool toolA --since 2025-06-01我习惯加--project限定当前项目加--tool限定来源。有一次为了找一段早已遗忘的“URL 编码导致签名校验失败”的对话我用一句kshell find signature mismatch urlencode直接定位到了三周前的会话那种感觉比手动翻历史舒服太多。搜索结果默认按时间倒序展示每条会话会显示时间、工具、项目、匹配到的前两行上下文。加了--context 5可以把匹配消息的前后 5 条也展开方便快速判断是不是正确结果。这个功能对排查“我记得解决过但又忘了怎么解决”的问题尤其有用。4.2 标签和项目归档把散会话归到具体上下文纯靠搜索还不够有些会话需要在更宏观的维度上组织。kshell 的标签体系很简单每条会话可以有多个标签标签分自动和手动两种。自动标签来自项目路径匹配。如果采集时能拿到工作目录kshell 会自动提取最后一级路径作为项目名拿不到的就标记为unknown-project。手动标签主要用来补充语义比如kshell tag 3a2b1c bugfix/urlencode kshell tag 3a2b1c 需要复查标签写好之后就可以按标签做批处理kshell list --tag need-review kshell archive --project moon-pay --tag donearchive命令会把符合条件的会话状态从“active”改成“archived”归档后的会话默认不会出现在list结果里但搜索时加--include-archived还是能找到。我每周五会花十分钟把本周解决问题的会话统一打上done并归档保持主列表干净。4.3 导出 Markdown/JSON备份和迁移的兜底方案本地数据库本身已经是备份了但为了保险和迁移kshell 必须支持导出。kshell export --project moon-pay --format markdown backup.md kshell export --project moon-pay --format json --since 2025-06-01Markdown 导出的结构很适合直接贴进团队文档每条会话一个二级标题对话内容按角色分块代码块保留语言标注。JSON 导出则保留更完整的信息包括时间戳、来源工具、标签、原始元数据方便以后迁移到别的工具或做统计分析。我自己每周会做一次全量 JSON 导出放到移动硬盘加密卷里用作兜底。有一次我本地 SSD 坏过一次靠这个导出折叠进新数据库除了最后几天的增量数据外几乎无损失。导出这件事看着简单真到要用的时候才知道它值多少钱。4.4 和 git 提交记录联动的小技巧会话检索最难的往往是“怎么建立关键词关联”。我会用 git 提交记录来串。比如一个修复提交的 commit message 是“fix: handle percent-encoding in query params”,我可以回查这个提交改了什么再从 commit message 里挑一个关键词去 kshell 搜索很容易找到当初生成这段代码的会话。反过来也可以给会话打 commit 标签kshell tag 9f0e2d1 commit/a3b5c7d这样当我在git log里看到某个提交时可以用kshell find a3b5c7d直接跳到当时生成该代码的完整讨论。这套联动并不复杂但把代码历史和会话历史串起来之后回溯上下文的时间从小时级变成了秒级。5. 开发过程中踩过的坑5.1 各家日志格式不统一normalizer 里全是补丁kshell 开发过程中最磨人的不是查询而是 normalizer。各家 AI 编程工具的历史格式简直是一个格式博览会有 JSONL 一行一条消息的有把整个会话封装进单个 HTML 文件的有纯文本用多个分隔符拼接的还有的日志里带完整 ANSI 转义序列。我维护了一个格式对比表目前遇到的代表性情况如下工具类型原始格式需要清洗的内容聊天型 CLIJSONL转义换行、重复时间戳补全型助手SQLiteBLOB 字段中的压缩文本网页版工具HTML无关导航、样式标签、脚本内容旧版本工具纯文本用分隔符区分的多轮对话最坑的是某工具死活找不到日志目录后来发现它把会话压缩成 zlib 存进了一个隐藏 SQLite 表里取出来后还要解压再解析。这种“隐藏格式”只能靠逐个工具实测发现没有捷径。我的经验是给每个采集器写独立测试用例用一小段真实历史记录当 fixture。这样工具升级导致格式变化时第一时间就能发现。别等数据已经入库很久了才发现解析错了回溯清洗比新增采集还费劲。5.2 会话续接是一个伪需求快照才是真问题项目做到一半时有开发者朋友提建议既然 kshell 能读取所有历史是不是还能跨工具“续接”会话比如让工具 B 基于工具 A 的语境继续回答这个功能听起来很美实际上做起来极其痛苦——各工具上下文协议不开放强行拼接也会让模型理解错乱。做了两个版本之后我把这个功能砍了。真正有价值的是“快照”而非“续接”。kshell 里给每条会话做定期快照保留某个时间点完整的消息序列、代码块和相关标签。搜索时看到快照就等于你在那个时间点把上下文完整地存了下来。拿日常例子说我不需要让工具 A 无缝接上工具 B 的对话我只需要在三天后能清楚看到工具 A 当时提出了什么方案工具 B 当时修正了什么问题最终采纳的是哪一个版本。快照提供的“客观历史”比那些花哨的“续接”实用得多。5.3 高频扫描与 SQLite 写放大轮询性能调优最初我把所有源的poll_interval都设成了 5 秒想着新会话能尽快入库存。结果 kshell 常驻一天后SQLite 文件从 1 MB 涨到了 35 MB查询明显变慢。后来才醒悟每条会话都算作一个事务写入频繁扫描会让 SQLite 不断做 WAL 合并基本是在自虐。调整策略很简单按工具实际使用频率分配轮询间隔。重度使用的聊天型工具用 30 秒轻度使用或只在某些项目出现的工具直接设成 0手动导入。同时在 SQLite 连接上开启了 WAL 模式让读写并发不那么互相阻塞PRAGMA journal_modeWAL; PRAGMA synchronousNORMAL;这几行配置之后数据库文件体积增长慢了很多查询响应时间一直稳定在毫秒级。做本地工具性能问题往往不是数据量大而是写得太频繁。把轮询节奏从“尽可能快”改成“够用就好”体验反而更好。5.4 隐私边界哪些会话内容不该入库kshell 有一个黑名单机制直接在采集层做过滤。配置里加一段[privacy] block_keywords [api_key, secret, password, token] block_paths [~/.secret-project]只要消息内容命中黑名单关键词或者会话发生在被屏蔽的目录采集器会直接跳过数据库里不会留下任何痕迹。这是我坚持要做的边界会话数据本地存储就已经隐私风险很高如果不做过滤等于把密钥、内部路径、敏感讨论全部集中到了一个数据库里万一被同步到网盘或泄露后果更严重。我不建议只是“入库后打码”因为打码前的原始内容已经在硬盘上存在过。从源头丢弃才是最干净的。对任何本地聚合工具来说采集能力越强越要主动控制数据边界。这不仅是技术选择也是使用习惯。6. 实际使用后的心得与下一步6.1 适合谁用不适合谁用用了几个月 kshell我可以比较明确地说它的适用边界。如果你平时只用一个 AI 编程工具而且那个工具自带历史搜索那 kshell 的价值不大没必要为管理而管理。相反如果你同时使用三种以上工具并且经常需要回查“之前某个工具给的解决方案”那聚合工具的意义就非常直接了。另外如果你特别在意隐私不放心任何本地聚合工具触碰各家的会话目录那 kshell 可能也不适合你。虽然它始终在本地运行但采集、解析、存储的链路越长攻击面肯定比“只用一个工具”要大。自己权衡就好。6.2 我的日常工作流变化现在我的工作流和之前相比变化很明显。白天我照样开三四个 AI 编程工具但不会再刻意去记“这个问题是在哪边问的”。晚上准备收工时跑一次kshell scan把当天增量收进来然后用kshell stats看一眼今天的会话分布和新增量。周五统一做标签归档和 JSON 导出。最大的变化是之前那种“找不到旧对话”的焦虑基本消失了。有一次我需要在某内部运营系统里复现一个半年前做过的数据处理逻辑我只记得当时是用某个聊天工具生成了一段 Python 脚本具体内容忘得一干二净。我打开 kshell用“运营系统 数据处理 Python”三个词搜第一次就找到了完整对话。那感觉就像给过去的自己打电话而电话接通了。6.3 后续我想补上的能力kshell 目前还是一个偏“个人库”的工具后续我有几个方向想补。一是把采集器做成插件机制以后接入新工具只需要写一个几十行的采集脚本不用改主程序。二是增加只读团队共享能力导出一份匿名化的会话库方便小组内部检索“这个问题之前处理过没有”。三是做一个简单的热力图统计按时间维度和工具维度展示自己的使用规律。这些功能我打算一个版本一个版本地加。工具本身不强求大而全先保证最核心的“统一存储、快速找到、随时导出”稳定可靠。如果你也在经历 AI 编程工具越装越多、会话散落一地的混乱我的建议很朴素不要指望一个工具能解决所有问题也先别急着把工具换成全家桶。先把自己最常用的两三个会话流统一起来给记录建一个能找得到的地方比任何花哨的功能都重要。我会继续把 kshell 当成自己的会话中央车站因为它解决的正是我那个最痛的问题。