はじめに

LLMを使っていると、「毎回似たような指示を書いているのに、出力が安定しない」という課題に直面することがあります。これは、入力の書き方や順序のわずかな違いによって出力が変わってしまうためです。安定した結果を得るためには、うまく書くことよりも一貫した構造でプロンプトを設計することが重要です。

この記事では、生成AI開発で注目されている LangChain を使い、再現性・保守性の高いプロンプトを設計する方法を紹介します。

LangChainとは

LangChain(ランチェーン) は、OpenAIやGoogle Geminiなどの大規模言語モデル(LLM)を効率的に活用するためのPythonライブラリです。プロンプト設計・外部ツール連携・メモリ管理・出力制御などを体系的に扱うことができ、AIアプリケーション開発の土台として広く使われています。

その中でもプロンプト設計に重要なのが PromptTemplate 機能 です。これはプロンプトをテンプレート化し、「固定ルール」と「変動要素」を分離することで、再利用性と安定性を高める機能です。

たとえば、「口調や出力形式は固定」「質問内容だけ毎回変える」といった使い方が簡単にできます。

LangChainでできること

LangChainでは、主に次のような機能を利用できます。

機能カテゴリ説明主なクラス活用例
プロンプト設計テンプレートで一貫性を保つPromptTemplate​ChatPromptTemplate役割・口調・出力形式を固定
モデル接続各種LLMを統一的に扱うChatOpenAI​, ChatGoogleGenerativeAIOpenAI ⇔ Gemini の切り替え
チェーン構築複数プロンプトを連携LLMChain​, SequentialChain生成→要約→整形 などの分割
出力制御JSONなど構造化出力に対応PydanticOutputParserAPIで安全なデータ返却
メモリ管理会話履歴を保持ConversationBufferMemoryチャットボット開発
外部連携APIやDBと接続Tool​, Agent検索・ファイル操作・自動化

再現性を高めるプロンプト設計

プロンプト設計の基本

プロンプト設計では、固定部分変動部分を分けて考えることが重要です。LangChainの PromptTemplate を使うと、これらを明確に分離でき、同じ構造で安定した出力が得られます。

区分内容
固定部分役割・口調・出力形式など常に同じルール役割: 専門家、口調: 丁寧、形式: 箇条書き
変動部分質問や条件など、毎回変わる部分「ダイエット中でも筋肉を落とさない食事は?」

PromptTemplateの使い方とコード例

以下は、Google Gemini モデルを使ってプロンプトテンプレートを作成する例です。

!pip install -U langchain langchain-google-genai protobuf==4.21.6
from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", api_key="API KEYを入力")

prompt = ChatPromptTemplate.from_messages([
  ("system", "あなたは{role}です。回答は{tone}で、出力形式は{format}です。"),
  ("human", "質問: {question}")
])
chain = prompt | llm | StrOutputParser()

result = chain.invoke({
  "role": "スポーツ栄養士",
  "tone": "科学的",
  "format":"簡潔な3つの箇条書き",
  "question": "ダイエット中でも筋肉を落とさない食事の工夫は?"
})
print(result)

 

■ 出力結果

このように、テンプレートを使うと構造を保ちながら質問だけを変えることができ、再現性が向上します。

テンプレートを使わない場合との比較

同じ内容を文字列連結で書くと次のようになります。

llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", api_key="API KEYを入力")

role = "スポーツ栄養士"
tone = "科学的"
format = "簡潔な3つの箇条書き"
question = "ダイエット中でも筋肉を落とさない食事の工夫は?"

prompt = f"あなたは{role}です。回答は{tone}で、出力形式は{format}です。\n質問: {question}"

result = llm.invoke(prompt)
print(result.content)

この書き方だと、要素を追加・変更するたびに文字列全体を直す必要があり、system と human の役割構造も崩れやすくなってしまいます。固定部分と可変部分の区別がつきにくく、プロンプトの形が一定せず、結果の再現性も下がってしまう点が課題です。

安定した出力と再利用性を実現するには、PromptTemplateを使ってプロンプトを構造化する設計が効果的です。

PromptTemplateの応用

1. 出力形式を制御

PydanticOutputParser​ を使うと、AIの出力をJSON構造に固定できます。この方法を使えば、「AIが返すデータの形」を決めておけるため、システム側での処理が安定します。

from pydantic import BaseModel, Field
from langchain_core.output_parsers import PydanticOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_google_genai import ChatGoogleGenerativeAI

class HealthAdvice(BaseModel):
    topic: str = Field(..., description="健康トピック名")
    tips: list[str] = Field(..., description="アドバイスを3つ")
    
llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", api_key="API KEYを入力")
parser = PydanticOutputParser(pydantic_object=HealthAdvice)
prompt = ChatPromptTemplate.from_template(
    "トピック: {topic}\nについて、科学的根拠に配慮し一言で回答してください。\n"
    "JSON形式で出力してください。\n"
    "{format_instructions}"
).partial(format_instructions=parser.get_format_instructions())
chain = prompt | llm | parser

result = chain.invoke({"topic": "睡眠の質向上"})
result.model_dump()

 

■ 出力結果

2. プロンプトテストの自動化

LangChainを使えば、プロンプトをテンプレート化して構造的に扱えるため、自動テストが容易になります。文字列を直書きする場合と違い、出力の形式や再現性を機械的に検証でき、品質を安定して保てます。

from langchain_google_genai import ChatGoogleGenerativeAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser

llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", api_key="API KEYを入力")
prompt = ChatPromptTemplate.from_messages([
    ("system", "あなたは{role}です。簡潔に3つ箇条書きで答えてください。"),
    ("human", "{question}")
])
chain = prompt | llm | StrOutputParser()

def test_prompt():
    result = chain.invoke({"role": "トレーナー", "question": "短時間でできる筋トレ方法"})
    print(result)
    bullets = sum(line.strip().startswith(("*", "・", "-")) for line in result.splitlines())
    if bullets != 3:
        raise AssertionError(f"異常終了:箇条書きが{bullets}個")
    print("正常終了")

test_prompt()

 

■ 出力結果

3. プロンプトチェーンで処理を分割

複数のプロンプトを「チェーン」でつなげ、以下のように「生成 → 要約」の2段階処理を自動化できます。このように「分業型のプロンプト設計」をすることで、結果の一貫性と開発効率が大きく向上します。

from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_google_genai import ChatGoogleGenerativeAI

llm = ChatGoogleGenerativeAI(model="gemini-2.5-flash", api_key="API KEYを入力")

# Step 1: 情報生成
prompt1 = ChatPromptTemplate.from_template("『{topic}』について詳しく説明してください。")

# Step 2: 要約
prompt2 = ChatPromptTemplate.from_template("以下の説明を読み、最も重要なポイントを1文で要約してください。\n\n{content}")

chain = (
    prompt1 | llm | StrOutputParser()
) | (
    prompt2 | llm | StrOutputParser()
)

result = chain.invoke({"topic": "筋トレが美容にもたらす効果"})
print(result)

 

■ 出力結果

 

さいごに

LangChainの PromptTemplate を活用することで、プロンプトの品質や再現性、保守性を着実に高めることができます。
プロンプトを構造的に整理し、テンプレートとして管理することで、誰でも同じ結果を再現しやすくなります。
さらに、出力形式の制御や処理の分割、外部連携などと組み合わせることで、より安定した仕組みを作ることも可能です。
こうした工夫を重ねることで、属人的なノウハウに頼らず、再利用しやすく継続的に改善できるプロンプト設計が実現できます。

お読みいただき、ありがとうございました。