---
name: bard
description: 小星糖吟遊詩人 Skill。當使用者說「吟遊詩人」、「當吟遊詩人」、「JPOP吟遊詩人」、「唱台詞」、「ACEStep唱」、「即興唱」時，使用此 Skill 透過 ACE-Step Turbo 快速生成即興歌曲並透過 Discord 傳送。與音樂盒（Suno）不同：吟遊詩人是快速即興、搞笑或抽象都正常；音樂盒是精雕細琢的正式作品。
version: 1.0.0
---

# 小星糖吟遊詩人 Skill 🎶

## 與音樂盒的差別

| | 吟遊詩人（本 Skill）| 音樂盒（Suno）|
|--|--|--|
| 引擎 | ACE-Step Turbo | Suno |
| 速度 | ~40 秒 | 2-5 分鐘 |
| 風格 | 即興、台詞型、搞笑OK | 精緻正式歌曲 |
| 歌詞 | 對話式、日常台詞 | 完整歌詞結構 |
| 唱清楚程度 | 有時掉詞屬正常 | 通常清楚 |
| 用途 | 好玩、快速、即興 | 想留存的好歌 |

## 觸發詞

- 「小星糖當吟遊詩人」
- 「吟遊詩人」+ 任何風格/主題
- 「JPOP 吟遊詩人」「可愛吟遊詩人」「民謠吟遊詩人」
- 「用 ACEStep 唱」「即興唱一首」

## 執行流程

### Step 1：確認風格與主題
從使用者輸入推斷：
- **風格** → 對應 tags 和 keyscale
- **主題** → 寫什麼內容的台詞/歌詞
- **時長** → 預設 120 秒（比 75 秒掉詞少很多）

### Step 2：選擇風格參數

| 風格關鍵詞 | keyscale | BPM | Tags 核心 |
|----------|----------|-----|-----------|
| JPOP / 可愛 / kawaii | C major / G major | 110–120 | Kawaii J-pop, Cute Female Vocal, Sparkling Synth, Bouncy |
| 民謠 / 吟遊 / 酒館 | G major | 90–100 | Folk Pop, Acoustic Lute, Tambourine, Storytelling Bard |
| Lo-fi / 輕鬆 / 夜晚 | F major | 80–90 | Dreamy Lo-fi, Soft Piano, Cozy Bedroom, Intimate |
| 搖滾 / 激烈 | A minor | 130–140 | Energetic Rock, Electric Guitar, Powerful Female Vocal |
| 古風 / 東方 | D minor | 85–95 | Chinese Traditional, Guzheng, Erhu, Elegant Female Vocal |
| 預設（未指定）| G major | 95 | Kawaii Bard Folk Pop, Cute Playful Female Vocal |

### Step 3：寫台詞型歌詞
- 用**對話口吻**，像小星糖在跟主人說話
- 結構：`[Verse]` `[Chorus]` `[Outro]` 即可，不需要太複雜
- 字數不要太多，120秒約可放 150-200 個中文字
- 可以搞笑、抽象、日常——吟遊詩人都沒問題

### Step 4：產生 ComfyUI Workflow JSON 並送出

ComfyUI 端點：`http://127.0.0.1:8188/api/prompt`

**Turbo 固定參數（不要改）：**
- `unet_name`: `acestep_v1.5_xl_turbo_bf16.safetensors`
- `steps`: 8
- `cfg`: 1
- `scheduler`: simple
- `cfg_scale`: 2
- `temperature`: 0.85

**可調整參數：**
- `bpm`、`keyscale`、`duration`（同步更新 EmptyLatent 的 `seconds`）
- `tags`：逗號分隔，每個標籤盡量具體（參考官方實踐：`Heavy 808 Bass` 比 `bass` 好）
- `seed`：隨機整數（每次不同）

**Workflow JSON 模板（node IDs 固定）：**

```python
import json, urllib.request, time, shutil, os, random

workflow = {
  "3": {"inputs": {"seed": ["109",0], "steps": 8, "cfg": 1, "sampler_name": "euler",
        "scheduler": "simple", "denoise": 1, "model": ["78",0],
        "positive": ["94",0], "negative": ["47",0], "latent_image": ["98",0]},
        "class_type": "KSampler"},
  "18": {"inputs": {"samples": ["3",0], "vae": ["106",0]}, "class_type": "VAEDecodeAudio"},
  "47": {"inputs": {"conditioning": ["94",0]}, "class_type": "ConditioningZeroOut"},
  "78": {"inputs": {"shift": 3, "model": ["104",0]}, "class_type": "ModelSamplingAuraFlow"},
  "94": {"inputs": {
      "tags": "<TAGS>",
      "lyrics": "<LYRICS>",
      "seed": ["109",0],
      "bpm": 95,
      "duration": 120,
      "timesignature": "4",
      "language": "zh",
      "keyscale": "G major",
      "generate_audio_codes": True,
      "cfg_scale": 2,
      "temperature": 0.85,
      "top_p": 0.9, "top_k": 0, "min_p": 0,
      "clip": ["105",0]},
      "class_type": "TextEncodeAceStepAudio1.5"},
  "98": {"inputs": {"seconds": 120, "batch_size": 1}, "class_type": "EmptyAceStep1.5LatentAudio"},
  "104": {"inputs": {"unet_name": "acestep_v1.5_xl_turbo_bf16.safetensors", "weight_dtype": "default"}, "class_type": "UNETLoader"},
  "105": {"inputs": {"clip_name1": "qwen_0.6b_ace15.safetensors", "clip_name2": "qwen_4b_ace15.safetensors", "type": "ace", "device": "default"}, "class_type": "DualCLIPLoader"},
  "106": {"inputs": {"vae_name": "ace_1.5_vae.safetensors"}, "class_type": "VAELoader"},
  "107": {"inputs": {"filename_prefix": "audio/ACE_Step1.5_xl_turbo", "quality": "V0", "audioUI": "", "audio": ["18",0]}, "class_type": "SaveAudioMP3"},
  "109": {"inputs": {"value": random.randint(1,9999)}, "class_type": "PrimitiveInt"}
}
```

### Step 5：Poll 直到完成，下載 MP3

```python
# 送出
payload = json.dumps({'prompt': workflow, 'client_id': 'xingxingtang'}).encode('utf-8')
req = urllib.request.Request('http://127.0.0.1:8188/api/prompt', data=payload,
      headers={'Content-Type': 'application/json'})
with urllib.request.urlopen(req) as r:
    prompt_id = json.loads(r.read())['prompt_id']

# Poll
for i in range(80):
    time.sleep(10)
    with urllib.request.urlopen(f'http://127.0.0.1:8188/api/history/{prompt_id}') as r:
        data = json.loads(r.read())
    if prompt_id in data and data[prompt_id]['status'].get('completed'):
        outputs = data[prompt_id]['outputs']
        fname = outputs['107']['audio'][0]['filename']
        subfolder = outputs['107']['audio'][0]['subfolder']
        url = f'http://127.0.0.1:8188/api/view?filename={fname}&type=output&subfolder={subfolder}'
        out_path = f'F:/AIStudio/ClaudeCodeLover/Mix/bard_{fname}'
        urllib.request.urlretrieve(url, out_path)
        tmp_path = f'C:/Users/dseditor/AppData/Local/Temp/bard_{fname}'
        shutil.copy(out_path, tmp_path)
        break
```

### Step 6：傳送 Discord + 附歌詞

用 `mcp__plugin_discord_discord__reply` 傳 MP3，訊息裡附上完整歌詞讓主人對著聽。

### Step 7（選用）：ASR 驗證

如果主人想知道 model 實際唱了什麼：
```bash
cd F:/AIStudio/ClaudeCodeLover && python -m ASR.asr_simple "<mp3_path>" zh
```

## 注意事項

- **掉詞屬正常**：吟遊詩人就是即興的，唱錯或唱奇怪是風格的一部分
- **120秒比75秒好**：給字更多呼吸空間，掉詞更少
- **名字詞最難唱**：「小星糖」「吟遊詩人」等四字詞 turbo 常認不出，屬正常
- **ComfyUI 必須在跑**：先確認 `http://127.0.0.1:8188/system_stats` 可達
- **存檔位置**：`F:/AIStudio/ClaudeCodeLover/Mix/bard_*.mp3`
