1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

紗綾音(2) Sayane ProfileとPrompt IRの設計:LLM向けプロンプトを直接書かない理由

1
Posted at

はじめに

前回は、Sayane(紗綾音)を使って、ローカルProfileからChatGPT / Claude向けのプロンプトを生成する最小フローを紹介しました。

第1回で扱った中心は、次の流れです。

Sayane Profile
  ↓
Prompt IR
  ↓
Adapter
  ↓
ChatGPT / Claude などのtarget向け出力

今回は、この中の Sayane ProfilePrompt IR に焦点を当てます。

なぜ、最初からChatGPT用プロンプトやClaude用プロンプトを書かないのか。なぜ、ProfileとPrompt IRを分けるのか。なぜ、Adapterという変換層を置くのか。

この記事では、Sayaneの設計意図を、実装者向けに整理します。

GitHubはこちらです。

https://github.com/zyx-corporation/sayane

この記事で扱うこと

この記事では、次の点を扱います。

Sayane Profileの役割
Prompt IRの役割
ProfileとPrompt IRを分ける理由
Adapterとの責務分離
targetごとにcompileする設計の意味

逆に、この記事ではCandidate、lineage、RDE評価の詳細には入りません。これらは後続回で扱います。

なぜプロンプトを直接書かないのか

LLMをひとつだけ使うなら、直接プロンプトを書いても大きな問題はありません。

たとえば、ChatGPT向けに次のようなシステムプロンプトを書くことはできます。

あなたはソフトウェアアーキテクトです。
回答は日本語で、簡潔かつ具体的にしてください。
Pythonではテスト容易性を重視してください。

これは単純で、すぐ使えます。

しかし、複数のLLMを使い分けると、直接プロンプト方式は少しずつ苦しくなります。

ChatGPTにはChatGPT向けの形式がある。ClaudeにはClaude向けの渡し方がある。Geminiやlocal-openwebuiでは、さらに違う制約があります。CursorやMCP経由で使う場合も、プロンプトの置き場所や呼び出し方が変わります。

このとき、すべてのtargetに対して直接プロンプト文字列を管理し始めると、次のような問題が起きます。

同じ内容を何度も書くことになります。targetごとの差分が見えにくくなります。あるtarget向けの修正が、他のtargetへ反映されなくなります。どれが正本なのか分からなくなります。

つまり、プロンプト文字列そのものを正本にすると、複数LLM運用では破綻しやすくなります。

そこでSayaneでは、プロンプト文字列ではなく、Profile を正本にします。

Sayane Profileとは何か

Sayane Profileは、ユーザーやプロジェクトの文脈を保持するローカルの正本です。

Profileには、たとえば次のような情報が入ります。

identity: 名前、役割、所属、肩書きなど
voice: 文体、トーン、説明の好み
values: 重視する価値観や判断基準
policy: 応答方針、禁止事項、優先事項
context: プロジェクトや作業に関する文脈

概念的には、Profileは「LLMにそのまま投げる文字列」ではありません。

Profileは、ユーザーやプロジェクトについての構造化された文脈です。

このProfileを、ChatGPT向け、Claude向け、Gemini向けなどに変換して使います。

ここで重要なのは、Profileがtarget非依存であることです。

Profileは、ChatGPTのためだけの設定ではありません。Claudeのためだけの設定でもありません。あくまで、ローカルに置かれた文脈の正本です。

Profileは何を持ち、何を持たないべきか

Profileには、長期的に再利用したい文脈を入れます。

たとえば、次のような情報はProfileに向いています。

ユーザーの基本的な役割
回答の言語設定
文体や説明粒度の好み
プロジェクトの基本方針
開発時に重視する設計原則
繰り返し使う制約条件

一方で、Profileに入れない方がよいものもあります。

一時的な会話の流れ、まだ検証していない仮説、その場だけの感情、一回限りの作業指示、長期的に採用するか分からない表現。こうしたものをすぐProfileに入れると、文脈がノイズで膨らみます。

Profileは、何でも入れるメモ帳ではありません。

Profileは、長期的に使う文脈の正本です。

この区別が、後続回で扱うCandidateやlineageにつながります。取り込んだ文脈をいきなりProfileへmergeせず、候補として扱う理由もここにあります。

Prompt IRとは何か

Prompt IRは、Prompt Intermediate Representationの略です。日本語では、プロンプト中間表現と呼べます。

Profileは、ユーザーやプロジェクトの文脈を保持する正本です。しかし、Profileをそのまま各LLMに渡すわけではありません。

まず、ProfileをPrompt IRへ変換します。

Prompt IRは、targetに依存しないプロンプトの内部表現です。

つまり、ChatGPT用のJSONでも、Claude専用の形式でもありません。Adapterが後からtarget別の出力へ変換するための中間構造です。

Prompt IRを挟むことで、Profileと各targetの形式を直接結びつけずに済みます。

これは、ソフトウェア設計ではよくある分離です。

内部の正本と外部への出力形式を分ける。内部モデルとプレゼンテーションを分ける。ドメインモデルとAPIレスポンスを分ける。

Sayaneでは、それをLLM向けプロンプト生成に適用しています。

ProfileとPrompt IRを分ける理由

ProfileとPrompt IRを分ける理由は、主に3つあります。

第一に、Profileを安定させるためです。

Profileはローカルの正本なので、できるだけtargetの都合に引きずられない方がよい。ChatGPTの形式が変わったからといって、Profileそのものを大きく変える設計にはしたくありません。

第二に、targetごとの違いをAdapter側へ閉じ込めるためです。

ChatGPT、Claude、Gemini、local-openwebuiでは、期待される入力形式や自然な構造が違います。その違いをProfileへ直接混ぜると、Profileが肥大化し、見通しが悪くなります。

第三に、テストしやすくするためです。

ProfileからPrompt IRへの変換をテストする。Prompt IRからtarget別出力への変換をテストする。このように分けることで、問題が起きたときに、どの層で壊れているのかを見つけやすくなります。

これは、CLIツールとしてもライブラリとしても重要です。

Adapterの責務

Adapterの責務は、Prompt IRをtarget別の形式へ変換することです。

Adapterは、Profileの正本を直接編集しません。Profileの意味そのものを勝手に変えるべきでもありません。

Adapterがやるべきことは、targetごとに必要な形式へ整えることです。

たとえば、ChatGPT向けにはmessages形式が自然かもしれません。Claude向けには別の構造が必要かもしれません。local-openwebui向けには、より単純なテキスト形式が扱いやすいかもしれません。

Adapterは、targetの都合を吸収する境界です。

この境界を置くことで、ProfileはProfileとして保ち、targetの変化にはAdapter側で対応できます。

同一Profileから複数targetへ出力する

Sayaneの基本的な使い方は、同じProfileからtargetだけを変えてcompileすることです。

sayane compile --target chatgpt
sayane compile --target claude

サンプルProfileを明示する場合は、次のようにします。

sayane compile --target chatgpt --profile examples/profiles/minimal.yaml
sayane compile --target claude --profile examples/profiles/minimal.yaml

ここで行っていることは、同じProfileを別々のtargetへ変換することです。

この設計では、正本はProfileにあります。

ChatGPT向け出力も、Claude向け出力も、正本そのものではありません。あくまでProfileから生成されたtarget別の成果物です。

この考え方は、ソースコードとビルド成果物の関係に近いです。

ソースコードが正本で、ビルド成果物はtargetごとに生成される。同じように、SayaneではProfileが正本で、各LLM向けプロンプトは生成物です。

Markdown contextとの関係

Sayane Profileは、単体のYAMLだけで完結するとは限りません。

context/ 配下にMarkdown文脈を置き、それをProfileの文脈として扱うことができます。

~/.sayane/
  profiles/
    default/
      sayane.profile.yaml
      context/
        project.md
        coding-policy.md
        ai-handoff.md

概念的には、Profileが文脈の中心であり、Markdown contextが補助的な文脈として接続されます。

新しいMarkdownファイルを追加した場合は、必要に応じてindexを更新します。

sayane storage index

その後、compileします。

sayane compile --target chatgpt

この仕組みにより、長いプロジェクト文脈や作業方針を、Profile本体にすべて詰め込まず、Markdownとして管理できます。

設計上の注意点

ProfileとPrompt IRを分ける設計には、利点があります。一方で、注意点もあります。

まず、Profileに何でも入れすぎると、compile結果が重くなります。LLMにはトークン制限があるため、文脈は多ければよいわけではありません。

次に、Prompt IRが複雑になりすぎると、Adapterの実装が難しくなります。中間表現は、表現力と単純さのバランスが必要です。

また、Adapterがtargetごとの最適化をやりすぎると、Profileの意図から離れる危険があります。Adapterは出力形式を整える層であり、Profileの意味を勝手に書き換える層ではありません。

Sayaneでは、このあたりの境界をできるだけ明確に保つことを重視しています。

今回のまとめ

今回の記事では、Sayane ProfileとPrompt IRの設計を説明しました。

Sayane Profileは、ユーザーやプロジェクトの文脈の正本です。

Prompt IRは、targetに依存しないプロンプト中間表現です。

Adapterは、Prompt IRをChatGPT、Claude、Gemini、local-openwebuiなどのtarget別出力へ変換する層です。

重要なのは、プロンプト文字列そのものを正本にしないことです。

複数LLM時代には、プロンプトをコピペで運ぶよりも、Profileを正本として管理し、必要なtargetへcompileする方が扱いやすくなります。

Sayaneは、そのための小さな基盤です。

連載予告

この連載では、Sayaneの設計と使い方を、実装者向けに順番に整理していきます。

第1回では、Sayaneをインストールし、ローカルProfileからChatGPT / Claude向けプロンプトを生成する最小フローを扱いました。

第2回である本記事では、Sayane ProfileとPrompt IRの設計を説明しました。

次回、第3回では、Adapterの作り方を扱います。同じPrompt IRから、targetごとの出力形式をどう生成するのか。ChatGPT向け、Claude向け、local-openwebui向けの違いをどこで吸収するのかを、実装方針として整理します。

その後は、Candidate / evaluate / lineageによる文脈更新管理、MCP ServerとしてCursorから使う方法、Markdown / Obsidian / Gitを使った文脈運用、RDE的な評価をどのように実装へ落とすかを順に扱う予定です。

1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?