# Prompt registry

SDK 內建的每一支 LLM system prompt（webagent narrator、planner、InlineAgent、markdown-edit、翻譯、STT 清理、Dwell 分類器）都以穩定 ID 註冊在 **process 級 registry** 上。Host 可以依 locale 覆蓋、加站台級補充規則、或列出目前註冊了哪些——整個 prompt surface 用同一個 API 管。

## 為什麼

v0.2.2 之前，prompt 各自寫在子系統模組裡。想要「日文版 planner prompt」或「InlineAgent 永不改語氣」就得 fork 模組。Registry 是這一切的「單一位置」。

## 存取方式

每個 `DotDotDuck` instance 都有 `dddk.prompts`。它就是 module-level 的 `promptRegistry` 這個 singleton——兩條 import 路徑都行，看你偏好哪種。

```ts
import { autoInstall, promptRegistry, PROMPT_IDS } from '@perhapxin/dddk';

const dddk = autoInstall();

// 慣例路徑——透過 instance
dddk.prompts.list();

// 也可以直接用 singleton
promptRegistry.list();
```

## 已註冊 ID

`PROMPT_IDS` enum 裡的每一個 ID 都有預設。完整列表：

| ID 常數 | 字串值 | 所屬子系統 |
|---|---|---|
| `INLINE_EDIT` | `'inline-edit.system'` | InlineAgent——替換長文中的某段片段 |
| `MARKDOWN_EDIT` | `'markdown-edit.system'` | Markdown 文件編輯工具（Plan runner 用） |
| `VOICE_CLEANUP` | `'voice-cleanup.system'` | Voice STT 清理——把原始逐字稿變成通順文字 |
| `TRANSLATE` | `'translate.system'` | Immersive-translate——頁面內文的即時翻譯 |
| `DWELL_CLASSIFY` | `'dwell-classify.system'` | Dwell 分類器——標註長按的元素並建議動作 |
| `PLANNER` | `'planner.system'` | Planner——為 WebAgent 寫執行計畫 |
| `WebAgent 主 narrator` | `'webagent-narrator.system'` | WebAgent narrator——agentic loop 的全能 prompt |
| `WEBAGENT_COT` | `'webagent-cot.system'` | WebAgent CoT 版——思考模式 narrator |
| `TASK` | `'task.system'` | Task runner——自由格式任務執行 prompt |

執行期用 `dddk.prompts.list()` 列出。

## 覆蓋單一 locale

```ts
dddk.prompts.override('inline-edit.system', 'ja', () => `
あなたはインライン編集アシスタントです。ユーザーが長いテキストの中の断片を選択し、指示を出しました…
`);
```

SDK 解析 prompt 的 fallback 順序：`override(id, currentLocale)` → `override(id, 'en')` → SDK 註冊的預設。所以你可以只做日文本地化，其他 locale 全部吃 SDK 預設。

有 runtime context 的 prompt（planner、webagent narrator、Dwell 分類器），override function 會拿到完整的 context 物件：

```ts
dddk.prompts.override('webagent-narrator.system', 'ja', (ctx, locale) => {
  // ctx 是 AssemblePromptInput——brand、persona、sitemap、session 等。
  return japaneseNarratorRenderer(ctx);
});
```

## 加站台級補充規則

`append()` 把額外內容尾接在解析後的 prompt 尾端。它對每個 locale 都會跑——很適合一句話規則、不用整個 prompt 重寫：

```ts
dddk.prompts.append('webagent-narrator.system', () => `
- 絕不主動提及價格。
- 所有事實引用來源。
- 拒絕跟 Acme 產品無關的請求。
`);
```

多個 `append()` 依註冊順序疊。要重置回 SDK 預設，用 `dddk.prompts.reset(id)`（清 override + appender，SDK 預設保留）。

## 完整 API

```ts
class PromptRegistry {
  registerDefault<Ctx>(id: string, render: (ctx: Ctx, locale: string) => string): void;
  override<Ctx>(id: string, locale: string, render: (ctx: Ctx, locale: string) => string): void;
  append<Ctx>(id: string, extra: (ctx: Ctx, locale: string) => string): void;
  render<Ctx>(id: string, ctx?: Ctx, locale?: string): string;
  reset(id: string): void;
  has(id: string): boolean;
  list(): string[];
}
```

- `registerDefault`——通常不用自己叫；子系統會在 module init 時 register。想 ship 全新的 prompt id（例如 domain-specific 工具），用這個。
- `override(id, locale, render)`——host 最常用。同 `(id, locale)` 後叫的贏。
- `append(id, extra)`——尾接補充內容。會疊。
- `render(id, ctx?, locale?)`——解析成最終字串。`locale` 預設 `'en'`。
- `reset(id)`——清 override + appender。SDK 預設保留。
- `has(id)`——這 id 有註冊嗎（預設或 override）？
- `list()`——所有註冊的 id，字母排序。

## 常見模式

### 多語系上線

```ts
const NARRATOR_JA = (ctx: AssemblePromptInput) => `...`;
const NARRATOR_ES = (ctx: AssemblePromptInput) => `...`;
const NARRATOR_FR = (ctx: AssemblePromptInput) => `...`;

dddk.prompts.override('webagent-narrator.system', 'ja', NARRATOR_JA);
dddk.prompts.override('webagent-narrator.system', 'es', NARRATOR_ES);
dddk.prompts.override('webagent-narrator.system', 'fr', NARRATOR_FR);
// EN + zh-TW 吃 SDK 預設。
```

### 合規層不動 base prompt

```ts
const compliance = () => `
合規需求：
- 絕不揭露使用者個資。
- 拒絕撰寫法律或醫療意見。
- 被問到價格時，回：「請聯絡 sales@acme.com。」
`;

// 把合規規則套到 SDK 每一支面向 LLM 的 prompt。
for (const id of dddk.prompts.list()) {
  dddk.prompts.append(id, compliance);
}
```

### 執行期切 locale

Registry 是在組 message 時才讀、不是在子系統 construction 時。改站台語言，下一輪 agent turn / 下一次 InlineAgent 動作 / 下一次翻譯自動用新 locale 的 prompt——不用重掛、不用 reload。

```ts
document.querySelector('#lang-picker')?.addEventListener('change', (e) => {
  const locale = (e.target as HTMLSelectElement).value;
  dddk.setLocale(locale);
  // 下一次 agent turn 用該 locale 的變體。
});
```

## 不做什麼

Registry 不會：

- 解析或執行 prompt。Runtime message plumbing（system role、temperature、tool schemas）仍在 callsite。
- Cache 解析後的字串。每次 `render(id, ctx, locale)` 都跑一次 renderer——夠便宜每 turn 都重跑。
- 儲存 credentials 或 LLM provider config。那是 `DotDotDuckConfig.llm` / `config.llm.provider` 的事。

## 相關

- [auto-install.md](./auto-install.zh-TW.md)——一行安裝、順便幫你註冊預設。
- [migrating.md](./migrating.zh-TW.md)——從 v0.2.1 採用 registry 的作法。
- [agent/](./agent/)——WebAgent narrator + planner 的 callsite。
