目次


1. 構造化プロンプトの背景

近年、LLM(大規模言語モデル)を用いたエージェントや生成アプリケーションが急速に普及し、システムプロンプトや一連の対話履歴をどのように設計・管理するかが開発者にとって重要な課題となっています。シンプルな構造のプロンプトであればプレーンテキストで管理できますが、複数のロールやタスク、長大な指示を含むシステムプロンプトはしばしば数百行に達し、内容を把握したり差分をレビューすることすら困難になります。加えて、画像やテーブルなどの複雑なデータを埋め込みたい場合や、プロンプトの一部を再利用したい場合には、JSON や YAML など別形式のテンプレートエンジンを自前で組み合わせる必要がありました。

そこで Microsoft が提案したのが POML(Prompt Orchestration Markup Language)です。POML はプロンプトを「文書」として構造化することを目的としたマークアップ言語であり、タグベースの構文でコンポーネントを分割し、LLM へ送信する前にプレーンテキストへ変換します。開発者は文書内にロールやタスク、出力形式、例示などを明示的なタグで記述できるため、読みやすさ・再利用性・メンテナンス性が大幅に向上します。

POML はまた、複雑なデータ統合やフォーマット依存の問題に対処するために、画像や表、外部ドキュメントなどのデータを専用タグで取り込み、表示方法をスタイルシートで制御できる仕組みを備えています。このようなアプローチにより、開発者はプロンプトの論理構造と見た目を切り離して管理でき、生成されたテキストを既存のフレームワークに渡すまでの工程を簡素化できます。

POML の登場は、単に新しいテンプレート言語が増えたというだけではありません。従来のプロンプト開発では「内容」と「形式」を同時に最適化する必要があり、細かなフォーマット変更でも挙動が変わるため試行錯誤に時間が掛かっていました。POML はそうした課題に対して、「構造化」「データ統合」「プレゼンテーション分離」の三本柱で解決策を提示しています。以下では、基本構文からテンプレートエンジン、メタデータやツール連携まで、POML の使い方を詳しく見ていきましょう。


2. POML の基本構文

POML ドキュメントは拡張子 .poml を持つファイルとして保存され、ルート要素に <poml> タグを置くのが基本です。この中に、プロンプトを構成するセマンティックなコンポーネントをネストしていきます。主要なタグには以下のようなものがあります:

タグ用途・説明
<role>エージェントやアシスタントの役割を記述する。
<task>ユーザーが依頼する具体的なタスクを記述する。
<output-format>応答の出力形式や口調などを指定する。
<example>入力 (<input>) と出力 (<output>) のペアを示し、例示によってモデルの出力を誘導する。
<img>画像ファイルを埋め込む。src 属性でパスを指定する。
<table>CSV 形式や二次元配列をテーブルとして表示する。
<document>外部ファイルをドキュメントとして取り込む。

タグベースの構文により、プロンプトの各構成要素が明確に分離され、読者やレビュワーはどこで役割が定義され、どこにタスクや例が書かれているかを即座に把握できます。例えば、猫のキャラクターを演じるプロンプトは次のように書きます:

<poml>
  <role>あなたは野生で生息する猫です。時折人間の家に迷い込みます。</role>
  <task>人間に対して愛らしい行動をとってください。</task>
  <img src="/column/articles/poml-guide/images/./images/cat.jpg" alt="猫の画像" />
  <output-format>
    常に可愛らしい鳴き声で返答してください。
  </output-format>
  <example>
    <input speaker="human">こんにちは、どこから来たの?</input>
    <output speaker="ai">ニャー!</output>
  </example>
</poml>

このドキュメントを POML エンジンで変換すると、Markdown 風のテキストが生成され、LLM に渡すチャットメッセージ配列に変換されます。POML ドキュメントは <role><task> といったセマンティックなタグを利用することで、プロンプトの論理構造と表示形式を同時に記述できるという点が特徴です。

また、POML には <document>, <table>, <img> といったリッチなコンポーネントが用意されており、テキストだけでは表現しにくい情報を組み込むことができます。表形式のデータを records 属性で二次元配列として渡すと、変換後に箇条書きや CSV として表示させることも可能です。スタイルシートを併用することで、特定の要素だけ JSON や CSV 表示に切り替えるなど、表示の自由度も高くなっています。


3. テンプレートエンジンで動的生成

POML にはテンプレートエンジンが組み込まれており、変数の定義やループ、条件分岐などを使用して動的にプロンプトを生成できます。これは、従来のプログラミング言語のテンプレートエンジンに似た機能を、プロンプト専用に洗練させたものです。テンプレートエンジンでよく使う構文を順に見ていきましょう。

変数の定義と利用

<let> タグを使うと変数を定義できます。name 属性で変数名、value 属性に JavaScript と互換性のある式を記述します。定義した変数は {{variable}} という二重中括弧で参照可能です。例えば、動物の種類を変数化してロールに埋め込むことができます。

<poml>
  <let name="animal" value="'猫'" />
  <role>あなたは野生で生息する{{animal}}です。時折人間の家に迷い込みます。</role>
  <task>人間に対して愛らしい行動をとってください。</task>
</poml>

外部ファイルからデータを読み込んで変数にすることもできます。たとえば、animal.json に猫の種別や鳴き声を保存し、<let src="/column/articles/poml-guide/images/./animal.json" /> でその内容を取り込むと、JSON オブジェクトとしてプロパティにアクセスできます。この仕組みは設定ファイルの読み込みやユーザー固有データのバインドに便利です。

ループと繰り返し

配列やリストを繰り返し処理したい場合は、コンポーネントに for="item in items" という属性を付けます。ループ中には loop.indexloop.lengthloop.firstloop.last などの特殊変数が利用でき、繰り返しの状況を把握できます。

<poml>
  <output-format>
    以下のような鳴き声で返答してください。
    <list for="cry in ['ニャー', 'アオーン', 'シャー!']">
      <item>{{cry}}</item>
    </list>
  </output-format>
</poml>

この例では listitem タグを使って鳴き声のリストを生成し、変換後は箇条書きとして表示されます。また、ループ内でインデックスを利用して例番号を自動生成することも可能です。

条件分岐

POML は if 属性で条件分岐を記述できます。if="condition" には JavaScript の論理式を指定し、条件が真の場合のみその要素を出力します。

<poml>
  <let name="settings" value='{ "isPlanMode": true }' />
  <task>
    あなたはコーディングエージェントです。以下の条件に従って行動してください。
    <list>
      <item if="settings.isPlanMode">コードの読み書きは行わずに、計画を立てることに専念してください。</item>
      <item if="!settings.isPlanMode">ユーザーからの指示に従ってコードを生成してください。</item>
    </list>
  </task>
</poml>

このように、テンプレートエンジンを活用することで条件に応じた挙動の切り替えや大量のデータからの生成が柔軟に行えます。従来のテンプレートライブラリと異なり、LLM プロンプトに特化しているため、チャットメッセージへの変換も自動的に行われ、複数の役割やタスクに対応した構造化プロンプトを簡潔に記述できます。


4. メタデータと拡張機能

POML では <meta> タグを用いてドキュメント全体に影響する設定やメタデータを追加できます。これにより、プロンプトの応答形式や外部ツールの仕様などを言語モデルに明示的に伝えることができます。

レスポンススキーマ

type="responseSchema" のメタタグでは、AI の応答が従うべき JSON スキーマを定義します。lang="json" 属性で記述する場合は純粋な JSON をそのまま記述し、lang を省略すると JavaScript 式として解釈されます。スキーマを指定することで、生成される出力の形式が保証されるため、後段のアプリケーションでのパースが容易になります。

ツールスキーマ

type="tool" または type="toolSchema" のメタタグを使うと、AI エージェントが呼び出す外部ツールのインターフェースを定義できます。例えば、天気情報を取得する getWeather ツールに必要な引数やその型を JSON 形式で記述できます。これにより、LLM はどのツールがどのようなパラメーターを受け取るのかを理解し、意図した通りにツールを使用するよう誘導できます。

ランタイムパラメーターとコンポーネント制御

type="runtimeParameters" は LLM の温度や最大出力トークン数など実行時のパラメーターを指定します。さらに、components 属性を用いると特定のコンポーネントを有効または無効にできます。-img を指定すれば画像コンポーネントを無効化し、+img で再度有効化するなど、プロンプトの動作環境に応じた細かな設定が可能です。

スタイルシート

POML では <stylesheet> タグを使って文書の表現を制御できます。CSS に似た構文で、特定のタグやクラスに対して表示形式やシンタックスを指定します。例えば、テーブルを CSV として出力したり、JSON としてレンダリングするなど、出力フォーマットの柔軟な切り替えが可能です。コンテンツとスタイルが分離されているため、同じ POML 文書でも用途に応じて見た目だけを簡単に変えられます。


5. 開発ツールと将来展望

POML の普及を支えるために、開発者向けのツール群が充実しています。Visual Studio Code 用の拡張機能をインストールすれば、シンタックスハイライトやオートコンプリート、構文エラーの診断、リアルタイムプレビューなどの機能を利用でき、POML: Open POML Preview コマンドから変換後のチャットメッセージを確認することもできます。API キーを設定すれば、VS Code のパネルから直接 LLM にプロンプトを送信し、応答をテストすることも可能です。

また、TypeScript や Python 向けの SDK が用意されており、pomljs パッケージを使うと POML ドキュメントを読み込み、内部表現(IR)を操作したり Markdown に変換する処理を簡潔に実装できます。これにより、Node.js やブラウザのフロントエンド、サーバーサイド、あるいは Python ベースのバックエンドで POML を組み込んだアプリケーションを構築できます。

POML はまだ新しい言語ですが、その目的は明確です。長大なプロンプトを構造化し、データと表示の分離を行うことで、開発者が 内容に集中できる環境を提供します。今後は複数のモデルやツールと連携するケースが増え、POML を拡張する提案も進んでいます。レスポンススキーマやツールスキーマを活用することで、エージェントが生成する結果の信頼性と安全性を高めることができ、将来的には POML 上での軽量な学習やローカルチューニングのような機能も期待されています。

最後に、POML を試してみたい方はまず VS Code 拡張機能をインストールし、簡単な .poml ファイルを作成してプレビューしてみましょう。POML の習得によって、プロンプト開発における再利用性と構造化を手に入れ、LLM アプリケーションの品質を一段高めることができます。


🔚 まとめ

  • POML は長大なシステムプロンプトを構造化し、再利用性とメンテナンス性を高めるためのタグベースのマークアップ言語です。
  • <role>, <task>, <example> などのセマンティックなタグにより、プロンプトの論理構造を明確に記述し、画像やテーブルなどのデータも簡潔に統合できます。
  • 内蔵のテンプレートエンジンでは、変数定義・ループ・条件分岐・外部ファイル読み込みなどが行え、動的なプロンプト生成が実現します。
  • <meta> タグを用いたレスポンススキーマやツールスキーマの定義、ランタイムパラメーター設定により、LLM の出力形式やツール連携を厳密に制御できます。
  • VS Code 拡張機能や TypeScript/Python SDK などの充実したツールが用意されており、POML はエンジニアがプロンプト開発に専念できる環境を提供します。

POML はまだ発展途上ですが、早期に触れておくことで将来のプロンプト開発の標準化に対応しやすくなります。OpenBridge では POML を用いた AI システムの設計や実装をサポートしていますので、興味のある方はぜひお気軽にご相談ください。