問題
2025年2月現在LangChain v0.3
全くの無知識からだと種類が多すぎて訳が分からない
LangChain
- ChatGPT等のLLMは種類が色々ある
- それぞれの使い方に微妙な違いがある
ものすごいざっくり言うとLLMへデータを送る、データを受け取るを共通化したwrapperのようなもの
v0.2で古いが、参考にはなった
これも古いが、参考にはなった
この人達はよく分かってる人と全く知識のない人とで一緒にやっていくので、よく分かってないこっちの気持ちを代弁してくれる感じで分かりやすい
LangChain Expression Language (LCEL)
linuxのコマンドで使える「|」と同じような役割、書き方でprompt -> llm -> output
みたいな一連の流れをchain
として一体化させて、invoke出来るという仕組み
tool
手順としては
- LLMに
bind_tools
して関数等をtoolとして渡しておく - toolにはdocstring等でLLMに詳しくどう言うものかを説明しておく
- LLMに問い合わせた際に適切にtoolを使ってもらうように説明しておく
- そうすることで適切にtoolを使って回答してくれる
例えばLLMだけでは分からない社内の情報はRAGやデータベースに保存されているので、そういう質問の場合はtoolを使って回答するとか
どのtoolへどういうパラメーターを使うかはLLM次第
APIを細かく見ると更に
- Core
- Langchain
- Text Splitters
- Community
- Experimental
などがある
Core
LangChainの本質的な部分のみ切り出したもの
Langchain
とりあえずこれさえimportしておけば全部あるバージョン?
Text Splitters
RAG等で処理したいテキストデータが大量すぎる場合に、処理しやすい単位で切り分ける必要がある
そういう時のためのもの
Community
サードパーティー用などその他細々したもの(特に使った事がないのでよく分からない)
Experimental
名前の通り
LangGraph
ある種のステートマシーン
LangChainを元にnode作ってedgeで繋いで...をコードで書くので、パッと見分かりにくいと評判
その内Xstateみたいにdictで表現できればいいなと思うけけど
でLangGraphの無料コースがある(約6時間)
LangGraph Studio
視覚的にLangGraphを動かせるIDE
現状LangGraphを視覚的に操作できるだけで、graphの編集機能はないと思う
Xstateのvisualizerに似てる
LangSmith
LangChain、LangGraphが複雑になる程に何がどうしてこうなったのか?とわからなくなるので、printデバッグ以外のトレーサーが必要になる
それがLangSmith
RAG
簡単に言うと検索できる外部記憶
結局LLMへ送信できるトークン数には上限があるので専門的な知識を全部渡して理解してもらおうとするには限度がある
なので、事前処理した(documentをembeddingしてvector storeに保存)データを元に、LLMへ投げる前に検索しておいて、LLMへ主要な部分だけを質問内容に繋げて聞くという手法
RAGはまだ成熟中のようで、どう言うchunkでembeddingした方が良いのかとか、Graph RAGとかAgentic RAGとか色々手探りな状況っぽい
その内LLMへ投げるトークン数が飛躍的に上がって、料金も下がる(又はローカルでも十分に動かせる)とかになればRAGは要らない?
なんとなく近い将来は専門知識を事前にトレーニングさせたプライベートなLLMを個別に持っておいて(ローカルなのかサービスとしてなのか)、その時々に適切なLLMを使うっていう感じになりそうだけど
AI Agent
LangGraphを組み上げていって、半自動で動くエージェントのようなものに仕立て上げたものって感じ
Human in the loop (HITL)
特定の処理で人間にその場で処理の判断を委ねる場合に使う
- interruptで人間側に処理の判断を委ねる
- Commandで人間の回答を返す
Functional API
LangGraphってもっさりして書きにくいし読みにくいよねを解消するβ版
memory
結局生のLLMはトレーニングした状態しか保持しておらず、毎度の問い合わせはstatelessなので、事前に聞いた事は何も考慮してくれない
(HTTPサーバーにcookieなしでアクセスしてるようなもの)
Agentまでくると事前に聞いた内容などを保持しておいて、それら過去の内容と合わせて回答を貰えるようにしたい、のでこれら以降のmemoryの使い分けが必要になる
Short term memory
StateやCheckpointが通常使われる
State
Agentみたいなものまででもなく普通にLangGraph(state machineとして)利用時には使うが、Xstateで言う所のcontextであり、LLMとのmessageを保存しておいたり、LLMとの会話で出てきた結果で重要なことを保存したりだとか
何を保存するかは自由
reducer
Stateを保存する際に各要素をどのように更新していくのかを決めるもの
例:
-
add_messages
- listにmessageを通常は簡単にappendする
- RemoveMessageをID付きで渡すとそのmessageをlistから消してくれる
-
operator.add
- 一般的なlistへのappendとしてのaddだったり、要は+演算子で出来ること
- などで、独自に作ることも出来る
Module 2 - Lesson 2: State Reducers
https://academy.langchain.com/
MessagesState
LLMへのmessageは通常保存しておくものなのでMessagesStateが用意されている
これはadd_messagesをreducerとしているので、どんどん追記していく
なので、会話を続ければ続ける程流すトークン数が増えていく(最初からの会話を全部流すので)
やり方は色々あるんだろうけど、例えば10件messageが溜まったのならその10件をLLMへ投げて一旦summaryとして要約してもらい、その10件はmessagesから消して次回の問い合わせはsummary + messagesとか工夫が必要
- summaryにするので情報は一部削られる事になる
Module2 - Lesson 5: Chatbot w/ Summarizing Messages and Memory
に例が出てくる
https://academy.langchain.com/
トークン数の上限がなくなるとか、ローカルで十分耐えれるLLMが動かせるとかになればこういう工夫も要らなくなりそう
Checkpoint
Stateの履歴のようなもの
Stateへの状態が変わる度にそれらをcheckpointとして保存しておいてくれて、例え同プロセスでなくても同じthread-idなら同じ情報が引き出せるし、過去のあるpointへ戻って再度LLMへ問い合わせたりとか出来る
- 必ずthread_idを指定して使う必要がある
- InMemorySaverは開発時にサラッと簡単に使うため
- 小規模ならSqliteSaver
- 一般的にはPostgresSaver
Long term memory
- Checkpointerはthread単位の保存
- Long termはuser単位の保存
threadはある一定の会話(話題)についての長期的な保存であり、threadで出てきた内容で長期的に、且つ別threadでも使いたい情報(ユーザーの氏名とか住所とか)がある場合にこれを使う
- thread間で同じ情報を共有したい場合の要はデータベース
InMemoryStore(開発時)とかPostgresStoreを使う
長期記憶のコンセプトとして3つに分けられ
- Semantic
- Episodic
- Procedural
こういう概念で保存していくと良いよ的な話が書いてある
Trustcall
Module 5 - Lesson 4: Memory Schema + Collection
LLMには.with_structured_output()
で定義したデータ型で返答してもらうように頼めるが、そのデータ型が複雑な場合はちゃんと処理できずに終わってしまう
これを解決するのがTrustcall
LLMに問い合わせる際にJSON丸ごとで返してと言わずに、JSONのpatch(差分)で返してくれといい具合に問い合わせてpatchを当てた最終的なJSONデータをstructured_outputとしてTrustcallが返してくれるようなイメージ
まとめ
全体的に使いこなせればすごいけど、まだv0.3とか0.2辺りで仕様変更も多いし(productionで使ってるところもあるらしいけど)、ライブラリ群の構成がゴロゴロ変わってるし、1ヶ月単位で追いつかないといけないぐらい
ちょっと古い情報を見てると全然違ったりするのでimportするだけでも大変