8.3.3 LangChain の基礎
- LangChain のようなチェーン型の抽象化が、なぜ自然に登場するのかを理解する
- Prompt、モデル、パーサー、検索器がチェーンの中でどこにあるかを理解する
- 最小例を通して、「前のステップの出力を次のステップへ渡す」感覚をつかむ
- それがなぜプロトタイプや線形ワークフローに向いているのかを理解する
まず地図を作ろう
Section titled “まず地図を作ろう”この節では、初心者にとって一番わかりやすい順番は「先にフレームワーク API を学ぶ」ことではなく、まず次をはっきり見ることです。
flowchart LR A["実際のアプリは 1 回だけモデルを呼ばない"] --> B["複数の小さなステップに分ける"] B --> C["各ステップに明確な入出力がある"] C --> D["それを順番につなぐ"]つまり、この節が本当に解決したいのは次の点です。
- なぜチェーン型の抽象化が自然に生まれるのか
- それは一体、何を整理してくれるのか
一、なぜ「チェーン」という抽象化が必要なのか?
Section titled “一、なぜ「チェーン」という抽象化が必要なのか?”実際のアプリは、たいていモデルを 1 回呼ぶだけではないから
Section titled “実際のアプリは、たいていモデルを 1 回呼ぶだけではないから”たとえば小さな QA システムを作るだけでも、次のようなステップがあります。
- ユーザーの クエリ を整える
- 文書を検索する
- prompt を組み立てる
- モデルを呼ぶ
- 出力を整形する
これをすべて 1 つの関数に手書きしても動かせますが、すぐに次のような状態になりがちです。
- 見通しが悪い
- 再利用しにくい
- デバッグしにくい
チェーン型の抽象化は何をしているのか?
Section titled “チェーン型の抽象化は何をしているのか?”それはつまり、
各ステップを責務がはっきりした小さなコンポーネントにして、順番につなぐ
ということです。
これが LangChain の最も重要な感覚です。
二、最小のチェーン例
Section titled “二、最小のチェーン例”class SimpleChain: def __init__(self, steps): self.steps = steps
def run(self, value): for step in self.steps: value = step(value) return value
def normalize_query(text): return text.strip().lower()
def retrieve_docs(query): if "返金" in query: return {"query": query, "docs": ["コース購入後 7 日以内なら返金できます。"]} return {"query": query, "docs": []}
def format_answer(payload): if payload["docs"]: return f"資料によると:{payload['docs'][0]}" return "関連する資料が見つかりませんでした。"
chain = SimpleChain([ normalize_query, retrieve_docs, format_answer])
print(chain.run(" 返金ポリシーは何ですか? "))期待される出力:
資料によると:コース購入後 7 日以内なら返金できます。このコードは何を教えているのか?
Section titled “このコードは何を教えているのか?”ここで教えているのは、LangChain の最も核心的な考え方です。
各ステップは自分の入出力だけに集中し、システム全体はそれをつないでタスクを完成させる。
これがチェーン型アプリケーションの一番重要な価値です。
三、Prompt はチェーンの中でどんな役割をするのか?
Section titled “三、Prompt はチェーンの中でどんな役割をするのか?”Prompt は「付属文」ではなく、1 つのコンポーネント
Section titled “Prompt は「付属文」ではなく、1 つのコンポーネント”多くのワークフローでは、Prompt 自体が途中の 1 ステップになります。
- クエリ を入力する
- もっとわかりやすい prompt テンプレートを作る
def build_prompt(payload): docs = payload["docs"] query = payload["query"] return f"次の資料をもとに質問に答えてください:資料={docs},質問={query}"
payload = {"query": "返金ポリシーは何ですか", "docs": ["コース購入後 7 日以内なら返金できます。"]}print(build_prompt(payload))期待される出力:
次の資料をもとに質問に答えてください:資料=['コース購入後 7 日以内なら返金できます。'],質問=返金ポリシーは何ですかこの例が伝えているのは、
Prompt もチェーンの中の中間変換ノードとして見られる
ということです。
四、さらに「モデル」のステップを加える
Section titled “四、さらに「モデル」のステップを加える”オフラインでも動くように、ここでも mock model を使います。
def mock_llm(prompt): return f"モデル出力:{prompt}"
chain = SimpleChain([ normalize_query, retrieve_docs, build_prompt, mock_llm])
print(chain.run("返金ポリシーは何ですか?"))期待される出力:
モデル出力:次の資料をもとに質問に答えてください:資料=['コース購入後 7 日以内なら返金できます。'],質問=返金ポリシーは何ですか?このステップでいちばん大事な気づき
Section titled “このステップでいちばん大事な気づき”次のものが見えてきます。
- 検索器
- prompt builder
- model
これらは実は、チェーン上の別々のノードにすぎません。
LangChain が「コンポーネントを組み立てるフレームワーク」に見えるのは、このためです。

五、なぜ出力パーサーも重要なのか?
Section titled “五、なぜ出力パーサーも重要なのか?”多くの人は入力 prompt とモデル出力だけに注目しますが、次のことを忘れがちです。
モデルが出力したあとも、システムはしばしば構造化処理を続ける必要がある。
たとえば:
- 一部のフィールドだけ取り出す
- JSON に変換する
- フロントエンド表示用の形式にマッピングする
def output_parser(text): return { "answer": text.replace("モデル出力:", ""), "ok": True }
chain = SimpleChain([ normalize_query, retrieve_docs, build_prompt, mock_llm, output_parser])
print(chain.run("返金ポリシーは何ですか?"))期待される出力:
{'answer': "次の資料をもとに質問に答えてください:資料=['コース購入後 7 日以内なら返金できます。'],質問=返金ポリシーは何ですか?", 'ok': True}これで次のことが、よりはっきりわかるようになります。
LangChain の本当の価値は、「異なるコンポーネントの境界をきれいに分けること」にあることが多い。
六、なぜプロトタイプに特に向いているのか?
Section titled “六、なぜプロトタイプに特に向いているのか?”それは、初期の LLM アプリは多くの場合、次のような形だからです。
- 比較的線形なフロー
- 複数のコンポーネントが順番に実行される
たとえば:
- クエリ を整える
- 検索する
- prompt を組み立てる
- モデルを呼ぶ
- 結果を解析する
これはまさに、チェーン型の抽象化が最も気持ちよくハマる場面です。
七、いつから苦しくなるのか?
Section titled “七、いつから苦しくなるのか?”フローが次のようになってくると、だんだん直線的なチェーンではつらくなります。
- 検索に失敗したら クエリ を書き換えてもう一度検索する
- 答えが十分に安定していなければ レビュー担当 に再チェックさせる
- あるリクエストはツールを使い、別のリクエストは使わない
このような場合、「1 本の直線チェーン」だけではだんだん無理が出てきます。
つまり、
システムに明確な状態分岐やループが出てきたら、チェーン型の抽象化だけでは足りないことがある。
これが、後で LangGraph のような、よりグラフ指向のフレームワークが必要になる理由です。
八、とても重要な実装上の注意
Section titled “八、とても重要な実装上の注意”LangChain を学ぶときに、最もよくある失敗は次のようなものです。
- 最初からたくさんのクラス名やインターフェースを覚えようとする
でも、より安定した学び方はたいてい次の順番です。
- まずチェーン型の抽象化が何を解決するのかを理解する
- そのあとで具体的な API を見る
そうしないと、次のようになりやすいです。
- フレームワークのコードは書ける
- でも、なぜそのように組むのかがわからない
初心者が最初に LangChain を使うときの安定した方法
Section titled “初心者が最初に LangChain を使うときの安定した方法”一般に、次の順番がいちばん安全です。
- まずは 1 本の線形チェーンだけ作る
- 各ノードの入出力をまずはっきり表示する
- そのあとで検索、パーサー、より複雑なコンポーネントを追加する
- 最後に、より複雑なグラフ型ワークフローを考える
このページを終えたら、この証拠カードを残します。
- 要求
- 入力、状態、tools/context、期待される出力の契約
- 検証済み出力
- パーサー/スキーマ、または業務ルール確認の結果
- 追跡記録
- モデル呼び出し、ツール/関数呼び出し、文書解析、または対話状態
- 失敗確認
- フォーマット不正、必須フィールド不足、古い状態、または誤ったツール
- 次の行動
- prompt、schema、state、API、または parsing の改善
この節でいちばん大事なのは、特定のクラス名を覚えることではなく、次を理解することです。
LangChain の核心的な価値は、「prompt、検索、モデル、解析」という頻出コンポーネントを、よりわかりやすい線形ワークフローとして整理することにある。
このチェーン思考が身につけば、あとで実際のフレームワークの API を見るときに、かなり理解しやすくなります。
この節で持ち帰るべきこと
Section titled “この節で持ち帰るべきこと”- LangChain はモデルを置き換えるものではなく、多段アプリケーションを整理するもの
- まずチェーンを理解してからフレームワークを学ぶほうが、API をいきなり覚えるより安定する
- プロトタイプや線形ワークフローには特に向いているが、あらゆる複雑なシステムの最終形ではない
- この
SimpleChainに、クエリ を検索しやすい形に書き換えるステップを 1 つ追加してみてください。 - 自分の言葉で説明してください。なぜ Prompt もチェーンの中の 1 つのコンポーネントとして見られるのでしょうか?
- 考えてみてください。フローに複雑な分岐が入り始めると、なぜチェーン型の抽象化はつらくなるのでしょうか?
- 自分の言葉で説明してください。LangChain はどのような形の問題に最も向いていますか?
参考実装と解説
- クエリ書き換えステップでは、同義語をそろえ、元の意図を保ったまま検索向きの表現に変えます。
- Prompt は入力を指示に変換し、出力の形を制約するので、チェーン内の1つの component と見なせます。
- 分岐、retry、状態に基づく判断、tool loop が増えると、線形の chain は扱いづらくなります。
- LangChain は、Prompt、retriever、tool、parser などを組み合わせた、比較的予測しやすい LLM workflow に向いています。