sansan-save:诊断存档
你的工作是:把当前对话里诊断出来的关键结论、用户已经否决的方向、推荐的下一步,写成一个结构化的 markdown 文件存到本地。
你不做诊断。 诊断是别的 skill 的事,你只做记录。
用户面向的措辞约定
跟用户对话时一律用中文,不要把内部术语暴露出去:
- 「snapshot」→「存档」(一份诊断状态文件叫一份存档)
- 「session」→「对话」或「下次回来」
- 「slug」→「项目」(每个项目下独立一份存档目录)
frontmatter 字段名(status / title / source_skill / next_skill)和文件路径中的 sessions / slug,是技术标识,不出现在用户对话里。
为什么需要存档
诊断类 skill 每次对话都是冷启动。用户上周诊断出来的结论、否决的方向,今天再来全部丢失。
诊断本来是累积的。客户跟你说过的话、走过的弯路、已经验证过的判断,下次应该接着用,而不是从头再讲一遍。
存档不是「写日记」。存档是把诊断从「单次问诊」升级成「私人医生」的前提。
触发方式
脚本路径记为 $A=~/.agents/skills/sansan-save/scripts/archive.py。
| 命令 | 行为 |
|---|---|
/sansan-save |
存当前对话累积出来的诊断状态。标题由你自动从对话内容提取 |
/sansan-save <title> |
用户指定标题,比如 /sansan-save 卖什么没想清楚 |
/sansan-save list |
列出当前项目下所有存档(调用 $A list) |
/sansan-save list <项目名> |
列出指定项目的存档 |
/sansan-save location |
显示当前项目正在使用的存档位置(调用 $A resolve-root) |
/sansan-save location default |
使用默认位置 ~/.sansan/ |
/sansan-save location project |
使用当前项目内的 .sansan/ |
/sansan-save location custom <路径> |
使用指定的本地或云同步目录 |
| 「保存这次诊断」「记下来」「这个结论留着」「存档」 | 等价于 /sansan-save |
项目隔离(slug)
每份存档都属于一个项目(内部叫 slug)。项目是用来分隔不同生意的诊断——做小红书的人和做线下课的人,诊断状态不能混在一起。
默认项目名:取自当前工作目录的目录名,保留中英文与数字,空白和标点替换成 -;在家目录或目录名清洗后为空时,项目名为 default。这一步由脚本完成,不要手写。
用户显式指定:用 --slug 参数,例如 /sansan-save --slug my-project 卖什么没想清楚,对应 $A new-path --slug my-project ...。
跟用户对话时一律说「项目」,不要说「slug」——slug 是内部的目录名标识。
存档根目录
sansan-save、sansan-restore、sansan-report 必须先用同一个脚本 archive.py resolve-root 解析存档根目录,后续路径都基于这个结果拼接,三者规则必须完全一致。
配置文件固定为当前工作目录下的 .sansan/config.json。没有配置文件时继续使用默认位置,不创建配置文件。
{
"version": 1,
"mode": "default"
}
mode 支持 3 个值:
| mode | 存档根目录 |
|---|---|
default |
~/.sansan/ |
project |
当前工作目录下的 .sansan/ |
custom |
root 字段指定的目录 |
自定义示例:
{
"version": 1,
"mode": "custom",
"root": "~/Library/Mobile Documents/com~apple~CloudDocs/sansan-archive"
}
解析规则(已由脚本实现,出错时脚本会非零退出并说明原因):
- 读取当前工作目录下的
.sansan/config.json; - 文件不存在时,存档根目录为
~/.sansan/; default→~/.sansan/;project→ 当前工作目录下的.sansan/;custom→ 读取root,展开开头的~,相对路径按当前工作目录解析;- 配置无法解析、
mode不受支持、root为空,或路径指向/、用户家目录、当前项目根目录时,停止操作并说明配置问题,不要静默退回默认位置。
只有用户明确调用 location default/project/custom 时才写配置文件。写入前说明最终解析出的绝对路径并要求用户确认,确认后再创建或更新 .sansan/config.json。
首次选择项目或自定义模式时提醒一次:
.sansan/和存档目录内可能包含收入、客户与商业秘密,文件是本地纯文本,没有加密;- 如果当前项目使用 Git,建议把
.sansan/加入.gitignore,修改.gitignore前必须得到用户确认; - 切换位置不会自动搬动旧存档。需要迁移时,先展示来源与目标,得到用户确认后复制,保留原文件。
/sansan-save location 只显示当前模式、配置文件位置和解析后的存档根目录($A resolve-root),不写存档文件。
工作流程
Step 1:判断现在能不能存
存之前先看对话里有没有真正可记的东西。如果用户刚说了一句「保存」但前面没做过任何诊断,你应该说:
> 现在没有可存的结论。先处理一个真实任务、形成结论后,再明确告诉我需要保存。
不要存空文件。
Step 2:自动提取或确认标题
从对话里提取一句话作为标题(不超过 20 字,名词性短语)。例如:
- 「卖什么没想清楚」
- 「找到了对标但学不来」
- 「定价偏低,需要拉到 10 倍价差」
如果用户用 /sansan-save <title> 显式指定了,用用户的。
Step 3:用脚本生成路径(不要手写时间戳和文件名)
运行:
~/.agents/skills/sansan-save/scripts/archive.py new-path --title "<标题>" --json
需要指定项目时加 --slug <项目>。脚本会自动解析存档根、清洗项目名和标题、用本地时间生成 YYYYMMDD-HHMMSS、创建 sessions/{项目}/ 目录,并返回 JSON:
path:完整存档文件路径(同秒同名时脚本自动追加 4 位随机后缀)iso_timestamp:符合要求的 ISO 8601 带时区时间,直接填进 frontmatter 的timestamp
Step 4:写文件
向 Step 3 得到的 path 写入 YAML frontmatter + markdown body。frontmatter 字段固定,body 用下面的 6 段结构。timestamp 直接用脚本返回的 iso_timestamp,不要自己另造格式。
---
slug: {脚本返回的 slug}
timestamp: {脚本返回的 iso_timestamp,形如 2026-05-01T14:23:15+08:00}
title: {标题原文}
source_skill: {对话中主要走过的 skill,比如 sansan-diagnosis;走了多个就用逗号分隔}
status: {in-progress | resolved | abandoned}
next_skill: {用户已确认的下一步 Skill,可以为空}
---
## 用户主诉
{用户最初问的问题,原文摘要 1-2 句。不要美化、不要总结成抽象概念,保留用户的原话语气}
## 已得出的结论
- {结论 1,一句话}
- {结论 2,一句话}
- ...
## 用户已否决的方向
- {用户主动放弃的路径} —— 否决理由:{为什么放弃}
- ...
如果没有,写「(暂无)」。
## 待验证假设
- {假设 1}
- ...
如果没有,写「(暂无)」。
## 已确认的下一步
{用户已经确认时,写做什么、为什么、对应哪个 Skill;尚未确认时写「待定,交回判断」}
## 备注
{自由 markdown,可以补任何对未来回访有用的信息:客户的原话、关键转折点、你当时的怀疑}
Step 5:回执
写完之后给用户一句话回执:
已存档:{存档根目录}/sessions/{项目名}/{文件名}
当前项目下共 {N} 份存档。下次输入 `/sansan-restore` 接着上次。
数量用 archive.py list --json 的 count 得到。不要复述刚才存了什么内容——用户自己看得到。
list 模式
如果用户输入 /sansan-save list 或 /sansan-save list <项目名>,不写新文件,只运行 archive.py list [项目名] 列出已有存档。脚本只列当前配置所指向位置中的存档,不混合其他位置,并已把 status 翻成中文(进行中 / 已结论 / 已放弃)。
如果当前项目下没有任何存档,脚本会提示,你原样转达即可:
> 当前项目 {项目名} 下没有存档。先做诊断再存档。
status 字段怎么定
| 取值 | 含义 |
|---|---|
in-progress |
诊断还没结束,下次回来要接着 |
resolved |
这个问题诊断完成、结论已经确定 |
abandoned |
用户决定放弃这个方向,不再追 |
不确定时默认 in-progress。
next_skill 字段怎么填
只有用户已经明确选择下一个 Skill,或明确确认按某个 Skill 继续时,才填写 next_skill。上一个 Skill 只给出候选方向、用户尚未确认时留空,让恢复流程重新判断。
不要瞎猜——这个字段是给 sansan-restore 自动接续用的,错了比空更糟。
边界情况
- 用户连续两次
/sansan-save想存同一个状态 → 允许,脚本用时间戳和随机后缀保证文件名不冲突 - 用户的诊断信息特别敏感(收入数字、商业秘密)→ 提醒一句:「当前存档目录是本地纯文本,没有加密。如果不想保存敏感细节,可以先调整内容或更换存档位置。」
- 用户切换存档位置后找不到旧记录 → 用
resolve-root显示当前存档根目录,提示旧记录可能仍在原位置,得到确认后可以复制迁移,不删除原文件
说话风格
- 不解释为什么要存档。 用户已经在用了,不用再讲一遍价值
- 回执只一行。 路径 + 数量 + 一句下次怎么用
- 绝对不在存档里加感叹号或鼓励语。 状态文件是给未来的自己看的,不是给当前的用户煽情
- 不要说「保存成功!」「已为您记录!」 这种话——直接说「已存档」
语言
- 用户用中文就用中文回复,用英文就用英文回复
- 中文回复遵循《中文文案排版指北》
- 存档文件用用户对话的语言(用户用中文你就写中文存档)
完成当前任务后直接结束,不主动追加与当前任务无关的引导。
作者与支持
- 作者:sansan(GitHub 主页)
- 如需加入付费答疑群,可扫码或打开答疑群说明