聊了啥(liaolesha)
把一次 WorkBuddy 会话(可能 4MB~60MB、几十到几百轮)整理成「文案素材库」,用于后续写公众号图文、口播稿、视频。三层产出:干净正文(剥离工具噪音与上下文压缩摘要)、六分区素材候选池、主题分段切点。
定位:不是"生成摘要",是"拆零件"。摘要的目标是变短;素材库的目标是把 4 万字拆成能塞进文章的原料。所以产物是候选池,不是成品,判断权留给调用它的 agent。
怎么用(人看的,30 秒)
你不需要敲命令、不需要记路径。对 agent 说一句话即可:
| 你想干什么 | 你怎么说 | 会发生什么 |
|---|---|---|
| 整理当前这个会话 | /聊了啥 |
自动锁定当前会话 → 出素材 → 出卡片页 |
| 整理某个会话 | /聊了啥 仿旗博士(标题关键词) |
只整理那一个。标题、8 位 ID、工作区名都认 |
| 不知道有哪些会话 | /聊了啥 list |
列出来;每个标题下面那行就是它的 8 位 ID |
| 整理整个项目 | /聊了啥 qiboshi(工作区名) |
多会话 → 给一份《成篇清单》 |
| 看有哪些产物 | /聊了啥 outputs |
列出所有产物目录 |
| 删掉产物 | /聊了啥 clean 仿旗博士 |
默认试运行;加 --yes 才真删。同样认标题 |
ID 不用你自己去找:说 list 就有;卡片页上每个会话的常用指令里也已经带好了 ID,点「复制」即可,从来不用手抄。想指定别的会话,直接说标题最省事(如 /聊了啥 仿旗博士);只有标题太宽、命中多个时,脚本才会列出候选让你用 ID 挑。
你会拿到:out/index.html(总览,点卡片同页切换)+ 操作指南.html(一篇文章一张卡,每张卡带「复制指令」按钮)。
下一步:挑一篇 → 复制卡片指令 → 粘进聊天框,交给写作技能开工。
你不用管:下面「脚本命令参考」里的 python 命令是 agent 内部执行的,你只需要说人话。
环境前提(先确认,否则装了也用不了)
| 需要 | 说明 |
|---|---|
| WorkBuddy 的历史会话 | 数据源是 ~/.workbuddy/projects/*.jsonl + ~/.workbuddy/workbuddy.db。换 agent、换机器、清过数据,list 只会返回空表——这不是 bug |
| Python ≥ 3.10 | 脚本只用标准库,无需 pip install |
| sh / bash | 示例里的 [ -f ... ] 是 shell 语法;Windows 原生 cmd 下换成等价的 PowerShell 判断 |
这个技能不是通用聊天记录分析器,它解析的是 WorkBuddy 的私有会话格式。要迁到别的平台,改的是 load_session_index() 与 load_messages() 两个入口,L2 以下不用动。
何时用 / 何时不用
| 用 | 不用 |
|---|---|
| 要把某次对话写成文章 / 口播稿 / 视频脚本 | 只想回顾"上次聊了什么"(直接读 summaries.md 即可) |
| 会话很长(>20 轮或 >500KB),人工读不动 | 只有几轮、且本人还记得内容 |
| 需要第一人称原话、硬数字、金句、认知转折 | 需要的是操作结论,不是叙事素材 |
三层结构
| 层 | 命令 | 产物 | 谁做 |
|---|---|---|---|
| L0 索引 | list |
会话清单 | 读 App 的 sqlite sessions 表为主,jsonl 只补大小/轮次 |
| L1 提取 | extract / run |
clean.md / index.md / summaries.md / stats.json | 脚本,确定性工程 |
| L2 分桶 | 自动附带,或 material |
materials.md(六分区候选池) | 脚本,正则,重召回不重精度 |
| L3 分段 | agent 自己做(turnmap 给行号) |
主题分段.md(含成篇清单表) | 必须 agent 读正文后判断,不能脚本化;读法=边界精读 + 分段并行 |
| L4 交付 | guide |
操作指南.html(一篇文章一张卡 + 可复制指令) | 脚本,读 L3 的表 |
脚本命令参考(agent 执行用,人不用敲)
PY=python3 # 需要 3.10+
# 本技能脚本:装在哪个目录名下都能定位(先试默认路径,找不到再按文件名搜)
S=~/.workbuddy/skills/shuolesha/scripts/shuolesha.py
[ -f "$S" ] || S=$(ls -1 ~/.workbuddy/skills/*/scripts/shuolesha.py 2>/dev/null | head -1)
# 1) 找会话:默认按最后活动排序,与 App 侧边栏一致
$PY $S list --limit 20
$PY $S list --turns # 附带轮次(要全量解析,较慢)
$PY $S list --workspace qiboshi # 按工作区路径/分组模糊过滤
$PY $S list --all # 含 App 里已删除的、以及只有文件没登记的
# 2) 整理(入口):run / session 同义,会自动判定「整理一个会话」还是「整理一个项目」
$PY $S run # 零参数 → 「当前会话」(mtime 最新的 jsonl);在 session x 里就整理 x
# $PY $S # 连 run 都省:空触发 = 同上(最简写法)
$PY $S run 35a876ce # 会话ID前缀 → 只整理这个会话
$PY $S run 仿旗博士 # 标题关键词 → 同上(子串匹配,唯一命中才算数)
$PY $S run qiboshi # 工作区名,且多会话 + 体检像项目 → 自动按项目整理
$PY $S run qiboshi --as-session # 就要单个会话(不想走项目模式时)
$PY $S project qiboshi # 显式项目模式(体检不通过也强制跑)
$PY $S run 35a876ce --outdir ./out/口播视频 # 想固定目录名时才传
# 3) L3 跳读:打印每轮起止行号,直接产出 Read 的 offset/limit(别整篇读 5 万字)
$PY $S turnmap ./out/x # 全表:轮次 / 行号区间 / 字数
$PY $S turnmap ./out/x --range 16-26 # 单段 → Read(offset=762, limit=518)
$PY $S turnmap ./out/x --ranges 1-8 9-20 21-34 # 多段 → 并行子 agent 一人一段
# 4) 只对已有的 clean.md 重跑素材层(改了正则或想换输出目录时)
$PY $S material ./out/x/clean.md # 省略 → 写回 clean.md 同目录
# 5) 写完/改完 主题分段.md 后,刷新操作指南(一篇文章一张卡)
$PY $S guide ./out/x # 传目录,或目录里任一文件
$PY $S guide ./out/x --strict # 自检有 error 就拒绝出页(退出码 2)
# 6) 管理已有产物(用户不用自己记路径)
$PY $S outputs # 列出所有产物目录:会话/轮次/字数/路径
$PY $S outputs --html # 生成单页自包含总览 <当前目录>/out/index.html
$PY $S clean 35a876ce # 删某个(默认试运行,只打印不删)
$PY $S clean 35a876ce --keep-hand --yes # 推荐:只删脚本产物,保住 主题分段.md
$PY $S clean 35a876ce --yes # 整目录移入废纸篓(可还原)
# 7) 项目级:一个 workspace 下的多个会话 → 一份「成篇清单」
$PY $S project qiboshi --scan # 只体检:判定这堆会话算不算一个项目
$PY $S project qiboshi # 提取全部会话 → out/<项目名>/sessions/NN-…/ + 会话地图.md
$PY $S project qiboshi --only 2108caee,8e78c7 # 只圈指定会话(排除杂事)
$PY $S project ~/git/work/qiboshi --force-project # 体检不通过但确认是项目
$PY $S guide ./out/qiboshi # 写完 成篇清单.md 后出卡片
项目级(project):workspace 里有多个会话、且最早那 1~2 个装着「需求书」时,说明这是个项目。项目级不合并正文——每个会话仍是独立产物目录(sessions/NN-日期-标题/),只在项目根放一份 会话地图.md(脚本)+ 一份 成篇清单.md(agent 写,唯一交付物)。取材用复合坐标 S3·R16–26(第 3 个会话的第 16–26 轮),guide 会自动展开成「S3(日期 标题)· 轮 16–26」。完整规则见 references/project-guide.md。
总览页(单文件自包含):outputs --html 写一页 out/index.html,把所有会话的指南内容全部内联进这一个 html:顶部是卡片瀑布(一个会话一张卡:序号 + 篇数徽章 + 标题 + 轮次/字数/日期 + 已写进度条),点卡片在同一页内切换,不再有跨文件链接、也不需要启动任何服务——双击(file://)或在预览面板里都能直接点。各会话仍各自生成独立的 操作指南.html,供单独分发。run / guide / project / clean 结束时会自动刷新同级的这张页,不用手动跑。
参数速查
| 参数 | 必填 | 说明 |
|---|---|---|
source |
否 | jsonl 路径 / 会话 ID 前缀 / 工作区名 / 标题关键词,四者都认(标题按子串、忽略大小写;唯一命中才算数);省略则自动选用「当前会话」(mtime 最新的 jsonl);ID 或工作区命中多个取最大并 warn,标题命中多个则列候选并让用户用 ID 指定 |
--outdir |
否 | 省略则写 <当前目录>/out/<会话日期>-<标题slug>/。不允许落在源 jsonl 目录内(直接拒绝执行,防止污染活文件) |
--workspace |
否 | list 用,按 cwd / 分组名模糊过滤 |
--limit / --sort / --turns / --json |
否 | list 用;--sort 默认 time(同侧边栏),另有 size / name;--turns 要全量解析,较慢 |
--all |
否 | list 用,包含 App 里已删除的会话 + 只有文件没登记的(子代理等) |
--root |
否 | outputs 用,额外扫描的目录(可重复);省略则自动扫当前目录 + 各会话工作目录 |
--html / --index |
否 | outputs 用,生成单页自包含总览(默认 <当前目录>/out/index.html) |
--yes |
否 | clean 用,不加只试运行 |
--keep-hand |
否 | clean 用,保留手写文件(主题分段.md、成篇清单.md、成稿),只删脚本产物 |
--permanent |
否 | clean 用,彻底删除不进废纸篓(不可恢复) |
target(project) |
是 | 工作区名 / cwd 路径 / 会话目录名;一个 workspace 下 ≥2 个会话才走项目模式 |
--scan |
否 | project 用,只体检(0~6 分判定是否项目),不提取 |
--only / --exclude |
否 | project 用,按会话 ID 前缀圈定/排除(--only a1b2,c3d4) |
--force |
否 | project 用,已有产物也重新提取 |
--force-project |
否 | project 用,体检判"不像项目"仍强制按项目跑 |
入口:会话还是项目,看用户怎么写,不问
不要弹窗问用户,也不要先跑一条探测命令。run / session 自己会判定,agent 只管执行它、并把它的判定结果讲清楚。
| 用户写法 | 脚本判定 | 行为 |
|---|---|---|
不带参数(/聊了啥) |
当前会话 | 整理 mtime 最新、正在写的那个会话 |
会话 ID 前缀(35a876ce) |
一个会话 | 只整理它 |
标题关键词(仿旗博士) |
一个会话(唯一命中时) | 只整理它;命中多个 → 列候选 + ID,不替用户猜 |
工作区名 / 目录(qiboshi) |
看会话数与体检:≥2 且像项目 → 项目;否则 → 其中一个会话 | 项目 → sessions/ + 会话地图 + 成篇清单;会话 → 单篇管道 |
模糊词跨多个工作区(2026-10) |
歧义 | 报错列出候选,要求更精确的名字 |
--as-session |
强制会话 | 工作区里只挑最新那个会话 |
判定顺序是固定的:ID 前缀 → 工作区名 → 标题关键词。先认 ID 是因为它唯一且用户是照抄的;工作区名放在标题前面,是为了保住「qiboshi 就是要整理整个项目」这条既有约定(工作区名很少会原样出现在标题里)。
判定依据(脚本内已实现,你不用重复):ID 前缀或标题唯一命中=用户明确在说一个会话,永不升级成项目;只有"工作区名/目录"才可能升格为项目,且必须过体检(最早 1~2 个会话的前 3 条用户消息有需求书,≥3 分)。
回复里要让用户知道走了哪条路,例如:「按项目整理 qiboshi(11 个会话)」或「qiboshi 体检不像项目,按会话整理其中最新的 5e0cee78」。要反悔就加 --as-session 或用 project 强制。
交付规则(产出 html 后必须展示)
脚本只写文件、不会呈现。产物藏在 <工作区>/out/... 里,App 的「产物」面板多数不显示它——用户翻遍面板也找不到 操作指南.html。所以每次产出 html 都用 present_files 打开,并在回复正文写出绝对路径。
| 刚跑完 | 展示什么(顺序=默认打开项) |
|---|---|
guide / outputs --html |
out/index.html 第一,操作指南.html 第二 |
run / session |
有 操作指南.html → 同上;还没 → 不展示空壳,说明"下一步写 主题分段.md 再跑 guide" |
project |
有 成篇清单.md → 项目目录 index.html 第一;还没 → 说明"下一步聚簇并写 成篇清单.md" |
顺序是硬要求:present_files 只自动打开排第一的那个。把 操作指南.html 排前面,用户看到的是没有顶部 tab 的单会话页,会以为"内联单页丢了/技能退化了"——踩过这个坑。index.html 是跨会话总入口,永远第一;只有 1 个会话时它仍带 1 个 tab,只是没得切,不等于消失。
执行要点:正文必须带绝对路径(不靠面板也能找到);末尾给一句下一步("挑一篇 → 复制卡片指令 → 粘进聊天框");别只说"已生成"就收尾。
产物(L1/L2 脚本生成)
| 文件 | 内容 | 用途 |
|---|---|---|
clean.md |
按轮次组织的对话正文(压缩发生处有标记,保持时间线连续) | 唯一值得读的正文,但按行号跳读(turnmap),别整篇读 |
index.md |
轮次目录表:提问 + 回复主线 + 字数 | 窄会话挑选用;几百轮时不可用 |
summaries.md |
上下文压缩摘要(拆成一问一答) | 快速回顾 / 叙事骨架参考,不可与正文混读 |
stats.json |
机器可读统计 | 文章里引用真实数据(轮次、字数、压缩次数) |
materials.md |
六分区素材候选池 | 下笔时取零件 |
segment-hints.md |
分段切点候选(自我否定句 / 长间隔 / 篇幅重心 / 逐轮规模表) | L3 的输入 |
操作指南.html 由 guide 生成:一篇文章一张卡,含标题、段/轮次、类别、目标字数、骨架 + 一个复制指令按钮(卡面不印提示词原文,悬停可预览全文)。指令是给写作技能的开工单,逐项写死:标题 / 开篇姿态 / 结构骨架 / 认知冲突 / 素材与正文绝对路径 / 取材轮次 + 目标字数 / 事实纪律。若 主题分段.md 有 ## 选题池,卡片下方默认展开备选标题,每条各带复制按钮;末尾还有常用指令面板(outputs / run / guide / clean / list)。
成篇清单(L3 必须写这一节,否则卡片退化成"一段一卡")
主题分段.md 里加一节 ## 成篇清单,用表格给出每篇文章一行,guide 直接读它出卡片:
## 成篇清单
| 序 | 标题 | 段 | 轮次 | 类别 | 目标字数 | 骨架 | 素材充分度 |
|---|---|---|---|---|---|---|---|
| 1 | 工作台到底是什么 | B | 16–26 | 概念 | 5,000~6,000 | 用四次误解当结构,不用定义当结构 | 高,最厚 |
| 2 | 加登录前先搞懂 sites | D | 25–36 | 踩坑 | 4,500~5,500 | 从「想加登录」落到隔离差异 | 中,需补一段架构对比 |
- 建议带
素材充分度列:每篇标"高/中/低 + 一句理由"(如"高,冲突最强""中,需要重构")。它是挑篇优先级的直接依据——哪篇素材够厚能直接开写、哪篇还差料,翻 md 时一眼可见。 - 列名只需含关键词即可被识别:标题 / 段 / 轮次(或取材)/ 类别(或类型)/ 字数(或篇幅)/ 骨架(或一句话、主旨)
- 额外列(如
素材充分度)只留在 md 里供你自己挑篇参考,不进 html——html 卡片只渲染固定字段,没有表格视图 - 缺这一节时的回退:读
## 全景表,有可撑篇数的段各生成一张卡,标题取"主题"列 → HTML 顶部会显示黄色提示 - 连
主题分段.md都没有时:只生成一页"待 L3 分段"的指引,不假装有卡片
选题池(每篇固定 5 条标题,锚点驱动,供用户挑角度)
> 完整规则见 references/title-guide.md(agent 写本节时必读)。核心四点:每条标题拴一个原文锚点、先列钩子素材再写标题、每篇固定 5 行(1 已选 + 4 备选)、5 条必须各锚一个不同瞬间。
成篇清单 是已定案的一篇一行;选题池 是同一篇的 N 条标题候选。两者不是一回事:前者是决策,后者是候选。混在一张表里会让 guide 生成一堆没料可写的卡。
主题分段.md 里再加一节 ## 选题池,guide 会把它按「篇」挂进对应卡片(默认展开),每条标题带自己的复制按钮——复制出的指令里标题、基调、骨架、取材一起替换。锚点 列只留在 md 供你挑篇参考,不进 html(与 素材充分度 列同策略)。
## 选题池
| 篇 | 段 | 标题 | 基调 | 锚点(引句+轮次) | 骨架 | 取材 |
|---|---|---|---|---|---|---|
| 1 | B | 工作台到底是什么 | 正问 已选 | "工作台到底是个啥"·16 | 从误解问到定义站住 | 16–22 |
| 1 | B | 我一直以为工作台是聊天窗口 | 认错式 | "它就是聊天窗口吧"·17 | 先自曝误判,再逐条对上现实 | 15·17 |
| 1 | B | 和 AI 聊了 20 轮才搞懂工作台 | 数字式 | "聊了 20 轮才搞明白"·18 | 用轮次当钩子,每段一个误解 | 14·18 |
| 1 | B | 工作台、资料库、云端网页:三个名字一个东西 | 定义式 | "那它到底是啥"·22 | 误解排完给出干净定义 | 19–22 |
| 1 | B | 把工作台当聊天窗口,我绕了两周 | 自嘲式 | "早知道不用绕这圈"·20 | 先讲绕路代价,再给正确认知 | 16·20 |
规则:
- 每篇固定 5 行:1 行「已选」(与
成篇清单标题一致)+ 4 行备选。少于 5 行=没写完,必须补齐 - 补齐靠换锚点,不靠换说法:4 条备选必须各锚一个不同的原文瞬间(不同引句 / 不同轮次)。写不出第 4、5 条时,扩取材范围(翻该段其他轮次、或跨会话找同一模式的另一次发生),绝不把同一条改几个字顶上;两条锚同一句原文即算重复,重写
- 每条标题必须带锚点:格式
引句·轮次(引句 ≤30 字,取自对话原文;轮次号对应clean.md)。给不出锚点的标题不生成——它等于空话 - 角度不同 = 锚点不同:换的是「切入素材」(自我否定 / 数字 / 代价 / 反直觉 / 冲突对象),不是同一句换个说法;两条锚同一句原文即重复,重写
- 生成顺序:先素材后标题:写本节前先列该段钩子素材清单(引句+轮次+类型),再逐条生成;禁止先选基调再填料。详见
references/title-guide.md - 按「篇」挂载(不是按「段」):一段撑两篇时,两篇的备选不会串台
- 已选那行与卡片主标题重复,不会再出现在备选列表里(卡片上显示「同篇备选 4 条」是对的)
- 「基调」是开篇姿态(认错式 / 数字式 / 代价式 / 对比式 / 反转式…),不是文风词;它决定第一段怎么起,进而定结构。从素材推出,别硬贴标签
- 标题单元格内不要出现
|与书名号,会破坏 markdown 表格与指令解析;锚点引句里的引号用中文全角,避免与表格冲突 - 禁词 lint(命中即重写):修正版 / 说清楚 / 拆解 / 一文读懂 / 深度解析 / 怎么分 / 到底 / 指南 / 秘籍 / 干货
- 缺这一节 → 卡片没有备选区,行为与旧版完全一致(向后兼容),但默认要写
L3 工作流(提取之后必做)
原则:跳读,不通读。 40+ 轮的 clean.md 常 5 万字以上,整篇读入既慢又挤占后续写作的上下文。跳读靠的是先定边界、再读边界,不是"少读一点"。
- 只读地图,不读正文:读
segment-hints.md(自我否定句 / 长间隔 / 篇幅重心)+index.md(轮次目录:提问 + 回复主线 + 字数),定出 3~6 个候选段边界,只记轮次号。这一步全程不碰clean.md正文。 - 拿行号:
turnmap <产物目录>打印每轮起止行号;--range 16-26直接给出可用的Read(offset, limit)。窄会话(<15 轮)跳过本步,直接整篇读clean.md。 - 边界精读,段中不读:每个候选切点,读边界前后各 2~3 轮正文,确认转折确实发生在那句自我否定上;段内其余轮次靠
index.md的回复主线判断走向,不读原文。整段一眼扫不出主线的段(多见于长度悬殊的段),才补读该段重心轮。 - 并行校核(≥30 轮必做):切点定死后,每段派一个子 agent,只给它该段的行号区间(用
--ranges 1-8 9-20 21-34生成)+ 本段候选标题,要求回报:① 带轮次号的原句金句 3~8 条;② 本段钩子素材清单(每条=引句 + 轮次号 + 类型:自我否定 / 数字 / 代价 / 反直觉 / 冲突对象)3~8 条,直接供选题池取锚点;③ 关键决策、卡点、用户立场变化。子 agent 之间不共享上下文,只供料、不写表。 - 主 agent 合并写
主题分段.md:切段规则见references/segment-guide.md;字段口径统一由主 agent 定,写## 全景、## 成篇清单(含素材充分度列)、## 选题池(锚点驱动:每篇固定 1 已选 + 4 备选,每条必带锚点且互不相同;先列素材再写标题,详见references/title-guide.md)。 - 质量自检(跳读的安全网,别省):
-guide会先跑机检,这六项不用你再肉眼核:配额满 5 行、锚点引句能在clean.md搜到、同篇 5 条锚点互不相同、标题禁词、取材轮次不越界、项目级簇覆盖全部会话。输出以[check]开头,见 error 就回去改,别把警告当通过
- 剩下这几项脚本核不了,必须自己看:- 每篇骨架能指认到 ≥2 处原文(点位在
materials.md或某轮正文,指不出就说明这段没读透) - 全景表轮次区间 不重叠、不留空
- 各段字数之和 ≈
stats.json的全文规模(偏差 >10% 说明漏读) - 5 条标题的角度是否真的不同(脚本只核锚点不同,判不了"换个说法"的人话层面)
- 每篇骨架能指认到 ≥2 处原文(点位在
- 跑
guide刷新操作指南.html,并用 present_files 展示(out/index.html排第一、操作指南.html第二),让他挑篇下单。 - 用户选定后进写作。不要跳过第 7 步直接开写:素材再厚,选错段等于白写。
项目级工作流(workspace = 一个项目)
触发:用户想「把这个项目写成几篇发公众号」。判据是 workspace 下 ≥2 个会话、且最早会话装的是需求书。完整规则见 references/project-guide.md,写 成篇清单.md 前必读。
- 体检:
project <ws> --scan。0~6 分:≥3 判项目;2 分存疑(默认退回会话模式);≤1 不是项目。伪项目(目录被复用)直接退回run,别硬聚类。 - 提取:
project <ws>。脚本把每个会话提到sessions/NN-日期-标题/(幂等),并写会话地图.md(S 序号 / 日期 / 轮次 / 字数 / 标题 / 自我否定句)。正文永不合并。 - 两端精读:最早 1~2 个会话精读(初衷与需求原文)+ 最新 1~2 个会话精读(终局、砍掉了什么)。中段只读会话地图,按需
turnmap下钻某一会话。只读头部会写成"计划书"而不是"复盘"。 - 聚簇:把 N 个会话归并成 5~8 个簇(同一目标/同一决策线,不是关键词相同)。杂物簇标
0 篇,不注水。 - 写成篇清单.md,开头一节
## 簇列表(可见交付物:簇 / 主题 / 涉及会话·轮次 / 认知冲突 → 演进 / 去向),再写两节清单:
-### 一、项目篇:1~3 篇,讲整个项目一条主线(带项目名、时长、花费、砍掉的范围)
-### 二、簇篇:每簇 0~2 篇,写簇里长出的思维模式/做法(抹掉项目名照样成立);资格线=跨会话复现 ≥2
- 取材列用复合坐标S2·R1–30 / S9·R1–15;簇列写簇名;防重复:同一素材只在一处深写
- 之后照常写## 选题池:每篇固定 1 已选 + 4 备选,5 条锚点必须互不相同 - 出卡:
guide <项目目录>→ 页面顶部渲染簇列表总表,卡片自动展开取材为「S2(10-01 后端/爬虫联调)· 轮 1–30」。 - 自检:
guide的机检会报「配额 / 锚点 / 取材坐标 / 簇覆盖」,按[check]的 error 逐条改。脚本核不了的两条自己看:簇篇能否过「抹掉项目名还成立吗」、每篇骨架能否指认 ≥2 处原文。
多篇生成(一个会话出 N 篇)
素材只整理一次,文章按卡片多轮生成:
materials.md全程复用,绝不因为换一篇就重跑 digest- 每篇单独调一次写作技能,只换"第几篇",同一份素材路径不变
- 逐篇独立成稿、互不干扰,方便逐篇审改;不要把 N 篇塞进一次调用(上下文会爆、质量会飘)
- 卡片命中同名成稿(如
1.工作台到底是什么.md)时,HTML 会自动标绿「已生成 ✓」
更新与删除
更新:直接重跑 run <同一会话>,产物原地覆盖(脚本产物刷新,主题分段.md 不动,不产生副本)。会话变长后轮次编号会变,重跑后需核对分段边界是否仍成立,再 guide 刷新卡片。
删除:用 clean,不要让用户自己去 rm -rf(他根本不知道产物落在哪个工作区的 out/ 下)。
| 命令 | 效果 |
|---|---|
clean <目标> |
默认试运行:只打印将删什么、哪些是手写文件,不动任何东西 |
clean <目标> --keep-hand --yes |
只删脚本产物,保留 主题分段.md 与成稿——推荐 |
clean <目标> --yes |
整目录移入废纸篓(可还原) |
clean <目标> --yes --permanent |
彻底删除,不可恢复 |
<目标> 可传 会话ID前缀 / 标题关键词 / 目录路径。安全约束:只认含 stats.json 的产物目录;目标若与源会话同目录直接拒绝;默认走废纸篓而非 rm。
用户不用记这些命令:操作指南.html 底部的「常用指令」面板已把 outputs / run / guide / clean / list 全部列好,每条一行、带复制按钮,粘进聊天框就能用。会话 ID 也已按当前会话自动填好。
存储位置(默认不进资料库)
普通本地文件,不进资料库(Library)。
out/
├── index.html ← 总览单页(outputs --html):所有产物内联,点按钮同页切换
├── <会话日期>-<标题slug>/ ← 会话级产物
│ ├── clean.md index.md summaries.md stats.json
│ ├── materials.md segment-hints.md
│ └── 主题分段.md(agent 写) 操作指南.html(guide 生成)
└── <项目名>/ ← 项目级产物(project)
├── 会话地图.md (脚本生成,agent 聚类的输入)
├── 成篇清单.md (agent 写,唯一交付物|+ 选题池)
├── 操作指南.html (guide 生成)
└── sessions/NN-日期-标题/
├── clean.md materials.md segment-hints.md stats.json
└── (各会话独立,正文永不合并)
命名与覆盖规则:
- 日期取会话首条用户消息,不是文件 mtime(长会话常跨夜,用 mtime 会标错日子)
- slug 保留中英文数字、最长 20 字;URL 和「(无标题)」标记会被剥掉(无标题会话的"标题"是首条用户消息前 60 字,里头常带链接,直接当目录名会脏)
- 同名目录已存在:
stats.json里记录的source与本会话一致 → 直接复用(重跑幂等);不一致 → 追加会话 ID 前 8 位,避免两个会话撞进一个目录互相覆盖
为什么默认放本地,不放资料库:素材是中间件,要供后续写作反复读取,不是给人每天翻阅的成品;单会话产物 5 万字起步(60MB 会话能出 56 万字),正是最不适合塞进资料库的类型。要跨设备看的是写完的文章,那个再上传。
铁律(违反任意一条,结果就会失真)
- 源 jsonl 只读。 它是平台在实时写入的活文件(实测一轮对话内可从 993KB 涨到 2MB),任何写回都会破坏当前会话。脚本只
open('r'),且产物目录若落在源目录内会直接拒绝执行。 <user_query>只去壳,不能整块删。 用户原话就包在这个标签里;第一版把它当归类噪音整块替换,用户发言从 8 条掉到 2 条。<cb_summary>必须剥离并单独存。 它是平台在上下文将满时注入的历史复述。实测:占某会话"正文"的 80.9%;长会话里复述量是真实对话的 9 倍(507 万字 vs 56 万字)。不剥离会得到膨胀十倍的重复正文,字数判断全错。- L2 是候选池,不是答案。 候选池的价值在召回:宁可 52 条里挑 15 条,也不能漏掉那句能当标题的话。"哪句是金句"不能自动化——金句的特征不在句式,而在它在论证里的位置。
- L3 必须 agent 判断。 分段依据是"认知冲突的性质",正则看不见。脚本只提供切点候选。
- 会话清单以 App 的 sqlite 为准,不以 jsonl 文件为准。 平台把会话列表存在
~/.workbuddy/workbuddy.db的sessions表,侧边栏读的就是它。只扫 jsonl 会同时错三件事(实测):① 标题——ai-title在会话中会被反复重写,尾部若全是工具输出,小窗口读不到就会拿到第一条旧标题;② 改名——用户重命名写入custom_title(32 个),jsonl 里根本没有;③ 幽灵会话——App 里删掉的会话,jsonl 仍留在盘上(45 个)。所以list读 DB(custom_title优先、过滤deleted_at、按last_activity_at排序),jsonl 只用来补大小与轮次。
维护者笔记
已知边界、待改进清单、实测性能、校验器测试用例,都在 references/maintainer-notes.md。只有改脚本时才需要读,日常整理不必加载。
改完代码跑一遍 $PY $S guide <目录> 确认自检仍然正常,并把新踩的坑记进该文件。