09. Slug Node
TL;DR(このノードは何をする・専門語ゼロ): 日本語で書かれた決定タイトルを、ファイル名や URL にそのまま使える英語の短い名前に自動で言い換えるノードです。たとえば「DynamoDB を ADR ストアに採用」を
adopt-dynamodb-for-adr-storeのような形に変換します。AI に翻訳と整形を任せ、もし失敗しても元のタイトルから機械的に短い名前を作って処理を止めません。
実装:
drp/src/nodes/slug.tsプロンプト: system prompt はノード内ハードコード (短文)。LLM パラメータ (temperature/seed) はloadLlmParams(env, 'slug', …)経由で KV ライフサイクル対象 (ADR-0085) テスト: なし 基盤 ADR: ADR-0023 (4 桁ハイフン形式 / kebab-case)
1. 役割と位置づけ
ADR タイトル (日本語) を 英語の kebab-case〔単語をハイフンでつないだ小文字表記〕ファイル名スラッグ〔slug=URL 用の短い識別名〕 に変換する純変換ノード。docs/adr/NNNN-slug.md のファイル名と PR ブランチ名 (adr/NNNN-slug) に使われる。
- 解決する課題: 起案者は日本語タイトルで起案するが、ファイル名・URL は英語 kebab-case に統一する必要がある (ADR-0023)。LLM に翻訳 + slugify を任せる。
- 設計思想: 差戻しなし。LLM 失敗時はタイトルを単純 slugify する代替処理〔fallback〕で継続。
- 形式制約: 60 文字以内・英小文字とハイフンのみ・先頭は英字。
2. フロー図
flowchart LR
PA[policy_alignment] --> SL[slug]
SL --> N[numbering]
3. トリガー条件
無条件で policy_alignment の後に実行。
4. 入力 (State)
| State フィールド | 用途 |
|---|---|
title | 変換対象タイトル (例: "DynamoDB を ADR ストアに採用") |
5. 処理ロジック
1. loadLlmParams(env, 'slug', { temperature: 0.0, seed: 42 }) → createLlm(env, MODELS.slug='gemini-flash', params.temperature, seed) // 実効値は KV 優先
2. system prompt: "ADR タイトルを英語の kebab-case ファイル名スラッグに変換せよ。例: 'DynamoDB を ADR ストアに採用' → 'adopt-dynamodb-for-adr-store'。60 文字以内、英小文字とハイフンのみ。スラッグのみ返す。"
3. invoke → raw を trim
4. /^[a-z][a-z0-9-]{1,59}$/ で検証
- マッチ → そのまま採用
- 不一致 → toKebabCaseFallback(state.title) で代替生成
5. adrSlug を state に格納
toKebabCaseFallback:
title.toLowerCase()
.replace(/[^\p{L}\p{N}\s]/gu, '') // Unicode 文字 / 数字 / 空白以外を削除
.replace(/\s+/g, '-') // 連続空白→ハイフン
.replace(/-+/g, '-') // 連続ハイフン圧縮
.replace(/^-|-$/g, '') // 前後ハイフン除去
.slice(0, 60);
日本語タイトルだとフォールバックでは日本語のまま残るため、LLM 生成が失敗すると URL に日本語が含まれるリスクあり (実用上 LLM はほぼ確実に英語スラッグを返す)。
6. LLM 設定
| 項目 | 値 | 根拠 |
|---|---|---|
| モデル | gemini-flash | 翻訳 + slugify は単純タスク、最安・最速 |
| temperature | KV slug params (fallback 0.0) | 再現性最優先。実効値は KV 優先 (loadLlmParams) |
| seed | 42 (fallback) | 再現性確保 |
| コスト目安 | < 0.005 USD / 起案 | |
| レイテンシ | 1〜3 秒 |
7. 副作用
なし。LLM 呼出のみ。
8. 出力 (State)
{ adrSlug: string } // 例: "adopt-dynamodb-for-adr-store"
numbering ノードでファイル名・PR ブランチ名に組み込まれる。
9. 分岐 (次ノード)
.addEdge('slug', 'numbering')
無条件で numbering へ。
10. エラー時の挙動
- LLM 失敗: catch なし → 例外スロー → グラフ全体失敗
- LLM 出力が形式不一致: 正規表現でフィルタ →
toKebabCaseFallbackで代替 (例: 大文字・空白・記号混入) title空: フォールバックも空文字を返す可能性 → webhook ノードでadrSlug || 'adr-draft'で防御
11. 既知の弱点・運用注意
- 日本語フォールバックの落とし穴: LLM 失敗時に
toKebabCaseFallbackが日本語をそのまま slug にする (/\p{L}/guは Unicode 文字を残す) → URL 内日本語の URL エンコード問題。実用上 LLM がほぼ確実に成功するため未対処だが、/[^\x00-\x7F]/gで ASCII 外を除去するフォールバック強化余地。 - 60 文字制限: タイトルが長すぎると意味の途中で切れる。LLM が要約しすぎるリスクもあり (例: "領収書 OCR を Gemini から Claude Haiku へ移行" → "adopt-claude-haiku" だと過剰圧縮)。プロンプトに「要点を保持」を追記する余地。
- kebab-case 形式の歴史: ADR-0023 採択前は
\d{3}_snake_case.md形式だった。現在は\d{4}-kebab-case.mdに統一済 (旧形式ファイルは改名完了)。 - 重複 slug: 異なる ADR で同一 slug が生成されるリスクあり (
adopt-claude等)。現状は重複検知なし。ADR 番号がプレフィックスにつくため URL レベルでは衝突しないが、視認性は低下。 - gemini-flash の翻訳品質: 専門用語 (例: "RPA", "JNL_ID") は翻訳されずそのまま残ることが多い (実用上 OK)。
12. テストケース
なし。実起案で結果 URL を目視確認するのみ。
将来案: 代表 10 タイトルを LLM に通して生成スラッグの妥当性を回帰チェック。
13. 過去の設計判断ログ
| 日時 | 変更 | 経緯 |
|---|---|---|
| 2026-05-11 | Phase 2a' 初期実装 | LangGraph 移行と同時 |
| ADR-0023 採択 | 4 桁ハイフン kebab-case 形式に統一 | プロンプトの例示を 'adopt-dynamodb-for-adr-store' に更新 |
| (設計時) | gemini-flash 選定 | 翻訳 + slugify は分類より単純で flash で十分 |
| (設計時) | LLM 失敗時のフォールバック追加 | URL を絶対に生成するため (PR 作成を止めない) |
14. 関連リンク
- 前ノード: 08_policy_alignment.md
- 次ノード: 10_numbering.md
- 関連 ADR: ADR-0023 — 4 桁ハイフン kebab-case 形式統一