LLM勉強会 〜基礎からエージェント設計まで〜

LLM勉強会

基礎からエージェント設計まで

Tomoki Yoshida (birder)🐦

DeNA
AI技術開発部AIイノベーショングループ

2025-12-01 (月) 13:00-16:00

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

みなさんの3時間絶対に無駄にしません!

本気で準備しました!
どうか今日だけは内職ご遠慮ください🙏

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

今日の流れ

  • イントロダクション
  • 前半
    • 知識
    • 実践演習(ハンズオン)
  • 後半
    • 知識
    • 実践演習(ハンズオン)
  • 案件の実例紹介

詳細時間配分

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

イントロダクション

Slackでぜひ盛り上がってください!

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

こんなこと思ったことありませんか?

  • 難しいタスクのプロンプトをチューニングしているけどうまくいかない
  • Web版Gemini/ChatGPTとAPI実装時の差分がわからないので、Webでやった技術検証をどこまで信じていいかわからない
  • DeepResearch/NotebookLMってすごいけど、内部どうなっているの?
  • LLMプロダクトのパーソナライズって何を考えればいいの?

今日はこれらの解決を目指します!

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

背景

  • AIの普及で非エンジニアでも誰でもなんでもできる時代になった
  • LLMを使うだけならAPIを呼ぶだけ(非エンジニアでも簡単)
  • 案件の数に比べて圧倒的にAIエンジニアの数が少ない

狙い

  • 誰でもLLMを組み合わせた設計をイメージできる
  • 簡単なPoCを誰でもできる
  • プロダクトのフィードバックループの設計イメージができる
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

今日みなさんが目指す姿

エンジニア

  • 適切に問題を分解し、LLMを組み合わせて問題を解ける
  • コンテキストエンジニアリングできる
  • データを活用した設計ができる
  • 今日の演習問題すべて解ける

設計し、実装までできる

非エンジニア

  • データを活用したプロダクト設計をイメージできる
  • (AI)エンジニアの業務理解
  • 用語や概念の理解
  • 簡単なPoCならできる

理解してイメージできる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

環境設定

情報の取り扱い注意

今日の勉強会では業務データ入力禁止

後半演習の一部でn8nを使いますが見られても良いデータのみ可

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

知識 ~前半~

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

いろんなLLM

  • Google
    • Gemini-2.5-Pro
    • Gemini-2.5-Flash
    • Gemini-2.5-Flash-Lite

何がどう優れているの?どう違うの?

「LLM Leaderboard」で検索!

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

LLMの仕組み

Next Token Prediction

次のカッコに入るのは何?

例:This is a (   )

を解いている

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

こんなのでうまく答えられるの?

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

Instruction Tuning

  • 指示に従うようにするためのファインチューニング

  • オープンなLLMで-Instruct-itって付いているのはこれ

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

Reasoning / Thinking とは?

  • 推論/思考してから応答するように学習されている(GPT-5, o1, Gemini-2.5-Pro等)
  • 推論/思考が無いモデルはそのまま応答を出力(GPT-4o, Gemini-2.5-Flash-Lite等)

DeepSeek-R1では思考を<think></think>で出力後、実際の応答が返ってくる

内容
入力 日本の首都は?
出力 <think>ユーザーは日本の首都について質問している。これは事実に基づく知識(Fact-based QA)である。私の知識によれば、日本の首都は東京である。</think>東京です。
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

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

基本テクニック

  • Markdown/XML記法で書く
  • 構造化出力を使う
  • 適切な単位でプロンプトを分ける
  • 入出力例を与える(Few-shot)
  • ステップの明示(Chain of Thought)
  • 理由を説明させてから回答させる
  • 役割付与
    (「あなたは優秀な〇〇です」)
  • 否定語の代わりに肯定文
    (「しないで」→「禁止する」)
  • ハルシネーション対策(「答えがない場合、無理に回答は禁止します」)

これはMUSTですが覚えるだけ

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

プロンプトチューニングでうまくいかないときに...

指示をどんどん足しまくらないで!!!

AIに適当に修正させないで!!

(修正させたなら必ず全部自分で見て)

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

プロンプトの洗練

うまく動かない時、指示を足すのではなく、一度全体を見直すことが大切

  • 重複語彙 / 冗長な表現の削除
  • 重要な指示は前方か後方へ
  • 改行位置を意味のあるまとまりで調整
  • 長い文を箇条書きで整理する
  • 重要な部分にだけ強調**を使う

無駄に長いプロンプトは、コストと応答時間の増加、内容の把握が困難になる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

良い例と悪い例をいくつか紹介

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

悪いプロンプト例1

# 指示
新入社員向けのビジネスマナー研修で使う、プレゼンテーション資料(1時間枠)の構成案を作成してください。

## 研修の目的
この研修のゴールは、新しく入社した社員たちが社会人としての基本的なマナーを身につけることです。
彼らが学生気分を払拭し、プロフェッショナルとして振る舞えるようになることが重要です。
対象は新入社員なので、とにかく分かりやすく、平易な言葉で説明することが求められます。

## ターゲット層
対象者は、当然ながら新卒入社の社員です。
彼らはビジネスの現場経験がまったくないことを前提に資料を作る必要があります。
したがって、専門用語や業界用語は絶対に使わず、具体的な事例をたくさん出して説明するようにしてください。
新入社員は集中力が続きにくいので、一方的な講義にならないよう工夫も必要です。

## 資料で扱うべき内容
資料全体は1時間程度で終わるようにしてください。
内容は、社会人としての基本である「挨拶と言葉遣い」「正しい身だしなみ」「電話応対の基本」「ビジネスメール作成のルール」を網羅的に含めてほしいです。
あと、これが一番大事なのですが、昨今の情勢を鑑み、情報セキュリティとコンプライアンスの重要性を理解してもらうため、
個人情報の取り扱いやSNS利用に関する簡単なクイズを、絶対に資料の最後に入れてください。これは必須項目です。
彼らが飽きないように、途中で簡単なグループワークやディスカッションを入れる案も欲しいです。

## トーン&マナー
新入社員が萎縮しないよう、基本的には親しみやすいトーンがいいですが、ビジネスマナーという真面目な内容を教える場なので、ある程度の緊張感も必要です。
堅苦しすぎず、かつ馴れ馴れしくない、バランスの取れた文体でお願いします。新入社員が飽きずに最後まで参加できるような雰囲気作りが大切です。
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

先程のプロンプトの悪いところ

同じ指示の重複

→ 「新入社員向け・分かりやすく・専門用語NG」という指示が、## 研修の目的## ターゲット層 に重複して存在している。(セクション分けしているのは良いが、)同じような内容を何箇所にも書いてプロンプトが無駄に膨らんでしまう

重要な指示が真ん中に来ている

→ 最も厳守すべき「コンプライアンスに関するクイズを絶対に入れる」という指示が、プロンプトの真ん中に埋もれて、無視されやすい

長々と書いている

→ 重要な指示が効きにくい上、人間が把握できなくなりチューニングできなくなる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

改善したプロンプト例1

# 指示
新入社員向け「ビジネスマナー研修(1時間)」のプレゼンテーション資料の構成案を作成してください。

# 必須要件
資料の最後に、必ず**「情報セキュリティとコンプライアンス(個人情報、SNS利用)に関するクイズ」を設ける**こと。

# 研修の概要
- ターゲット: 新卒社員(ビジネス経験ゼロ)
- 目的: 社会人としての基本マナー習得、プロ意識の醸成(脱・学生気分)
- トーン: 堅苦しすぎず、親しみやすいが緊張感も保つ

# 資料で扱う内容
- 挨拶と言葉遣い
- 身だしなみ
- 電話応対の基本
- ビジネスメールのルール

# 構成上の指示
- 専門用語は使用禁止。具体的な例を多用すること。
- 一方的な講義を避け、飽きさせない工夫(例:グループワーク案)を盛り込むこと。
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

悪い例2

# 指示
来週開催する社内AI勉強会について、
告知文(Slack投稿用)を作成してください。

# 記載事項
* **目的:**
    * 全社員のAIリテラシー向上と、業務効率化のアイデア発見。
* **日時:**
    * 12月1日(月)13:00〜16:00。この日時は絶対に間違えないでください。
* **場所:**
    * オンライン。URLは別途案内することを記載。
* **対象者:**
    * 全社員(エンジニア以外も歓迎)。
* **内容:**
    * 「ChatGPTの基本的な使い方」と「現場での活用事例紹介」。
* **補足:**
    * この勉強会は参加必須ではなく、あくまで任意参加であることを明記してください。
  • 冒頭の同じ1文なのに無駄な改行
  • 箇条書き項目をすべて強調(AIに書かせるとこうなる)

改善例2

# 指示
来週開催する社内AI勉強会のSlack投稿用告知文を作成してください。

# 記載事項
- 目的: 全社員のAIリテラシー向上と、業務効率化のアイデア発見
- 日時: **12月1日(月)13:00〜16:00**(日時厳守)
- 場所: オンライン(URLは別途案内)
- 対象者: 全社員(エンジニア以外も歓迎)
- 内容: 「ChatGPTの基本的な使い方」と「現場での活用事例紹介」
- 補足: この勉強会は**任意参加であることを必ず明記**すること
  • 意味のない改行を削除
  • 重要部分だけ強調
  • 肯定文に変更
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

実践演習(ハンズオン)〜前半〜

  • 穴埋め問題になっている./practice/を編集して進めてみよう
  • uv run python practice/genai_ver/a1.pyのように実行できます
  • 困ったらAIに聞いたり、答えの./src/を見ながら進めてOK

エンジニア向け: Google公式SDKのgenai_verとLangChainのlangchain_verを用意しています。速く終わったら両方見てみよう。

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

基礎: APIを呼ぶ

演習A1: 入力された文から趣味を単語で抽出してみよう

./practice/genai_ver/a1.pyを埋める

演習A2: 温度を調整して出力の差を感じよう

./practice/genai_ver/a2.pyを埋める

  1. いくつかキーワードを与えて小説を書いてもらおう
  2. 文を与えて翻訳させてみよう
  • 温度を調整して、何回か実行してみよう
  • どんなタスクにどんな温度が適切かわかりましたか?
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

基礎: APIを呼ぶ

演習A3: 思考のON/OFFを切り替えてレイテンシの差を感じよう

  1. 思考モデルで思考を切ってみよう
  2. 思考過程を表示してみよう

演習A4: 連続的な対話の履歴を管理しよう

  • 連続的な会話でちゃんと覚えているかどうか確認しよう
  • LLMには毎回全ての会話履歴が送られていることを体験する

参考:マルチターンはSDKで用意されているが、プロダクト実装では使わないと思う

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

構造化出力を体験する

演習B: ECサイトに寄せられたコメントを処理する

入力文サンプル:

スマート加湿器を購入。静音性は期待通り。給水が面倒なのがマイナス。5点満点中3点といったところ。

入力サンプルはいろんなパターンで試してみよう(人力 or Web Gemini)

演習B1: コメントがポジティブかネガティブかクラス分類してみよう

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

構造化出力を体験する

演習B2: コメントから「商品名」 「ポジティブな点」 「ネガティブな点」を抽出して「5段階のスコア」をつけてみよう

  • 複数項目といろんな型を体験する

演習B3: コメントをカテゴリ別に分類し、各カテゴリでポジティブ/ネガティブな点を抽出してみよう

  • カテゴリ(機能、品質、価格、デザイン、使い勝手)ごとに、ポジティブな点とネガティブな点を抽出
  • ネスト構造化出力を体験する
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

複数LLMに分ける

  • 複雑なタスクを1つのLLMにやらせると、性能が足りなかったり、速度が落ちる
  • タスクを分解して、複数LLMを組み合わせる体験をしよう

演習C: 技術記事のドラフトを多角的に分析・改善するシステムを作ろう

機能要件:

  • 記事の評価
    • 技術的正確性(コードの妥当性、説明の正確さ)
    • わかりやすさ(初心者への配慮、説明の丁寧さ)
    • 構成・論理展開(目次構成、話の流れ)
    • SEO最適化(タイトル、見出し、キーワード)
  • 問題点の特定と改善案の生成
  • 修正版の作成
  • フィードバックループ

入力: 技術記事のドラフト(マークダウン形式)
出力: 評価結果、改善提案、修正版

これを見たときあなたならどう設計しますか?

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

複数LLMに分ける

たとえばこんな感じ

  • 機能要件の
    問題点の特定と改善案の生成
    は評価側に根拠として含める
  • 問題点の特定を評価側で
    改善案の生成は修正側で多段など
    設計思想によってさまざま

また、他のLLMを分けるケースとして
場合分けがプロンプトに入る場合
も挙げられる。
(プログラム側で制御しよう)

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

複数LLMに分ける

演習C1: 複数の評価軸を別々で処理してみよう

  • 独立している処理は分割できる
  • 逐次実行と並列実行の実行時間を比較してみよう (エンジニアのみ)

演習C2: 評価結果を使って、記事を修正してみよう

  • C1の4つの評価結果を受け取って記事を修正

演習C3: 修正・評価ループを作る(エンジニアのみ)

  • C1を修正が必要かどうかも出力させてループを抜けるルートを作る
  • 最大3回まで繰り返す(無限ループ注意
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説A

(ネタバレしたくない方はここで戻ってください)

ソースコード:

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説A1~A3: 基本, 温度, 思考の設定(genai)

from google import genai
from google.genai.types import GenerateContentConfig, ThinkingConfig

client = genai.Client(vertexai=True)

input_text = "私はサッカーを趣味にしています。"
response = client.models.generate_content(
    model="gemini-2.5-flash", # A1: contentsにプロンプトを渡す(pythonの文法で変数は{}で埋め込める)
    contents=f"""入力文から趣味を単語で抽出してください。
入力文: {input_text}
""",
    config=GenerateContentConfig(
        temperature=0.1,           # A2: 温度の調整
        thinking_config=ThinkingConfig(
            thinking_budget=0,     # A3: 思考の上限を0にする
            include_thoughts=True, # A3: レスポンスに思考過程を含める(thinking_budgetが0の場合は使えない)
        ),
    ),
)

このプロンプトを変えて実行するだけでWeb版Geminiとの差分を吸収できますね!

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説A1~A3: 基本, 温度, 思考の設定(LangChain)

from langchain_google_vertexai import ChatVertexAI
from langchain_core.prompts import PromptTemplate

llm = ChatVertexAI(
    temperature=1,         # A2: 温度の調整
    model="gemini-2.5-flash",
    thinking_budget=0,     # A3: 思考の上限を0にする
    include_thoughts=True, # A3: レスポンスに思考過程を含める(thinking_budgetが0の場合は使えない)
)
prompt = PromptTemplate.from_template( # A1: プロンプトを渡す({}は自動で変数化される)
    """入力文から趣味を単語で抽出してください。
入力文: {input_text}"""
)
chain = prompt | llm

# 実行時に変数を渡す
result = chain.invoke({"input_text": "私はサッカーを趣味にしています。"})# A1: 実行時に変数へ代入できる
print(result.content)

LangChainだと事前に定義した変数を呼び出し時に埋める書き方が自然にできる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説A4: 対話(genai)

history = []
while True:
    user_input = input("入力してください: ")
    history.append(types.Content(role="user", parts=[types.Part(text=user_input)]))

    response = client.models.generate_content(
        model="gemini-2.5-flash-lite",
        contents=history,
        config=types.GenerateContentConfig(system_instruction="必ず英語で応答してください"),
    )

    history.append(types.Content(role="model", parts=[types.Part(text=response.text)]))
    print(response.text)

同じセッションの対話は毎回すべてLLMに入力されているのを実感しよう
→ Web版Geminiで検証するときにコンテキストをリセットする大切さがわかりますね
(長くなるほど性能が落ちます)

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説A4: 対話(LangChain)

llm = ChatVertexAI(model="gemini-2.5-flash-lite")
prompt = ChatPromptTemplate.from_messages(
    [
        ("system", "必ず英語で応答してください"),
        MessagesPlaceholder(variable_name="history"),
    ]
)
chain = prompt | llm | StrOutputParser()

history = []
while True:
    user_input = input("入力してください: ")
    history.append(HumanMessage(content=user_input))
    response = chain.invoke({"history": history})
    history.append(AIMessage(content=response))
    print(response)
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説B

(ネタバレしたくない方はここで戻ってください)

ソースコード:

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説B1: 構造化出力(genai)

from pydantic import BaseModel, Field
class CommentAnalysis(BaseModel): # 出力したい形式をクラスで定義する
    sentiment: Literal["positive", "negative", "neutral"] = Field(
        description="判定結果。positive: ポジティブ、negative: ネガティブ、neutral: ニュートラル"
    )

input_text = "スマート加湿器を購入。静音性は期待通り。給水が面倒なのがマイナス。5点満点中3点といったところ。"
response = client.models.generate_content(
    model="gemini-2.5-flash-lite",
    contents=f"""次のコメントがポジティブかネガティブかニュートラルか判定してください。
`{input_text}`
""",
    config=GenerateContentConfig(
        response_mime_type="application/json",  # Json Output
        response_schema=CommentAnalysis,        # 構造化出力クラスを指定
    ),
)
comment_result = CommentAnalysis.model_validate_json(response.text)

プロンプトで出力を指示するのではなく、APIとして構造化出力をサポートしている!

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説B1: 構造化出力(LangChain)

from pydantic import BaseModel, Field
class CommentAnalysis(BaseModel): # 出力したい形式をクラスで定義する
    sentiment: Literal["positive", "negative", "neutral"] = Field(
        description="判定結果。positive: ポジティブ、negative: ネガティブ、neutral: ニュートラル"
    )

llm = ChatVertexAI(model="gemini-2.5-flash-lite")
prompt = PromptTemplate.from_template(
    """次のコメントを分析して、商品名、ポジティブな点、ネガティブな点、5段階のスコアを返してください。
`{input_text}`
"""
)
chain = prompt | llm.with_structured_output(CommentAnalysis) # 構造化出力
result = chain.invoke(
    {
        "input_text": "スマート加湿器を購入。静音性は期待通り。給水が面倒なのがマイナス。5点満点中3点といったところ。"
    }
)

当然LangChainでもサポート。OpenAIでもOllamaでも同じ書き方できるのが嬉しい。

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説B2,B3: より複雑な構造化出力

# 【B2】 str(文字列)の他にint(整数)も使える
class CommentAnalysis(BaseModel):
    product_name: str = Field(description="商品名")
    positive_points: str = Field(description="ポジティブな点")
    negative_points: str = Field(description="ネガティブな点")
    score: int = Field(description="5段階のスコア", ge=1, le=5)

# 【B3】 クラスを入れ子にもできる
class CategoryFeedback(BaseModel): # str(文字列)の他にint(整数)も使える
    category: Literal["機能", "品質", "価格", "デザイン", "使い勝手"] = Field(description="カテゴリ")
    positive_points: str = Field(description="そのカテゴリに関するポジティブな点")
    negative_points: str = Field(description="そのカテゴリに関するネガティブな点")
class CommentAnalysis(BaseModel):
    product_name: str = Field(description="商品名")
    categories: list[CategoryFeedback] = Field(description="カテゴリ別のフィードバック")
    overall_score: int = Field(description="5段階の総合スコア", ge=1, le=5)

任意の型を出力に定義できるので、「文字列で返ってきたらどうしよう」の心配が不要

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説C

(ネタバレしたくない方はここで戻ってください)

ソースコード:

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説C: 構造化出力の組み合わせ

  • C1: 各評価項目について次のような評価を出力させる
    class Evaluation(BaseModel):
        """評価結果"""
        needs_revision: bool = Field(description="修正が必要かどうか")
        good_points: list[str] = Field(description="優れている点")
        bad_points: list[str] = Field(description="改善が必要な点")
    
    # エンジニア向け: LangChainだと並列処理を楽に書ける
    parallel_chain = RunnableParallel({
        "technical_accuracy": create_evaluation_chain(TECHNICAL_ACCURACY_PROMPT),
        "clarity": create_evaluation_chain(CLARITY_PROMPT),
        "structure": create_evaluation_chain(STRUCTURE_PROMPT),
        "seo": create_evaluation_chain(SEO_PROMPT),
    })
    
  • C2: レビュー結果をプロンプトに入れて修正させる
  • C3: C1で出力した修正必要の有無に応じて終了基準を作ってループを作るだけ
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

知識 ~後半~

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

フィードバックループを持ち成長するプロダクト

作りたいですよね?

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

LLM時代のデータ活用

プロダクト全体の最適化

  • ファインチューニング(FT)
  • 強化学習(RL)
  • プロンプト最適化

ユーザー個人への最適化(パーソナライズ)

  • コンテキストエンジニアリング
  • RAG

ユーザーごとにモデル保持するのは非現実的なので、基本このパターンになるはず

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

モデル学習の種類とイメージ

STEP: 事前学習 → ファインチューニング → 強化学習

手法 例え 学習のさせ方
事前学習 義務教育 言葉、計算、一般常識を学ぶ。まだ料理はできない
ファインチューニング 料理学校 「このレシピ通りに作りなさい」と教わる
基礎的な調理スキルと知識を身につける
強化学習 実地研修 客に出した料理に対して「美味しい」「塩辛い」と評価される → 客が喜ぶ味付けや、好まれる接客を身につける

LLMを使う多くの企業は、プロンプトチューニングとファインチューニングだけやる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

ファインチューニングと強化学習の比較

比較項目 ファインチューニング 強化学習
主な目的 指示従順性の獲得: 特定の形式や知識を教え込む 人間との調和: 安全性、有用性、ニュアンスを調整する
データ形式 「入力」と「正解」のペア
例:Q:首都は? A:東京
回答の「比較」や「採点」
例:回答A > 回答BGOOD/BADなど
学習の仕組み 次単語の予測 (Token Level)
正解データと一言一句合わせようとする
報酬スコアの最大化 (Sentence Level)
文章全体としての良し悪しを評価
得意なこと ・新しい知識の注入
・JSONなど特殊形式の出力
・口調(キャラ付け)の固定
・嘘(ハルシネーション)の抑制
・有害な回答の回避
・「もっと丁寧に」など曖昧な指示への対応

プロンプトチューニングで限界ならファインチューニングが候補に入る。強化学習まで必要なケースは稀。
参考:Geminiのファインチューニングは簡単にできる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

ファインチューニング

事前学習済みモデルを特定タスクに微調整する

低ランクの小さな重みを付け加えるPEFT(Parameter-Efficient Fine Tuning)のLoRA(Low-Rank Adaptation)が主流

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

強化学習(理解しなくて良い。飛ばします)


強化学習でもLoRAを使う


RLHF (Reinforcement Learning from Human Feedback) / DPO (Direct Preference Optimization)

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

本当に重要なのはここからです!!

パーソナライズへ

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

コンテキストエンジニアリングの必要性

LLMの限界

LLMに与える情報を管理してあげる必要がある

コンテキストエンジニアリング

無数に増えていく(ユーザーの)情報を

  • どう保存するか(そのまま?ラベル付け?集計?圧縮?)
  • どう検索するか(最新N件?関連度?重要度?)
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

プロダクト作りで気にするところ

  • データ取得時の検索クエリ / データ保存時の加工処理が案件ごとの設計ポイント!

このあとコンテキストエンジニアリングの一種とみなせるRAGの説明をしますが、一般的なRAGは既にいろんなクラウドサービスが実装しています。非エンジニアは完全に理解する必要はありません。コンテキストエンジニアリングの理解をするための例として考えてください。

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

RAG(Retrieval-Augmented Generation)

  • 外部データを検索して応答する

典型的なRAGシステムの全体像:

  • RAGはコンテキストエンジニアリングの一種と言える
  • LLMは応答インターフェースでしかない(いかにうまく情報取ってこれるか勝負)
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

RAGの構成要素: 1. クエリ拡張

  • ユーザーの曖昧な入力を、LLM等を使って具体的かつ検索しやすい形に変換する
簡単な文脈補完
USER: 社内の経費精算の締め切りはいつ?
AI: 月末です
USER: それを過ぎたらどうなる?
  • それを過ぎたらどうなる?を検索しても関連ドキュメントを探せない
  • 経費精算の締め切りを過ぎた場合のペナルティや対応を検索する
言い換えや解答予測
USER: PCが重いときの対処法は?
  • PC 動作 遅い 対処システム パフォーマンス 低下 原因メモリ不足 解消方法CPU使用率 高いなどを並列で検索して結果を統合する
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

RAGの構成要素: 2. ハイブリッド検索

  • DBの情報すべてLLMに渡すのは無理なので、LLMに渡す情報の絞り込み

全文検索: 文字列完全一致で検索(インデックスで高速化)
ベクトル検索:

Embedding: 文字列からベクトル空間へ

ベクトル検索: 大量のレコードから近い表現を高速に検索できる(近似最近傍探索)

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

RAGの構成要素: 3. リランキング 4. グラウンディング

3. リランキング: さらなる絞り込み!

  • 検索でヒットした多数の候補から、本当に関連する文書を高精度なモデルで並び替え、上位のものだけを抽出
  • 高精度なモデル
    • クロスエンコーダー(入力: 質問と文書のペア, 出力: 関係度スコア)
    • 多段階にするならLLMが使われることも

4. 応答 + グラウンディング:

  • 抽出した情報をコンテキストに入れて、ユーザーの質問に応答
  • グラウンディング: 情報ソースとの紐づけ(回答の根拠
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

RAGの前処理: ドキュメント保存時の前処理

  • 必要な情報をうまく検索するためには、保存の仕方も重要になる

チャンキング:

  • ドキュメントをチャンクに分割してDBに格納
  • 切り方: ファイル単位、文書構造単位(章とか)、文字数、意味のまとまり
  • 切られて文脈が途切れる問題への対策例:
    • チャンクを階層的にして親チャンクをLLMに渡す
    • チャンクに「全体から見たそのチャンクの要約や文脈」を含める
    • Agenticに足りない情報を取りに行く
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

エージェントについて知ろう

世の中のすごいプロダクトの中身を推測できるようになる

実はさっきのRAGはNotebookLMの中身の推測でした
(OSSでNotebookLMを目指しているレポジトリは先程のような構成)

ここからの話は非エンジニアの方は「へーそんなのもあるんだ」くらいで聞いてください。

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

ReAct(Reasoning + Acting) Agent

  • エージェントの基礎
  • ReAct: Thought (思考)→Action (行動)→Observation (観察)

ツール群: Web検索, コード実行, 画像生成, ファイル検索, コンテキスト取得
→ ツール群にコンテキスト取得が入るとAgentic RAGになる

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

Reflexion(内省)

  • Reflexion: 結果の振り返りを行い次の試行に活かす

  • 先程のReActと組み合わせることもできる
  • プランを立てて結果に基づきプランを修正するAdaptive Planningもある
    • CursorやClaude Codeもプラン立てて修正しながら動きますよね
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

実践演習(ハンズオン)〜後半〜

  • 前半と同様、穴埋め問題になっている./practice/を編集して進めてみよう
  • uv run python practice/genai_ver/a1.pyのように実行できます
  • 困ったらAIに聞いたり、答えの./src/を見ながら進めてOK
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

APIで使えるその他のオプション

  • Web版で普通にサポートしている機能をAPIでも使ってみよう
  • D1のみ必須、その他はオプション

演習D1: マルチモーダル入力

演習D2: グラウンディング(検索)

  • 今日の東京の天気を調べさせよう

演習D3: Pythonコード実行

  • のグラフを描かせてみよう

演習D4: Tool Calling

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

LLMを支える周辺技術

演習E1: 2つの文の意味的類似度を計算してみよう

  • Embedding APIを呼んでベクトル化してコサイン類似度を計算する

演習E2: Google DeepResearchを再現するために必要な設計を考えよう

  • 設計問題でコード不要(非エンジニアはスキップしてもOK)

演習E3: LangChainのReAct Agentを使ってみよう(エンジニアのみ)

  • カスタムで適当な関数を与えて挙動を見る
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

便利なツール

演習F1: n8nを使って構造化出力したLLMを組み合わせてみよう

  • 入力の「名前と趣味を抽出」後、「趣味は英語に」、「名前の姓/名判定」をしよう
  • GUIで簡単に構築できることを体験する

演習F2: LLMのトレースを経験してみよう (エンジニアのみ)

  • ReAct Agentをトレースで観察してみよう(以下どちらか)

LangSmith: SaaS

  • 登録後.envにAPIキー設定するだけ
  • データが送信されるので注意

LangFuse: ローカルホスト

  • docker compose upしてlocalhost:3000でkey発行し.env
  • langchainのcallbacksに設定
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説D

(ネタバレしたくない方はここで戻ってください)

ソースコード:

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説D1~D4: オプション機能

def get_current_temperature(location: str) -> str:
    """今日の気温を調べる関数"""   # docstringがコンテキストとして渡る
    return "今日の気温は25度です"
response = client.models.generate_content(
    model="gemini-2.5-flash",
    contents=[
        types.Part.from_bytes(data=image_bytes, mime_type="image/png"), # D1: 画像入力
        "画像の内容を説明してください",
    ],
    config=types.GenerateContentConfig(
        tools=[
            types.Tool(google_search=types.GoogleSearch()), # D2: Google検索
            types.Tool(code_execution=types.ToolCodeExecution), # D3: コード実行
            get_current_temperature, # D4: 自作関数も渡せれる
        ]
    ),
)

各々、1行加えるだけでできるので簡単!Web版Geminiで検証が不安ならAPIでも簡単

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E&F

(ネタバレしたくない方はここで戻ってください)

ソースコード:

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E1: コサイン類似度(genai)

target_texts = ["漫画", "アニメ"]
result = client.models.embed_content(
    model="gemini-embedding-001",
    contents=target_texts,
    config=types.EmbedContentConfig(task_type="SEMANTIC_SIMILARITY"),
)

embedding1 = np.array(result.embeddings[0].values)
embedding2 = np.array(result.embeddings[1].values)

normed_embedding1 = embedding1 / np.linalg.norm(embedding1)
normed_embedding2 = embedding2 / np.linalg.norm(embedding2)
print("cosine similarity: ", np.dot(normed_embedding1, normed_embedding2))

自然言語が数値ベクトルに変換されて、類似性を比較できることを実感できたらOK

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E1: コサイン類似度(LangChain)

target_texts = ["漫画", "アニメ"]
model = VertexAIEmbeddings(model="gemini-embedding-001")
results = model.embed(
    target_texts,
    embeddings_task_type="SEMANTIC_SIMILARITY",
)

embedding1 = np.array(results[0])
embedding2 = np.array(results[1])

normed_embedding1 = embedding1 / np.linalg.norm(embedding1)
normed_embedding2 = embedding2 / np.linalg.norm(embedding2)
print("cosine similarity: ", np.dot(normed_embedding1, normed_embedding2))
Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E2: Deep Research設計

たとえばこんな感じに設計できます:

オープンソースでもいくつか出ています

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説F1: n8nを使ってみよう

LLMはどちらでもOK:

  • Basic LLM Chain
  • AI Agent

Structured Output Parserを
うまく使おう

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E3&F2: ReAct Agentのトレース

@tool
def func_add(a: int, b: int) -> int:     # 自作関数
    """足し算をする"""                     # Agentは、docstringを読んで判定してくれる
    print("called func_add")
    return a + b
@tool
def func_mul(a: int, b: int) -> int:
    """掛け算をする"""
    print("called func_mul")
    return a * b
agent = create_agent( # 関数を渡す
    model=ChatVertexAI(model="gemini-2.5-flash-lite"), tools = [func_add, func_mul]
)
result = agent.invoke(
    {"messages": [("human", "3と4を足した値に1+3を足した値同士を掛け算するとどうなる?")]}
)

たとえばこんな例だと中身はどうなるでしょうか?

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説E3&F2: ReAct Agentのトレース

内部でループが回り、
3回LLMが呼ばれている
事がわかる

トレースのLangSmithは
環境変数設定するだけ
(LangChainの場合)
.env.sample参照

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

解説F2: トレースツールLangSmithについて

LangSmith: LangChainが提供しているLLM OpsのSaaS

  • LLM実行結果のトレース
    • 結果に手動アノテーションできる → 質の良いデータセットを作れる
    • 自動評価(LLM as a Judge)も仕込める
  • プロンプトバージョン管理
    • Web UIからもコードからも呼べる → サーバーの更新を待たずに更新ができる
  • プロンプトチューニング画面
    • 変数の入力がUIからできる → 本番プロンプトをPdMがチューニングしやすい
    • 実行結果に即時アノテーションできる → 自分の評価を残しておける

→ データが整えばファインチューニング / プロンプト最適化の可能性がある

Tomoki Yoshida (birder)🐦️ - DeNA
LLM勉強会 〜基礎からエージェント設計まで〜

締め

  • 全員LLM設計できるようになった
  • 全員PoCできるようになった
  • 全員AIフィードバックループをイメージできるようになった

今日のコードはすべてレポジトリにあるので、各案件でコピペして行うとPoC/実装が速くなります!

n8n/LangSmithなど使いたい需要あれば一緒にツール申請まわり進めましょう!

Tomoki Yoshida (birder)🐦️ - DeNA

タイトルのみページ番号スキップ

中央寄せ

中央寄せ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

中央寄せ

中央寄せ

中央寄せ

中央寄せ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

タイトルのみページ番号スキップ

中央寄せ

中央寄せ

--- # マルチエージェントの構成 -

中央寄せ

中央寄せ

中央寄せ