源码解读⑤:自动渲染 SKILL.md 与中文拼音 slug——skill_writer.py 与 version_manager.py 深度剖析
【免费下载链接】ex-skill前任 skill项目地址: https://gitcode.com/gh_mirrors/exsk/ex-skill
前言.skill(ex-skill)是一个把前任聊天记录「蒸馏」成 AI Skill 的开源项目:导入微信、iMessage、短信、照片后,自动生成「像她一样说话」的 Claude Code 技能。本篇源码解读聚焦项目里最核心的两个工具脚本——skill_writer.py负责自动渲染 SKILL.md并把中文名转成中文拼音 slug,version_manager.py负责版本存档与一键回滚。读懂它们,你就理解了整个「前任 Skill」的出生与进化机制 📦
一、先看全貌:一个 Skill 目录长什么样
每次成功创建一位「前任」,项目会在exes/{slug}/下生成一套标准文件。以仓库自带的示例 exes/example_xiaomei/ 为例:
| 文件/目录 | 作用 |
|---|---|
SKILL.md | 渲染后的完整技能(共同记忆 + 人物性格 + 运行规则) |
memories.md | 共同记忆正文(关系时间线、日常仪式、重要时刻) |
persona.md | 人物性格正文(表达风格、情感逻辑、纠正记录) |
memories_skill.md/persona_skill.md | 两个「单功能版」技能,触发词分别是/{slug}-memories、/{slug}-persona |
meta.json | 元数据:昵称、slug、版本、纠正次数、更新时间 |
versions/ | 历史版本存档,由版本管理器维护 |
knowledge/ | 存放原始资料(chats / photos / social) |
注意区分两个SKILL.md:仓库根目录的 SKILL.md 是创建器技能(教 AI 如何收集资料、分析、生成);而exes/{slug}/SKILL.md是每个前任自动渲染出来的成品技能,本篇主角就是负责渲染它的那段代码。
二、中文拼音 slug:从「小美」到xiaomei的目录命名术
目录名不能直接用中文,项目用了一个简洁的 slug 化方案,代码见 slugify():
- 优先使用 pypinyin:若环境装了
pypinyin,直接取lazy_pinyin(name)并用下划线拼接,「小美」→xiaomei; - 降级兜底:没装依赖时走 fallback——只保留 ASCII 字母数字和
-_,空格转下划线,保证不报错; - 统一清洗:用正则把连续下划线压成一个、去掉首尾下划线;如果最后结果为空(比如名字全是纯符号),兜底返回
"ex"。
这个 slug 贯穿全局:目录名、meta.json里的字段、以及最终触发词/{slug},都来自它。所以示例中 meta.json 里才写着"slug": "example_xiaomei",你用/example_xiaomei就能唤起这位示例前任。💡
三、自动渲染 SKILL.md:一份模板 + 元数据,一次成型
渲染的核心是 SKILL_MD_TEMPLATE 模板——一个带 YAML frontmatter 的固定骨架:
--- name: ex_{slug} description: {name},{identity} user-invocable: true --- # {name} ## PART A:共同记忆 {memories_content} ## PART B:人物性格 {persona_content} ## 运行规则 1. 先由 PART B 判断:回不回、什么心情回 2. 再由 PART A 提供记忆 3. 输出时保持 PART B 的表达风格真正干活的是 create_skill(),执行顺序非常「工程化」:
- 建目录:一次性
mkdir出versions/与knowledge/三件套; - 写正文:
memories.md、persona.md原样落盘(UTF-8); - 渲染 SKILL.md:把两份正文填进模板;
- 生成两个单功能技能:
memories_skill.md、persona_skill.md,让用户可以只聊「记忆」或只聊「性格」; - 写 meta.json:补上
slug、created_at、updated_at,版本初始为v1,corrections_count归零。
其中身份描述由 build_identity_string() 拼装,它从meta["profile"]里取字段,用逗号串成一句人话。以示例数据为例,输入 meta.json 的档案,输出的就是:
在一起 两年半,大学同学,分手 一年,UI 设计师,MBTI ENFP
这句话会直接写进渲染后SKILL.md的description里——一句话让 AI 知道「你是谁、她是谁」。📝
四、进化模式:update_skill 的「先存档、后写入」原则
前任 Skill 支持持续进化(追加新聊天记录、纠正性格偏差)。update_skill() 的做法堪称教科书:
- 版本号 +1:从
meta.json读出v1,解析成数字加 1 得到v2; - 先存档:把当前
SKILL.md、memories.md、persona.md拷贝到versions/v1/——保证任何更新都「有退路」; - 应用增量:memories patch 直接追加;persona 侧支持两种写法——普通追加,或纠正记录(correction),会被精确插入到
## Correction 记录小节下,并顺手把corrections_count+1; - 重新渲染:读完更新后的两份正文,再套一次模板重写
SKILL.md; - 刷新 meta:写入新版本号与 UTC 时间戳。
配合 prompts/correction_handler.md 的纠正流程,用户只要说一句「她不会这样」,Skill 就会把自己越调越像。这套「模板即事实来源」的设计(SKILL.md 永远由 memories + persona 重新生成,而不是手工维护)是整个项目最值得借鉴的一点。✅
五、version_manager.py:版本清单、安全回滚与自动瘦身
tools/version_manager.py 只有三个动作,却把版本管理的三个痛点全解决了:
| 动作 | 解决的问题 | 关键实现 |
|---|---|---|
list | 历史版本有哪些? | list_versions() 遍历versions/,展示版本号、存档时间、文件清单 |
rollback | 更新翻车了怎么办? | rollback():先把当前状态存成{版本}_before_rollback(回滚本身也留退路),再从目标版本恢复三个文件,并把 meta 标记为目标版本_restored、记录rollback_from |
cleanup | 存档越攒越多? | cleanup_old_versions() 按修改时间排序,只保留最近MAX_VERSIONS = 10份 |
用户侧对应的就是根级 SKILL.md 里定义的管理命令:/ex-rollback {slug} {version}直接触发回滚,/list-exes则调用 skill_writer.py 的 list 动作,以「slug、昵称、身份描述、版本、纠正次数、更新日期」的整齐格式列出所有前任。📋
六、串联全链路:从录入到回滚
把两个脚本放回完整流程,职责分工一目了然:
昵称「小美」 ──slugify()──▶ xiaomei(目录名 / 触发词) 原始资料 ──prompts 分析──▶ memories.md + persona.md ──SKILL_MD_TEMPLATE 渲染──▶ exes/xiaomei/SKILL.md 追加/纠正 ──update_skill()──▶ 存档 versions/v1 → 写入 → 重渲染 → v2 翻车了? ──rollback()──▶ 先备份当前,再恢复 v1,全程可逆- 创建入口:skill_writer.py 的 create 分支
- 更新入口:skill_writer.py 的 update 分支
- 回滚入口:version_manager.py 的 rollback 分支
七、小结
- slug 是身份:
slugify()用 pypinyin 把中文名转成安全的拼音目录名,并保留无依赖兜底,一处生成、全局复用; - SKILL.md 是渲染产物:模板 + 元数据一次成型,更新时永远重新渲染,避免多文件手工维护漂移;
- 进化必须可逆:
update_skill先存档后写入,rollback连回滚本身都再备份一层,最多保留 10 个版本自动瘦身。
想动手验证的话,安装依赖后在仓库根目录执行一次列表命令即可看到效果:
pip3 install -r requirements.txt python3 tools/skill_writer.py --action list --base-dir ./exes下一篇我们将拆解 prompts 目录下的分析提示词工程,看看「共同记忆」与「人物性格」是如何被结构化提取出来的。
【免费下载链接】ex-skill前任 skill项目地址: https://gitcode.com/gh_mirrors/exsk/ex-skill
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考