RAG入門:AIに知識を与える技術
ハンズオン:LangChainで基本的なRAGを実装する
写したコードは、1 年後に動かない
RAG のチュートリアルを検索すると、コピーすれば動く 50 行のコードがいくらでも出てきます。そして、その多くはもう動きません。
LangChain はこの数年で、パッケージの分割も、クラス名も、呼び出し方も繰り返し変わってきました。去年の記事のコードをそのまま貼ると、たいてい import の行で止まります。運悪く動いてしまうと、今度は非推奨のまま古い書き方が残り続けます。
だから、ここで覚えてほしいのはコードそのものではなく、箱を並べる順番です。順番さえ頭に入っていれば、公式ドキュメントで今の名前を引くだけで書けます。逆に順番を知らないままコードを写すと、1 行変わっただけで手が止まります。
6 つの箱を、この順に並べる
順番は、読み込む、分ける、ベクトルにする、入れる、引く、渡す、の 6 つです。前の 4 つが Indexing、後ろの 2 つが Retrieval と Generation にあたります。第 1 章の 3 工程が、そのままコードの形になったものだと思ってください。
最初の 2 つは、資料を読み込んでチャンクに切るところまでです。
Python
docs = TextLoader("./handbook.txt").load()
splitter = RecursiveCharacterTextSplitter(chunk_size=800, chunk_overlap=100)
chunks = splitter.split_documents(docs)次が、切ったチャンクをベクトルにしてデータベースへ入れるところです。多くのライブラリでは、この 2 つが 1 行にまとまっています。ここで埋め込みモデルを指定していることだけ、意識しておいてください。引くときも同じものを使います。
Python
store = Chroma.from_documents(chunks, embedding=OpenAIEmbeddings())最後が、質問を受けて引き、LLM に渡すところです。retriever が Retrieval、chain が Generation を担当します。k で取得件数を指定します。
Python
retriever = store.as_retriever(search_kwargs={"k": 4})
chain = build_qa_chain(llm=ChatOpenAI(model="gpt-4o-mini"), retriever=retriever)
print(chain.invoke("有給休暇の申請期限を教えてください"))3 つ目のブロックの組み立て方は、版によっていちばん変わりやすい部分です。既製の便利クラス 1 つで済ませる書き方から、プロンプトを自分で書いて部品をつなぐ書き方へ移ってきているので、そこは公式の最新版を見てください。前回学んだ制約や出典の指示は、このプロンプトに書きます。
動かないときは、どの箱で止まったかを見る
うまくいかないとき、いきなり最後の回答だけを見ても原因は分かりません。箱ごとに出力を確かめてください。
まず chunks の件数を数えます。1 件しか無ければ、読み込みは成功しているのに分割が効いていません。0 件なら、そもそもファイルを読めていません。ここで数十件に分かれていれば、前半 3 つの箱は動いています。
次に retriever が返したチャンクを、そのまま表示します。ここで的外れなものが並んでいれば、原因は検索より前にあります。逆に、正しいチャンクが並んでいるのに回答が外れているなら、直す場所はプロンプトです。
この 2 か所を見るだけで、6 つの箱のどこで止まったかがほぼ特定できます。まずは手元のテキストファイル 1 本で、最後まで通してみてください。PDF や複数ファイルは、1 本が通ってからで十分です。
復習ミニクイズ
RAGの実装において、読み込んだドキュメントを「Text Splitters」でチャンク分割する主な理由として、最も適切なものはどれですか?