LangChainのPrompt templateでループを使う方法の紹介 #LangChain

ジェネラティブエージェンツの大嶋です。

この記事では、LangChainのPrompt templateでループを使う方法を紹介します。

想定シーン

プロンプトのテンプレートを作成する際、ループ処理を入れたくなることがあります。

たとえば以下は、ユーザーの入力に応答するためのエージェントを選択する、というプロンプトの例です。

ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
- name: researcher
  description: リサーチャー
- name: coder
  description: コーダー

上記のプロンプトのうち「Agents」の箇所のエージェント一覧は、プログラムで動的に埋めたい場合もあります。

つまり、プログラム中に以下のようにagentsというエージェントの一覧の変数があるとします。

from pydantic import BaseModel

class Agent(BaseModel):
    name: str
    description: str

agents = [
    Agent(name="researcher", description="リサーチャー"),
    Agent(name="coder", description="コーダー"),
]

この「agents」という変数を使って、以下のようなプロンプトのテンプレートからプロンプトを組み立てたい、ということです。

ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
<ここをagents変数のループで組み立てたい>

このようなシーンでは、Prompt templateの中でループのような処理を記述したくなります。

LangChainのtemplate_format

実はLangChainのPromptTemplateやChatPromptTemplateには、「template_format」というパラメータがあります。

python.langchain.com

template_formatには「f-string」「mustache」「jinja2」の3種類を指定することができます。

デフォルト値は「f-string」ですが、「mustache」または「jinja2」を使えば、ループのような処理を記述できます。

mustacheを使う例

mustacheを使う場合、ChatPromptTemplateを以下のように記述します。

from langchain_core.prompts import ChatPromptTemplate

system_prompt_template = """\
ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
{{#agents}}
- name: {{name}}
  description: {{description}}
{{/agents}}
"""

prompt = ChatPromptTemplate.from_messages(
    messages=[
        ("system", system_prompt_template),
        ("human", "{{query}}"),
    ],
    template_format="mustache",
)

template_format="mustache"としていることと、mustacheの記法でプロンプトを記述していることがポイントです。

このプロンプトの穴埋めを実行します。

query = "今日のニュースは?"
prompt_value = prompt.invoke({"agents": agents, "query": query})
print("=== system ===")
print(prompt_value.messages[0].content)
print("=== human ===")
print(prompt_value.messages[1].content)

すると、以下のようにagents変数の値がループでプロンプトに埋め込まれます。

=== system ===
ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
- name: researcher
  description: リサーチャー
- name: coder
  description: コーダー

=== human ===
今日のニュースは?

jinja2を使う例

jinja2を使う場合、ChatPromptTemplateを以下のように記述します。

from langchain_core.prompts import ChatPromptTemplate

system_prompt_template = """\
ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
{%- for agent in agents %}
- name: {{agent.name}}
  description: {{agent.description}}
{%- endfor %}
"""

prompt = ChatPromptTemplate.from_messages(
    messages=[
        ("system", system_prompt_template),
        ("human", "{{query}}"),
    ],
    template_format="jinja2",
)

template_format="jinja2"としていることと、jinja2の記法でプロンプトを記述していることがポイントです。

このプロンプトの穴埋めを実行します。

query = "今日のニュースは?"
prompt_value = prompt.invoke({"agents": agents, "query": query})
print("=== system ===")
print(prompt_value.messages[0].content)
print("=== human ===")
print(prompt_value.messages[1].content)

すると、以下のようにagents変数の値がループでプロンプトに埋め込まれます。

=== system ===
ユーザーの入力に応答するために適切なエージェントを選択してください。

Agents:
- name: researcher
  description: リサーチャー
- name: coder
  description: コーダー
=== human ===
今日のニュースは?

注意事項

注意事項として、PromptTemplateのAPIリファレンスには以下のセキュリティ上の警告が書かれています。

Security warning:

Prefer using template_format=”f-string” instead of template_format=”jinja2”, or make sure to NEVER accept jinja2 templates from untrusted sources as they may lead to arbitrary Python code execution.

As of LangChain 0.0.329, Jinja2 templates will be rendered using Jinja2’s SandboxedEnvironment by default. This sand-boxing should be treated as a best-effort approach rather than a guarantee of security, as it is an opt-out rather than opt-in approach.

Despite the sand-boxing, we recommend to never use jinja2 templates from untrusted sources.

引用元:https://python.langchain.com/api_reference/core/prompts/langchain_core.prompts.prompt.PromptTemplate.html

信頼できないソースから取得したjinja2のテンプレートを使用すると、任意のPythonコード実行の可能性があるという内容であり、この点には注意が必要です。

まとめ

以上、LangChainのPrompt templateでループを使う方法を紹介しました。

LangChainのPromptTemplateやChatPromptTemplateには、「template_format」というパラメータがあり、「mustache」または「jinja2」を使えばループのような処理を記述できます。

なお、この記事のコードはGoogle Colab上のlangchain-core==0.3.25で動作確認しました。 最近はGoogle ColabにLangChainがプリインストールされているので、このようなちょっとした動作確認をしたいときは嬉しいですね。