---
title: llama.cppとPLaMo翻訳モデルでOpenAI API互換の翻訳サーバーを立てる
summary: この記事では、llama.cppでPLaMo翻訳モデルをローカルのOpenAI API互換サーバーとして立ち上げ、日英翻訳をcurlで実行する手順を紹介します。
tags: ["llama.cpp", "OpenAI", "PLaMo"]
categories: ["AI", "LLM", "llama.cpp"]
date: 2025-12-22T14:24:41Z
updated: 2025-12-23T09:27:12Z
---

llama.cppで[PLaMo翻訳モデル](https://huggingface.co/pfnet/plamo-2-translate)を使い、OpenAI API互換サーバーを立てて翻訳を行うメモ。

### llama.cppのインストール

macOSの場合、Homebrewでインストールできます。

```bash
brew install llama.cpp
```

次のバージョンで試しました。

```bash
$ llama-server --version
ggml_metal_device_init: tensor API disabled for pre-M5 and pre-A19 devices
ggml_metal_library_init: using embedded metal library
ggml_metal_library_init: loaded in 0.007 sec
ggml_metal_rsets_init: creating a residency set collection (keep_alive = 180 s)
ggml_metal_device_init: GPU name:   Apple M4 Max
ggml_metal_device_init: GPU family: MTLGPUFamilyApple9  (1009)
ggml_metal_device_init: GPU family: MTLGPUFamilyCommon3 (3003)
ggml_metal_device_init: GPU family: MTLGPUFamilyMetal3  (5001)
ggml_metal_device_init: simdgroup reduction   = true
ggml_metal_device_init: simdgroup matrix mul. = true
ggml_metal_device_init: has unified memory    = true
ggml_metal_device_init: has bfloat            = true
ggml_metal_device_init: has tensor            = false
ggml_metal_device_init: use residency sets    = true
ggml_metal_device_init: use shared buffers    = true
ggml_metal_device_init: recommendedMaxWorkingSetSize  = 103079.22 MB
version: 7490 (5182dd64c)
built with AppleClang 17.0.0.17000404 for Darwin arm64
```


### OpenAI API Serverの起動

[GGUF形式のPLaMo翻訳モデル](https://huggingface.co/mmnga/plamo-2-translate-gguf)を使ってllama.cppでOpenAI API互換サーバーを起動します。

```bash
llama-server -hf mmnga/plamo-2-translate-gguf --alias plamo2-translate --port 8000
```

初回実行時はモデルのダウンロードが行われます。サーバーが起動すると、`http://localhost:8000`でOpenAI API互換のエンドポイントが利用可能になります。

### 翻訳APIの呼び出し

PLaMo翻訳モデルは以下の形式のプロンプトを使用します。

```
<|plamo:op|>dataset
translation
<|plamo:op|>input lang=Japanese
ここに翻訳対象のテキストを書きます
<|plamo:op|>output lang=English
```

#### 日本語から英語への翻訳

curlで日英翻訳を実行してみます。

```bash
curl http://127.0.0.1:8000/v1/chat/completions \
  --json '{
    "model": "plamo2-translate",
    "messages": [
      {
        "role": "user",
        "content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=Japanese\n家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。\n<|plamo:op|>output lang=English"
      }
    ]
  }' -s | jq .
```

レスポンスの`content`フィールドに翻訳結果が返されます。

```json
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Our household finances are in a mess. We're constantly being chased by monthly bills and have to keep dipping into our savings.\n"
      }
    }
  ],
  "created": 1766412843,
  "model": "plamo2-translate",
  "system_fingerprint": "b7490-5182dd64c",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 23,
    "prompt_tokens": 41,
    "total_tokens": 64
  },
  "id": "chatcmpl-IksvG1y16PKmmILVu1IFECqIk6pQJkgf",
  "timings": {
    "cache_n": 0,
    "prompt_n": 41,
    "prompt_ms": 15.982,
    "prompt_per_token_ms": 0.38980487804878045,
    "prompt_per_second": 2565.3860593167315,
    "predicted_n": 23,
    "predicted_ms": 563.22,
    "predicted_per_token_ms": 24.487826086956524,
    "predicted_per_second": 40.83661801782607
  }
}
```

「火の車」という慣用句が適切に翻訳されていることが分かります。

#### 英語から日本語への翻訳

逆方向の翻訳も同様に実行できます。`input lang`と`output lang`を入れ替えます。

先ほどの日英翻訳結果を、今度は日本語に翻訳してみます。

```bash
curl http://127.0.0.1:8000/v1/chat/completions \
  --json '{
    "model": "plamo2-translate",
    "messages": [
      {
        "role": "user",
        "content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=English\nOur household finances are in a mess. We''re constantly being chased by monthly bills and have to keep dipping into our savings.\n<|plamo:op|>output lang=Japanese"
      }
    ]
  }' -s | jq .
```

```json
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "私たちの家計はめちゃくちゃだ。毎月の支払いに追われてばかりで、貯金を切り崩さなければならない状態が続いている。\n"
      }
    }
  ],
  "created": 1766412917,
  "model": "plamo2-translate",
  "system_fingerprint": "b7490-5182dd64c",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 20,
    "prompt_tokens": 46,
    "total_tokens": 66
  },
  "id": "chatcmpl-SZQLJKG4azv7woIIYzUTWyjc1DaUnbgZ",
  "timings": {
    "cache_n": 0,
    "prompt_n": 46,
    "prompt_ms": 10.012,
    "prompt_per_token_ms": 0.21765217391304348,
    "prompt_per_second": 4594.486616060727,
    "predicted_n": 20,
    "predicted_ms": 495.466,
    "predicted_per_token_ms": 24.7733,
    "predicted_per_second": 40.36603924386335
  }
}
```

元の日本語とは異なる表現になっていますが、意味は正しく翻訳されています。

### チャットテンプレートを使ったプロンプト形式の簡略化

上記の方法では、毎回プロンプト形式を指定する必要があります。llama.cppのチャットテンプレート機能を使うと、この定型部分を隠蔽してシンプルなAPIリクエストにできます。
ただし、翻訳元と翻訳先の言語をパラメータ化する方法がわからなかったため、ここでは日英翻訳専用のテンプレートを作成する例を示します。

以下のJinjaテンプレートを`/tmp/ja2en.jinja`として保存します。

```jinja
<|plamo:op|>dataset
translation
<|plamo:op|>input lang=Japanese
{% for message in messages %}
{% if message.role == 'user' %}{{ message.content }}{% endif %}
{% endfor %}
<|plamo:op|>output lang=English
```

このテンプレートを使ってサーバーを起動します。

```bash
llama-server -hf mmnga/plamo-2-translate-gguf --alias plamo2-translate --port 8000 --chat-template-file /tmp/ja2en.jinja
```

これで、プロンプト形式を意識せずに翻訳対象のテキストのみを送信できます。

```bash
curl http://127.0.0.1:8000/v1/chat/completions \
  --json '{
    "model": "plamo2-translate",
    "messages": [
      {
        "role": "user",
        "content": "家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。"
      }
    ]
  }' -s | jq .
```

### router modeを使用する

llama.cppの新機能である[router mode](https://huggingface.co/blog/ggml-org/model-management-in-llamacpp)を使うと、複数のモデルを同時にサーバーでホストし、APIリクエストごとに使用するモデルを切り替えられます。
この場合は、チャットテンプレートを使用しない方がいいかもしれません。

router modeでは起動時にモデル名を指定しません。ただし、モデルは事前にダウンロードしておく必要があります。

```bash
llama-server --port 8000
```

次のようなログが出ます

```
main: router server is listening on http://127.0.0.1:8000
main: NOTE: router mode is experimental
main:       it is not recommended to use this mode in untrusted environments
```

リクエスト時に`model`パラメータで使用するモデルを指定します。

```bash
curl http://127.0.0.1:8000/v1/chat/completions \
  --json '{
    "model": "mmnga/plamo-2-translate-gguf",
    "messages": [
      {
        "role": "user",
        "content": "<|plamo:op|>dataset\ntranslation\n<|plamo:op|>input lang=Japanese\n家計は火の車だ。毎月の支払いに追われ、貯金を切り崩す日々が続いている。\n<|plamo:op|>output lang=English"
      }
    ]
  }' -s | jq .
```

次のようなレスポンスが返されます。

```json
{
  "choices": [
    {
      "finish_reason": "stop",
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "Our household is in dire straits. We're constantly being chased by monthly payments and having to withdraw from our savings.\n"
      }
    }
  ],
  "created": 1766450748,
  "model": "mmnga/plamo-2-translate-gguf",
  "system_fingerprint": "b7490-5182dd64c",
  "object": "chat.completion",
  "usage": {
    "completion_tokens": 23,
    "prompt_tokens": 41,
    "total_tokens": 64
  },
  "id": "chatcmpl-CLI7iYPEAXoH4qtZeQHSDIomjC3kTljz",
  "timings": {
    "cache_n": 0,
    "prompt_n": 41,
    "prompt_ms": 18.575,
    "prompt_per_token_ms": 0.45304878048780484,
    "prompt_per_second": 2207.2678331090174,
    "predicted_n": 23,
    "predicted_ms": 579.414,
    "predicted_per_token_ms": 25.19191304347826,
    "predicted_per_second": 39.69527833293638
  }
}
```


---

llama.cppを使うことで、PLaMo翻訳モデルをOpenAI API互換のサーバーとしてローカルで実行できました。OpenAI APIで翻訳ができれば、既存のアプリケーションからも翻訳機能を利用しやすくなります。
