# チャットテンプレートの作成

チャットテンプレートは、トークナイザーの`~PreTrainedTokenizer.chat_template`属性に保存される[Jinja](https://jinja.palletsprojects.com/en/stable/templates/)テンプレートです。Jinjaは、Python風のコードや構文で記述できるテンプレート言語です。

```jinja
{%- for message in messages %}
    {{- '<|' + message['role'] + '|>\n' }}
    {{- message['content'] + eos_token }}
{%- endfor %}
{%- if add_generation_prompt %}
    {{- '<|assistant|>\n' }}
{%- endif %}
```

しばらく眺めていると、これは `{%-` という奇妙な構文こそあるものの、実質的には Python に非常に似ていることに気づくはずです。このテンプレートはメッセージのリストを反復し、各メッセージについてロールとコンテンツを出力した後に、シーケンス終了トークンを出力します。`add_generation_prompt=True` の場合、会話の末尾にアシスタントメッセージの開始ヘッダーを追加します。

書き出されたテンプレートを文字列として読み込み、トークナイザーの`chat_template`属性に代入します。設定が完了すると、[apply_chat_template()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template)を呼び出すたびにこのテンプレートが使用されます。また、[save_pretrained()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained)または[push_to_hub()](/docs/transformers/main/ja/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub)が呼び出された場合、トークナイザーとともに保存されます。テンプレートは、トークナイザーディレクトリ内の`chat_template.jinja`ファイルに保存されます。このファイルを直接編集してテンプレートを変更することもでき、テンプレート文字列を操作するよりも簡単であることが多いです。Transformersがサポートするその他のディスク上の形式については、以下の[チャットテンプレートの保存と読み込み](#storing-and-loading-chat-templates)を参照してください。

## テンプレート作成のコツ

Jinja テンプレートの作成を始める最も簡単な方法は、既存のテンプレートを参照することです。任意のチャットモデルに対して `print(tokenizer.chat_template)` を実行すると、そのモデルが使用しているテンプレートを確認できます。ツール呼び出しを行わず、RAG もサポートしていないシンプルなモデルから始めることをお勧めします。ツール使用モデルは非常に複雑なテンプレートを持つことがあるためです。最後に、フォーマットや構文の詳細については、[Jinja のドキュメント](https://jinja.palletsprojects.com/en/stable/templates/#synopsis)を参照してください。

チャットテンプレートの作成に特化して、遭遇する可能性のある具体的なヒントや落とし穴がいくつかあります。このセクションでは、それらの一部をより詳しく説明します。

### マルチモーダルチャットテンプレートの作成

マルチモーダルテンプレートでは、`chat_template` 属性はトークナイザーではなく **processor** に設定されます。メッセージの `content` キーは、単一の文字列ではなく、多くの場合、コンテンツの辞書のリストです。リスト内の各コンテンツ項目の型を確認し、それに応じて処理することをお勧めします。

一般的に、テンプレートは画像や動画データに直接アクセスすべきではありません。これは通常、テンプレートのレンダリングが完了した後にプロセッサによって処理されます。代わりに、テンプレートは画像や動画の内容に遭遇した際に、`<|image|>` や `<|video|>` のような単一の特殊トークンを出力するようにしてください。プロセッサは、後でこの単一の特殊トークンを画像または動画のトークン列に展開します。出力する具体的なトークンは、使用しているモデルによって異なります。既存のマルチモーダルプロセッサを読み込んで、データがどのように処理されているかを確認することを強くお勧めします。

以下のテンプレート例は、画像とテキストが混在するコンテンツを処理します。

```jinja
{%- for message in messages %}
    {%- if loop.index0 == 0 %}
        {{- bos_token }}
    {%- endif %}
    {{- '<|start_header_id|>' + message['role'] + '<|end_header_id|>\n\n' }}
    {%- if message['content'] is string %}
        {{- message['content'] }}
    {%- else %}
        {%- for content in message['content'] %}
            {%- if content['type'] == 'image' %}
                {{- '<|image|>' }}
            {%- elif content['type'] == 'text' %}
                {{- content['text'] }}
            {%- endif %}
        {%- endfor %}
    {%- endif %}
    {{- '<|eot_id|>' }}
{%- endfor %}
{%- if add_generation_prompt %}
    {{- '<|start_header_id|>assistant<|end_header_id|>\n\n' }}
{%- endif %}
```

このマルチモーダルテンプレートは、上記のよりシンプルなテンプレートと非常に似ていますが、`content`リストを確認し、それらを反復して必要に応じて`<|image|>`トークンをレンダリングします。これにより、ユーザーテキストの「流れの中に」画像を挿入できます。

すべてのモデルがこの方式で動作するわけではありません。たとえば、すべての画像をユーザーメッセージの末尾に移動するモデルもあります。チャットテンプレートは、常にモデルが学習された形式と一致させる必要があります。

### 空白のトリミング

Jinja は、テキストブロックの前後にある空白をすべて出力します。これはチャットテンプレートにおいて問題となることがあります。モデルの学習時に存在しなかった余分な空白が追加されると、パフォーマンスが低下する可能性があるためです。空白を除去するには、Jinja の行構文に `-` を追加します。これにより、Python 的なインデントや改行でテンプレートを作成できますが、レンダリングされた出力にインデントが誤って出力されることを防ぐことができます。

以下の例のテンプレートは`-`を使用していないため、出力に余分な空白が表示されます。

```jinja
{% for message in messages %}
    {{ message['role'] + message['content'] }}
{% endfor %}
```

意図した内容のみが出力されることを保証するために、`-`の使用を強く推奨します。

```jinja
{%- for message in messages %}
    {{- message['role'] + message['content'] }}
{%- endfor %}
```

### 特殊変数とコールラブル

テンプレートにおける定数は、`messages` 変数と `add_generation_prompt` ブール値のみです。ただし、[apply_chat_template()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template) メソッドに**渡される他のすべてのキーワード引数**にアクセスできます。

これにより柔軟性が得られ、仕様の設計時に想定していなかったユースケースへの対応も可能になります。最も一般的な追加変数は `tools` で、JSON スキーマ形式のツールリストを保持します。変数名は自由に指定できますが、標準 API とのテンプレートの互換性を保つため、この用途には慣例に従って `tools` を使用することを強く推奨します。

`tokenizer.special_tokens_map` に含まれるトークンにもアクセスできます。これには、`bos_token` や `eos_token` のような特殊トークンが含まれていることがよくあります。これらのトークンは、`{{- bos_token }}` のように、名前を直接指定してアクセスします。

呼び出し可能な関数が2つ利用できます。呼び出すには、`{{- function_name(argument) }}`を使用します。

- `raise_exception(msg)` は `TemplateException` を送出します。これは、デバッグや、テンプレートの誤った使用に関するユーザーへの警告に役立ちます。
- `strftime_now(format_str)` は、特定の形式で現在の日時を取得します。これはシステムメッセージで頻繁に必要とされる機能です。Python では [datetime.now().strftime(format_str)](https://docs.python.org/3/library/datetime.html#datetime.datetime.now) と同等です。

### Python 以外の Jinja での互換性

Jinja は複数の言語で実装されており、一般的に同じ構文を使用します。Python でテンプレートを作成すると、文字列に対して [lower](https://docs.python.org/3/library/stdtypes.html#str.lower) や、辞書に対して [items](https://docs.python.org/3/library/stdtypes.html#dict.items) といった Python のメソッドを使用できます。ただし、テンプレートが Python 以外の実装で使われる場合、例えば Javascript や Rust でデプロイする場合は、これらのメソッドは動作しません。

すべての Jinja 実装間で互換性を確保するために、以下の変更を行ってください。

- Python のメソッドを Jinja のフィルターに置き換えます。例えば、`string.lower()` は `string|lower` に、`dict.items()` は `dict|dictitems` に置き換えます。ほとんどの変更は同じパターンに従いますが、`string.strip()` は `string|trim` に置き換えられます。フィルターの完全なリストについては、[組み込みフィルター](https://jinja.palletsprojects.com/en/3.1.x/templates/#builtin-filters)のリストを参照してください。
- `True`、`False`、`None`（これらは Python 固有の記法です）を、それぞれ `true`、`false`、`none` に置き換えてください。
- dict や list を直接レンダリングすると、他の実装では異なる結果が返される場合があります。例えば、文字列のエントリがシングルクォートからダブルクォートに変更される可能性があります。これを避けるために、[tojson](https://jinja.palletsprojects.com/en/3.1.x/templates/#jinja-filters.tojson) フィルターを追加して一貫性を保ちます。

### 大きなテンプレート

新しいモデルや、[tool-calling](./chat_extras)やRAGといった機能を備えたモデルでは、100行を超える大きなテンプレートが必要になる場合があります。大きなテンプレートは、別ファイルに記述する方が簡単です。別ファイル内の行番号は、テンプレートの解析や実行時のエラーにおける行番号と完全に一致するため、潜在的な問題のデバッグが容易になります。

テンプレートを別ファイルに記述し、チャットテンプレートに抽出します。

```py
open("template.jinja", "w").write(tokenizer.chat_template)
```

編集済みのテンプレートをトークナイザーに読み戻すこともできます。

```py
tokenizer.chat_template = open("template.jinja").read()
```

## チャットテンプレートの保存と読み込み

Chat テンプレートは、ディスク上に複数の異なる形式で保存されます。最新のチェックポイントでは、テンプレートが独立した `.jinja` ファイルとして保存される一方、古いチェックポイントでは、トークナイザーまたはプロセッサーの設定に埋め込まれています。

### ストレージ形式

テンプレートは、以下のいずれかの形式で保存できます。

- `chat_template.jinja`（推奨）。リポジトリのルートに配置される独立した Jinja ファイルで、単一のチャットテンプレートが含まれます。[save_pretrained()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) がデフォルトで書き出す形式です。テンプレートを独立したファイルとして保存することで、確認・編集・差分比較が容易になります。tokenizer と processor の両方が `chat_template.jinja` を同じ方法で読み込みます。
- `additional_chat_templates/<name>.jinja`。モデルが複数の名前付きテンプレート（例：`default` テンプレートと、別の `tool_use` テンプレート）を同梱する場合に使用される、独立した Jinja ファイルのディレクトリです。`default` テンプレートは引き続きリポジトリのルートにある `chat_template.jinja` に配置されますが、それ以外の名前付きテンプレートはすべて `additional_chat_templates/<name>.jinja` に配置します。ここで、ファイル名のステム（拡張子を除いた部分）がテンプレート名になります。

> [!WARNING]
> 以下のレガシー形式は、後方互換性のための読み込みのみを目的として保持されています。これらにチャットテンプレートを保存しないでください。

- `tokenizer_config.json` 内の `chat_template` フィールド。独立した `.jinja` ファイルが導入される以前に使用されていた、読み取り専用のレガシー形式です。テンプレートは `tokenizer_config.json` 内に JSON 文字列として埋め込まれます。モデルに複数の名前付きテンプレートがある場合、このフィールドは単一の文字列ではなく、`{"name": ..., "template": ...}` の辞書のリストになります。この形式を使用している既存のリポジトリは引き続き読み込むことができますが、[save_pretrained()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) は代わりに最新の `.jinja` 形式で書き出します。

- `chat_template.json`。旧型のマルチモーダルプロセッサチェックポイントで使用される、読み取り専用のレガシー形式です。`{"chat_template": "<template string>"}` という形式の JSON ファイルです。この形式を使用している既存のリポジトリは引き続き読み込むことができますが、[save_pretrained()](/docs/transformers/main/ja/model_doc/wav2vec2#transformers.Wav2Vec2Processor.save_pretrained) は代わりに最新の `.jinja` 形式で書き出します。レガシーの `chat_template.json` と最新の `.jinja` ファイルが混在しているプロセッサのリポジトリでは、読み込み時にエラーが発生します。

### 読み込みの優先順位

[from_pretrained()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.from_pretrained)を呼び出す際、Transformersは固定された優先順位に基づいてストレージ形式を解決します。単体の`.jinja`ファイルは、configに埋め込まれたテンプレートよりも優先されます。ローダーは以下のようになります。

1. `tokenizer_config.json`（プロセッサの場合は、レガシーの`chat_template.json`）に存在する`chat_template`フィールドを読み取ります。
2. リポジトリのルートに`chat_template.jinja`が存在する場合はそれを読み取り、`default`テンプレートとして使用します。この場合、ステップ1は上書きされます。
3. `additional_chat_templates/` 内のすべての `.jinja` ファイルを読み取り、ファイル名の語幹をキーとしてマージします。

結果に単一の`default`テンプレートがある場合、`~PreTrainedTokenizer.chat_template`はその文字列に設定されます。複数の名前付きテンプレートが存在する場合、`chat_template`は`{name: template_string}`の辞書になります。その場合、[apply_chat_template()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template)はツールが渡されたときに`tool_use`エントリを選択し、それ以外の場合は`default`を選択します。

### 保存

[save_pretrained()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.save_pretrained) および [push_to_hub()](/docs/transformers/main/ja/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub) は、デフォルトで `.jinja` 形式で書き出します。単一の文字列テンプレートは `chat_template.jinja` として保存されます。名前付きテンプレートの辞書の場合、`default` エントリは `chat_template.jinja` に、残りの各エントリは `additional_chat_templates/` 配下に1ファイルずつ書き出されます。重複を避けるため、`chat_template` フィールドは `tokenizer_config.json` から削除されます。

レガシー形式のいずれかにテンプレートを保存するサポートされた方法はありません。これらの形式は、古いリポジトリの読み込みにのみ使用されます。

### 古いリポジトリの更新

`tokenizer_config.json` または `chat_template.json` に埋め込まれたテンプレートを、推奨される `.jinja` 形式に移行するには、再度読み込んで保存してください。

ロードステップは、リポジトリが`chat_template`で使用するレガシー形式を正規化し、`~PushToHubMixin.push_to_hub`は`chat_template.jinja`ファイルを返します。

```py
from transformers import AutoTokenizer

tokenizer = AutoTokenizer.from_pretrained("your-org/your-model")
tokenizer.push_to_hub("your-org/your-model")
```

## ツールのテンプレート

ツール用のテンプレートを書くための特定の形式はありませんが、標準的な API に従うことをお勧めします。これにより、ユーザーがモデルでツールを使用するためにカスタムコードを書く必要なく、テンプレートが幅広いモデルで利用可能になります。

> [!WARNING]
> 空白や特殊トークンなどのフォーマットはモデルによって異なります。モデルが学習された際のフォーマットと完全に一致していることを確認してください。

次のセクションでは、ツール用のテンプレートを作成するための標準APIの要素をリストしています。

### ツール定義

[Tools](./chat_extras) は Python の関数または JSON スキーマとして渡されます。関数が渡された場合、JSON スキーマが自動的に生成され、テンプレートに渡されます。テンプレートが `tools` 変数にアクセスする際は、常に JSON スキーマのリストとして扱われます。

テンプレートは常にツールをJSONスキーマとして受け取りますが、モデルが学習時に使用した形式に合わせるためにレンダリングする際、この形式を根本的に変更する必要がある場合があります。例えば、[Command-R](./model_doc/cohere)は、Pythonの関数ヘッダーで定義されたツールで学習されました。テンプレートは内部でJSONスキーマの型を変換し、入力されたツールをPythonのヘッダーとしてレンダリングします。

以下の例は、JSON スキーマ形式でツールがどのように定義されるかを示しています。

```json
{
  "type": "function",
  "function": {
    "name": "multiply",
    "description": "A function that multiplies two numbers",
    "parameters": {
      "type": "object",
      "properties": {
        "a": {
          "type": "number",
          "description": "The first number to multiply"
        },
        "b": {
          "type": "number",
          "description": "The second number to multiply"
        }
      },
      "required": ["a", "b"]
    }
  }
}
```

チャットテンプレートにおけるツール定義の処理の例を以下に示します。具体的なトークンやレイアウトは、モデルが学習された際に使用されたものに合わせる必要があります。

```jinja
{%- if tools %}
    {%- for tool in tools %}
        {{- '<tool>' + tool['function']['name'] + '\n' }}
        {%- for argument in tool['function']['parameters']['properties'] %}
            {{- argument + ': ' + tool['function']['parameters']['properties'][argument]['description'] + '\n' }}
        {%- endfor %}
        {{- '\n</tool>' }}
    {%- endfor %}
{%- endif %}
```

### ツール呼び出し

ツール定義のレンダリングに加えて、テンプレート内で**ツール呼び出し**と**ツール応答**もレンダリングする必要があります。

ツール呼び出しは、一般的に`"assistant”`メッセージの`tool_calls`キーに渡されます。ほとんどのツール呼び出しモデルは単一のツール呼び出しのみをサポートしますが、この値は常にリストです。そのため、リストには通常、単一の要素のみが含まれます。

```json
{
  "role": "assistant",
  "tool_calls": [
    {
      "type": "function",
      "function": {
        "name": "multiply",
        "arguments": {
          "a": 5,
          "b": 6
        }
      }
    }
  ]
}
```

ツール呼び出しを処理するための一般的なパターンを以下に示します。これを起点として使用できますが、テンプレートがモデルの学習時に使用された形式と一致していることを必ず確認してください。

```jinja
{%- if message['role'] == 'assistant' and 'tool_calls' in message %}
    {%- for tool_call in message['tool_calls'] %}
            {{- '<tool_call>' + tool_call['function']['name'] + '\n' + tool_call['function']['arguments']|tojson + '\n</tool_call>' }}
    {%- endfor %}
{%- endif %}
```

### ツール応答

Tool responses は `tool` ロールを持つメッセージの dict です。tool calls と比べてはるかにシンプルで、通常は `role`、`name`、`content` キーのみが含まれます。

```json
{
  "role": "tool",
  "name": "multiply",
  "content": "30"
}
```

一部のテンプレートでは、`name` キー自体が不要な場合があります。その場合は、テンプレートを `content` キーのみを読み込むように記述できます。

```jinja
{%- if message['role'] == 'tool' %}
    {{- "<tool_result>" + message['content'] + "</tool_result>" }}
{%- endif %}
```

## コントリビュート

テンプレートの準備が整ったら、トークナイザーの`chat_template`属性に設定し、[apply_chat_template()](/docs/transformers/main/ja/internal/tokenization_utils#transformers.PreTrainedTokenizerBase.apply_chat_template)でテストします。期待通りに動作する場合は、[push_to_hub()](/docs/transformers/main/ja/main_classes/model#transformers.utils.PushToHubMixin.push_to_hub)でHubにアップロードします。

モデルの所有者でなくても、空のチャットテンプレートや誤ったチャットテンプレートを持つモデルにテンプレートを追加することは有益です。モデルのリポジトリで[プルリクエスト](https://hf.co/docs/hub/repositories-pull-requests-discussions)を開いて、テンプレートを追加してください!

```py
tokenizer.chat_template = template
tokenizer.push_to_hub("amazing_company/cool_model", commit_message="Add chat template", create_pr=True)
```

