408 lines
15 KiB
Markdown
408 lines
15 KiB
Markdown
我这样每次都确认太麻烦,能不能写个交接文档,如果需要的话。
|
||
我准备重启Cluade,下次启动会加上参数`--permission-mode auto`,这样就不用每次都确认了。
|
||
|
||
但是我还想续着本次的工作。你看我怎么样处理合适,你做哪些?
|
||
|
||
|
||
|
||
# 1
|
||
|
||
```bash
|
||
# 在本地路径下,建一个工作目录,比如`D:\AI\ClaudeCode\voice-ge`,运行powershell,
|
||
|
||
# cli进入工作目录
|
||
cd D:\AI\ClaudeCode\voice-ge
|
||
|
||
# 启动Claude 自动审批模式
|
||
claude --permission-mode auto
|
||
|
||
# 以下是对话内容:
|
||
|
||
```
|
||
|
||
我有一个Mmx CLI的Skill,你看看能不能用?
|
||
|
||
# 2
|
||
|
||
你也用的是minimax,账户是一套。它的这些能力,准确说应该你也有,你能做这些么?
|
||
|
||
# 3
|
||
|
||
那么我准备基于mmx cli开发一个语音生成的系统,或者叫Agent系统。
|
||
|
||
分为环境准备和工作两部分:
|
||
|
||
**环境准备:**
|
||
1. 我指定一个我的语音文件,确定生成的voice id,你调用Mmx 生成这个voice id,记录下来。
|
||
2. 我可能有多个语音,账户下生成多个voice id
|
||
|
||
**工作:**
|
||
1. 有voice id 清单,我选择具体的voice id
|
||
2. 指定一个口播稿路径,基于路径下的文稿,在指定的目标目录下生成语音文件
|
||
1. 可能是一个口播稿,也可能是多个;
|
||
2. 口播稿文件名,需要与口播稿同名,只是后缀不同:口播稿是.md格式,生成的语音文件看能生成什么格式的。
|
||
|
||
**使用方式:**
|
||
1. 是命令行的方式,我可以包装成一个技能,直接在Cluade Code,Codex或者OpenClaw中进行调用。
|
||
|
||
基于以上,你能帮我做一个规划么?
|
||
你的工作目录是`D:\AI\ClaudeCode\voice-ge`,你可以认为这个就是开发用的路径。你的规划,将来的脚本或者过程产物都放在这里。
|
||
|
||
# 4
|
||
|
||
plan我看了,我同意。
|
||
只是一个小小的修正,我本地是有Conda环境的,不要直接用python,在Conda中建一个与本次配套的新环境,及环境清单(如果我想移植到别的机器的话);将来测试跑脚本,都在Conda的环境里。
|
||
|
||
接下来你就要进入开发阶段了么?
|
||
|
||
# 5
|
||
|
||
远程仓库:https://git.wantsong.life/wantsong/voice-generation.git
|
||
我们把这个项目push 到远程仓库里。
|
||
|
||
# 6
|
||
|
||
我输入 `voice-gen init`报错了,下面是执行信息:
|
||
|
||
```bash
|
||
(base) PS C:\Users\wangq> conda activate voice-gen
|
||
(voice-gen) PS C:\Users\wangq> cd D:/AI/ClaudeCode/voice-ge
|
||
(voice-gen) PS D:\AI\ClaudeCode\voice-ge> voice-gen init
|
||
voice-gen : 无法将“voice-gen”项识别为 cmdlet、函数、脚本文件或可运行程序的名称。请检查名称的拼写,如果包括路径,请确
|
||
保路径正确,然后再试一次。
|
||
所在位置 行:1 字符: 1
|
||
+ voice-gen init
|
||
+ ~~~~~~~~~
|
||
+ CategoryInfo : ObjectNotFound: (voice-gen:String) [], CommandNotFoundException
|
||
+ FullyQualifiedErrorId : CommandNotFoundException
|
||
|
||
```
|
||
|
||
# 7
|
||
|
||
我确认一下,我刚执行了:
|
||
|
||
```bash
|
||
(base) PS C:\Users\wangq> conda activate voice-gen
|
||
(voice-gen) PS C:\Users\wangq> cd D:/AI/ClaudeCode/voice-ge
|
||
(voice-gen) PS D:\AI\ClaudeCode\voice-ge> cd D:\Mine\口播
|
||
(voice-gen) PS D:\Mine\口播> voice-gen init
|
||
initialized in D:\Mine\口播
|
||
(voice-gen) PS D:\Mine\口播> voice-gen voices list
|
||
(no voices registered; run `voice-gen voices add <name> <mp3>`)
|
||
(voice-gen) PS D:\Mine\口播>
|
||
```
|
||
|
||
现在通知我没有`no voices registered`,服务器上的已经删过了是么?
|
||
|
||
因为voice id是和minimax cli的账号绑定的,如果我在本机这个路径下做了voice id,那么我在别的机器也用这个账号的话,init时应该能拿下来。
|
||
除了init外,是否应该有一个`pull voice id`的指令,把voice id在本地与服务器同步一下。
|
||
|
||
|
||
# 8
|
||
|
||
现在服务器上的文件都是旧的,需要清掉。
|
||
先清服务器上的旧文件。
|
||
|
||
我在`D:\Mine\口播`环境下,上传新的音频,在新环境里测试。完了再说看需要补充哪些功能。
|
||
|
||
|
||
# 9
|
||
```bash
|
||
voice-gen voices add wantsong ./your-sample.mp3 --notes "anchor"
|
||
```
|
||
|
||
示例命令中的--notes "anchor" 是什么意思,是voice-id么?
|
||
我看你刚给的,又变成了"anchor voice",这个给谁用的,我能填中文么?
|
||
|
||
voiceid默认就是音频文件名对吧?
|
||
|
||
# 10
|
||
|
||
`voice-gen voices add`这个命令需要更新,增加`friendly name`,或者将notes直接改成friendly name。
|
||
|
||
notes对我没用,将来显示`friendly name`,我直接用这个:
|
||
|
||
```bash
|
||
voice-gen voices add wantsong C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a --name "BroTsong-2026-06-10"
|
||
```
|
||
你看下这个命令格式是否正确。
|
||
|
||
json形如:
|
||
|
||
```json
|
||
|
||
"voices": {
|
||
"wantsong": {
|
||
"file_id":396061597295017,
|
||
"filename": "your-sample.mp3",
|
||
"created_at": "2026-06-10T...",
|
||
"friendly_name": "BroTsong-2026-06-10" }
|
||
}
|
||
```
|
||
|
||
你看下我的这个诉求,是否合理,合理的话。
|
||
1. 改程序
|
||
2. 我的这个`D:\Mine\口播`环境需要重新初始化么,还是直接可以继续。
|
||
|
||
|
||
`C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a`
|
||
|
||
# 11
|
||
|
||
`Dict key`干啥用的?
|
||
你的意思比如wantsong类似于张三,可以作为分组,他名下有好多voice;还可以有个李四,名下也好多voice。这个我这里不需要。
|
||
|
||
直接拿 BroTsong-2026-06-10 当 dict key 和 --voice 的入参,不要 wantsong。我开始没明白json里的wantong是啥。这样的话,json里也不用出现`friendly_name`了。
|
||
那么调整完,形如:
|
||
|
||
```bash
|
||
voice-gen voices add wantsong C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a --name "BroTsong-2026-06-10"
|
||
```
|
||
|
||
```json
|
||
|
||
"voices": {
|
||
"BroTsong-2026-06-10": {
|
||
"file_id":396061597295017,
|
||
"filename": "your-sample.mp3",
|
||
"created_at": "2026-06-10T..." }
|
||
}
|
||
```
|
||
|
||
这样就ok了。
|
||
|
||
若对齐思路,你可以做调整了。
|
||
|
||
# 12
|
||
|
||
我执行:
|
||
```bash
|
||
voice-gen voices add BroTsong-2026-06-10 "C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a"
|
||
```
|
||
|
||
报错了:
|
||
|
||
```bash
|
||
(voice-gen) PS D:\Mine\口播> voice-gen voices add BroTsong-2026-06-10 "C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a"
|
||
Traceback (most recent call last):
|
||
File "<frozen runpy>", line 198, in _run_module_as_main
|
||
File "<frozen runpy>", line 88, in _run_code
|
||
File "C:\Users\wangq\miniconda3\envs\voice-gen\Scripts\voice-gen.exe\__main__.py", line 5, in <module>
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 226, in main
|
||
return cmd_voices_add(args)
|
||
^^^^^^^^^^^^^^^^^^^^
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 121, in cmd_voices_add
|
||
info = _upload_voice_file(audio)
|
||
^^^^^^^^^^^^^^^^^^^^^^^^^
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 61, in _upload_voice_file
|
||
raise MmxError(exit_code=result.returncode, stderr=result.stderr, command=" ".join(cmd))
|
||
voice_gen.errors.MmxError: mmx command failed (exit=1): {
|
||
"error": {
|
||
"code": 1,
|
||
"message": "API error: HTTP 404 (HTTP 404)"
|
||
}
|
||
} [cmd: mmx file upload --non-interactive --quiet --output json --file C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a --purpose voice_clone]
|
||
```
|
||
|
||
# 13
|
||
|
||
应该是生成了'BroTsong-2026-06-10' voice,按照你的测试,生成了hello.md
|
||
|
||
然后生成语音报错了
|
||
|
||
```bash
|
||
(voice-gen) PS D:\Mine\口播> voice-gen voices add BroTsong-2026-06-10 "C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a"
|
||
registered 'BroTsong-2026-06-10' → file_id=407449867293232
|
||
(voice-gen) PS D:\Mine\口播> echo "Hello, this is a test." > scripts/hello.md
|
||
(voice-gen) PS D:\Mine\口播> voice-gen gen --voice BroTsong-2026-06-10 scripts
|
||
✗ hello.md: 'utf-8' codec can't decode byte 0xff in position 0: invalid start byte
|
||
done: 0 ok, 1 failed
|
||
```
|
||
|
||
# 14
|
||
|
||
执行后,报错:
|
||
|
||
```md
|
||
(voice-gen) PS D:\Mine\口播> voice-gen gen --voice BroTsong-2026-06-10 scripts
|
||
✗ hello.md: mmx command failed (exit=1): {
|
||
"error": {
|
||
"code": 1,
|
||
"message": "API error: voice id not exist (HTTP 200)"
|
||
}
|
||
} [cmd: mmx speech synthesize --non-interactive --quiet --text Hello, this is a test. --voice 407449867293232 --format mp3 --out output\hello.mp3]
|
||
done: 0 ok, 1 failed
|
||
(voice-gen) PS D:\Mine\口播>
|
||
```
|
||
|
||
# 15
|
||
|
||
报错:
|
||
|
||
```bash
|
||
|
||
(voice-gen) PS D:\Mine\口播> voice-gen voices list
|
||
(no voices registered; run `voice-gen voices add <name> <mp3>`)
|
||
(voice-gen) PS D:\Mine\口播> voice-gen voices add BroTsong-2026-06-10 "C:\Users\wangq\Documents\录音\BroTsong-Saying.m4a"
|
||
Traceback (most recent call last):
|
||
File "<frozen runpy>", line 198, in _run_module_as_main
|
||
File "<frozen runpy>", line 88, in _run_code
|
||
File "C:\Users\wangq\miniconda3\envs\voice-gen\Scripts\voice-gen.exe\__main__.py", line 5, in <module>
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 357, in main
|
||
return cmd_voices_add(args)
|
||
^^^^^^^^^^^^^^^^^^^^
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 248, in cmd_voices_add
|
||
_clone_voice_file(info["file_id"], args.name)
|
||
File "D:\AI\ClaudeCode\voice-ge\src\voice_gen\cli.py", line 197, in _clone_voice_file
|
||
raise MmxError(
|
||
voice_gen.errors.MmxError: mmx command failed (exit=1): voice_clone failed: status_code=2039 msg=voice clone voice id duplicate raw={'input_sensitive': False, 'input_sensitive_type': 0, 'demo_audio': '', 'base_resp': {'status_code': 2039, 'status_msg': 'voice clone voice id duplicate'}} [cmd: curl ... https://api.minimaxi.com/v1/voice_clone]
|
||
```
|
||
|
||
# 16
|
||
|
||
好的测试成功了,拿到了音频。
|
||
提交并push到远程,标记一下,这个是可以跑通的版本。
|
||
|
||
现在应该有一套完整的Voice id的管理,及与服务器同步的命令。我考虑的有以下,不一定准确,你自己判断。
|
||
制订个管理和同步的规划:
|
||
|
||
1. 项目init时,应该去pull一下
|
||
2. 应该在create mp3 前,去pull 一下。若服务器没有,MP3也做不成功。
|
||
3. 功能上,应该有pull命令
|
||
4. 可以由用户指定,删除某个voice id,同步服务器删除。
|
||
5. 与服务器操作前,应该都需要pull,包括add
|
||
|
||
|
||
# 17
|
||
|
||
基于服务器不能删的话,那么我们做个简化
|
||
|
||
- 加 pull 命令
|
||
- 在 add 前做pull,判断是否能加
|
||
|
||
工作范围,我调整如下:
|
||
|
||
##范围| 命令 |状态 |
|
||
|---|---|
|
||
| `voices add` |改:加服务端去重预检 |
|
||
| `voices list` | 不变 |
|
||
| `voices cleanup` | 不变 |
|
||
| **`voices pull`** | 新增|
|
||
| **`voices status`** | 新增 |
|
||
| `gen` | 不变 |
|
||
| `init` | 不变(保持离线)|
|
||
| README + SKILL.md |同步 |
|
||
|
||
`voices status`是做什么的,查服务器端的Voices list?
|
||
如果是的话,可否考虑:
|
||
|
||
- voices list local
|
||
- voices list server
|
||
|
||
为了避免类似的情况,我前面的voiceid其实已经打了时间戳。那么pull下来就好了。不做`voices remove`
|
||
|
||
你觉得呢?
|
||
|
||
# 18
|
||
|
||
我们现在的remove是假的,由于server 不支持,我建议删掉这个命令:`voice-gen voices remove <name>`,不然会造成混淆。
|
||
即,服务器上不支持删,客户端上也不支持。
|
||
|
||
pull 从 filename派生,所以我后续会保持,文件名与voice id一致。那么,我们需要改add:
|
||
1. 形如`voice-gen voices add "C:\Users\wangq\Documents\录音\BroTsong-Saying-2026-06-10.m4a"`,这样没有命名的问题,就用文件名,也不需要重新命名。你add的时候friendly name就从文件名取。
|
||
2. 增加pull
|
||
|
||
前面上传的文件没有日期,后面的文件都会有日期,所以也没问题。
|
||
|
||
你觉得呢?
|
||
|
||
**后续我的测试:**
|
||
这个改完后,
|
||
1. 我重新找个文件夹,初始化`init`,然后pull下来,会有一个从服务器上拉下来的voice,我尝试生成
|
||
2. 我上传一个新的语音文件,尝试生成
|
||
|
||
# 19
|
||
|
||
##
|
||
|
||
你后续测试的预期行为**测试1(新目录 + pull)**:
|
||
```
|
||
powershellmkdir D:\Mine\pull-test;
|
||
cd D:\Mine\pull-test
|
||
voice-gen init # voices.json 是空的(local 也空)
|
||
voice-gen pull # 自动从服务器拉下所有 voice_clone 文件,按 filename派生 voice_id
|
||
|
||
#当前服务端有:BroTsong-Saying.m4a(file_id407449867293232)
|
||
# → 本地会有一个 entry:{"BroTsong-Saying": {file_id:..., filename:"BroTsong-Saying.m4a", ...}}
|
||
voice-gen listvoice-gen gen --voice BroTsong-Saying scripts
|
||
|
||
```
|
||
|
||
**测试2(新文件 add + 生成)**:
|
||
```
|
||
powershell#准备一个带日期的文件,比如 BroTsong-Saying-2026-06-10.m4avoice-gen
|
||
|
||
voices add "C:\path\to\BroTsong-Saying-2026-06-10.m4a"
|
||
|
||
#内部:filename → "BroTsong-Saying-2026-06-10" voice_id# 上传 + voice_clone +存 registryvoice-gen
|
||
list#应该有:BroTsong-Saying-2026-06-10 file_id=... filename=...
|
||
voice-gen gen --voice BroTsong-Saying-2026-06-10 scripts
|
||
```
|
||
|
||
# 20
|
||
|
||
之前,我们传的`BroTsong-Saying.m4a`,voice-id为`BroTsong-2026-06-10`,现在pull下来后默认按文件名走的话,这个名字对应不上了。
|
||
|
||
```bash
|
||
(voice-gen) PS D:\Mine\口播2> voice-gen pull
|
||
usage: voice-gen [-h] {init,voices,gen} ...
|
||
voice-gen: error: argument command: invalid choice: 'pull' (choose from 'init', 'voices', 'gen')
|
||
(voice-gen) PS D:\Mine\口播2> voice-gen voices pull
|
||
file_id=407449867293232 filename=BroTsong-Saying.m4a
|
||
friendly name [BroTsong-Saying]:
|
||
+ 'BroTsong-Saying' file_id=407449867293232 filename=BroTsong-Saying.m4a
|
||
pulled: 1 added
|
||
(voice-gen) PS D:\Mine\口播2> voice-gen gen --voice BroTsong-Saying scripts
|
||
✗ hello.md: mmx command failed (exit=1): {
|
||
"error": {
|
||
"code": 1,
|
||
"message": "API error: voice id not exist (HTTP 200)"
|
||
}
|
||
} [cmd: mmx speech synthesize --non-interactive --quiet --text Hello, this is a test. --voice BroTsong-Saying --format mp3 --out output\hello.mp3]
|
||
done: 0 ok, 1 failed
|
||
(voice-gen) PS D:\Mine\口播2> voice-gen list server
|
||
usage: voice-gen [-h] {init,voices,gen} ...
|
||
voice-gen: error: argument command: invalid choice: 'list' (choose from 'init', 'voices', 'gen')
|
||
(voice-gen) PS D:\Mine\口播2> voice-gen voices list server
|
||
BroTsong-Saying.m4a file_id=407449867293232
|
||
(voice-gen) PS D:\Mine\口播2>
|
||
```
|
||
|
||
上面是我的测试,我觉得服务器上应该有记录friendLy name。
|
||
你检查下api,是否可以查到friendly name。
|
||
完了我们再判断,这里怎么优化。
|
||
|
||
# 21
|
||
|
||
行了,不用改了。先提交并push到远程,标记为完善了voice id命名从文件名取,增加了pull。
|
||
|
||
我在PowerShell里做了测试。那么Cluade code/这里可以按技能调用么?
|
||
|
||
# 22
|
||
|
||
我看到这个目录,放在这里的话,是不是本机都能看见? `C:\Users\wangq\.agents\skills`
|
||
|
||
`C:\Users\wangq\.claude\skills`,cluade的技能其实都引用的是`C:\Users\wangq\.agents\skills`这里的。
|
||
|
||
另外,文件夹有点问题,skill的默认文件夹叫`voice-gen`更合理些,包括技能名字也叫`voice-gen`
|
||
我在新机器上,是否能够利用npx 这样的命令,给一个我的git服务器链接,就能安装技能?
|
||
|
||
所以,如果我说的没问题的话,先回答问题,再处理:
|
||
1. 更新相应的说明文件,配置文件
|
||
2. 删掉 claude skills,将skill放到`.agents/skills/`,`.claude/skills/`里应该只是个引用
|
||
|
||
|
||
|
||
|