Slide 1

Slide 1 text

Contextual Retrievalやってみたよ 10/28 【生成AI/LLM LT大会】今注目している技術や最新動向を共有! @moritalous

Slide 2

Slide 2 text

自己紹介 森田 和明 富士ソフト株式会社 主任 / フェロー(アーキテクト・エバンジェリスト) AWS Ambassador(2023~) AWS Top Engineer(2020~) AWS All Certifications Engineer(2024) AWS Community Builder(2024) 生成AIに限らず、AWS関係のアーキテクトとエバンジェリストをやってます Java Webアプリ開発出身 新しいもの好き X / Qiita / GitHub : @moritalous 2 「Jumping deer with japanese temple」 Amazon Titan Image Generatorにて生成

Slide 3

Slide 3 text

3 Bedrockの入門書を執筆しました!!

Slide 4

Slide 4 text

Contextual Retrieval とは?? 4 Anthropicがブログで公開したRAGの精度向上テクニック https://www.anthropic.com/news/contextual-retrieval

Slide 5

Slide 5 text

従来のRAGの概要① 5 ● 生成AI単体では、最新情報や専門分野に関する知識がないため誤った回答を 生成をしてしまう ● RAG(Retrieval-Augmented Generation:検索拡張生成)という手法を 使用することで回答精度を向上させることができる ● RAGでは一般的に以下のような処理を行う ○ ドキュメント小さなチャンクに分割する(通常は数百トークン程度) ○ 埋め込みモデルを使用してチャンクをベクトルに変換する ○ ベクトルをデータベースに登録し、類似のチャンクを検索する(セマンティッ ク検索)

Slide 6

Slide 6 text

従来のRAGの概要② 6 ● ベクトルを使用した類似性検索では完全一致するキーワード(例えばエラーコー ドなど)の検索は苦手 ● これを補う方法としてBM25 (Best Matching 25)と組み合わせたハイブ リッド検索がある(ブログでは「スタンダードRAG」と表現される)

Slide 7

Slide 7 text

従来のRAGの課題 7 ● チャンクに分割することによりコンテキスト(文脈)がなくなってしまう ● 財務情報を対象にチャンク分割を行うと以下のようなチャンクが発生してしま う ● この課題の解決法として「Contextual Retrieval」が紹介されています ****株式会社の2024年の決算について報告します。 会社の収益は前四半期より 3% 増加しました。 この業界では**の影響をうけた結果となりました。 チャンク→ 会社の収益は前四半期より 3% 増加しました。 どこの会社? 何年度?? 原因は??

Slide 8

Slide 8 text

Contextual Retrievalの仕組み 8 ● 分割したチャンクの先頭に、「チャンクの文脈を説明する短文」を追加する ● 短文を追加した状態でベクトル化とBM25のインデックス登録を行う ● 短文の生成はClaude 3 Haikuで行う プロンプト

Slide 9

Slide 9 text

Contextual Retrievalで検索精度改善 9 ● Contextual Retrievalの導入で上位20チャンクの検索失敗率が5.7%か ら3.7%に減少(35%の改善)

Slide 10

Slide 10 text

やってみた 10

Slide 11

Slide 11 text

ウィキペディアの1記事を題材に検証しました 11

Slide 12

Slide 12 text

手順 12 1. HTMLを取得し整形しMarkdown化 2. LangChainのDocument Loaderで500トークンごとにチャンク分け import requests from bs4 import BeautifulSoup from markdownify import markdownify as md url = "https://ja.wikipedia.org/wiki/進撃の巨人" response = requests.get(url) soup = BeautifulSoup(response.text) body = soup.find("div", id="bodyContent") for target in ["style", "script", "noscript"]: tags = body.find_all(target) for t in tags: t.clear() md_contents = md(body.decode_contents(),   strip=["a", "head", "script", "style"]).strip() print(md_contents) from langchain_core.documents import Document from langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, chunk_overlap=30, ) documents = [Document(page_content=md_contents, metadata={"source": url})] documents = text_splitter.split_documents(documents)

Slide 13

Slide 13 text

チャンクの例 13 ● 単純に分割したチャンク 圧倒的な力を発揮する「獣の巨人」に対し、エルヴィンは自らと新兵たちの特攻を囮にした奇襲作戦を実行する。エルヴィ ンと新兵たちは「獣の巨人」の投石によって無惨にも散っていった。そして作戦通りに、リヴァイは「獣の巨人」がエル ヴィン達の特攻に気を取られているうちに「獣の巨人」に接近して奇襲を仕掛け、「獣の巨人」との戦闘になる。リヴァイ の圧倒的な力の前に「獣の巨人」は為す術もなく切り刻まれ、遂に本体であるジークを巨人の体の中から引きずり出し、捕 らえることに成功する。 リヴァイは、エルヴィンに渡されていた「注射薬」によって生き残りの兵士に「獣の巨人」の力を継承させようと、周りを 見渡して生き残りを探すが、その直後、突然現れた「四足歩行型の巨人」によってジークは救出されてしまう。更に、「鎧 の巨人」もミカサの雷槍により倒されるが、ライナーもまた「四足歩行型の巨人」に救出され、ベルトルト以外の壁外勢力 3人は撤退する。 ● 生成した「チャンクの文脈を説明する短文」 「ウォール・マリア最終奪還作戦」に関する記述。 チャンクの文内では「ウォール・マリア最終 奪還作戦」とは触れられていない (前後のチャンクには存在)

Slide 14

Slide 14 text

出来上がったチャンク 14 ● 組み合わせてできたチャンク 「ウォール・マリア最終奪還作戦」に関する記述。 圧倒的な力を発揮する「獣の巨人」に対し、エルヴィンは自らと新兵たちの特攻を囮にした奇襲作戦を実行する。エルヴィ ンと新兵たちは「獣の巨人」の投石によって無惨にも散っていった。そして作戦通りに、リヴァイは「獣の巨人」がエル ヴィン達の特攻に気を取られているうちに「獣の巨人」に接近して奇襲を仕掛け、「獣の巨人」との戦闘になる。リヴァイ の圧倒的な力の前に「獣の巨人」は為す術もなく切り刻まれ、遂に本体であるジークを巨人の体の中から引きずり出し、捕 らえることに成功する。 リヴァイは、エルヴィンに渡されていた「注射薬」によって生き残りの兵士に「獣の巨人」の力を継承させようと、周りを 見渡して生き残りを探すが、その直後、突然現れた「四足歩行型の巨人」によってジークは救出されてしまう。更に、「鎧 の巨人」もミカサの雷槍により倒されるが、ライナーもまた「四足歩行型の巨人」に救出され、ベルトルト以外の壁外勢力 3人は撤退する。 なるほど これだと検索精度が良くなりそうだ だがしかし

Slide 15

Slide 15 text

コストが気になる 15

Slide 16

Slide 16 text

コストが気になる 16 ● 毎回ドキュメント全体を含めて「チャンクの文脈を説明する短文」を作成させて いる 入力トークン:136,150トークン $0.03404 出力トークン:36トークン $0.00005 合計:$0.03408 / 1チャンク 「ウォール・マリア最終奪還作戦」に関する記述。 1ドキュメント(383チャンク)だと$13.05 高い... ←ここに毎回ドキュメント全体を含めている 入力プロンプト 出力プロンプト

Slide 17

Slide 17 text

そんなあなたに「プロンプトキャッシュ」 17 ● Claudeではベータ機能として「プロンプトキャッシュ」が提供されています (AWSのAmazon Bedrockでは未提供) ● キャッシュへの登録はちょっとだけ割高だけど、キャッシュからの取得は激安 message = client.beta.messages.create( max_tokens=1024, messages=[ { "role": "user", "content": [ { "type": "text", "text": TEMPLATE_1.replace("{WHOLE_DOCUMENT}", md_contents), "cache_control": {"type": "ephemeral"}, }, { "type": "text", "text": TEMPLATE_2.replace( "{CHUNK_CONTENT}", document.page_content ), }, ], } ], model=model, ) from anthropic import Anthropic client = Anthropic( api_key=api_key, default_headers={ "anthropic-beta": "prompt-caching-2024-07-31" }, ) プロンプトキャッシュを使えば 1ドキュメント$1.66 87%割安!!

Slide 18

Slide 18 text

だがしかし 18

Slide 19

Slide 19 text

立ちはだかるレート制限 19 ● Rate limitsがきつく、Tier 1(わたし)の場合、1分間に1チャンクしか処理で きない(※ドキュメント全体は約14万トークン) ● 更にプロンプトキャッシュは5分間のみ有効 という時間制限もある ● ドキュメント全体の渡し方はもうひと工夫 必要そうです

Slide 20

Slide 20 text

救世主あらわる 20

Slide 21

Slide 21 text

● 最大10,000メッセージまたは最大32MBを上限に送信し、バッチで処理を実 行 ● コストはリアルタイム推論の50%! ● キャッシュとバッチは同時利用が可能 ● 24時間以内に処理が完了する ● 気長に待つのかと思いきや、爆速でした。。 (ベータ版だからかもですが、50チャンク分を1分以内に処理できました) ● 金額算出は間に合いませんでしたが、十分に利用価値はありそう!! 10/8にバッチAPIが登場しました!(ベータ版) 21

Slide 22

Slide 22 text

コード例 22 batches_response = client.beta.messages.batches.create( betas=["prompt-caching-2024-07-31"], requests=[ { "custom_id": f"id_{n}", "params": { "model": model, "max_tokens": 1024, "messages": [ { "role": "user", "content": [ { "type": "text", "text": TEMPLATE_1.replace( "{WHOLE_DOCUMENT}", md_contents ), "cache_control": {"type": "ephemeral"}, }, { "type": "text", "text": TEMPLATE_2.replace( "{CHUNK_CONTENT}", doc.page_content ), }, ], } ], }, } for n, doc in enumerate(batch_doc) ], )

Slide 23

Slide 23 text

最後に 23 ● Contextual Retrievalはチャンクに分割しても文脈を保持する工夫 ● 仕組みはシンプルだが、お金が気になるところ ● Bedrockでも使いたい!! https://github.com/boto/boto3/issues/4262