🦙

Meta Llama Academy準備レポート:Llama 3.2を用いたローカルLLM構築とプロンプトエンジニアリング実践

に公開

11/20~11/21に開催された「Meta Llama Academy」に参加しました。
https://luma.com/q6q73ztw?tk=Ra16rq
本イベントに参加するにあたっていくつか事前準備や情報収集を行ったため、記事にして残そうと思っています。

記事のボリュームが大きいため、目的に応じて以下のセクションを参照いただければと思っています。

  • 第1章〜第4章:イベントレポート + 環境構築
    • ローカルLLMのメリット、技術パラメータの解説、Dockerによる環境構築手順についてまとめています。
  • 第5章:Llamaでのプロンプトエンジニアリング・実装内容・技術紹介
    • TypeScript/Swiftを用いた実装コードをベースに、Llamaのプロンプトフォーマット、RAG、Tool Callingなどの応用実装について解説しています。

1. ローカルLLMは他のLLMとどう違うのか

そもそもこのハッカソンに参加するまで、私はChatGPTやGeminiなどのクラウドベースのLLMを主に使用しており、ローカルLLM(Llama等)の実装経験は皆無でした。

なので、そもそもローカルLLMってなにができるのかについて調査したところ、エンジニアリングの観点から以下のような明確なメリットがあることを知りました。

  • 完全無料(Cost Efficiency):自分のPCのリソースを使うため、API利用料やサブスクリプション費用がかかりません。トークン量を気にせず試行錯誤できます。
  • プライバシーとセキュリティ(Privacy):データが外部のサーバーに送信されず、すべてローカルネットワーク内で完結します。機密情報や個人的なデータを安心して処理させることができます。
  • オフライン・低遅延(Latency Reduction):インターネット通信を行わないため、ネットワーク遅延がなく、オフライン環境でも高速に応答を得られます。
  • カスタマイズ性(Customization):モデルの「温度感(Temperature)」を調整したり、特定の役割を与えたカスタムモデル(Modelfile)を容易に作成できます。
  • モデルが豊富:画像処理に特化したモデルや日本語性能を強化したモデルなど、用途に応じて最適なモデルを差し替えることができます。

このように多くのメリットがある一方で、ハードウェアリソースの管理という課題にも直面しました。 実際にDockerでVisionモデルを実行しようとした際、以下のようなメモリエラーが発生しました。

["max_tokens": 256, "prompt": "What kind of plant is this?", "images": "[base64 omitted]", "model": "llama3.2-vision"]
Error: Vision HTTP Error (500): {"error":"model requires more system memory (10.9 GiB) than is available (7.2 GiB)"}

使用するモデルがどれくらいのメモリを必要とするのか、事前のサイジングが重要であることを痛感しました。

2. LLMの技術概念を把握する

仕事として主に開発でLLMを使うことがメインなので、どのような原理でLLMって動いているのかについてはかなり疎かったです。
また、これまでの使用してきたLLMはどれも「流行っているから」「みんな言うから」という理由でした。今回をきっかけに、選択してLLMやそのモデルを選択できるようになるようになれればと思い、少しではありますがLLMで使用される技術について深掘りました。
この記事では主に、モデル選定や性能比較の際に欠かせない、LLMの中核となる技術パラメータについて取り上げます。
1. モデルパラメーター(Parameters)
モデルがトレーニング中に学習する内部的な重みとバイアスの集合。パラメーター数が多いほどモデルの知識と推論能力は向上しますが、推論に必要な計算リソース(VRAM等)も比例して増大します。
2. コンテキスト長(Context Length)
モデルが一度に処理できる最大のトークン数(入力+出力)。
3. 埋め込み長(Embedding Length)
入力テキスト内の各トークンを表現するために使用されるベクトル表現の「次元数」。この次元数が多いほど、単語や概念間のより複雑なニュアンスや意味的な関係性を捉えることが可能になり、モデルの表現力が豊かになる。
4. 量子化(Quantization)
モデルの重み(パラメーター)の精度を落とすことで、モデルサイズを圧縮する技術。これにより、非力なハードウェアやメモリの少ない環境でも大規模モデルの実行が可能になります。
5. Temperature(温度)
出力の「創造性・ランダム性」を制御する値(0.0 〜 2.0)。

  • 低い値(0.0-0.3): 決定論的。コード生成など一貫性が必要な場合に適しています。
  • 中間値(0.7-1.0): バランスの取れた創造性
  • 高い値(1.5-2.0): 非常に創造的。アイデア出しなどに適していますが、ハルシネーションも増えます。

6. Max Tokens(最大トークン数):
生成される応答の最大長を制御します。コスト管理とレスポンス時間の最適化に重要な値です。

7. Top-P(Nucleus Sampling)
確率分布の上位P%のトークンからサンプリングを行う手法(0.0 〜 1.0)。

  • : Top-P=0.9の場合
    • 全トークンを確率順に並べる: [0.4, 0.3, 0.15, 0.1, 0.05]
    • 累積確率が0.9に達するまで選択: [0.4, 0.3, 0.15, 0.1] ✅(合計0.95)
    • 最後の0.05は除外 ❌
  • 使い分け:
    • Top-P=1.0: 全トークンが候補(多様性最大)
    • Top-P=0.9: バランス良く多様性を確保(推奨)
    • Top-P=0.5: より保守的で一貫性重視

3. Llamaのドキュメントとモデル選定

ドキュメントを確認すると、パラメータ数によって「どのようなタスクに向いているか」が明記されています。
https://ollama.com/library/llama3.2:latest

Llama 3.2には「1B」「3B」といったサイズが用意されています(B = Billion(10億))。コンピューティングリソースと精度のトレードオフを意識し、以下のように比較・整理しました。

項目 1B モデル (Llama 3.2 1B) 3B モデル (Llama 3.2 3B) 比較のポイント
パラメータ数 約10億 約30億 値が大きいほど複雑な処理が可能
賢さ・精度 基本的なタスク向け(要約、単純な書き換え) より高性能(指示の遵守、論理的推論、ツール利用が得意) パラメータが増えると文脈理解力が向上
ファイルサイズ 小さい (約1.3GB) 大きい (約2.0GB) ストレージ容量とメモリ使用量に影響
主な用途 エッジデバイス(スマホ等)での実行、個人のメモ整理 高度なアシスタント機能、より複雑な会話や論理的処理

4. Docker環境の構築

ここからは実際の環境構築手順です。今回は再現性を高めるためにDockerを活用しました。

1. イメージを取得

docker pull ollama/ollama:latest

Ollamaサーバーソフトウェア本体のDockerイメージをダウンロードします。
https://hub.docker.com/r/ollama/ollama

2. コンテナ起動

docker run -d -v ollama:/root/.ollama -p 11434:11434 --name ollama ollama/ollama

ダウンロードしたイメージを使い、Ollamaコンテナを起動します。これがOllamaサーバーの本体になります。

  • -d: コンテナをバックグラウンドで実行します(Detachedモード)
  • -v ollama:/root/.ollama: ダウンロードしたモデル(llama3.2など)をPC本体に保存するための設定(ボリューム)です。これがないとコンテナを停止・削除するたびにモデルが消えてしまいます。
  • -p 11434:11434: Ollamaが通信に使うポート(11434)を、PC本体のポートと接続します
  • --name ollama: コンテナにollamaという分かりやすい名前を付けます

3. ollama がインストールされているか確認

docker exec -it ollama ollama list

ollamaコンテナ内で ollama list コマンドを実行し、現在ダウンロード済みのモデル一覧を表示します。

4. 動作させるモデルをプルする

docker exec -it ollama ollama pull llama3.2
docker exec -it ollama ollama pull llama3.2-vision
docker exec -it ollama ollama pull nomic-embed-text
docker exec -it ollama ollama pull llama-guard3

コンテナ内で必要なモデルをダウンロードしておきます。

5. Llamaの実装とプロンプトエンジニアリング

環境構築が完了したので、ここからはアプリケーションへの実装に入ります。 Meta Llama Academyの資料や公式ガイドを参考に、普段使い慣れているTypeScriptとSwiftでコードをリファクタリングし、CLIツールとして再実装しました
Prompt Engineering with Llama 2 & 3
Introducing Multimodal Llama 3.2
Ollama Course – Build AI Apps Locally
Llama API Guide

作成したリポジトリ:
https://github.com/test-aitest/llama-prompt-cli

単にAPIを叩くだけではなく、実際にコードに落とし込むことで、パラメータの影響やプロンプトの挙動が直感的に理解できるようになりました。以下、このリポジトリの実装内容に沿って、Llama 3.2を使いこなすための技術詳細を解説します。

エンドポイントとストリーミング

今回は主に2つのエンドポイントを使用しました。

1. /api/generate - 単発生成
2. /api/chat - 会話形式

また、UXにおいて重要なのが**ストリーミング(Streaming)**です。
非ストリーミング(stream: false:

  • 全ての応答が生成されるまで待機
  • シンプルな実装
  • バッチ処理に適している

ストリーミング(stream: true:

  • リアルタイムで応答を受信
  • ユーザー体験の向上
  • チャットアプリケーションに最適

プロンプトフォーマットとテンプレート

モデルのバージョンによって、プロンプトを構成する特殊トークン(Special Tokens)が異なります。ここを間違えると精度が著しく低下するため注意が必要です。

Llama 2 のプロンプトフォーマット

特殊トークン

トークン 意味 用途
<s> Beginning of Sequence (BOS) シーケンスの開始
</s> End of Sequence (EOS) シーケンスの終了
[INST] Instruction Start ユーザー指示の開始
[/INST] Instruction End ユーザー指示の終了
<<SYS>> System Start システムプロンプトの開始
<</SYS>> System End システムプロンプトの終了

基本フォーマット

シングルターン(システムプロンプト付き):

<s>[INST] <<SYS>>
{{ system_prompt }}
<</SYS>>

{{ user_message }} [/INST] {{ model_answer }} </s>

Llama 3 のプロンプトフォーマット

Llama 3では完全に新しいトークン体系を導入し、より構造化されたフォーマットになりました。

特殊トークン

トークン 意味 用途
<|begin_of_text|> Beginning of Text (BOS) テキスト全体の開始
<|end_of_text|> End of Text (EOS) テキスト全体の終了
<|start_header_id|> Header Start ロール(役割)の開始
<|end_header_id|> Header End ロール(役割)の終了
<|eot_id|> End of Turn メッセージの終了(ターンの終わり)

ロール(Roles)

  • system: システムプロンプト(任意)
  • user: ユーザーのメッセージ
  • assistant: AIアシスタントの応答

基本フォーマット

シングルターン(システムプロンプト付き):

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

{{ system_prompt }}<|eot_id|><|start_header_id|>user<|end_header_id|>

{{ user_message }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>

マルチターン会話:

<|begin_of_text|><|start_header_id|>system<|end_header_id|>

{{ system_prompt }}<|eot_id|><|start_header_id|>user<|end_header_id|>

{{ user_message_1 }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>

{{ model_answer_1 }}<|eot_id|><|start_header_id|>user<|end_header_id|>

{{ user_message_2 }}<|eot_id|><|start_header_id|>assistant<|end_header_id|>

プロンプトエンジニアリング

プロンプトエンジニアリングは、LLMから最適な応答を引き出すための技術です。

1. Zero-Shot Learning(ゼロショット学習)

例を示さず、プロンプトのみでタスクを実行。シンプルで実装が容易であり、汎用的なタスクに有効です。

// src/Prompt Engineering with Llama 2 & 3/typescript/L4_prompt_engineering_techniques.ts より
const prompt = `
次の文章の感情を分析してください(ポジティブ/ネガティブ/中立):

「この製品は期待以上でした!」

感情:
`;

const response = await client.generate(prompt);
// 出力: "ポジティブ"

2. Few-Shot Learning(フューショット学習)

例を示してパターンを学習させる手法です。Zero-Shot(例なし)と比較して、特定のフォーマットで出力させたい場合に威力を発揮します。

const prompt = `
以下の例に従って、文章の感情を分析してください。

例1:
文章: 「最高の一日でした!」
感情: ポジティブ

例2:
文章: 「がっかりしました。」
感情: ネガティブ

例3:
文章: 「普通の商品です。」
感情: 中立

実際の分析:
文章: 「素晴らしいサービスでした!」
感情:
`;

const response = await client.generate(prompt);
// 出力: "ポジティブ"

3. Role Prompting(ロールプロンプティング)

LLMに特定の役割を与えることで、回答の質やトーンをコントロールします。

const systemPrompt = `
あなたは20年の経験を持つシニアSwiftエンジニアです。
技術的に正確で、初心者にも分かりやすい説明を心がけてください。
`;

const messages = [
  { role: 'system', content: systemPrompt },
  { role: 'user', content: 'Swiftのイニシャライザについて教えてください' }
];

// より専門的で教育的な応答が得られる

実装例(海賊口調のライフコーチ):

const pirateCoach = `
あなたは海賊の船長であり、人生のコーチでもあります。
海賊らしい口調(「Arrr!」「Matey!」など)で、
前向きなアドバイスをしてください。
`;

const response = await client.generate(pirateCoach + '\n\n仕事で失敗しました...');
// "Arrr, Matey! 失敗は成功への航路じゃ!次は違う風に帆を張るんじゃ!"

4. Chain-of-Thought(CoT)推論

「ステップバイステップで考えて」と指示することで、論理的な推論精度を向上させる手法です。数学的な問題や複雑なロジックを解かせる際に有効です。

const prompt = `
問題: レストランの請求書が$42.50でした。15%のチップを払いたいです。
合計でいくら払えばいいですか?

ステップバイステップで計算してください:
`;

const response = await client.generate(prompt);

/*
出力:
ステップ1: 15%のチップを計算
$42.50 × 0.15 = $6.375

ステップ2: チップを四捨五入
$6.38

ステップ3: 合計を計算
$42.50 + $6.38 = $48.88

答え: $48.88
*/

マルチターン会話の管理

チャットボットを作成する場合、コンテキストウィンドウ(記憶容量)の管理が重要になります。 Llama 3.2は128K tokensという巨大なコンテキストを持っていますが、無限ではありません。

実装した戦略:

  1. 会話履歴の維持: 過去のやり取りを配列で保持し、APIに送信する。
  2. 古いメッセージの削除: トークン制限に近づいたら、古いやり取りを削除
  3. セッション永続化: 会話履歴をJSONファイルに保存し、再起動後も会話を継続できるようにする。

マルチモーダル(画像認識)

Llama 3.2 Visionモデルを使用すると、テキストと画像を同時に処理できます。 今回は「OCR(文字認識)」や「レシート解析」の実装を行いました。画像はBase64エンコードしてAPIに渡す必要がありますが、テキスト処理と同じ感覚で画像の内容について質問できるのは非常に強力だと感じました。

安全性とモデレーション

プロダクトとして公開する場合、入出力の安全性チェックは必須です。 MetaはLlama Guardという専用モデルを提供しており、これを「入力チェック用」や「出力チェック用」のフィルタとして挟むことで、暴力、ヘイトスピーチ、個人情報漏洩などのリスクを13のカテゴリで判定できます。

13の安全性カテゴリ:

  • S1: 暴力犯罪
  • S2: 非暴力犯罪
  • S3: 性犯罪
  • S4: 児童性的虐待
  • S5: 名誉毀損
  • S6: 専門的な助言の誤用
  • S7: プライバシー侵害
  • S8: 知的財産権
  • S9: 無差別兵器
  • S10: ヘイトスピーチ
  • S11: 自殺・自傷行為
  • S12: 性的コンテンツ
  • S13: 選挙干渉
  • S14: コード実行機能の悪用

RAG(検索拡張生成)

RAG(Retrieval-Augmented Generation)は、LLMが知らない外部知識を検索して注入する手法です。

  1. ドキュメントをEmbedding(ベクトル化)して保存。
  2. ユーザーの質問に関連するチャンクを検索。
  3. 検索結果をコンテキストとしてLLMに渡し、回答を生成。

ハルシネーション防止

ハルシネーションは、LLMが事実でない情報を生成する現象のことです。

防止策:

  1. コンテキスト制約: 「コンテキストに基づいてのみ答えよ」と指示
  2. 信頼度の表示: 「確信度が低い場合は明示せよ」
  3. 引用の要求: 「情報源を引用せよ」

Tool Calling(関数呼び出し)

Tool Callingは、LLMが自ら「この質問に答えるには、この関数を実行する必要がある」と判断し、関数を呼び出す機能です。

今回は、複数のツールの中から適切なものを選択する実装や、ツール実行結果をもとにさらに次の行動を決定する「Re-Actループ」の実装までを行いました。これにより、LLM単体では不可能な「現在の天気の取得」や「データベース操作」などが可能になります。

6. まとめ

今回のMeta Llama Academyへの参加と実装を通じて、ローカルLLMは単なる「チャットボット」ではなく、アプリケーションの中核に組み込める「推論エンジン」であることを実感しました。 特にLlama 3.2は軽量ながら非常に賢く、個人のPCでも十分に実用的なアプリケーションが開発可能だと感じました。

本記事で紹介したコードが、ローカルLLM開発の一助になれば幸いです。また、これからMetaのハッカソンに参加される方はぜひ準備の参考になれれば嬉しいです。

参照

Prompt Engineering with Llama 2 & 3
Introducing Multimodal Llama 3.2
Ollama Course – Build AI Apps Locally
Chat and conversation with Llama API
Prompt engineering

Discussion