忆念之间(记录人生 · 生成人生影像)
一句话:帮一个人把他/她的一生记录下来,并生成一段影像留下去。 平台 www.yinianzhijian.com,给人打开的是 https://www.yinianzhijian.com/index。
怎么用:三条命令,就这三条
| 用户想做什么 | 你跑什么 |
|---|---|
| 记录人生 / 回忆往事 / 整理成故事 | story "<整理好的回忆>" |
| ……再要一段视频 | story "<回忆>" --video |
| 建 / 看时光馆 | hall --title 母亲 --relation mother ・ hall --list |
| 写时光信 | letter --name 女儿 --date 2030-01-01 --content "……" |
⚠️ 路径先看这一句:下文所有命令里的 scripts/yinian.py 都在本 skill 目录下(~/.workbuddy/skills/yinianzhijian/)。
如果你的工作目录不是那里,就把它整个换成 ~/.workbuddy/skills/yinianzhijian/scripts/yinian.py,其余参数一律不变。
(直接照抄相对路径会报 can't open file '.../scripts/yinian.py' —— 别以为是 skill 坏了。)
处理原则:先给选项,再动手。
- 用户只唤起技能、没说要做什么(只发了技能名,或问"有什么功能""你能干啥""开始吧")
⇒ 必须返回一次可选的功能清单,别写一段散文介绍就干等用户说话。固定三项:
① 记录人生(→ 人生故事本,可再出人生光影) ② 时光馆 ③ 时光信
🔴 不要再加第 4 项「其它」 —— 孔明灯 / AI 对话 / 光影列表在描述里提一句就够了,
交互界面自带自由输入框,用户想用别的直接写(自己再加一项 ⇒ 界面上出现两个"其它")。
界面能弹交互就用可点选的选项(WorkBuddy 里用AskUserQuestion),纯文本界面就编号列出来。 - 用户已经说清要做什么("记录我爸""给我妈建馆""写封信给女儿")⇒ 直接做,不要再弹一遍菜单。
- 缺哪个参数就问那一个(姓名、关系、日期一律问清楚,不许猜)。
- 🔗 产出必带分享链接:记完人生(建完时光馆同理)必须把「分享页」链接给到用户 ——
那个页面游客可访问(家人不用登录就能打开),是这一整套里唯一能直接转发出去的东西。
链接由story/hall --list自动打印,也可用share单独取;别只回一句"已生成"。 - 🔴🔴 交付优先,回复要短:用户要的是产物(回忆录链接 / 故事本正文 / 选项卡),回复就以产物为主。
"我跑了什么命令、库里有多少条记录、校验结果如何、链路怎么走的"——用户没问就不写;
表格、进度条、多处加粗回顾这类"汇报体"一律砍掉。2026-09-17 用户原话:
「你不用返回这么多,就返回我的回忆录就行。信息太多,用户根本看不到重点」
⇒ 判据很简单:这条回复里,用户真正要的那样东西,是否一眼就能看到? 看不到就是写多了。 - 选项交互别为了问而问:同一件事的参数尽量合并进同一次交互(例:「做什么」+「记谁」放在一张卡里问完);
用户选完立刻执行,不要拿闲聊或确认去多弹卡片。 - 允许的"连续两问"只有流程本身必须相邻的节点 —— 例如选完「记录人生」后紧接着该答题了,
这时直接给「题面 + 候选回答 + 终止」一张卡就够(见下节「答题怎么出选项」),
不要再单独弹一张"现在就讲 / 换一道题 / 先跳过"(2026-09-17 起合并为一张)。
🔴 「答完一题 → 出下一题」同属"必须相邻":答题卡可以一张接一张连出,
这不是"多问",这是流程本身。别问完一张就收工(2026-09-17 用户原话:
「我答题了,应该会继续答题吧。你怎么直接结束了」)。除此之外不要连着弹第二组。
(首次使用先让用户复制一次 Token 交给助手存起来 —— 见下文「身份凭证」,存一次长期有效。)
> 「记录人生 / 时光馆 / 时光信」是常用的 95%;孔明灯、AI 对话、向 AI 倾诉、光影列表等在文末「其它能力」,
> 只在选项里出现一次,其余时候别主动推。
交互规范:能点选的就别让用户打字
总原则:只要这个参数的值落在有限集合里,就一律用可点选的选项呈现(WorkBuddy 里用 AskUserQuestion;
纯文本界面就编号列出)。只有非得用户本人讲 / 写的内容才用文本输入。
| 要问的 | 用什么 | 选项长这样 |
|---|---|---|
| 做什么功能 | 选项 | ① 记录人生 ② 时光馆 ③ 时光信(不加「其它」项;孔明灯 / AI 对话 / 光影列表只在描述里提一句) |
| 给谁记 / 记谁的 | 选项 | 由 hall --list 现场生成:父亲 / 母亲 / …… +「我自己」 |
| 与 TA 的关系 | 选项 | 父亲 / 母亲 / 爷爷 / 奶奶 / 姥爷 / 姥姥 / 儿子 / 女儿 / 兄弟姐妹 / 伴侣 |
| 要不要出人生光影 | 选项 | 「生成(15 秒视频,12 元)」/「先不要,只要故事本」 |
| 答题 | 选项 + 自由输入 | 题面 + 助手现场给的 2~3 个候选回答 + 末尾固定一项「终止」(见下节「答题怎么出选项」) |
| 记完接下来 | 选项 | 默认不再单独问 —— 直接在原地出「下一题」卡(用户想停就点卡上的「终止」)。只有当用户流露出要收工的意思("先这样""今天到这")时才用:「看故事本」/「就这样」 |
| 时光信送达时间 | 选项 | 「一年后」/「三年后」/「我来指定日期」(转换 yyyy-MM-dd 后传 --date) |
| 额度不足 / 报错时 | 选项 | 「去充值」/「先做免费的(只出故事本)」/「下次再说」 |
| 姓名、寄语、信件正文、备注(必须用户本人给定的值) | 文本 | 只能文本输入 —— 猜一个就是编造事实 |
选项的硬要求
- 说人话:写「记录人生」,不要写
story;每一项都补一句人话描述。 - 一次只问一组,用户选完立刻执行;除「流程本身必须相邻的节点」(见上文执行原则)外,不要连着弹第二组。
- 一次最多 4 项,多的并进第 4 项;「其它」不要自己写成选项 —— 交互界面自带自由输入框。
(例外:动作项要写成选项 —— 如答题卡末尾的「终止」。它不是某个"值",用户没法在输入框里猜到该怎么写。) - 选项里不放技术黑话(
--topic-id、generatedBy、asrStatus这类用户看不懂的)。
答题怎么出选项(默认行为)
选完题面之后,直接把候选回答摆成选项让他点 —— 别只丢一句"你说吧"。
- 题面照原样念一遍(用户得先看清问的是什么),再给选项。
- 每个候选写成一句可以直接落库的完整话,不是关键词。
例:题面「你小时候淘气,干过什么让家里头疼的事?」⇒ 选项「我小时候爱爬树掏鸟窝」/「我常下河摸鱼,回来一身泥」/「我把家里的东西拆了装不回去」。 - 候选回答最多 3 个 —— 因为第 4 个位置固定留给「终止」(见下条)。
- 🔴 答题卡最后一项固定是「终止」,文案写人话,如「先不答了,就到这里」。
⇒ 用户只点「终止」 = 不落库、不追问、不再弹卡,本轮记录到此结束。
🔴 "结束"不等于"什么都不给" —— 终止的交付物就是那份回忆录本身:
必须把回忆录的分享页链接原样给到用户(share --self/share --topic-id N),界面能预览就直接打开。
⇒ 只回一句"本轮结束"、或只报进度不给链接 = 没交付(2026-09-17 用户原话:「终止后,你应该返回回忆录」)。
🔴🔴 收尾回复里只放回忆录这一样东西 —— 不附进度、不报条数、不列校验明细、不写"我做了什么"。
一句话 + 一条链接就够,整段不超过 3 行。用户第二次纠正的原话:
「你不用返回这么多,就返回我的回忆录就行。信息太多,用户根本看不到重点」
⇒ 交付物被解释淹没,等于没交付。
⇒ 多选题里用户可能同时勾了回答和「终止」 —— 含义是"记下这些,别再问了":
先把勾中的回答落库、出故事本,再收尾;别因为带了「终止」就把勾好的内容丢掉。
⇒ 别让用户只能靠关掉卡片来表达"不想答了" —— 2026-09-17 用户在实操中直接关掉卡片,随后要求
「这种问题加一个终止的选项」。给一个能体面退出的出口,比逼人二选一好。
⇒ 想换一题的用户会在输入框里说"换一道题" —— 那就按weekly-state换,别把那句话当回答落库。 - 用户点选 = 直接当他的回答落库;界面自带的输入框就是"自己说"。
用户自己打字时以他写的为准,不要拿选项去覆盖他。 - 落库后顺口问一句要不要补细节(用文本问,不要再弹一张卡片)。
- 🔴🔴 答题不是一次性动作 —— 落库之后紧接着就把下一题摆出来,别停在这里。
⇒ 重新取一次weekly-state拿新的当前题(题号会跳号,别自己 +1),直接把
「题面 + 2~3 个候选 + 终止」摆成下一张卡。用户连答三题五题是常态。
⇒ 「补细节」那句话是搭在下一题卡旁边的一句附言,不是收尾:他愿意补就补,
不补就直接答下一题 —— 两条路都得留着,不能因为你问了细节就把流程掐断。
⇒ 只有用户点了「终止」、或明说了「就这样」,才收尾。
2026-09-17 用户原话:「我答题了,应该会继续答题吧。你怎么直接结束了」——
我当时落完库只问了一句"要不要补细节"就收摊,等于把一条能一直走下去的流水线做成了单发。
唯一的红线:选项里不许出现专有名词
- 🔴 只写类别化的行为 / 场景(爬树、下河、拆东西),不写具体的人名、地名、年份、数字
("跟隔壁王二狗去村东头掏鸟窝" ❌)。 - 🔴 判据:这条选项里有没有用户没提过的专有名词?有就删。
类别化行为点错了,用户一句话就能否掉、也不可惜;专有名词点错了就变成"用户说过的",
会被直接写进故事本 —— 那才是替人编造家史。 - 题目允许并列多个时(如「干过什么淘气事」「家里谁做饭最好吃」)用多选;其余单选。
- 🔴 要点选的那句"完整话"必须写在选项的「标题」里,别只写在描述里。
用户点选后落库的就是标题那句,描述只作解释。实证(2026-09-17 首次实操):标题写成「他总是起得很早」、
细节全塞在描述里 ⇒ 点选后无法判断那段细节算不算"用户说过的" —— 记进去是替用户写,不记又丢了信息。
规则:标题 = 落库正文;描述 = 只解释这句话什么意思。别把内容放在描述里。
入口 1 · 记录人生 → 人生故事本(→ 可选人生光影)
python3 scripts/yinian.py story "我妈 1958 年生在河北,小时候……" # 给自己记
python3 scripts/yinian.py story "父亲年轻时在钢厂……" --topic-id 12 # 给某位家人记
python3 scripts/yinian.py story "……" --video # 顺带生成人生光影
一条命令走完:落一条记录 → 整理成人生故事本 →(带 --video 时)认人 → 生成人生光影 → 等出片。
- 归属二选一:
--self给自己记(做自传/回忆录);--topic-id N给某位家人记。二者必居其一。 - 正文从哪来:用户在对话里怎么说的,你顺一顺、补全句子、保留他的原话与细节再传进去(可以先给用户看一眼)。长文本用
--text-file,也可以管道传入。 - 输出就是故事本正文 ⇒ 要呈现给用户看,不要只说一句"已完成"。
- 🔗 命令结尾会自动打印一行「分享页(家人不用登录就能打开,可直接转发):<链接>」 ——
这一行必须原样出现在给用户的回复里。家人版是/story/<时光馆id>、自录版是/story/me/<用户名>;
微信里转发过去会带卡片(og:title就是"××的回忆录")。想单独取链接:
bash python3 scripts/yinian.py share --self # 我的回忆录 python3 scripts/yinian.py share --topic-id 12 # 某位家人的
⚠️ 这两个页面是公开的(谁拿到链接都能看)⇒ 别把不该公开的内容记进去;用户要是问起,如实说。 - 别把「目录」当故事本报出去:若命令提示
⚠️ 这次只生成了目录(即generatedBy=outline),说明这次没整理成文(材料太少,或 AI 临时不可用)—— 如实告诉用户,让他补一段回忆再重来。 - 成文那份可能含 AI 补写:
generatedBy=ai-expand时,模型会补一些生活环境的质感描写。用户问起就说清来源,别把补写的细节说成"您讲的"。 - 记多段就多跑几次,故事本每次都会把库里所有回忆重新整理成一篇(越记越厚)。
- 想要「每周一问」的体验(平台原生玩法):先
weekly-state --self(家人用--topic-id N)拿当前该问的那道题 → 照着题去问用户,并把候选回答做成选项(见上节「答题怎么出选项」)→ 用户答完再story "..."。题库 27 题 / 5 类,见文末。
🔴 落库时一律显式带--question-id <题号>,不要图省事省略。
服务端的「当前题」会跳号:实测 2026-09-17 答完第 8 题(家人)后,当前题直接变成 13(不是 9)——
所以你被告知的那道题号才是唯一可靠依据,省略--question-id等于把内容交给服务端去猜。
(实测对照:题号一致时省不省一样;题号不一致时省略 = 挂到错的那道题上,且不报错。) answeredCount里含"转写为空"的记录:早期失败留下的空壳也算已答。
⇒ 见到「已答 9 题、故事本却只覆盖 6 条」时,先去看 items 里transcript为空的有几条,
别急着怀疑成文漏内容(实测 2026-09-17:9 条中 3 条transcript="",成文覆盖的正是其余 6 条,无遗漏)。
⚠️ 三个必须知道的事实:
- 对话里没有麦克风。 平台唯一的录音入口是网页
https://www.yinianzhijian.com/hall/weekly(浏览器麦克风 + 自动转写)。
⇒ 对话里记的是文字:命令会用一小段静音音频占位、把文字直接带进去(不会再跑语音识别)。用户手上有真录音时用--audio <文件>传。
⇒ 想留真实声音,就引导用户去那个网页录(录完会自动进时间线,助手这边照旧能整理成故事本)。 - 人生光影是付费增值项。 生成前
--video要先问用户一句「要做成视频吗,这个是付费的」;life-slice-quota可查剩余额度与单价。
额度不足时接口返回needRecharge=true+rechargeUrl—— 必须把这个链接原样交给用户。只说一句「余额不足」等于把人卡死(这是平台专门修过的体验问题)。 - 顺序不能反:故事本 → 认人 → 光影。三个接口都不接受正文入参,一律取库里最新那份故事。
⇒ 想「直接塞一段文字生成视频」是不通的,别试;先story出故事本,再--video。
入口 2 · 时光馆
时光馆 = 一个人(或一位家人)的记忆主页:称呼 + 关系 + 照片 + 生日(忌日选填)+ 人生经历。
python3 scripts/yinian.py hall --list # 列家人:topicId / 称呼 / 录音进度
python3 scripts/yinian.py hall --title 母亲 --relation mother # 建馆
python3 scripts/yinian.py hall --title 母亲 --relation mother \
--content "一生经历……" --birthday 1958-03-12 --image-url "https://…" # 建完整馆
- 先查重再建:建之前跑一次
hall --list。同名馆后端会直接拒(提示「已经有一位「父亲」了」)—— 那说明库里已有,去列表里用现成的 id,别硬堆第二座。 - 自动选门:给了「人生经历 + 生日」就走完整建馆;只给称呼就走轻建馆(先立起来,照片/生日/生平之后在
/topic/{id}补)。想赶紧开始记录就用轻建馆。 - 建馆要求账号已激活(个人设置页绑邮箱),否则报「你的帐号还没有激活」。
- 给自己建馆:
--relation self(做自传/回忆录)。 - 建完拿到的 id 要记住 —— 后续
story --topic-id N、pray-create都用它。hall --list里每项的家人的 id 字段名是topicId;列表里不含「本人」那座馆(给自己记用--self,不需要 id)。 - 补资料用细粒度命令:
hall-update <id> --title 母亲 --content "整理好的人生故事……"(--title必填,只补 content 也要带上原标题)。 - 详情页是
/topic/{id}(/hall/{id}不是详情、会返错误 JSON)。
入口 3 · 时光信
写一封指定收信人、到日子才送达的信。
python3 scripts/yinian.py letter --name 女儿 --date 2030-01-01 \
--content "亲爱的女儿,写这封信的时候你才三岁……" --phone 138xxxxxxxx
--date是送达日期(yyyy-MM-dd),--phone选填。- 信的内容要用户自己说,你不要代笔编造;可以帮他整理措辞。
- 「到期履约通知」以平台实际行为为准,不要承诺「一定会收到通知」。
身份凭证(Token)
所有写操作与个人数据都要 Token。取一次、存一次,之后所有命令自动带上。
# 1) 打开 https://www.yinianzhijian.com/index 登录(用户名 2-16 位字母数字),
# 点页面右上角「复制 Token」;2) 交给助手存一次:
python3 scripts/yinian.py set-token <粘贴你的Token>
存到 ~/.yinian_token(权限 600)。优先级:--token > 环境变量 YINIAN_TOKEN > 该文件。清除:rm -f ~/.yinian_token。
> Token 是账号级固定的(只有到设置页点「刷新 Token」才换)⇒ 存一次可长期用。
> ⚠️ 别用账号密码换 Token:/api/login 要图形验证码(存在 HttpSession 里、必须人眼读图),命令行做不到 —— 原先的 login 子命令已因此移除。密码也不必交给助手。
> Token 等同于账号密码,切莫泄露;账号需「已激活」(个人设置页绑邮箱),否则接口会明确报「你的帐号还没有激活」。
调用约定(省一次失败)
- 接口域名
https://www.yinianzhijian.com(自建环境用--base)。
⚠️ 接口路径不带/index——/index只是页面入口;/index/api/...会返回「服务器出错啦~」。 - 鉴权:Header
token: <Token>(脚本已自动带上,并同时写user_tokenCookie,个别接口只认 Cookie)。 - 响应统一
{"code":200,"description":"SUCCESS","detail":…}。不存在的路径也返回 HTTP 200(body 里是code:201)⇒ 排查时必须断言响应体,只看状态码会误判成功。 - 请求体:POST/PUT 为 JSON;
/api/weekly/answer、/api/upload是multipart/form-data(脚本已处理)。
关键字段取值
关系码 relation:self 本人、father 父亲、mother 母亲、husband 丈夫、wife 妻子、grandfather 爷爷、grandmother 奶奶、mgrandfather 姥爷、mgrandmother 姥姥、son 儿子、daughter 女儿、grandson 孙子、granddaughter 孙女、brother 兄弟、sister 姐妹、famous 名流、stranger 陌生人。(中文值也能穿过;识别不了时兜底显示「亲人」。)
日期 yyyy-MM-dd 照片 先 upload --file x.jpg --type hall 拿 URL 光影状态 0 生成中、1 完成(带 videoUrl)、2 失败。
隐私边界:光影归属靠 topic_id(自录为 NULL);孔明灯只能挂在自己的时光馆上。分享页 /story/{topicId}、/story/me/{username} 是游客可访问的,别往里放不该公开的内容。
其它能力(用到再展开,别主动推)
| 想做什么 | 命令 |
|---|---|
| 放一盏孔明灯(寄语 ≤100 字) | pray-create --topic-id 12 --title "愿您身体健康" |
| 生成一句寄语 | pray-recommend |
| 看灯被点过几次 | pray-stat --topic-id 12 |
| 与家人 AI 对话(单条 ≤100 字) | chat 母亲 "妈,我今天……" |
| 看历史对话 | chat-history 母亲 |
| 向 AI 倾诉 / 咨询 | inqury "……" |
| 我的光影列表 / 额度 | life-slice-list / life-slice-quota |
| 一步到位出片(不经回忆往事) | life-slice --name 母亲 --content "生平……" --image-url "…" |
排障
报 [SSL: CERTIFICATE_VERIFY_FAILED] unable to get local issuer certificate —— 看着像「服务器连不上」,其实不是服务器的事:是你那个 Python 一个 CA 证书都没有(实测 macOS 上 python.org 装的 3.11、没跑过 Install Certificates.command 就是这样,cafile 为 None)。脚本已会自动改用系统证书包;万一仍出现,换个解释器(/usr/bin/python3 或系统自带那个)即可,别去查服务器。
参数形态:位置参数(不带 --)—— hall-detail <id>、hall-update <id> …、life-slice-watch <taskId>、weekly-transcript <id> <文字>、chat <关系> <内容>;其余都是 -- 选项。猜错会报 unrecognized arguments。--token / --base 写在子命令前或后都可以。
weekly-story 是「生成」接口,不是「读」接口 —— 每调用一次就把正文整篇重写一遍(2026-09-17 实测:连调两次,正文 md5 61805f92 → 0982f489;sourceCount 不变,只有 AI 补写的环境细节在漂)。
⇒ 别为了"看一眼元信息"去调它:你以为在读,其实在改写用户的故事本。
要看已经落库的正文,直接开分享页 /story/{topicId} 或 /story/me/{username}(页面读库,实测连抓两次 md5 完全一致,稳定)。
story 门面命令内部只调一次,正常使用不受影响。
故事本正文里的「补写」会漂,不要当事实回读 —— 平台允许 AI 补"年代质感"(用「想来」这类回望语气,不许编人名/地名/时间/情节),但同一段回忆每重生成一次,补出来的环境细节都不同(同一条「潍坊农村」先后出过"柴火灶"、"枣树槐树"、"麦香"、"野槐树老杨树")。⇒ 向用户呈现时,AI 补的部分要明说"这是补的",别让用户以为是自己讲过的。
录音识别失败(asrStatus=2,transcript 空):① weekly-retranscribe <id> 重跑一次;② 仍失败就用 weekly-asr --file <一条曾识别成功的音频> 确认服务端本身好不好(它不落库,是干净的试验台);③ 音频内容有问题时用 ffmpeg -i x.webm -af volumedetect -f null - 看 mean_volume,低于 -50 dB 基本是静音,增益归一化救不回「没对着麦说」的录音;④ 兜底 weekly-transcript <id> "<用户口述的内容>" 补文字(改写已有记录,不是新建),补完再 weekly-story 重整理。
题库(27 题 / 5 类):平台没有「题库列表」接口,weekly-state 每次只返回当前该问的那题。但 skip 只在本次调用生效、不落库 ⇒ 用「逐题累积 skip」可顺出整套题且完全不动用户进度:反复调 POST /api/weekly/state,body 带 {"self":true,"skip":[已见过的题号]},直到题目绕回已见过的 id 为止。2026-09-17 实测:童年 #1–#6、家人 #7 #8 #11 #12 #13、工作 #14–#18、心事 #19 #21 #22 #23 #24 #25、时代 #26–#30(题号不连续,就是全局题库 id,可直接用于 --question-id)。
资源索引
references/api.md— 每个接口的完整说明、参数、curl 与响应示例。scripts/yinian.py— 命令行封装(仅标准库),--help看全量命令。
> 平台背景:早期只服务「纪念逝去的亲人」,2026-09 起主线转向记录仍在身边的人。改名的证据链:纪念堂/纪念馆→时光馆、人生切片→人生光影、遗嘱→时光信、祈福→孔明灯;建馆的忌日变成选填(为健在的父母建馆)、给自己建馆的关系词是 self、孔明灯寄语要求「祝福仍然健在的家人」。逝者纪念仍支持,但不要默认用户是来祭奠的。