8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

プロンプトで祈るのはもうやめる。Outlines/Guidance で LLM の出力を 100% 制御する技術

Last updated at Posted at 2026-02-02

プロンプトで祈るのはもうやめる

Outlines / Guidance で LLM の出力を 100% 制御する技術

はじめに:開発現場における「お祈り」の限界

LLMをシステムに組み込む際、私たちはいつまでプロンプトで「お祈り」を続けるのでしょうか。

  • 「以下のJSONフォーマットで出力してください」
  • 「余計な前置きや挨拶は書かないでください」
  • 「必ず数値は半角で書いてください」

どれだけ丁寧にプロンプトエンジニアリングを施しても、LLMは確率的モデルです。
temperature=0 にしたところで、構造的な整合性が 100% 保証されるわけではありません

よくある事故例:

  • JSONの閉じカッコが抜ける
  • 数値型を要求したのに文字列型が返ってくる
  • {"error": "..."} ではなく I'm sorry, I can't... と喋り出す

これらを防ぐためにリトライ処理(Re-prompting)を実装するのは、レイテンシとコストの無駄です。

本記事では、自然言語による指示(Prompt Engineering)ではなく、
論理的な制約(Constrained Decoding) によって LLM の出力を 100% 制御 する技術、
特に Outlines に焦点を当てて解説します。


なぜ「100%」と言えるのか?(仕組みの解説)

従来の「プロンプトで頑張る」アプローチと、
Outlines などの Structured Generation(構造化生成) の違いは、

生成プロセスそのものに介入しているかどうか

にあります。


通常の生成プロセス

LLMは Vocabulary(語彙)に含まれる全トークンに対して確率(Logits)を計算し、サンプリングします。

「JSONを出せ」と言われても、

  • Here
  • Sure
  • I’m sorry

といったトークンの出現確率は ゼロにはなりません


Constrained Decoding(制約付きデコーディング)

Outlines は、

  • 正規表現(Regex)
  • 文脈自由文法(CFG)
  • JSON Schema / Pydantic

有限オートマトン(FSM: Finite State Machine) に変換します。

生成の各ステップで:

  1. 現在の FSM の状態を確認
  2. 遷移可能なトークン 以外
  3. Logits を -inf(無限小)にマスク

つまり、

構造的に不正なトークンは、物理的に選択できない

状態になります。

これが「100% 制御できる」と言える数学的な根拠です。


技術検証:Outlines を使ってみる

実際に Outlines を使って、制御された生成を試してみます。


検証環境

  • Library: outlines
  • Model: Qwen/Qwen-2.5-7B-Instruct
  • Backend: vLLM
  • GPU: (必要に応じて記載)
import outlines

# モデルのロード(vLLMバックエンドを使用すると高速)
model = outlines.models.transformers(
    "Qwen/Qwen-2.5-7B-Instruct"
)

Case 1: 正規表現(Regex)による制御

IPアドレスのような厳密なフォーマットが必要なケース。

# IPアドレスの正規表現
regex = r"((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){3}" \
        r"(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)"

generator = outlines.generate.regex(model, regex)

prompt = "私のローカルサーバーのIPアドレスは、"
result = generator(prompt)

print(result)

検証結果

192.168.1.10

余計な前置きや文章は一切生成されません

通常の LLM だと:

Sure, a typical local IP address is 192.168...

のようなテキストが混ざりがちですが、
Regex 制約下では 数字とドット以外のトークンは生成不能 になります。


Case 2: Pydantic(JSON Schema)による完全スキーマ制御

実務で最も使うパターン。
Agent のツール引数生成などで必須です。

from pydantic import BaseModel, Field
from enum import Enum

class WeaponType(str, Enum):
    SWORD = "sword"
    BOW = "bow"
    MAGIC = "magic"

class Character(BaseModel):
    name: str = Field(..., description="キャラクターの名前")
    age: int = Field(..., description="年齢")
    weapon: WeaponType = Field(..., description="使用する武器")
    power: int = Field(..., description="戦闘力 (0-100)")

generator = outlines.generate.json(model, Character)

prompt = "中世ファンタジー風の最強の戦士を作成してください。"
result = generator(prompt)

print(result)
print(type(result))

検証結果

name='アリス' age=24 weapon=<WeaponType.SWORD: 'sword'> power=99
<class '__main__.Character'>

注目ポイント

  • 型安全性
    age, power は必ず int
  • Enum制約
    axe など未定義値は絶対に出ない
  • パース不要
    Pydantic オブジェクトとしてそのまま扱える

Case 3: 速度とトークン効率

「制約をかけると遅くなるのでは?」と思われがちですが、
実際には 高速化するケースが多い です。

理由:

  • 思考過程
  • 前置き
  • 謝罪文

といった 不要トークンを生成しない ため。

例(参考)

  • 通常生成

    • 平均 1.5 秒
    • リトライ込み:3.0 秒
  • Outlines

    • 平均 0.8 秒

まとめ:プロンプトエンジニアから「モデル制御エンジニア」へ

LLMの出力は、もはや「お祈り」するものではありません。
設計するものです。

  • 確実性
    正規表現・スキーマによる 100% フォーマット保証
  • 効率性
    リトライ・例外処理の削減
  • 安全性
    Enum や型による予期せぬ値の排除

RAG や自律型 Agent において、
不安定な出力は致命的なバグになります。

Outlines、Guidance、OpenAI Structured Outputs の背後にある
Logit Masking / Constrained Decoding を理解し、
モデルの手綱を握れるエンジニアになることが、
これからの NLP / LLM エンジニアには求められるでしょう。

8
7
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
8
7

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?