IT女子 アラ美お疲れ様です!IT業界で働くアライグマです!
「LLMアプリを作りたいけど、LangChainは複雑すぎる」「依存関係が多すぎてメンテナンスが大変」「もっとシンプルな方法はないのか」
こうした悩みを抱えているAI開発者は多いのではないでしょうか。LangChainは確かに強力なフレームワークですが、Zennやはてなブックマークで話題になっているように、「過度な抽象化」「ブラックボックス化」「頻繁な破壊的変更」といった課題が指摘されています。
本記事では、LangChainを使わずにLLMアプリを開発する実践的な代替アーキテクチャを解説します。直接SDK利用、軽量ラッパー、カスタムチェーン実装の3つのアプローチを、実装例とともに紹介します。
LangChainが抱える3つの構造的問題



LangChainは2023年の登場以来、LLMアプリ開発のデファクトスタンダードとして広く使われてきました。しかし、実際にプロダクション環境で運用すると、以下の3つの構造的問題に直面します。
まず、過度な抽象化による複雑性の増大です。LangChainは「あらゆるユースケースに対応する」ことを目指した結果、抽象化レイヤーが深くなりすぎています。シンプルなプロンプト送信でさえ、Chain → Memory → Callback → Output Parser という複数のレイヤーを経由するため、デバッグが困難になります。
次に、依存関係の肥大化とバージョン管理の困難さです。LangChainは多数のオプショナル依存を持ち、特定のLLMプロバイダーやベクトルDBを使うために追加パッケージが必要になります。また、v0.1からv0.2、v1.0への移行で破壊的変更が頻発し、既存コードの保守コストが高くなっています。
最後に、ブラックボックス化によるトラブルシューティングの難しさです。LangChainの内部実装は複雑で、エラーが発生したときに「どこで何が起きているのか」を追跡するのが困難です。特に、プロンプトテンプレートの展開やトークン数計算のロジックが隠蔽されているため、予期しない動作に遭遇することがあります。
これらの問題は、AIエンジニアがLangChainを推奨しない理由という記事でも詳しく解説されており、多くの開発者が同様の課題を感じています。また、生成AI時代に市場価値を上げるリスキリング戦略でも、シンプルな技術選定の重要性を解説しています。



前提条件と環境整理
本記事で紹介する代替アーキテクチャを実装するための前提条件を整理します。
想定読者のスキルレベル:
- Pythonの基本的な文法を理解している
- REST APIの基本概念を知っている
- LLMの基本的な仕組み(プロンプト、トークン、温度パラメータなど)を理解している
利用するツール・ライブラリ:
- Python 3.9以上
- openai 1.10.0以上(OpenAI公式SDK)
- anthropic 0.18.0以上(Anthropic公式SDK)
- pydantic 2.0以上(データバリデーション用)
- httpx 0.25.0以上(非同期HTTP通信用)
実行環境の注意点:
- APIキーは環境変数で管理し、コードに直接埋め込まないこと
- 本番環境ではレート制限とリトライ処理を実装すること
- トークン消費量を監視し、コスト管理を行うこと
これらの前提を満たしていれば、LangChainなしでも十分に実用的なLLMアプリを構築できます。実際、Model Context Protocol (MCP) 実践ガイドでも紹介したように、シンプルなアーキテクチャの方が長期的な保守性が高くなります。



アプローチ1:直接SDK利用パターン
最もシンプルで推奨される方法は、OpenAIやAnthropicの公式SDKを直接利用することです。LangChainのような中間レイヤーを挟まず、APIを直接呼び出すことで、コードの見通しが良く、デバッグが容易になります。
以下は、OpenAI SDKを使った基本的な実装例です。
import os
from openai import OpenAI
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))
def generate_response(prompt: str, model: str = "gpt-4") -> str:
response = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "あなたは親切なアシスタントです。"},
{"role": "user", "content": prompt}
],
temperature=0.7,
max_tokens=1000
)
return response.choices[0].message.content
# 使用例
result = generate_response("Pythonの非同期処理について教えてください")
print(result)
このコードはわずか20行で、LangChainのLLMChainと同等の機能を実現しています。LangChainを使った場合と比較すると、以下のメリットがあります。
- 依存関係が最小限(openaiパッケージのみ)
- エラー発生時のスタックトレースが読みやすい
- APIの仕様変更に即座に対応できる
- トークン消費量やレスポンスタイムを直接取得できる
ストリーミング対応も簡単に実装できます。
def generate_response_stream(prompt: str, model: str = "gpt-4"):
stream = client.chat.completions.create(
model=model,
messages=[
{"role": "system", "content": "あなたは親切なアシスタントです。"},
{"role": "user", "content": prompt}
],
stream=True
)
for chunk in stream:
if chunk.choices[0].delta.content:
yield chunk.choices[0].delta.content
# 使用例
for text in generate_response_stream("Pythonの非同期処理について教えてください"):
print(text, end="", flush=True)
このアプローチは、Tokentap完全ガイドで紹介したようなトークン消費の可視化とも相性が良く、コスト管理が容易です。



アプローチ2:軽量ラッパーパターン
直接SDK利用で十分なケースが多いですが、複数のLLMプロバイダーを切り替えたい場合や、共通のエラーハンドリングを実装したい場合は、軽量なラッパークラスを作成すると便利です。LangChainのような重厚なフレームワークではなく、必要最小限の機能だけを持つラッパーです。
以下は、OpenAIとAnthropicを統一インターフェースで扱える軽量ラッパーの実装例です。
from typing import Iterator, Literal
from openai import OpenAI
from anthropic import Anthropic
class LLMClient:
def __init__(self, provider: Literal["openai", "anthropic"]):
self.provider = provider
if provider == "openai":
self.client = OpenAI()
self.default_model = "gpt-4"
else:
self.client = Anthropic()
self.default_model = "claude-3-5-sonnet-20241022"
def generate(self, prompt: str, system: str = None) -> str:
if self.provider == "openai":
messages = []
if system:
messages.append({"role": "system", "content": system})
messages.append({"role": "user", "content": prompt})
response = self.client.chat.completions.create(
model=self.default_model,
messages=messages
)
return response.choices[0].message.content
else:
response = self.client.messages.create(
model=self.default_model,
system=system or "あなたは親切なアシスタントです。",
messages=[{"role": "user", "content": prompt}],
max_tokens=1000
)
return response.content[0].text
# 使用例:プロバイダーを簡単に切り替え
llm = LLMClient("openai")
result = llm.generate("Pythonの非同期処理について教えてください")
print(result)
このラッパーはわずか40行で、LangChainのChatOpenAIやChatAnthropicと同等の機能を提供します。重要なのは、自分で実装しているため、内部動作が完全に把握できる点です。エラーが発生しても、どこで何が起きているのかすぐに分かります。
さらに、リトライ処理やレート制限対応も簡単に追加できます。
import time
from tenacity import retry, stop_after_attempt, wait_exponential
class LLMClient:
# ... 前述のコード ...
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def generate_with_retry(self, prompt: str, system: str = None) -> str:
return self.generate(prompt, system)
このアプローチは、Claude Code vs Cursor徹底比較で紹介したような、複数のAIツールを使い分ける場合にも有効です。



実装後の効果検証(ケーススタディ)



実際に、あるSaaS企業でLangChainから直接SDK利用に移行したプロジェクトの事例を紹介します。
状況(Before)
- LangChain 0.1系で構築したチャットボットが、v0.2への移行で動作不良
- 依存関係の競合により、本番環境でのデプロイに3日間を要していた
- エラー発生時のスタックトレースが深すぎて、原因特定に平均2時間かかっていた
- 月間のトークン消費量が可視化できず、コスト管理が困難
行動(Action)
- LangChainを完全に削除し、OpenAI SDKの直接利用に切り替えた
- 必要な機能(会話履歴管理、リトライ処理)を軽量ラッパーとして実装した
- トークン消費量をログに記録し、コスト可視化ダッシュボードを構築した
- CI/CDパイプラインで依存関係の競合チェックを自動化した
結果(After)
- デプロイ時間が3日から30分に短縮(94%削減)
- エラー原因特定時間が平均2時間から15分に短縮(87%削減)
- トークン消費量の可視化により、月間コストを23%削減
- コードベースが1,200行から400行に削減され、新メンバーのオンボーディング期間が半減
このケーススタディは、Pipはもう古い?超高速パッケージマネージャー『uv』への完全移行ガイドで紹介した依存関係管理の改善とも相性が良く、開発体験の大幅な向上につながりました。



さらなる実践・活用に向けて
LangChainを使わないLLMアプリ開発をさらに発展させるためのステップを紹介します。また、生成AI時代に市場価値を上げるリスキリング戦略も参考にしてください。
短期的に取り組むべきこと:
- 既存のLangChainコードを段階的に直接SDK利用に置き換える
- トークン消費量とレスポンスタイムのモニタリングを実装する
- エラーハンドリングとリトライ処理を標準化する
中長期的に目指すべきこと:
- 複数のLLMプロバイダーを統一インターフェースで扱える社内ライブラリを構築する
- プロンプトのバージョン管理とA/Bテストの仕組みを整備する
- RAGやエージェント機能を、必要最小限の実装で追加する


ワークライフバランスを重視し、安定した環境で長く働きたい方は、以下の社内SE特化型エージェントなどを検討してみてください。
| 比較項目 | 社内SE転職ナビ | レバテックキャリア | リクルートエージェント |
|---|---|---|---|
| ターゲット | 社内SE・定着率重視客先常駐なし | Web・SIer全般キャリアアップ重視 | 全職種・大量募集広く浅く |
| 残業時間の確認 | 厳密に審査済み | 担当者に確認要 | 不明確な場合が多い |
| 面接対策 | 「面接1回」も交渉可 | 専門的な対策あり | 担当者による |
| おすすめ度 | 安定志向なら必須 | A挑戦したい人向け | B求人数重視 |
| 公式サイト | 無料相談する | - | - |



まとめ
本記事では、LangChainを使わずにLLMアプリを開発する3つのアプローチを紹介しました。
- 直接SDK利用:最もシンプルで推奨される方法。OpenAI/Anthropic SDKを直接使う
- 軽量ラッパー:複数プロバイダー対応や共通処理が必要な場合に有効
- カスタムチェーン:複雑なワークフローも、必要最小限の実装で実現可能
重要なのは、「フレームワークに依存せず、自分でコードを書く」ことです。LangChainのような重厚なフレームワークは、短期的には便利に見えますが、長期的には保守コストが高くなります。シンプルなアーキテクチャを選択することで、コードの見通しが良くなり、デバッグが容易になり、チーム全体の生産性が向上します。
まずは小さなプロジェクトから、直接SDK利用を試してみてください。LangChainなしでも、十分に実用的なLLMアプリが構築できることを実感できるはずです。













