「LangChainとLangGraphによるRAG・AIエージェント［実践］入門」でつまずいたことメモ:12章

# はじめに

[「LangChainとLangGraphによるRAG・AIエージェント［実践］入門」](https://gihyo.jp/book/2024/978-4-297-14530-9)の第12章で私がつまずいたことのメモです。

（このメモのほかの章へ：[1章](https://qiita.com/segavvy/items/b31efa98a2d4c20e9ec8) / [2章](https://qiita.com/segavvy/items/2801a079c686c906e582) / [3章](https://qiita.com/segavvy/items/97ab2c1958b894124d44) / [4章](https://qiita.com/segavvy/items/41225198677cf4f498b5) / [5章](https://qiita.com/segavvy/items/7751b41db1e9c672b6f1) / [6章](https://qiita.com/segavvy/items/3185ca18afe28c96e6bd) / [7章](https://qiita.com/segavvy/items/ac0ce03f9b3cd83ee5ed) / [8章](https://qiita.com/segavvy/items/8109b3a1ed3a4efe1140) / [9章](https://qiita.com/segavvy/items/a4f9d80fe6a3f541d4b5) / [10章](https://qiita.com/segavvy/items/0cbd272e280cfaf6ee28) / [11章](https://qiita.com/segavvy/items/7e4c1af72b2280665be3) / 12章 / [まとめ](https://qiita.com/segavvy/items/265b101a6ebb82284e4b)）

:::note info
この記事は個人で作成したものであり、内容や意見は所属企業・部門見解を代表するものではありません。
:::

# 第12章 LangChain/LangGraphで実装するエージェントデザインパターン

いよいよ最終章。前章に続き西見さん担当です。

## 12.1 本章で扱うエージェントデザインパターン

前章で学んだ中から実際に7パターンを実装します。

## 12.2 環境設定

Anthropicがお初です。本の付録A.1節と若干画面が変わっていますが、APIキー取得までにつまずくことはないでしょう。なお、途中で組織名（Org name）を聞かれるのですが、とりあえず個人なので「segavvy」にしてみました。以下のページに個人でも使えるとあるのでおそらく大丈夫かと思います。

https://support.anthropic.com/ja/articles/8987200-anthropic-api%E3%82%92%E5%80%8B%E4%BA%BA%E5%88%A9%E7%94%A8%E3%81%A7%E4%BD%BF%E7%94%A8%E3%81%99%E3%82%8B%E3%81%93%E3%81%A8%E3%81%AF%E3%81%A7%E3%81%8D%E3%81%BE%E3%81%99%E3%81%8B

あと、APIキー取得までクレジットカードの登録なしにできるので多少は無料で使えるのかと思いきや、実際には課金が必要です。これについては後述します。

## 12.3 パッシブゴールクリエイター (Passive Goal Creator)

脱線しますが、コラムのSettingsクラスの解説を見ていて、環境変数を登録してしまうと12.2で設定した環境変数を上書きしてしまうのでは？と思い、ちょっと調べてみました。

https://docs.pydantic.dev/latest/concepts/pydantic_settings/#dotenv-env-support

> Even when using a dotenv file, pydantic will still read environment variables as well as the dotenv file, environment variables will always take priority over values loaded from a dotenv file.
> (Google翻訳) dotenv ファイルを使用している場合でも、pydantic はdotenv ファイルと同様に環境変数を読み取りますが、 環境変数は常に dotenv ファイルから読み込まれた値よりも優先されます。

なるほど、もともと環境変数を優先する仕組みになっていました。

## 12.4 プロンプト／レスポンス最適化 (Prompt/Response Optimizer)

SMARTに基づいて目標設定をさせるのがおもしろいです。よく社員の目標設定で「もっと具体的にしろ」とか「数値で確認できるようにしろ」とか言われる根源のフレームワークですね。SMARTの原則については[ググるとたくさん解説があります](https://www.google.com/search?q=SMART%E5%8E%9F%E5%89%87)。

あと、実際のコードを見て、レスポンス最適化について勘違いしていたことに気づきました。てっきり「LLMからのレスポンス結果に対して最適化」するための後処理かと思っていたのですが、そうではなく、最適化したレスポンスを引き出すために「指示を最適化」するための前処理の仕組みでした。確かに前章の表11.1でも「目標設定と計画生成」にくくられています。レスポンス結果に対する後処理はリフレクションパターンのお仕事でした。

## 12.5 シングルパスプランジェネレーター (Single-Path Plan Generator)

実際に試したところ、私の時は本とかなり異なる結果になりました。

```text:試してみた結果
以下に、カレーライスの3つの異なるレシピを基にした詳細なレポートを作成しました。

---

## カレーライスレシピレポート

### レシピ1: 定番カレーライス
- **材料**:
  - 鶏肉: 200g
  - 玉ねぎ: 1個
  - にんじん: 1本
  - じゃがいも: 2個
  - カレールー: 1箱
  - 水: 600ml

- **手順**:
  1. 鶏肉を一口大に切ります。
  2. 玉ねぎ、にんじん、じゃがいもを適当な大きさに切ります。
  3. 鍋に油を熱し、鶏肉を炒めます。
  4. 野菜を加えてさらに炒めます。
  5. 水を加えて煮込み、カレールーを溶かします。
  6. とろみがつくまで煮込みます。

- **特徴**: 家庭で簡単に作れる定番のカレーライス。さまざまな具材を使い、バランスの取れた味わい。

### レシピ2: 簡単カレーライス
- **材料**:
  - 豚肉: 200g
  - 玉ねぎ: 1個
  - にんじん: 1本
  - じゃがいも: 2個
  - カレールー: 1箱
  - コンソメ: 1個
  - 水: 600ml

- **手順**:
  1. 豚肉を一口大に切ります。
  2. 玉ねぎ、にんじん、じゃがいもを適当な大きさに切ります。
  3. 鍋に油を熱し、豚肉を炒めます。
  4. 野菜を加えてさらに炒めます。
  5. 水とコンソメを加えて煮込み、カレールーを溶かします。
  6. とろみがつくまで煮込みます。

- **特徴**: 短時間で作れる簡単なカレーライス。コンソメを加えることで、深みのある味わいに。

### レシピ3: 本格カレーライス
- **材料**:
  - 牛肉: 200g
  - 玉ねぎ: 1個
  - にんじん: 1本
  - じゃがいも: 2個
  - カレールー: 1箱
  - トマト缶: 1缶
  - 水: 600ml

- **手順**:
  1. 牛肉を一口大に切ります。
  2. 玉ねぎ、にんじん、じゃがいもを適当な大きさに切ります。
  3. 鍋に油を熱し、牛肉を炒めます。
  4. 野菜を加えてさらに炒めます。
  5. 水とトマト缶を加えて煮込み、カレールーを溶かします。
  6. とろみがつくまで煮込みます。

- **特徴**: 市販のルウを使いながらも、トマト缶を加えることで本格的な味わいを楽しめるカレーライス。

### 比較と共通点
- **共通点**:
  - すべてのレシピで玉ねぎ、にんじん、じゃがいも、カレールーを使用しています。
  - 調理時間が比較的短く、家庭で手軽に作れる点が共通しています。

- **相違点**:
  - 使用する肉の種類が異なります（鶏肉、豚肉、牛肉）。
  - 追加の調味料や材料が異なります（コンソメ、トマト缶）。
  - 味わいの特徴が異なり、定番、簡単、本格とそれぞれのスタイルに合わせた工夫がされています。

### まとめ
これらのカレーライスのレシピは、調理のしやすさや使用する材料の違いにより、さまざまな味わいを楽しむことができます。定番のカレーライスは家庭的な味わいを提供し、簡単カレーライスは忙しい日でも手軽に作れるように工夫されています。本格カレーライスは、ひと手間加えることでお店のような味わいを楽しむことができます。これらのレシピを試すことで、カレーライスの多様な魅力を発見できるでしょう。
```

私の場合は3種類のレシピを紹介してくれました。本の結果と大きく異なった原因は、パッシブゴールクリエイターの生成結果です。

```text:optimized_goal
インターネットを利用して、カレーライスのレシピを3つ調査し、必要な材料と手順をリストアップする。その後、リストを基にカレーライスを作るための詳細なレポートを生成する。(測定基準: 3つの異なるカレーライスのレシピを調査し、それぞれの材料と手順をリストアップしたレポートを生成することで達成度を測定する。)
```

ここで3つのレシピを答える方針になった訳ですね。質問がアバウトだとパッシブゴールクリエイターの結果が揺れるので、その先の処理も揺れます。ゴールクリエイターの結果は重要です。

脱線しますが、コラムで解説されているcreate_react_agent関数が便利ですね。ツールを渡せば必要に応じて使ってくれます。6.5で複数のRetrieverから選択する実装がありましたが、これを使ったらもっと簡単に書けそうです。

## 12.6 マルチパスプランジェネレーター (Multi-Path Plan Generator)

本の例はシンプルで、シングルパスプランのそれぞれのタスクにオプションを持たせる形になっています。ただ、実際にはタスクの実行結果によって後続のタスクが動的に変わるようなこともあるでしょう。そのような複雑な条件分岐をどう実現すればいいのかはうまく想像できませんでした。

タスクの途中でゴールに向けたプランを再作成させるような形になるのかと思いますが、そこまでいくと迷走してコントロールできなくなりそうな感じもしますね。そう考えると、やはり最初にプランを作り切ってしまうのが正しい形なのでしょうか。マルチパスなプランというのはかなり奥が深そうです。

## 12.7 セルフリフレクション (Self-Reflection)

教訓をデータベースに蓄積して次回以降に活かすという作りに驚きです。さすが「実践」入門です。

なお、目標設定やタスク分解時もリフレクション結果を活用していますが、この時、リフレクションマネージャー内部では「クエリー」で「タスクとその振り返り」をベクトル検索する形になっています。この両者は意味的に近い内容にならないこともあるかと思いますので、リフレクションのデータベースにクエリーも含めておくと精度を向上できそうな予感がします。

## 12.8 クロスリフレクション (Cross-Reflection)

これはわかりやすいです。このために12.2でAnthropicの登録が必要でした。ただし、アカウントを作ってAPIキーを取得しただけではエラーになってしまいます。

```shell:実行時エラー
anthropic.BadRequestError: Error code: 400 - {'type': 'error', 'error': {'type': 'invalid_request_error', 'message': 'Your credit balance is too low to access the Anthropic API. Please go to Plans & Billing to upgrade or purchase credits.'}}
```

無料枠はないみたいなので、少しお金を払いましょう。Billingのページにいきます。

https://console.anthropic.com/settings/billing

「Complete account setup and buy credits to get started」（Google翻訳：アカウントの設定を完了し、クレジットを購入して開始してください）と言われるので、設定を完了させます。個人なので最初の選択肢は「Individual」を選びました。こんな感じです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139624/d6750e09-8ff0-6cd7-9167-255850208319.png)

この次のページでクレカの情報を入力します。なお、Business tax IDの入力はしないで先に進めました。そして、とりあえず最低金額の5$だけ購入してみました。1年間有効みたいですね。

![anth.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139624/d96e2201-339d-3178-d349-da7b72ac03d0.png)

これで無事クロスリフレクションが試せます。LangSmithで見たらきちんと呼べていました。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/139624/d326358e-2d01-f606-68bf-feb018948d3d.png)

余談ですが、Reflection.idの「リフレクション内容に一意性を与えるためのID」という説明に対して、GPT-4oは番号のみだったりキーワードと番号の組み合わせを生成してくるのに対し、Claude 3.5 Sonnetだと説明文っぽいものを返してくることが多いことに気づきました（上図の矢印の部分）。プログラム上はUUIDで上書きしてしまうので何が返ってもいいのですが、そのうちLLMがこの説明に対してUUIDのようなものを返してくるようになるかも知れません。

## 12.9 役割ベースの協調 (Role-Based Cooperation)

単純に異なる役割の結果をまとめるだけでなく、動的に役割を作ってしまうのがおもしろいです。実行してみたところ、聞いたこともない専門家の方々を編み出してくれました。

```text:生成された役割
{
  "tasks": [
    {
      "description": "インターネットでカレーライスの基本的なレシピを検索し、信頼できるサイトを選ぶ。",
      "role": {
        "name": "レシピ探求者",
        "description": "カレーライスの基本的なレシピをインターネットで探し出し、信頼性の高い情報源を選定する役割です。この役割は、情報の海から最も信頼できる情報を見つけ出すための鋭い洞察力と判断力を必要とします。",
        "key_skills": [
          "情報収集能力",
          "批判的思考",
          "信頼性評価"
        ]
      }
    },
    {
      "description": "選んだレシピの材料と手順を詳細に確認し、必要な材料と調理器具をリストアップする。",
      "role": {
        "name": "キッチンアーキビスト",
        "description": "選定したレシピの材料と手順を詳細に分析し、必要な材料と調理器具を整理する役割です。料理の成功に必要なすべての要素を体系的にリスト化することが求められます。",
        "key_skills": [
          "分析力",
          "組織力",
          "細部への注意力"
        ]
      }
    },
    {
      "description": "材料の購入方法や代替品についてインターネットで調査し、購入計画を立てる。",
      "role": {
        "name": "調達プランナー",
        "description": "必要な材料の購入方法を調査し、最適な購入計画を立てる役割です。代替品の選定やコスト効率の良い購入方法を見つけることが求められます。",
        "key_skills": [
          "調査能力",
          "計画力",
          "コスト管理"
        ]
      }
    },
    {
      "description": "調理手順に関する動画や記事をインターネットで探し、調理の流れを視覚的に理解する。",
      "role": {
        "name": "ビジュアルシェフ",
        "description": "調理手順を視覚的に理解するために、動画や記事を探し出す役割です。視覚的な情報を通じて調理の流れを把握し、実際の調理に役立てます。",
        "key_skills": [
          "視覚的理解力",
          "メディアリテラシー",
          "情報統合力"
        ]
      }
    },
    {
      "description": "調理中の注意点やコツについてインターネットで調べ、調理の際に参考にする。",
      "role": {
        "name": "クッキングセイジ",
        "description": "調理中の注意点やコツをインターネットで調べ、調理の際に活用する役割です。料理の質を高めるための知識を集め、実践に活かします。",
        "key_skills": [
          "知識収集力",
          "実践力",
          "問題解決能力"
        ]
      }
    }
  ]
}
```

タスクに応じて専門家を召喚できるのは興味深いです。単にLLMに演じさせてるだけでは？と思われるかも知れませんが、今のLLMの持つ知見は相当なものです。さらにRAGを組み合わせて専門家に応じた知識や情報を与えたら、かなりすごいことになりそうです。AIエージェントの可能性を感じさせてくれる例題でした。

## 12.10 まとめ

AIエージェントの実装は油断するとコードがかなり複雑になりそうです。本に書いてある通りなのですが、なるべく独立性を持ったクラスや関数として切り出して単体で品質を確保できる形にもっていき、それを組み上げる形にしないとダメですね。

前章で勘違いしていたことにも気づくことができ、AIエージェントの可能性も感じさせてくれる非常におもしろい章でした。

（このメモのほかの章へ：[1章](https://qiita.com/segavvy/items/b31efa98a2d4c20e9ec8) / [2章](https://qiita.com/segavvy/items/2801a079c686c906e582) / [3章](https://qiita.com/segavvy/items/97ab2c1958b894124d44) / [4章](https://qiita.com/segavvy/items/41225198677cf4f498b5) / [5章](https://qiita.com/segavvy/items/7751b41db1e9c672b6f1) / [6章](https://qiita.com/segavvy/items/3185ca18afe28c96e6bd) / [7章](https://qiita.com/segavvy/items/ac0ce03f9b3cd83ee5ed) / [8章](https://qiita.com/segavvy/items/8109b3a1ed3a4efe1140) / [9章](https://qiita.com/segavvy/items/a4f9d80fe6a3f541d4b5) / [10章](https://qiita.com/segavvy/items/0cbd272e280cfaf6ee28) / [11章](https://qiita.com/segavvy/items/7e4c1af72b2280665be3) / 12章 / [まとめ](https://qiita.com/segavvy/items/265b101a6ebb82284e4b)）