> ## Documentation Index
> Fetch the complete documentation index at: https://docs.sambanova.ai/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# カスタムチャットテンプレートと出力パース入門

SambaNova の Chat Completions API を呼び出すと、プラットフォームはサーバー側でモデルのデフォルトの Jinja ベースのチャットテンプレートを適用し、メッセージをモデルが受け取る生のプロンプトに整形します。ほとんどのユースケースではこの動作が適切ですが、シナリオによっては、プロンプトの整形と出力のパースをクライアント側で制御する必要があります。

本ページでは、カスタムテンプレートと組み合わせて Completions API を使用すべき場面とその理由、およびカスタム出力パーサーの実装方法を解説します。インタラクティブな完全版のウォークスルーについては、[Custom Chat Templates AI スターターキット](https://github.com/sambanova/ai-starter-kit/tree/main/chat_templates) をご覧ください。

## カスタムチャットテンプレートを使用する場面

次のような場合は、Chat Completions API の代わりに、カスタムチャットテンプレートと Completions API を使用してください。

* **プロンプト構造を完全に制御する必要がある場合。** ワークフローによっては、Chat Completions API のパラメータでは指定できないカスタム変数、特殊トークン、指示を挿入する必要があります。カスタマイズを行わない SambaCloud の標準モデルには、組み込みの Function calling (関数呼び出し) を備えた Chat Completions API の使用を推奨します。詳細は [Function calling と JSON モード](/docs/ja/features/function-calling) をご参照ください。
* **BYOC (Bring Your Own Checkpoint) モデルを使用している場合。** SambaStack にデプロイされたファインチューニング済みのチェックポイントは、ベースモデルとは異なるチャットテンプレートを使用していることがあります。サーバー側でベースモデルのデフォルトテンプレートを適用させると、これらのチェックポイントに対して不正なプロンプトが生成されます。
* **モデルが非標準のツール呼び出し出力形式を使用している場合。** ファインチューニングされたモデルは、デフォルトのパーサーでは処理できない形式 (たとえば JSON ではなく XML マーカー) でツール呼び出しを出力することがあります。

## 仕組み

`/v1/chat/completions` を呼び出す代わりに、プロンプト文字列を自身でレンダリングし、`/v1/completions` に直接送信します。サーバーは生の文字列を受け取り、テンプレートを一切適用せずに、そこから続きを生成します。

このワークフローは次の 4 つのステップで構成されます。

1. **チャットテンプレートの読み込み。** Hugging Face のトークナイザーから Jinja テンプレートを取得するか、カスタムテンプレートを作成します。
2. **プロンプトのレンダリング。** テンプレートをメッセージとツール定義に適用し、生のプロンプト文字列を生成します。
3. **Completions API の呼び出し。** レンダリングした文字列を `/v1/completions` に送信します。
4. **出力のパース。** 生のテキスト応答を、ツール呼び出しを含む構造化された assistant メッセージに変換します。

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

### Hugging Face モデルからの読み込み

`transformers` ライブラリを使用してベースモデルのトークナイザーを読み込み、組み込みのチャットテンプレートを抽出します。

```python theme={}
from transformers import AutoTokenizer
 
tokenizer = AutoTokenizer.from_pretrained(
    "meta-llama/Llama-3.1-8B-Instruct",
    use_auth_token="<your-hf-token>"
)
chat_template = tokenizer.chat_template
```

Llama ファミリーなどのゲート付きモデル (gated model) には Hugging Face トークンが必要です。[Hugging Face のトークン設定](https://huggingface.co/settings/tokens) をご参照ください。

### カスタム Jinja テンプレートの定義

チェックポイントがベースモデルと異なるテンプレートを使用している場合は、Jinja2 テンプレートを直接記述します。テンプレートは、少なくとも `messages`、`tools`、`add_generation_prompt` の各変数を処理できる必要があります。

```python theme={}
from jinja2 import Environment, TemplateSyntaxError
 
custom_template = """
{{ bos_token }}
{%- for message in messages %}
    {%- if message['role'] == 'user' %}
        {{ '<|User|>' + message['content'] }}
    {%- elif message['role'] == 'assistant' %}
        {{ '<|Assistant|>' + message['content'] + eos_token }}
    {%- endif %}
{%- endfor %}
{% if add_generation_prompt %}{{ '<|Assistant|>' }}{% endif %}
"""
 
# Validate syntax before use
try:
    Environment().parse(custom_template)
except TemplateSyntaxError as e:
    raise ValueError(f"Invalid Jinja template at line {e.lineno}: {e.message}")
```

## プロンプトのレンダリング

Jinja2 を使用して、テンプレートをメッセージとツール定義に適用します。トークナイザーから読み込んだテンプレートを使用する場合は、`bos_token` や `eos_token` などのトークナイザー属性をコンテキスト変数として渡します。カスタムテンプレートの場合は、これらの値を明示的に指定してください。

```python theme={}
from jinja2 import Template
from datetime import datetime
 
messages = [
    {"role": "system", "content": "You are a helpful assistant."},
    {"role": "user", "content": "What is the population of Bogota?"}
]
 
tools = [
    {
        "type": "function",
        "function": {
            "name": "get_population",
            "description": "Returns the population of a city.",
            "parameters": {
                "type": "object",
                "properties": {
                    "city": {"type": "string", "description": "Name of the city"}
                },
                "required": ["city"]
            }
        }
    }
]
 
context = {
    "messages": messages,
    "tools": tools,
    "add_generation_prompt": True,
    "bos_token": "<|begin_of_text|>",
    "eos_token": "<|eot_id|>",
    "date_string": datetime.now().strftime("%d %b %Y"),
}
 
rendered_prompt = Template(chat_template).render(**context).strip()
```

## Completions API の呼び出し

レンダリングしたプロンプト文字列を `/v1/completions` エンドポイントに送信します。このエンドポイントは生の文字列を受け取り、生の文字列を返します。サーバー側でテンプレートは適用されません。

```python theme={}
from sambanova import SambaNova
 
client = SambaNova(
    api_key="<your-sambanova-api-key>",
    base_url="https://api.sambanova.ai/v1"
)
 
response = client.completions.create(
    model="Meta-Llama-3.1-8B-Instruct",
    prompt=rendered_prompt,
    max_tokens=2048,
    temperature=0.0,
)
 
raw_output = response.choices[0].text
```

## モデル出力のパース

生のテキスト応答は、構造化された assistant メッセージにパースする必要があります。適切なパーサーは、モデルが出力するツール呼び出しの形式によって異なります。

### JSON 形式 (Llama 系)

Llama の instruction チューニング済みモデルは、応答テキスト内に JSON オブジェクトとしてツール呼び出しを出力します。

```python theme={}
import json
 
def parse_llama_output(response: str) -> list[dict]:
    """Extract JSON tool calls from a Llama-style response."""
    tool_calls = []
    brace_count, start = 0, None
 
    for i, ch in enumerate(response):
        if ch == "{":
            if brace_count == 0:
                start = i
            brace_count += 1
        elif ch == "}" and start is not None:
            brace_count -= 1
            if brace_count == 0:
                block = response[start:i + 1]
                obj = json.loads(block)
                tool_calls.append({
                    "type": "function",
                    "function": {
                        "name": obj["name"],
                        "arguments": json.dumps(obj["parameters"])
                    }
                })
                start = None
    return tool_calls
```

### XML 形式 (DeepSeek 系)

DeepSeek モデルは、XML マーカーでツール呼び出しを区切ります。

```python theme={}
import re
import json
 
def parse_deepseek_output(response: str) -> list[dict]:
    """Extract XML-delimited tool calls from a DeepSeek-style response."""
    tool_calls = []
    pattern = r"<｜tool▁call▁begin｜>(.*?)<｜tool▁sep｜>(.*?)<｜tool▁call▁end｜>"
 
    for name, args in re.findall(pattern, response, re.DOTALL):
        tool_calls.append({
            "type": "function",
            "function": {
                "name": name.strip(),
                "arguments": args.strip()
            }
        })
    return tool_calls
```

### assistant メッセージの構築

ツール呼び出しを抽出したら、OpenAI 互換形式で最終的な assistant メッセージを組み立てます。

```python theme={}
def build_assistant_message(response: str, tool_calls: list) -> dict:
    if tool_calls:
        return {"role": "assistant", "content": None, "tool_calls": tool_calls}
    return {"role": "assistant", "content": response.strip(), "tool_calls": []}
```

## カスタムパーサー

モデルがデフォルト以外の形式でツール呼び出しを出力する場合は、カスタムパーサーを実装してください。パーサーは、生の応答文字列を受け取り、OpenAI 互換形式のツール呼び出し dict のリストを返す必要があります。

```python theme={}
def parse(response: str) -> list[dict]:
    """
    Custom parser template.
    Returns a list of tool-call dicts:
    [{"type": "function", "function": {"name": str, "arguments": str}}, ...]
    """
    # Implement your extraction logic here
    return []
```

> **注:** カスタムパーサーはユーザーが用意したコードを実行します。信頼できるコードのみを実行してください。これはサンドボックス化された実行環境ではありません。

## 次のステップ

* エンドツーエンドの完全なワークフロー、インタラクティブな Streamlit アプリ、Jupyter ノートブックについては、[Custom Chat Templates AI スターターキット](https://github.com/sambanova/ai-starter-kit/tree/main/chat_templates) をご覧ください。
* カスタムテンプレートを使用しない標準的な Function calling については、[Function calling と JSON モード](/docs/ja/features/function-calling) をご参照ください。
