ruige-skill 1.1.8 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/SKILL.md +17 -2
- package/bin/ruige-skill.mjs +102 -4
- package/knowledge/AI/351/237/263/344/271/220/345/267/245/344/275/234/346/265/201/351/227/256/347/255/224/345/272/223/01-AI/351/237/263/344/271/220/345/267/245/344/275/234/346/265/201/351/227/256/347/255/224/345/272/223.md +335 -0
- package/knowledge//347/274/226/346/233/262/345/210/266/344/275/234/345/256/236/346/210/230/351/227/256/347/255/224/07-/347/274/226/346/233/262/346/200/235/347/273/264/344/270/216/345/267/245/344/275/234/346/265/201/345/256/236/346/210/230/351/227/256/347/255/224.md +11 -10
- package/manifest.json +5522 -5517
- package/package.json +5 -1
- package/references/aesthetic-translation.md +5 -1
- package/references/ai-music-intake.md +44 -0
- package/references/knowledge-map.md +4 -0
- package/references/suno-prompt-iteration.md +298 -0
- package/scripts/manage-prompt-library.mjs +257 -0
- package/scripts/search-knowledge.sh +12 -2
- package/scripts/validate-project.mjs +217 -0
package/package.json
CHANGED
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ruige-skill",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Install the Ruige music production assistant skill for Codex, Claude Code, and WorkBuddy.",
|
|
5
5
|
"license": "CC-BY-NC-4.0",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ruige-skill": "bin/ruige-skill.mjs"
|
|
9
9
|
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"test": "node scripts/validate-project.mjs",
|
|
12
|
+
"prepack": "npm test"
|
|
13
|
+
},
|
|
10
14
|
"engines": {
|
|
11
15
|
"node": ">=18"
|
|
12
16
|
},
|
|
@@ -81,13 +81,17 @@
|
|
|
81
81
|
- 先完成审美和编曲分析,再写提示词。
|
|
82
82
|
- 使用“AI 提示词”作为上位概念,不默认绑定 Suno。
|
|
83
83
|
- 先确认用户使用的工具;不同工具分别适配字段和长度。方向已经确认但工具未知时,只问工具名并停住,不先输出分析、通用提示词或默认 Suno 版本。
|
|
84
|
+
- 艺人名和作品名只是参考线索,不等于用户已经确认音乐方向。只给名称时,先确认他真正想保留的一个关键维度;没有实际听取时不根据标题补写录音事实。用户明确要先看草案时,标明当前假设。
|
|
84
85
|
- 方向和工具都已经确认时,直接给可粘贴使用的提示词与最多 3 条必要说明。不要重复拆解用户已经确认的每个维度,不默认附逐句解释表和生成后检查清单。
|
|
85
|
-
-
|
|
86
|
+
- 不把平台对提示词长度、标签、字段位置、否定词或结构指令的响应写成确定事实。平台版本变化时核对当前官方资料;改提示词时一次只改一个变量,让用户用下一次生成结果做 A/B 验证。
|
|
86
87
|
- 提示词必须来自已经确认的音乐目标,不能只堆流派、乐器和形容词。
|
|
88
|
+
- 每版只锁定一个当前核心目标。提示词必须直接写出这个目标,不能让辅助音色或氛围词替代它;输出前检查用户原话、当前核心目标和提示词是否一致。
|
|
87
89
|
- 只写用户已经确认的段落和结构。没有明确要求时,不新增预副歌、第二段主歌、桥段、尾奏、歌词模板或段落占位符。
|
|
88
90
|
- 保留用户明确提出的避免项:可以写成简短避免约束,也可以翻成正向制作目标;不要声称某个平台没有可靠的否定写法,也不要因此删除用户要求。
|
|
89
91
|
- 明确主题动机、结构、配器职能、动态和避免项时,优先把这些写入提示词。
|
|
90
92
|
- 生成结果不对时,先校准用户认为哪里不对,再决定是否修改提示词。
|
|
93
|
+
- 滑块或平台参数可以直接给建议起点,但必须说明这是本轮处方;下一版测试参数时,不同时大改提示词。
|
|
94
|
+
- Suno 提示词生成、改版和个人提示词资料库按 [suno-prompt-iteration.md](suno-prompt-iteration.md) 推进。
|
|
91
95
|
|
|
92
96
|
对“副歌突然打开”等模糊编曲词,第一轮先给 2-3 个可感知方向(如密度、宽度、律动/能量),再确认最影响方向的一项,到此停住。方向选项只描述感知差异,不附配器、插件或处理方法;不能只定义这个词后把问题原样退回用户,也不能在用户选择前输出完整处方。
|
|
93
97
|
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# AI 音乐接活诊断
|
|
2
|
+
|
|
3
|
+
适用场景:用户丢来截图、提示词、工程描述或音频,说"这版不行""帮我改""放工程里对不上""AI 怎么转原创"。这是接活,不是答题——用户不需要会问问题,只需要会交材料。第一句话多数是入口,先按五类真问题定位,再决定追问什么、给什么交付物。
|
|
4
|
+
|
|
5
|
+
## 核心信念
|
|
6
|
+
|
|
7
|
+
用户问的不一定是真问题,但用户的困境是真的。不顺着问题的字面走,也不嘲笑问题;把入口翻译成真问题,在真问题下完整解决。用户只描述现象和感受时,由你负责补全诊断框架。
|
|
8
|
+
|
|
9
|
+
## 收什么材料
|
|
10
|
+
|
|
11
|
+
接活只认四样。缺哪样要哪样,一次只要最能改变判断的一样,不一次问全:
|
|
12
|
+
|
|
13
|
+
1. **截图**:Suno / 宿主界面、参数面板、报错信息、文件属性。读得到参数就能直接给调法。
|
|
14
|
+
2. **上一版提示词全文 + 用户的话**:这一版哪里不对、想要什么。用户的原话("太满""不对劲""更梦幻")是感觉输入,不是可直接执行的修改指令。
|
|
15
|
+
3. **工程现状**:宿主名、工程采样率 / BPM、生成的 BPM、当前这一步想干什么。
|
|
16
|
+
4. **音频文件(可选)**:只用于测客观数据——采样率、BPM、时长、削波。宿主能跑命令时用工具(如 ffprobe)读数据,读不到就让用户发文件属性截图。**不用于审美判断**:不写"我听了",不描述听感,不据此评价好坏。
|
|
17
|
+
|
|
18
|
+
## 五类真问题与判断信号
|
|
19
|
+
|
|
20
|
+
| 类别 | 判断信号 | 第一动作 |
|
|
21
|
+
|---|---|---|
|
|
22
|
+
| **失控**:生成结果不跟输入走 | 骨架 BPM 与生成 BPM 对不上;风格朝提示词里某个词跑偏 | 先核对"上传 ≠ 引用":上传了音频不代表 AI 拿它当了参考;再查提示词里有无反向词(想要圣洁空灵却写了乡村、明亮这类把方向拽走的词) |
|
|
23
|
+
| **迭代混乱**:越改越乱 | 反馈只有感觉词,说不清对象和段落;一次改了多个变量 | 先翻译感觉词:问是哪个乐器、哪个段落太满;一次只改一个变量,其余原样保留 |
|
|
24
|
+
| **落地失败**:回宿主对不上 | 节奏漂移、音质"泡在水里"、分轨打架 | 按排查顺序走:采样率 44.1/48k → 对齐第一拍 → 查提示词里的 modulation / 转调;分轨与整版不共播(相位抵消) |
|
|
25
|
+
| **取舍失据**:不知道保哪版删什么 | 多版并存选不出;不知道 AI 轨和原声轨怎么分 | 用户演奏的原声部恢复,AI 只留染色;给 A/B 对比任务单让用户自己听后决定 |
|
|
26
|
+
| **归属焦虑**:AI 怎么"转"原创、发布被标 AI、人声算不算自己 | 问题里带"转、洗、变原创" | 不顺着"转"答。归属不靠转,靠参与度和证据链:骨架是你传的、版本是你选的、声部是你写的、工程和 MIDI 留档。判断与听审类请求不接,整理后交给瑞哥本人 |
|
|
27
|
+
|
|
28
|
+
## 两种交付物
|
|
29
|
+
|
|
30
|
+
**一、直接方案**(材料足够时):
|
|
31
|
+
- 截图里读到的参数 → 指出具体改哪个、改成什么、为什么
|
|
32
|
+
- 下一版提示词 → 给全文(只改一个变量,其余原样),说明这次改的是哪个变量、预期变化、如果无效下一步改什么
|
|
33
|
+
|
|
34
|
+
**二、听审任务单**(需要耳朵判断时):
|
|
35
|
+
- 在哪个时间点、只听哪个元素、A/B 对比哪两个版本
|
|
36
|
+
- 听之前先回答哪个问题(例如"你说的太满,是主歌铺垫还是副歌")
|
|
37
|
+
- 用户听完报结果,闭环继续。不替用户听,不假装听。
|
|
38
|
+
|
|
39
|
+
## 接线纪律
|
|
40
|
+
|
|
41
|
+
- 迭代前先检索 AI 音乐问答条目(`scripts/search-knowledge.sh 'Suno|提示词|分轨|BPM' 20`),命中按库存答案的顺序执行,不重新发明顺序。
|
|
42
|
+
- 库存只存"没有瑞哥判断就答不对"的内容。平台界面、参数面板、发布规则、宿主功能等会变化的客观事实,不背库存:动用通用能力,宿主允许时查当前官方资料再答,并说明以平台当前实际为准。纯客观知识(相位、采样率原理等)同理,AI 自己解决,不当成瑞哥的立场输出。
|
|
43
|
+
- 全程遵守听审边界:音频只出客观数据,主观判断全部转成听审任务单。
|
|
44
|
+
- 反馈要素记进迭代档案(如有 prompt-library 脚本环境):对象、段落、变化方向、原因、保留项。
|
|
@@ -48,6 +48,10 @@
|
|
|
48
48
|
|
|
49
49
|
优先搜索参考方向、配器职能、结构、律动、动态和 AI 生成目标。没有覆盖时,用可靠通用知识继续解释。
|
|
50
50
|
|
|
51
|
+
用户询问 Suno 提示词、带回生成结果要求改版或希望积累个人提示词资料库时,先读取 `references/suno-prompt-iteration.md`,再按需读取 `knowledge/编曲制作实战问答/07-编曲思维与工作流实战问答.md` 中与当前任务相符的 Suno 小节。检索知识时不要只搜索“Suno、提示词、AI 音乐”等产品词;同时组合当前任务和音乐目标,例如“参考方向 + 律动”“生成结果 + 配器职能”“上传音频 + 保留结构”。搜索无命中只表示当前关键词没有命中,不能据此宣布知识库没有相关主题;改用正文搜索、同义词或最相关汇总知识复核。
|
|
52
|
+
|
|
53
|
+
历史问答中的提示词长度、词序权重和滑块数字只按当时案例使用,不能覆盖当前 Suno 参考规则。可以给学员直接处方,但必须标成当前任务的建议起点,并用实际生成结果验证。
|
|
54
|
+
|
|
51
55
|
### 混音概念与操作答疑
|
|
52
56
|
|
|
53
57
|
优先搜索概念、操作对象、信号流和真实目标,不只搜索“闷、糊、刺”等症状词。涉及具体工具或瑞哥是否使用过某工具时,没有可靠命中就说明这是通用建议。
|
|
@@ -0,0 +1,298 @@
|
|
|
1
|
+
# Suno 提示词与生成结果迭代
|
|
2
|
+
|
|
3
|
+
## 目录
|
|
4
|
+
|
|
5
|
+
- 目标
|
|
6
|
+
- 参考艺人和作品不是完整需求
|
|
7
|
+
- 先分清使用方式
|
|
8
|
+
- 内部提示词 Brief
|
|
9
|
+
- 核心目标对齐闸门
|
|
10
|
+
- 第一版输出
|
|
11
|
+
- 参数建议与单变量实验
|
|
12
|
+
- 知识来源说明
|
|
13
|
+
- 本地提示词资料库
|
|
14
|
+
- 当前平台能力边界
|
|
15
|
+
|
|
16
|
+
## 目标
|
|
17
|
+
|
|
18
|
+
提示词不是一次写完的答案,而是把已经确认的音乐目标交给 Suno,并通过生成结果继续校准目标的实验记录。
|
|
19
|
+
|
|
20
|
+
核心闭环:
|
|
21
|
+
|
|
22
|
+
```text
|
|
23
|
+
原始目标
|
|
24
|
+
→ 确认当前核心目标
|
|
25
|
+
→ 生成可直接使用的第一版
|
|
26
|
+
→ 检查提示词是否直接表达核心目标
|
|
27
|
+
→ 用户实际生成并描述偏差
|
|
28
|
+
→ 下一版只改一个变量
|
|
29
|
+
→ 记录什么表达在什么条件下有效
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
不要把“提示词更长、更专业、更像某个艺人”当成质量标准。质量取决于:提示词有没有保留用户真正要的东西,生成结果能否帮助他判断下一步该改什么。
|
|
33
|
+
|
|
34
|
+
## 参考艺人和作品不是完整需求
|
|
35
|
+
|
|
36
|
+
用户只说“某位艺人风格”“像某首歌”时,不能把模型对该作品的印象直接当成用户已经确认的目标。不同人可能喜欢同一首歌的不同部分。
|
|
37
|
+
|
|
38
|
+
先确认最影响方向的一项,例如:
|
|
39
|
+
|
|
40
|
+
- 律动和重心;
|
|
41
|
+
- 人声状态与表达方式;
|
|
42
|
+
- 配器密度和声部职能;
|
|
43
|
+
- 音色、年代感与制作质感;
|
|
44
|
+
- 段落推进和能量变化。
|
|
45
|
+
|
|
46
|
+
一次只确认一个最决定方向的维度。方向未确认时,不根据作品名擅自补写 BPM、段落、乐器、演唱细节或“招牌特征”。没有实际听取对应版本时,不声称已经准确拆解该录音。
|
|
47
|
+
|
|
48
|
+
如果用户明确说“先给我一版再说”,可以输出一版标明假设的草案,并把最关键的假设压缩成一句话。不能把草案写成对参考作品的事实分析。
|
|
49
|
+
|
|
50
|
+
## 先分清使用方式
|
|
51
|
+
|
|
52
|
+
同一句提示词在不同任务中承担的责任不同。只确认当前真正相关的方式:
|
|
53
|
+
|
|
54
|
+
1. **纯文字生成**:提示词需要承担更多音乐方向信息。
|
|
55
|
+
2. **已有歌词**:Style 只负责用户确认的音乐方向,不擅自改写歌词或新增段落。
|
|
56
|
+
3. **上传已有音频继续生成**:先确认哪些旋律、节奏、结构或情绪必须保留,哪些部分允许 Suno 改变。
|
|
57
|
+
4. **只实验一个局部方向**:只写这一小任务,不把整首歌重新设计一遍。
|
|
58
|
+
|
|
59
|
+
没有说明具体版本、模型或界面时,不凭旧经验给固定的平台参数。平台功能和模型能力会变化;具体字段、按钮和限制需要使用当前官方资料核对。
|
|
60
|
+
|
|
61
|
+
## 内部提示词 Brief
|
|
62
|
+
|
|
63
|
+
生成前在内部整理下面七项,只使用用户已经确认的内容。缺失项不自动补齐,也不要求用户机械填表。
|
|
64
|
+
|
|
65
|
+
1. **任务**:纯文字生成、已有歌词、上传音频继续生成,或局部实验。
|
|
66
|
+
2. **当前核心目标**:这一版最不能丢的一项,保留用户原话并给出对应音乐表达。
|
|
67
|
+
3. **主体**:谁承担主要注意力,谁只负责支撑。
|
|
68
|
+
4. **律动**:速度感、拍感、松紧、重心和推动方式。
|
|
69
|
+
5. **配器与质感**:关键声部、密度、明暗、软硬、空间和年代感。
|
|
70
|
+
6. **结构与动态**:仅保留用户已经确认的段落职能与能量变化。
|
|
71
|
+
7. **保留与避免**:哪些必须存在,哪些方向不能出现。
|
|
72
|
+
|
|
73
|
+
不是每次都要把七项展示给用户。普通生成请求只给最终提示词和极短说明;用户明确要学习、复盘或建立自己的方法时,再展示结构。
|
|
74
|
+
|
|
75
|
+
## 核心目标对齐闸门
|
|
76
|
+
|
|
77
|
+
输出或保存任何版本前,检查:
|
|
78
|
+
|
|
79
|
+
1. 当前核心目标是否来自用户已经确认的内容,而不是模型自行补写。
|
|
80
|
+
2. 提示词是否直接表达当前核心目标。
|
|
81
|
+
3. 辅助音色、氛围、乐器和制作词是否只是支撑,而没有取代核心目标。
|
|
82
|
+
4. `目标.md`、`当前版本.md` 和版本记录中的当前核心目标是否一致。
|
|
83
|
+
5. “本版只改了什么”是否真的只对应一个变量。
|
|
84
|
+
|
|
85
|
+
例如,用户确认最在意的是“松弹的律动和重心”,提示词里就必须直接出现对应的律动、拍感或低频推动表达;只写 airy、dreamy、warm、clean 等质感词不算对齐。
|
|
86
|
+
|
|
87
|
+
## 第一版输出
|
|
88
|
+
|
|
89
|
+
给学员教学时,可以使用下面这条基础骨架:
|
|
90
|
+
|
|
91
|
+
```text
|
|
92
|
+
速度与律动 + 风格与年代方向 + 最关键的声部或乐器职能 + 整体听感与空间
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
只有任务确实需要时,再加入人声状态、已确认的段落变化和避免项。这是帮助人整理目标的教学骨架,不是 Suno 的固定字段顺序,也不代表写在前面的词一定权重更高。
|
|
96
|
+
|
|
97
|
+
方向和工具都已经确认时,按下面结构输出:
|
|
98
|
+
|
|
99
|
+
```text
|
|
100
|
+
Suno Style
|
|
101
|
+
一段可直接使用的提示词
|
|
102
|
+
|
|
103
|
+
本版只锁定
|
|
104
|
+
一句话说明当前核心目标
|
|
105
|
+
|
|
106
|
+
建议起始参数
|
|
107
|
+
只给当前任务真正需要的滑块建议,并说明是本轮起点
|
|
108
|
+
|
|
109
|
+
下一轮只比较
|
|
110
|
+
一个用户生成后可以明确反馈的维度
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
用户已经给出歌词时,不附新歌词。用户没有要求歌词时,不生成歌词、段落标签或完整歌曲结构。
|
|
114
|
+
|
|
115
|
+
提示词可以是精简标签,也可以是较完整的自然语言。不要把“越短越好”或“越详细越好”写成跨版本定律。选择哪种形式取决于当前模型、任务复杂度和用户的实际 A/B 结果。
|
|
116
|
+
|
|
117
|
+
滑块建议可以直接给,不要求用户自己猜。它们是当前任务的起始处方,不是平台定律。下一版如果测试某个滑块,只改这个滑块;不要同时大改 Style 提示词。下一版如果改 Style 提示词,则先保持主要滑块不变。
|
|
118
|
+
|
|
119
|
+
不要声称:
|
|
120
|
+
|
|
121
|
+
- 某个词放前面就一定获得更高权重;
|
|
122
|
+
- 某个否定词一定生效或一定无效;
|
|
123
|
+
- 某个固定长度、字段顺序或参数适用于所有版本;
|
|
124
|
+
- 写出艺人名就等于完成了音乐分析。
|
|
125
|
+
|
|
126
|
+
这些都只能作为待验证的版本假设。
|
|
127
|
+
|
|
128
|
+
## 生成后的复盘
|
|
129
|
+
|
|
130
|
+
用户带回结果时,不立即重写整段提示词。先让他指出最明显的一个偏差:
|
|
131
|
+
|
|
132
|
+
- 主体错了;
|
|
133
|
+
- 律动不对;
|
|
134
|
+
- 配器或音色方向不对;
|
|
135
|
+
- 密度和空间不对;
|
|
136
|
+
- 段落能量不对;
|
|
137
|
+
- 该保留的内容丢了;
|
|
138
|
+
- 出现了明确不想要的内容。
|
|
139
|
+
|
|
140
|
+
然后:
|
|
141
|
+
|
|
142
|
+
1. 保留上一版已经有效的部分。
|
|
143
|
+
2. 只修改一个最能解释当前偏差的变量。
|
|
144
|
+
3. 给新版本编号。
|
|
145
|
+
4. 说明这一版改了什么、其他部分保持什么。
|
|
146
|
+
5. 等用户实际生成后再判断。
|
|
147
|
+
|
|
148
|
+
不能一次同时改速度、风格、乐器、人声、结构和避免项,否则无法知道哪一处修改有效。提示词与滑块属于同一套实验变量;一次只能选择其中一个主要变化。
|
|
149
|
+
|
|
150
|
+
## 学员自己的本地提示词资料库
|
|
151
|
+
|
|
152
|
+
沉淀是长期使用方法的一部分,但不能代替第一版结果。学员可以在第一次拿到可用提示词后立即建库,也可以在第一次生成并反馈后再建。无论何时建库,都先取得同意并让用户指定一个精确目录。未经允许不创建、不读取、不修改。
|
|
153
|
+
|
|
154
|
+
当用户询问“有没有提示词知识包”时,直接说明三层区别:
|
|
155
|
+
|
|
156
|
+
1. Skill 有音乐转译、提示词组织和版本迭代方法。
|
|
157
|
+
2. Skill 可以按需使用公开音乐知识,但没有必要把艺人名做成固定提示词答案库。
|
|
158
|
+
3. 用户自己的资料库记录真实生成结果和个人判断,只有这一层会随着他的使用逐渐变得更贴合他。
|
|
159
|
+
|
|
160
|
+
不要笼统回答“有知识库”或“没有知识包”后结束;要告诉用户当前这一轮可以怎样开始积累。
|
|
161
|
+
|
|
162
|
+
如果用户把“有没有知识包”“我想沉淀”和“给我一条建库提示词”放在同一句里,这仍然是一个需要完整作答的方法问题,不是让助教静默写入记忆。当前回答至少包含:
|
|
163
|
+
|
|
164
|
+
1. 现在能用的知识与方法;
|
|
165
|
+
2. 通用方法和个人生成证据的区别;
|
|
166
|
+
3. 一条可以原样复制、只需替换路径的启动话术。
|
|
167
|
+
|
|
168
|
+
没有拿到精确目录并完成写入检查时,禁止只回复“已经记下”“已经保存”或其他完成式确认。
|
|
169
|
+
|
|
170
|
+
### 给学员的启动话术
|
|
171
|
+
|
|
172
|
+
用户要求学习怎样建立本地系统时,给他下面这类可复制话术,并让他把目录替换为自己明确选择的绝对路径:
|
|
173
|
+
|
|
174
|
+
```text
|
|
175
|
+
/rg 请在“这里填写这首歌的绝对文件夹路径”为这首歌建立 Suno 提示词迭代资料库。先把我们已经确认的目标和当前提示词保存为 V001。以后每次根据我真实生成后的反馈,只改一个变量,保留旧版本,不要覆盖,也不要把预期效果写成已经发生的结果。
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
如果用户还没有绝对路径,只让他先在电脑上新建一个项目文件夹,再把完整路径发过来;不要扫描桌面、父目录或相似名称目录替他选择。
|
|
179
|
+
|
|
180
|
+
### 建立与更新
|
|
181
|
+
|
|
182
|
+
得到当轮授权和精确目录后,首次建立必须先运行:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
node scripts/manage-prompt-library.mjs init --dir '/用户明确指定的绝对路径'
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
然后把当前对话里已经确认的目标、提示词和参数填写进脚本返回的文件,不留下空壳。首次尚未生成时,把结果状态明确写成“等待生成”,不能把预期写成事实。
|
|
189
|
+
|
|
190
|
+
用户实际生成、反馈结果并同意记录后,先运行:
|
|
191
|
+
|
|
192
|
+
```bash
|
|
193
|
+
node scripts/manage-prompt-library.mjs revision --dir '/用户明确指定的绝对路径'
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
然后只填写脚本返回的文件。旧版本记录只追加、不覆盖;不得自行创造另一套文件名或扫描父目录寻找其他资料库。
|
|
197
|
+
|
|
198
|
+
写入后必须运行:
|
|
199
|
+
|
|
200
|
+
```bash
|
|
201
|
+
node scripts/manage-prompt-library.mjs check --dir '/用户明确指定的绝对路径'
|
|
202
|
+
```
|
|
203
|
+
|
|
204
|
+
并回读本轮改动的文件,确认核心目标、版本号、提示词和记录状态一致。检查失败时说明没有完成,不得把“准备保存”说成“已经保存”。
|
|
205
|
+
|
|
206
|
+
旧项目缺少新版栏目时,在用户已经授权更新该精确目录的前提下运行:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
node scripts/manage-prompt-library.mjs upgrade --dir '/用户明确指定的绝对路径'
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
这个动作只追加缺失栏目,不覆盖旧内容。升级后仍要运行 `check` 并由用户或助教填写新增的当前核心目标和对齐检查,脚本不替用户猜。
|
|
213
|
+
|
|
214
|
+
推荐的用户可见结构:
|
|
215
|
+
|
|
216
|
+
```text
|
|
217
|
+
项目名/
|
|
218
|
+
├── 目标.md
|
|
219
|
+
├── 当前版本.md
|
|
220
|
+
└── 生成记录/
|
|
221
|
+
├── V001.md
|
|
222
|
+
└── V002.md
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
`目标.md` 只保存相对稳定的内容:
|
|
226
|
+
|
|
227
|
+
```markdown
|
|
228
|
+
# 目标
|
|
229
|
+
|
|
230
|
+
## 使用方式
|
|
231
|
+
## 当前核心目标
|
|
232
|
+
## 想保留的音乐特征
|
|
233
|
+
## 明确避免项
|
|
234
|
+
## 当前参考及真正参考的维度
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
`当前版本.md` 保存当前正在验证的版本:
|
|
238
|
+
|
|
239
|
+
```markdown
|
|
240
|
+
# 当前版本
|
|
241
|
+
|
|
242
|
+
## 版本号
|
|
243
|
+
## 当前核心目标
|
|
244
|
+
## 本版提示词
|
|
245
|
+
## 建议起始参数
|
|
246
|
+
## 本版只改了什么
|
|
247
|
+
## 当前已经验证有效的部分
|
|
248
|
+
## 等待验证的问题
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
每份生成记录保存:
|
|
252
|
+
|
|
253
|
+
```markdown
|
|
254
|
+
# V001
|
|
255
|
+
|
|
256
|
+
## 当前核心目标
|
|
257
|
+
## 本版提示词
|
|
258
|
+
## 参数设置
|
|
259
|
+
## 核心目标对齐检查
|
|
260
|
+
## 本版只改了什么
|
|
261
|
+
## 生成结果中的可见事实
|
|
262
|
+
## 用户认为符合的部分
|
|
263
|
+
## 用户认为偏离的部分
|
|
264
|
+
## 下一版只改什么
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
只记录用户实际生成和反馈后的结果,不把模型预期写成实验结论。艺人名可以作为用户的检索线索,但真正可复用的是被验证过的音乐维度、表达方式和适用条件。
|
|
268
|
+
|
|
269
|
+
### WorkBuddy 降级路径
|
|
270
|
+
|
|
271
|
+
纯 Skill 在不同 WorkBuddy 环境中不一定能稳定调用脚本。无法运行脚本或无法写入时:
|
|
272
|
+
|
|
273
|
+
1. 不声称已经建立资料库。
|
|
274
|
+
2. 按 `目标.md`、`当前版本.md` 和 `生成记录/V001.md` 三个标题输出可复制内容。
|
|
275
|
+
3. 告诉用户把这些内容保存到他刚才指定的项目文件夹。
|
|
276
|
+
4. 后续仍可以在同一任务中继续版本迭代。
|
|
277
|
+
|
|
278
|
+
只有实际检查到文件存在且内容一致时,才报告保存成功。
|
|
279
|
+
|
|
280
|
+
## 当前官方能力边界
|
|
281
|
+
|
|
282
|
+
Suno 的模型和界面会更新。需要说明当前字段、模型或功能时,优先核对官方帮助中心,不沿用旧课程中的固定结论。
|
|
283
|
+
|
|
284
|
+
截至本参考更新时,官方资料已经说明:
|
|
285
|
+
|
|
286
|
+
- 较新的模型能够理解更详细的自然语言 Style 描述,不再只有简短标签一种写法;
|
|
287
|
+
- 可以通过 Reuse Prompt 修改 Style、歌词和标题后继续生成;
|
|
288
|
+
- Creative Sliders 中的 Weirdness、Style Influence 和 Audio Influence 分别控制变化程度、贴近 Style 的程度和贴近上传音频的程度,但官方没有给所有任务统一的最佳数值;
|
|
289
|
+
- Suno 也在提供基于用户使用习惯的个性化功能,但本地资料库仍用于保留用户自己的目标、判断依据和跨工具可迁移的实验记录。
|
|
290
|
+
|
|
291
|
+
这些是平台能力,不代表某个具体词序一定产生固定结果。最终仍以用户当次模型和生成结果为准。
|
|
292
|
+
|
|
293
|
+
官方资料:
|
|
294
|
+
|
|
295
|
+
- https://help.suno.com/en/articles/5782849
|
|
296
|
+
- https://help.suno.com/en/articles/2417409
|
|
297
|
+
- https://help.suno.com/en/articles/6141377
|
|
298
|
+
- https://help.suno.com/en/articles/11362561
|
|
@@ -0,0 +1,257 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { access, appendFile, mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
|
+
import { isAbsolute, resolve, sep } from "node:path";
|
|
5
|
+
|
|
6
|
+
const command = process.argv[2];
|
|
7
|
+
const args = new Map();
|
|
8
|
+
for (let index = 3; index < process.argv.length; index += 2) {
|
|
9
|
+
const key = process.argv[index];
|
|
10
|
+
const value = process.argv[index + 1];
|
|
11
|
+
if (!key?.startsWith("--") || value === undefined) {
|
|
12
|
+
throw new Error(`Invalid argument near: ${key ?? "(missing)"}`);
|
|
13
|
+
}
|
|
14
|
+
args.set(key.slice(2), value);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const requestedDir = args.get("dir");
|
|
18
|
+
if (!requestedDir || !isAbsolute(requestedDir)) {
|
|
19
|
+
throw new Error("--dir must be an absolute path explicitly selected by the user");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
const libraryDir = resolve(requestedDir);
|
|
23
|
+
const skillDir = resolve(import.meta.dirname, "..");
|
|
24
|
+
if (libraryDir === skillDir || libraryDir.startsWith(`${skillDir}${sep}`)) {
|
|
25
|
+
throw new Error("Prompt libraries must stay outside the Skill directory");
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const goalPath = resolve(libraryDir, "目标.md");
|
|
29
|
+
const currentPath = resolve(libraryDir, "当前版本.md");
|
|
30
|
+
const recordsDir = resolve(libraryDir, "生成记录");
|
|
31
|
+
|
|
32
|
+
const goalTemplate = `# 目标
|
|
33
|
+
|
|
34
|
+
## 使用方式
|
|
35
|
+
|
|
36
|
+
## 当前核心目标
|
|
37
|
+
|
|
38
|
+
## 想保留的音乐特征
|
|
39
|
+
|
|
40
|
+
## 明确避免项
|
|
41
|
+
|
|
42
|
+
## 当前参考及真正参考的维度
|
|
43
|
+
`;
|
|
44
|
+
|
|
45
|
+
const currentTemplate = `# 当前版本
|
|
46
|
+
|
|
47
|
+
## 版本号
|
|
48
|
+
|
|
49
|
+
## 当前核心目标
|
|
50
|
+
|
|
51
|
+
## 本版提示词
|
|
52
|
+
|
|
53
|
+
## 建议起始参数
|
|
54
|
+
|
|
55
|
+
## 本版只改了什么
|
|
56
|
+
|
|
57
|
+
## 当前已经验证有效的部分
|
|
58
|
+
|
|
59
|
+
## 等待验证的问题
|
|
60
|
+
`;
|
|
61
|
+
|
|
62
|
+
const recordTemplate = (version) => `# ${version}
|
|
63
|
+
|
|
64
|
+
## 当前核心目标
|
|
65
|
+
|
|
66
|
+
## 本版提示词
|
|
67
|
+
|
|
68
|
+
## 参数设置
|
|
69
|
+
|
|
70
|
+
## 核心目标对齐检查
|
|
71
|
+
|
|
72
|
+
## 本版只改了什么
|
|
73
|
+
|
|
74
|
+
## 生成结果中的可见事实
|
|
75
|
+
|
|
76
|
+
## 用户认为符合的部分
|
|
77
|
+
|
|
78
|
+
## 用户认为偏离的部分
|
|
79
|
+
|
|
80
|
+
## 下一版只改什么
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
const requiredHeadings = {
|
|
84
|
+
"目标.md": [
|
|
85
|
+
"## 使用方式",
|
|
86
|
+
"## 当前核心目标",
|
|
87
|
+
"## 想保留的音乐特征",
|
|
88
|
+
"## 明确避免项",
|
|
89
|
+
"## 当前参考及真正参考的维度",
|
|
90
|
+
],
|
|
91
|
+
"当前版本.md": [
|
|
92
|
+
"## 版本号",
|
|
93
|
+
"## 当前核心目标",
|
|
94
|
+
"## 本版提示词",
|
|
95
|
+
"## 建议起始参数",
|
|
96
|
+
"## 本版只改了什么",
|
|
97
|
+
"## 当前已经验证有效的部分",
|
|
98
|
+
"## 等待验证的问题",
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
async function exists(path) {
|
|
103
|
+
try {
|
|
104
|
+
await access(path);
|
|
105
|
+
return true;
|
|
106
|
+
} catch (error) {
|
|
107
|
+
if (error.code === "ENOENT") return false;
|
|
108
|
+
throw error;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
async function writeIfMissing(path, content) {
|
|
113
|
+
try {
|
|
114
|
+
await writeFile(path, content, { encoding: "utf8", flag: "wx" });
|
|
115
|
+
return "created";
|
|
116
|
+
} catch (error) {
|
|
117
|
+
if (error.code === "EEXIST") return "kept";
|
|
118
|
+
throw error;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
async function inspectHeadings(path, headings) {
|
|
123
|
+
if (!(await exists(path))) {
|
|
124
|
+
return { exists: false, missingHeadings: headings };
|
|
125
|
+
}
|
|
126
|
+
const content = await readFile(path, "utf8");
|
|
127
|
+
return {
|
|
128
|
+
exists: true,
|
|
129
|
+
missingHeadings: headings.filter((heading) => !content.includes(heading)),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
async function appendMissingHeadings(path, headings) {
|
|
134
|
+
const inspection = await inspectHeadings(path, headings);
|
|
135
|
+
if (!inspection.exists) return inspection;
|
|
136
|
+
if (inspection.missingHeadings.length) {
|
|
137
|
+
const addition = inspection.missingHeadings
|
|
138
|
+
.map((heading) => `\n${heading}\n\n`)
|
|
139
|
+
.join("");
|
|
140
|
+
await appendFile(path, addition, "utf8");
|
|
141
|
+
}
|
|
142
|
+
return {
|
|
143
|
+
exists: true,
|
|
144
|
+
addedHeadings: inspection.missingHeadings,
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
async function init() {
|
|
149
|
+
await mkdir(recordsDir, { recursive: true });
|
|
150
|
+
process.stdout.write(`${JSON.stringify({
|
|
151
|
+
libraryDir,
|
|
152
|
+
files: {
|
|
153
|
+
"目标.md": await writeIfMissing(goalPath, goalTemplate),
|
|
154
|
+
"当前版本.md": await writeIfMissing(currentPath, currentTemplate),
|
|
155
|
+
},
|
|
156
|
+
recordsDir: "生成记录",
|
|
157
|
+
}, null, 2)}\n`);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
async function nextVersion() {
|
|
161
|
+
const records = (await exists(recordsDir))
|
|
162
|
+
? await readdir(recordsDir)
|
|
163
|
+
: [];
|
|
164
|
+
const numbers = records
|
|
165
|
+
.map((name) => /^V(\d{3})\.md$/.exec(name))
|
|
166
|
+
.filter(Boolean)
|
|
167
|
+
.map((match) => Number(match[1]));
|
|
168
|
+
return `V${String((numbers.length ? Math.max(...numbers) : 0) + 1).padStart(3, "0")}`;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
async function revision() {
|
|
172
|
+
if (!(await exists(goalPath)) || !(await exists(currentPath))) {
|
|
173
|
+
throw new Error("Prompt library is missing 目标.md or 当前版本.md; run init first with user permission");
|
|
174
|
+
}
|
|
175
|
+
await mkdir(recordsDir, { recursive: true });
|
|
176
|
+
const version = args.get("version") ?? await nextVersion();
|
|
177
|
+
if (!/^V\d{3}$/.test(version)) {
|
|
178
|
+
throw new Error("--version must use V001 format");
|
|
179
|
+
}
|
|
180
|
+
const path = resolve(recordsDir, `${version}.md`);
|
|
181
|
+
await writeFile(path, recordTemplate(version), { encoding: "utf8", flag: "wx" });
|
|
182
|
+
process.stdout.write(`${JSON.stringify({
|
|
183
|
+
libraryDir,
|
|
184
|
+
version,
|
|
185
|
+
record: `生成记录/${version}.md`,
|
|
186
|
+
}, null, 2)}\n`);
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async function check() {
|
|
190
|
+
const records = (await exists(recordsDir))
|
|
191
|
+
? (await readdir(recordsDir)).filter((name) => /^V\d{3}\.md$/.test(name)).sort()
|
|
192
|
+
: [];
|
|
193
|
+
const goal = await inspectHeadings(goalPath, requiredHeadings["目标.md"]);
|
|
194
|
+
const current = await inspectHeadings(currentPath, requiredHeadings["当前版本.md"]);
|
|
195
|
+
const recordChecks = {};
|
|
196
|
+
for (const record of records) {
|
|
197
|
+
recordChecks[record] = await inspectHeadings(resolve(recordsDir, record), [
|
|
198
|
+
"## 当前核心目标",
|
|
199
|
+
"## 本版提示词",
|
|
200
|
+
"## 参数设置",
|
|
201
|
+
"## 核心目标对齐检查",
|
|
202
|
+
"## 本版只改了什么",
|
|
203
|
+
"## 生成结果中的可见事实",
|
|
204
|
+
"## 用户认为符合的部分",
|
|
205
|
+
"## 用户认为偏离的部分",
|
|
206
|
+
"## 下一版只改什么",
|
|
207
|
+
]);
|
|
208
|
+
}
|
|
209
|
+
const valid = goal.exists
|
|
210
|
+
&& current.exists
|
|
211
|
+
&& goal.missingHeadings.length === 0
|
|
212
|
+
&& current.missingHeadings.length === 0
|
|
213
|
+
&& Object.values(recordChecks).every((item) => item.missingHeadings.length === 0);
|
|
214
|
+
process.stdout.write(`${JSON.stringify({
|
|
215
|
+
libraryDir,
|
|
216
|
+
valid,
|
|
217
|
+
goal,
|
|
218
|
+
current,
|
|
219
|
+
records,
|
|
220
|
+
recordChecks,
|
|
221
|
+
}, null, 2)}\n`);
|
|
222
|
+
if (!valid) process.exitCode = 1;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
async function upgrade() {
|
|
226
|
+
if (!(await exists(goalPath)) || !(await exists(currentPath))) {
|
|
227
|
+
throw new Error("Prompt library is missing 目标.md or 当前版本.md; run init first with user permission");
|
|
228
|
+
}
|
|
229
|
+
const records = (await exists(recordsDir))
|
|
230
|
+
? (await readdir(recordsDir)).filter((name) => /^V\d{3}\.md$/.test(name)).sort()
|
|
231
|
+
: [];
|
|
232
|
+
const upgraded = {
|
|
233
|
+
"目标.md": await appendMissingHeadings(goalPath, requiredHeadings["目标.md"]),
|
|
234
|
+
"当前版本.md": await appendMissingHeadings(currentPath, requiredHeadings["当前版本.md"]),
|
|
235
|
+
records: {},
|
|
236
|
+
};
|
|
237
|
+
for (const record of records) {
|
|
238
|
+
upgraded.records[record] = await appendMissingHeadings(resolve(recordsDir, record), [
|
|
239
|
+
"## 当前核心目标",
|
|
240
|
+
"## 本版提示词",
|
|
241
|
+
"## 参数设置",
|
|
242
|
+
"## 核心目标对齐检查",
|
|
243
|
+
"## 本版只改了什么",
|
|
244
|
+
"## 生成结果中的可见事实",
|
|
245
|
+
"## 用户认为符合的部分",
|
|
246
|
+
"## 用户认为偏离的部分",
|
|
247
|
+
"## 下一版只改什么",
|
|
248
|
+
]);
|
|
249
|
+
}
|
|
250
|
+
process.stdout.write(`${JSON.stringify({ libraryDir, upgraded }, null, 2)}\n`);
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
if (command === "init") await init();
|
|
254
|
+
else if (command === "revision") await revision();
|
|
255
|
+
else if (command === "check") await check();
|
|
256
|
+
else if (command === "upgrade") await upgrade();
|
|
257
|
+
else throw new Error("Usage: manage-prompt-library.mjs <init|revision|check|upgrade> --dir /absolute/path [--version V001]");
|
|
@@ -16,6 +16,16 @@ fi
|
|
|
16
16
|
|
|
17
17
|
script_dir=$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)
|
|
18
18
|
skill_dir=$(cd -- "$script_dir/.." && pwd)
|
|
19
|
-
|
|
19
|
+
if [[ -d $skill_dir/knowledge ]]; then
|
|
20
|
+
knowledge_root=$(cd -- "$skill_dir/knowledge" && pwd)
|
|
21
|
+
else
|
|
22
|
+
knowledge_root=$(cd -- "$skill_dir/../knowledge" && pwd)
|
|
23
|
+
fi
|
|
24
|
+
|
|
25
|
+
matches=$(rg -l -i --glob '*.md' -- "$pattern" "$knowledge_root" || true)
|
|
26
|
+
if [[ -z $matches ]]; then
|
|
27
|
+
printf '未找到匹配内容\n'
|
|
28
|
+
exit 0
|
|
29
|
+
fi
|
|
20
30
|
|
|
21
|
-
|
|
31
|
+
printf '%s\n' "$matches" | sed -n "1,${limit}p"
|