HAROLABO Tech Blog

テクノロジーを使いやすく!ハロラボの技術情報発信メディアです。

日本語LLMを使えるAPIサーバーをローカルで動かす

猫も杓子もLLMの時代、一家に一台はLLMサーバーを持っておかないといけません。
というわけで、LLMを使えるAPIサーバーを立ててみました。

日本語LLMはELYZAさんのLlama-3-ELYZA-JPを利用しました。

note.com

huggingface.co

Hugging Faceに記載されている通りに実行すれば、LLMをローカルで動かすことができます。

LM Studioというツールがあり、そのツールを使うと、モデルのダウンロードから、チャット利用、APIサーバーの起動までできますので、簡単に動かすことができます。

準備

  1. LM Studioをインストールする。

    https://lmstudio.ai/

  2. 左メニューからDeveloperを選択し、APIサーバーモードにする。

  3. モデルをダウンロードする。

    上部のSelect a model to loadを選択し、モデル名を入力して検索すると、Hugging Faceに登録されたモデルが出てくるので、選択します。 今回はelyza/Llama-3-ELYZA-JP-8B-GGUを入力します。

  4. 左上のStatusのトグルボタンをクリックしてRunningにします。

これで準備完了です。 とっても簡単ですね。

APIの使い方

OpenAI API互換なのでとても簡単です。

import openai

client = openai.OpenAI(
    base_url="http://localhost:1234/v1",
    api_key = "dummy_api_key"
)

completion = client.chat.completions.create(
    model="dummy_model_name",
    messages=[
        {"role": "system", "content": "あなたは誠実で優秀な日本人のアシスタントです。特に指示が無い場合は、常に日本語で回答してください。"},
        {"role": "user", "content": "古代ギリシャを学ぶ上で知っておくべきポイントは?"}
    ]
)

print(completion)

これを実行すると、結果が表示されます。

ストリーミングで使う

私の環境はノートPCで、Core i7-13620H 2.40GHz、32GB RAM、NVIDIA GeForce RTX4060 Laptop GPUという構成です。 上記のコードではレスポンスが返ってくるまでに20秒弱かかってしまうので、あまり実用的ではないですね。

ストリーミング機能を使って、レスポンスを改善できるか試してみます。 LM Studioのドキュメントのストリーミングのサンプルを少し変更して簡単にしたのがこちらです。

from openai import OpenAI

system_prompt = "あなたは誠実で優秀な日本人のアシスタントです。特に指示が無い場合は、常に日本語で回答してください。"

client = OpenAI(base_url="http://127.0.0.1:1234/v1", api_key="lm-studio")
MODEL = "dummy_model_name"

def process_stream(stream, add_assistant_label=True):
    """Handle streaming responses from the API"""
    collected_text = ""
    first_chunk = True

    for chunk in stream:
        delta = chunk.choices[0].delta

        # Handle regular text output
        if delta.content:
            if first_chunk:
                print()
                if add_assistant_label:
                    print("Assistant:", end=" ", flush=True)
                first_chunk = False
            print(delta.content, end="", flush=True)
            collected_text += delta.content

    return collected_text

def chat_loop():
    messages = [
        {
            "role": "system",
            "content": system_prompt
        }
    ]
    
    while True:
        user_input = input("\nYou: ").strip()
        if user_input.lower() == "quit":
            break

        messages.append({"role": "user", "content": user_input})

        # Get initial response
        response_text = process_stream(
            client.chat.completions.create(
                model=MODEL,
                messages=messages,
                stream=True,
                temperature=0.2
            )
        )

        text_in_first_response = len(response_text) > 0
        if text_in_first_response:
            messages.append({"role": "assistant", "content": response_text})

if __name__ == "__main__":
    chat_loop()

入力から最初のレスポンスが返ってくるまでは一瞬で、それから全部の文章が表示し終わるまでに10秒強ほどとなりました。
これなら十分実用的に使えます。

システムプロンプトを変えれば、どんな種類の応答も実現できます。 プロンプトの書き方はいろんな記事があるので、参考にするとよいでしょう。

zenn.dev

tech-blog.cloud-config.jp

note.com