LangGraphでAdaptive-RAGエージェントを構築!『チェンソーマン』について聞く

【LangGraph】Adaptive RAGを構築!『チェンソーマン』について聞く

Adaptive-RAGとは、質問の内容に応じて動的に最適な検索方法を変えるアプローチです。

この記事では、LangGraphを使ってAdaptive-RAGエージェントを構築する方法を紹介します。

ざっくり言うと

  • Adaptive-RAGは質問の内容に応じて動的に最適な検索方法を変える手法
  • RAGエージェント構築にはLangGraphを使用する
  • LLMには無料の日本語モデルのLlama-3-ELYZA-JP-8Bを使用する

11/27開催のウェビナーをお知らせ!

目次

LangGraphでAdaptive-RAGを構築する

見出し

Adaptive-RAGとは、質問の内容に応じて動的に最適な検索方法を変えるアプローチです。

この記事では、ユーザーの質問にもとづいて、適切な情報源(Web検索またはベクトルストア)を選択するAdaptive-RAGを使ったエージェントを構築します。

RAGエージェントの構築には、LangGraphを使用します。

LangGraphは、LangChainやLLMを使ってAIエージェントを構築するライブラリです。

LangGraphのワークフローは次のとおりです。

workflow
ワークフローの説明
  • ルーター:質問に応じてベクトルストアを使うか、Web検索するかを選択する
  • Retriver:質問をもとにベクトルストアからドキュメントを取得する
  • Web検索:取得したドキュメントで役に立つ回答が得られないときにWeb検索を行う
  • ドキュメントの関連性評価:取得したドキュメントが質問に関連しているか評価する
  • リライトの判定:関連性評価に応じて、回答を生成するか、質問をリライトするか判定する
  • 回答の生成:取得したドキュメントをもとにRAGで回答を生成する
  • 質問のリライト:取得したドキュメントが質問に関連していないとき、質問をリライトする
  • ハルシネーションの判定:生成結果が事実に基づいているかを評価する
  • 有用性の判定:回答が質問の役に立つかを評価する

LangGraphの実行環境

見出し画像

この記事で用意した実行環境は以下のとおりです。

  • GPU:NVIDIA A100 80GB
  • GPUメモリ(VRAM):80GB
  • OS :Ubuntu 22.04
  • Docker

Dockerで環境構築

Dockerを使用してLangChainの環境構築をします

Dockerの使い方は以下の記事をご覧ください。

STEP
Dockerfileの作成

Ubuntuのコマンドラインで、Dockerfileを作成します。

mkdir adaptive_rag
cd adaptive_rag
nano Dockerfile

Dockerfileに以下の記述を貼り付けます。

# ベースイメージ(CUDA)の指定
FROM nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04

# 必要なパッケージをインストール
RUN apt-get update && apt-get install -y python3-pip python3-venv git nano curl pciutils lshw python3-dev graphviz libgraphviz-dev pkg-config

RUN curl -fsSL https://ollama.com/install.sh | sh

# 作業ディレクトリを設定
WORKDIR /app

# アプリケーションコードをコピー
COPY . /app

# Python仮想環境の作成
RUN python3 -m venv /app/.venv

# 仮想環境をアクティベートするコマンドを.bashrcに追加
RUN echo "source /app/.venv/bin/activate" >> /root/.bashrc

# JupyterLabのインストール
RUN /app/.venv/bin/pip install Jupyter jupyterlab

# LangChain関連のインストール
RUN /app/.venv/bin/pip install ollama langchain-ollama langchain langsmith langgraph langchain-chroma faiss-gpu langchain-community langchain_huggingface langchain_core tiktoken pygraphviz

# コンテナの起動時にbashを実行
CMD ["/bin/bash"]
コマンドの説明

FROM nvidia/cuda:12.1.0-cudnn8-devel-ubuntu22.04

CUDA12.1のベースイメージを指定しています。

RUN apt-get update && apt-get install -y python3-pip python3-venv git nano curl pciutils lshw python3-dev graphviz libgraphviz-dev pkg-config

必要なパッケージをインストールしています。

RUN curl -fsSL https://ollama.com/install.sh | sh

Linux版のOllamaをインストールしています。PythonでOllamaを動かす際にもLinux版Ollamaのインストールが必要になりますのでご注意ください。

RUN /app/.venv/bin/pip install Jupyter jupyterlab

JupyterLabをインストールしています。

RUN /app/.venv/bin/pip install ollama langchain-ollama langchain langsmith langgraph langchain-chroma faiss-gpu langchain-community langchain_huggingface langchain_core tiktoken pygraphviz

LangChainとOllama関連のパッケージをインストールしています。

LLMはOllamaのライブラリを使って動かしますので、PyTorchやTransformerは別途インストール不要です。

[Ctrl + S]キーで変更内容を保存し、[Ctrl + X]キーで編集モードから抜けます。

STEP
docker-compose.ymlファイルの作成

docker-compose.ymlでDockerコンテナの設定をします。

docker-compose.ymlのYAMLファイルを作成して開きます。

nano docker-compose.yml

以下のコードをコピーして、YAMLファイルに貼り付けます。

services:
  adaptive_rag:
    build:
      context: .
      dockerfile: Dockerfile
    image: adaptive_rag
    runtime: nvidia
    container_name: adaptive_rag
    ports:
      - "8888:8888"
    volumes:
      - .:/app/adaptive_rag
    deploy:
      resources:
        reservations:
          devices:
            - driver: nvidia
              count: 1
              capabilities: [gpu]
    command: >
      bash -c '/usr/local/bin/ollama serve & /app/.venv/bin/jupyter lab --ip="*" --port=8888 --NotebookApp.token="" --NotebookApp.password="" --no-browser --allow-root'

[Ctrl + S]キーで変更内容を保存し、[Ctrl + X]キーで編集モードから抜けます。

コマンドの説明

bash -c ‘/usr/local/bin/ollama serve

Ollama Serverを起動しています。PythonのOllamaを使用する際に、Ollama Serverを起動しておく必要がありますので、ご注意ください。

& /app/.venv/bin/jupyter lab –ip=”*” –port=8888 –NotebookApp.token=”” –NotebookApp.password=”” –no-browser –allow-root’

JupyterLabを8888番ポートで起動しています。

STEP
Dockerコンテナを起動

Dockerfileからビルドしてコンテナを起動します。   

docker compose up

 

Dockerの起動後にブラウザの検索窓に”localhost:8888″を入力すると、Jupyter Labをブラウザで表示できます。

localhost:8888

環境変数・LLM・Retrieverの設定

見出し画像

Dockerコンテナで起動したJupyter Lab上でLangChainを使ったRAGの実装をします。

STEP
環境変数の設定

LangChainとTavilyのAPIに関する環境変数を設定します。

import os
from uuid import uuid4

unique_id = uuid4().hex[0:8]

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_PROJECT"] = f"adaptive_rag - {unique_id}"
os.environ["LANGCHAIN_ENDPOINT"] = "https://api.smith.langchain.com"
os.environ["LANGCHAIN_API_KEY"] = "***************"
os.environ["TAVILY_API_KEY"] = "***************"
コードの説明

unique_id = uuid4().hex[0:8]

8桁のランダムな一意の識別子unique_idを生成しています。

os.environ[“LANGCHAIN_TRACING_V2”] = “true”

この設定により、LangChainのトレースが可能になります。

os.environ[“LANGCHAIN_PROJECT”] = f”adaptive_rag – {unique_id}”

angChainプロジェクトの名前を設定しています。ここでは、生成したunique_idを使用してプロジェクト名を「adaptive_rag – {unique_id}」の形式で一意にしています。

os.environ[“LANGCHAIN_ENDPOINT”] = “https://api.smith.langchain.com”

LangChainのAPIエンドポイントを指定しています。

os.environ[“LANGCHAIN_API_KEY”] = “***************”

LangChain APIを利用するためのAPIキーを設定しています。

os.environ[“TAVILY_API_KEY”] = “***************”

Tavily APIを利用するためのAPIキーを設定しています。

STEP
LLMの設定

日本語LLMモデル「Llama-3-ELYZA-JP-8B-q4_k_m.gguf」をダウンロードします。

!curl -L -o Llama-3-ELYZA-JP-8B-q4_k_m.gguf "https://huggingface.co/elyza/Llama-3-ELYZA-JP-8B-GGUF/resolve/main/Llama-3-ELYZA-JP-8B-q4_k_m.gguf?download=true"

Llama-3-ELYZA-JPについては、別記事で詳しく解説しています。

LLMの実行にはOllamaを使用します。

LLMのモデルがOllama使えるようにプロンプトテンプレートを指定して、モデルを作成します。

import ollama
from langchain_ollama.chat_models import ChatOllama

modelfile='''
FROM ./Llama-3-ELYZA-JP-8B-q4_k_m.gguf
TEMPLATE """{{ if .System }}<|start_header_id|>system<|end_header_id|>

{{ .System }}<|eot_id|>{{ end }}{{ if .Prompt }}<|start_header_id|>user<|end_header_id|>

{{ .Prompt }}<|eot_id|>{{ end }}<|start_header_id|>assistant<|end_header_id|>

{{ .Response }}<|eot_id|>"""
PARAMETER stop "<|start_header_id|>"
PARAMETER stop "<|end_header_id|>"
PARAMETER stop "<|eot_id|>"
PARAMETER stop "<|reserved_special_token"
'''

ollama.create(model='elyza8b', modelfile=modelfile)
llm = ChatOllama(model="elyza8b", temperature=0)
llm.invoke(("human","チェンソーマンを知っていますか?")).content
コードの説明

FROM ./Llama-3-ELYZA-JP-8B-q4_k_m.gguf

ダウンロードしたモデルのパスが入ります。

TEMPLATE “””{{ if .System }}<|start_header_id|>system<|end_header_id|>…

モデルで使用するプロンプトテンプレートが入ります。

ollama.create(model=’elyza8b’, modelfile=modelfile)

モデルとプロンプトテンプレートを使ってOllama用のモデルを作成します。modelにはOllamaで呼び出す際に使用する名前をつけられます。

ChatOllama(model=”elyza8b”, temperature=0)

ChatOllamaをインスタンス化してLLMモデルを実行できる状態にしています。

確認のため、LLMからテキストを生成しています。

'チェンソーマンは、藤本タツキによる日本の漫画作品です。2020年から「週刊少年ジャンプ」で連載が開始されました。\n\n物語は、デンジという少年が主人公で、デンジは貧乏生活を送る中で、チェンソー頭の悪魔・パワーと出会い、契約を交わして悪魔を倒す仕事「デビルハンター」を始めることになります。\n\nチェンソーマンは、ダークファンタジー要素が強く、残酷な描写やグロテスクな表現も含まれていますが、同時に、人間の心理や社会問題を深く掘り下げたストーリー展開や、キャラクターの複雑な背景設定などが評価されています。\n\n2022年にはTVアニメ化もされました。'
STEP
Wikipediaからデータを取得

ベクトルストアに格納するデータをWikipediaから取得します。

Wikipediaは、『チェンソーマン』のページを指定しています。

from langchain_community.document_loaders import WebBaseLoader
from langchain.text_splitter import CharacterTextSplitter

urls = [
    "https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%9E%E3%83%B3",
]

docs = [WebBaseLoader(url).load() for url in urls]
docs_list = [item for sublist in docs for item in sublist]

text_splitter = CharacterTextSplitter.from_tiktoken_encoder(
    separator = "\n",
    chunk_size= 1000,
    chunk_overlap=100,
)

doc_splits = text_splitter.split_documents(docs_list)
doc_splits[4]
コードの説明

WebBaseLoader(url).load()

指定したWebページのHTMLコンテンツを取得し、HTMLコンテンツを解析し、使いやすいデータ構造に変換し、そのデータを読み込みます。

CharacterTextSplitter.from_tiktoken_encoder…

テキストを指定された条件でチャンクに分割します。

separator = “\n”

チャンクを分割する際の区切り文字を「\n」として指定します。

chunk_size

各チャンクの最大トークン数を設定します。

chunk_overlap

各チャンク間で重複するトークン数を設定します。

確認のため、分割したチャンクを1つ表示しています。

Document(metadata={'source': 'https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%9E%E3%83%B3', 'title': 'チェンソーマン - Wikipedia', 'language': 'ja'}, page_content='ある日、デンジは仕事を斡旋していたヤクザに騙され、「ゾンビの悪魔」によって殺害されてしまうが、ポチタが心臓となったことで復活。「チェンソーの悪魔」の力を手に入れたデンジはゾンビの集団を一掃し、現場に駆け付けた公安のデビルハンターであるマキマに導かれ、その身を管理されることになる。\n東京の公安本部へ移ったデンジは、マキマの命令を受けて先輩の早川アキ、デンジのバディとして選出された「血の魔人」パワーと同居する。公安ではかつて5分で120万弱の人を殺した「銃の悪魔」の討伐を掲げており、デンジは仲間たちとともに様々な敵と戦いを繰り広げる。やがてデンジは悪魔でありながら人々のために戦う姿が話題を呼び、次第にヒーローとして扱われてゆくことになる。\n第2部「学園編」\n悪魔でありながらデビルハンターとして活躍する謎のヒーロー「チェンソーマン」が世間を賑わせている時代。第四東高等学校に通う少女三鷹アサは、両親を悪魔に殺された過去を持ち、人々から英雄視されているチェンソーマンを嫌っていた。\n周囲に馴染めず孤立した日々を送っていたアサは、ある日悪魔と契約したクラスメイトによって殺されてしまうが「戦争の悪魔」ことヨルと一心同体になったことで蘇る。「チェンソーマンを倒すことができれば肉体を返してやっても良い」とヨルに告げられたアサは、それ以来自身の身体を悪魔と共有する奇妙な共同生活を送ることになる。一方、チェンソーマンの正体であるデンジは「自分がチェンソーマンであることを周囲に打ち明けたい」という想いを胸に秘めながら、偶然アサと同じ高校で学校生活を送っていた。')
STEP
Retrieverの構築

Wikipediaのデータをもとにベクトル検索が可能なRetrieverを構築します。

from langchain.vectorstores import FAISS
from langchain_huggingface import HuggingFaceEmbeddings

embedding = HuggingFaceEmbeddings(model_name="intfloat/multilingual-e5-large")
vectorstore = FAISS.from_documents(doc_splits, embedding)
retriever = vectorstore.as_retriever()
question = "デンジとは"
retriever.invoke(question)
コードの説明

HuggingFaceEmbeddings(model_name=”intfloat/multilingual-e5-large”)

テキストをベクトル表現に変換する埋め込みモデルを読み込みます。

intfloat/multilingual-e5-largeは日本語性能が高く、無料で使える埋め込みモデルです。

FAISS.from_documents(splits, embedding)

ベクトル検索用のインデックスを作成しています。インデックスはDBのテーブルのような概念です。

vectorstore.as_retriever()

インデックスからRetriverを作成しています。

retriever.invoke(question)

質問の内容に類似したドキュメントをRetriverで検索します。

Retriverから取得したデータを確認しています。

[Document(metadata={'source': 'https://ja.wikipedia.org/wiki/%E3%83%81%E3%82%A7%E3%83%B3%E3%82%BD%E3%83%BC%E3%83%9E%E3%83%B3', 'title': 'チェンソーマン - Wikipedia', 'language': 'ja'}, page_content='登場人物[編集]\n声はテレビアニメ版の声優。演は舞台版での俳優。\n主要人物[編集]\n公安編[編集]\nデンジ\n声 - 戸谷菊之介[11][12](幼少期 - 井上麻里奈[13])\n演 - 土屋直武[14](幼少期 - 岩田陽葵[15])\n本作の主人公。自称16歳[8]。「チェンソーの悪魔」であるポチタとともに非正規のデビルハンターとして働く少年[12]。死別した父親が遺した借金の返済に追われる日々を送っていたが、あるとき「ゾンビの悪魔」と契約したヤクザたちに殺害された際に、ポチタが心臓となり身体を再生させたことでチェンソーの悪魔へと変身する能力を手に入れた。その力でゾンビの悪魔とヤクザたちを皆殺しにした後、マキマに拾われて公安所属のデビルハンターとなる。\n大胆不敵な自信家で、素直かつ単純な性格。物事を深く考えることが嫌いで自分の欲望に忠実だが、それらは過酷な幼少期を過ごした経験と「普通に生きて普通に死ぬ」という人並みの生活と幸福への憧れによるものである[8]。幼少期からデビルハンター以外の生き方を知らずに過ごしてきたため、協調性や一般常識に欠けるが、次第に他者への思いやりや自分の生き方について考えるなど成長を見せるようになる。'),
以下省略

各種Chainの構築

見出し画像

LangGraphのワークフロー内の関数で使用するChainを構築していきます。

  • ルーターに使用するChain
  • ドキュメントの関連性を評価するChain
  • 回答を生成をするChain
  • ハルシネーションを評価するChain
  • 回答の有用性を評価するChain
  • 質問をリライトするChain
  • Web検索をするTool
STEP
ルーターに使用するChain

ユーザーの質問を解析して、適切なデータソース(「ベクトルストア」または「ウェブ検索」)をルーティングするためのChainを構築しています。

# router_chain
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import PromptTemplate

prompt = PromptTemplate(
    template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
    あなたは、ユーザーの質問をベクトルストアまたはウェブ検索にルーティングする専門家です。
    チェンソーマンに関する質問についてはベクトルストアを使用してください。
    これらのトピックに関連する質問のキーワードに厳密である必要はありません。それ以外の場合はウェブ検索を使用してください。
    質問に基づいてバイナリーチョイス「web_search」または「vectorstore」を返してください。
    前置きや説明なしで、単一のキー「datasource」を持つJSONとして返してください。
    ルーティングする質問: {question}
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>""",
    input_variables=["question"],
)

llm = ChatOllama(model="elyza8b",format="json",temperature=0)
router_chain = prompt | llm | JsonOutputParser()
question = "デンジとは?"
docs = retriever.get_relevant_documents(question)
doc_txt = docs[0].page_content
print(router_chain.invoke({"question": question}))
print("\n質問:\n" + question)
print("\nベクトルストアから取得した情報:\n" + doc_txt)

ユーザーの質問とベクトルストアから取得した情報

{'datasource': 'vectorstore'}

質問:
デンジとは?

ベクトルストアから取得した情報:
登場人物[編集]
声はテレビアニメ版の声優。演は舞台版での俳優。
主要人物[編集]
公安編[編集]
デンジ
声 - 戸谷菊之介[11][12](幼少期 - 井上麻里奈[13])
演 - 土屋直武[14](幼少期 - 岩田陽葵[15])
本作の主人公。自称16歳[8]。「チェンソーの悪魔」であるポチタとともに非正規のデビルハンターとして働く少年[12]。死別した父親が遺した借金の返済に追われる日々を送っていたが、あるとき「ゾンビの悪魔」と契約したヤクザたちに殺害された際に、ポチタが心臓となり身体を再生させたことでチェンソーの悪魔へと変身する能力を手に入れた。その力でゾンビの悪魔とヤクザたちを皆殺しにした後、マキマに拾われて公安所属のデビルハンターとなる。
大胆不敵な自信家で、素直かつ単純な性格。物事を深く考えることが嫌いで自分の欲望に忠実だが、それらは過酷な幼少期を過ごした経験と「普通に生きて普通に死ぬ」という人並みの生活と幸福への憧れによるものである[8]。幼少期からデビルハンター以外の生き方を知らずに過ごしてきたため、協調性や一般常識に欠けるが、次第に他者への思いやりや自分の生き方について考えるなど成長を見せるようになる。

ベクトルストアにルーティングする場合、{'datasource': 'vectorstore'}の出力がされます。

コードの説明

PromptTemplate()

プロンプトのテンプレートを定義するために使用されるクラスです。テンプレート内で、指定された変数に動的に値を埋め込み、AIに指示を与えるプロンプトを生成します。

template には、AIに対してどのような指示を与えるかを記述します。

<|begin_of_text|><|start_header_id|>system<|end_header_id|> ...<|eot_id|>には、システムプロンプトが入ります。

<|start_header_id|>user<|end_header_id|>...<|eot_id|>には、ユーザーの質問が入ります。

<|start_header_id|>assistant<|end_header_id|>...には、LLMの回答が入ります。

input_variables では、テンプレート内で動的に置き換えられる変数名をリストで指定します。

この場合、questionの変数が指定されています。

prompt | llm | JsonOutputParser()

プロンプトの作成から、LLMによる応答の生成、JSON形式の出力までを一連の処理として組み合わせたChainを構築しています。

router_chain.invoke({“question”: question})

questionをプロンプトに渡して、定義したChainを実行しています。

STEP
ドキュメントの関連性を評価するChain

ユーザーの質問と取得したドキュメントが関連しているかを評価するChainを構築しています。

評価はバイナリースコア(yes または no)で表され、その結果はJSON形式で返されます。

# retrieval_chain
prompt = PromptTemplate(
    template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|> 
    あなたは、「ユーザーの質問」と「取得したドキュメント」の関連性を評価する採点者です。
    もしユーザーの質問のキーワードが、ドキュメント含まれる場合は、「関連性がある」と評価してください。
    厳密な評価は不要です。
    ドキュメントが質問に関連しているかどうかを示すために、バイナリースコア「yes」または「no」を付けてください。
    バイナリースコアは、前置きや説明なしで、単一のキー「score」を持つJSONとして提供してください。
     <|eot_id|><|start_header_id|>user<|end_header_id|>
    取得したドキュメント: \n\n {documents} \n\n
    ユーザーの質問: {question} \n <|eot_id|><|start_header_id|>assistant<|end_header_id|>
    """,
    input_variables=["question", "documents"],
)

llm = ChatOllama(model="elyza8b",format="json",temperature=0)
retrieval_chain = prompt | llm | JsonOutputParser()
question = "デンジとは?"
docs = retriever.invoke(question)
doc_txt = docs[0].page_content
print(retrieval_chain.invoke({"question": question, "documents": doc_txt}))
print("\nユーザーの質問:\n" + question)
print("\n取得したドキュメント:\n" + docs[0].page_content)

出力結果の確認

{'score': 'yes'}

ユーザーの質問:
デンジとは?

取得したドキュメント:
登場人物[編集]
声はテレビアニメ版の声優。演は舞台版での俳優。
主要人物[編集]
公安編[編集]
デンジ
声 - 戸谷菊之介[11][12](幼少期 - 井上麻里奈[13])
演 - 土屋直武[14](幼少期 - 岩田陽葵[15])
本作の主人公。自称16歳[8]。「チェンソーの悪魔」であるポチタとともに非正規のデビルハンターとして働く少年[12]。死別した父親が遺した借金の返済に追われる日々を送っていたが、あるとき「ゾンビの悪魔」と契約したヤクザたちに殺害された際に、ポチタが心臓となり身体を再生させたことでチェンソーの悪魔へと変身する能力を手に入れた。その力でゾンビの悪魔とヤクザたちを皆殺しにした後、マキマに拾われて公安所属のデビルハンターとなる。
大胆不敵な自信家で、素直かつ単純な性格。物事を深く考えることが嫌いで自分の欲望に忠実だが、それらは過酷な幼少期を過ごした経験と「普通に生きて普通に死ぬ」という人並みの生活と幸福への憧れによるものである[8]。幼少期からデビルハンター以外の生き方を知らずに過ごしてきたため、協調性や一般常識に欠けるが、次第に他者への思いやりや自分の生き方について考えるなど成長を見せるようになる。

質問とドキュメントが関連する場合、{'score': 'yes'}とJSON形式で出力されます。

STEP
回答を生成をするChain

回答を生成するChainを構築します。

# generate_chain
from langchain_core.output_parsers import StrOutputParser

prompt = PromptTemplate(
    template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|> 
    あなたは質問応答のためのアシスタントです。
    以下の取得したコンテキストを使って質問に答えてください。
    答えがわからない場合は、正直に「わかりません」と言ってください。
    回答は最大で3文にまとめ、簡潔にしてください。
    <|eot_id|><|start_header_id|>user<|end_header_id|>
    質問: {question} 
    コンテキスト: {context} 
    回答: <|eot_id|><|start_header_id|>assistant<|end_header_id|>""",
    input_variables=["question", "context"],
)

def format_docs(docs):
    return "\n\n".join(doc.page_content for doc in docs)

llm = ChatOllama(model="elyza8b",temperature=0)
generate_chain = prompt | llm | StrOutputParser()

question = "デンジとは?"
docs = retriever.invoke(question)
generation = generate_chain.invoke({"context": docs, "question": question})
print("\nユーザーの質問:\n" + question)
print("\n生成した回答:\n" + generation)
コードの説明

prompt | llm | StrOutputParser()

プロンプトの作成から、LLMによる応答の生成、文字列形式の出力までを一連の処理として組み合わせたChainを構築しています。

出力結果の確認

ユーザーの質問:
デンジとは?

生成した回答:
この文章は、漫画『チェンソーマン』のあらすじと設定を説明したものです。

内容は以下の通りです。

デンジは臓器提供者となったことで復活し、「チェンソーの悪魔」の力を手に入れました。公安に管理され、デビルハンターとして活動することになります。デンジは「銃の悪魔」討伐を目指す中で、人々から英雄視されるようになります。

一方、三鷹アサは両親を悪魔に殺された過去を持つ少女でしたが、「戦争の悪魔」ヨルと契約し、蘇りました。アサはチェンソーマンの正体であるデンジと同じ高校に通うことになります。

また、文章中には「チェンソーマン」の真の姿や能力についても触れられています。チェンソーマンはポチタの真の姿で、デンジが自我を失った際に変身する強大な存在です。悪魔を殺し回るだけでなく、食べた悪魔の名前を持つ存在をこの世から消し去ってしまう能力も持っています。
STEP
ハルシネーションを評価するChain

生成された回答が事実に基づいているかどうかを判定するChainを構築しています

# hallucination_chain
prompt = PromptTemplate(
template=""" <|begin_of_text|><|start_header_id|>system<|end_header_id|> 
    あなたは、ある回答が事実に基づいているか、または事実によって裏付けられているかを評価する採点者です
    <|eot_id|><|start_header_id|>user<|end_header_id|>
    以下は事実です:
    \n ------- \n
    {documents} 
    \n ------- \n
    以下は回答です: {generation}
    回答が事実に基づいているか、または裏付けられているかを示すために、
    バイナリスコアを「score」というキーを持つJSON形式で、「yes」または「no」で評価してください。
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>""",
    input_variables=["generation", "documents"],
)

llm = ChatOllama(model="elyza8b",format="json",temperature=0)
hallucination_chain = prompt | llm | JsonOutputParser()
print(hallucination_chain.invoke({"documents": docs, "generation": generation}))
print("\n回答:\n" + generation)
print("\n事実:\n" + docs[0].page_content)

出力結果の確認

{'score': 'yes'}

回答:
この文章は、漫画『チェンソーマン』のあらすじと設定を説明したものです。

内容は以下の通りです。

デンジは臓器提供者となったことで復活し、「チェンソーの悪魔」の力を手に入れました。公安に管理され、デビルハンターとして活動することになります。デンジは「銃の悪魔」討伐を目指す中で、人々から英雄視されるようになります。

一方、三鷹アサは両親を悪魔に殺された過去を持つ少女です。彼女は「戦争の悪魔」ヨルと契約し、蘇りました。アサはチェンソーマンを倒すことができれば肉体を返してやっても良いと告げられます。

デンジは正体を隠しながら学校生活を送る中で、アサと出会うことになります。

事実:
登場人物[編集]
声はテレビアニメ版の声優。演は舞台版での俳優。
主要人物[編集]
公安編[編集]
デンジ
声 - 戸谷菊之介[11][12](幼少期 - 井上麻里奈[13])
演 - 土屋直武[14](幼少期 - 岩田陽葵[15])
本作の主人公。自称16歳[8]。「チェンソーの悪魔」であるポチタとともに非正規のデビルハンターとして働く少年[12]。死別した父親が遺した借金の返済に追われる日々を送っていたが、あるとき「ゾンビの悪魔」と契約したヤクザたちに殺害された際に、ポチタが心臓となり身体を再生させたことでチェンソーの悪魔へと変身する能力を手に入れた。その力でゾンビの悪魔とヤクザたちを皆殺しにした後、マキマに拾われて公安所属のデビルハンターとなる。
大胆不敵な自信家で、素直かつ単純な性格。物事を深く考えることが嫌いで自分の欲望に忠実だが、それらは過酷な幼少期を過ごした経験と「普通に生きて

回答が事実に基づいている場合、{'score': 'yes'}の出力がされます。

STEP
回答の有用性を評価するChain

生成された回答が質問を解決するのに役に立つかを判定するChainを構築しています

# answer_chain
prompt = PromptTemplate(
    template="""<|begin_of_text|><|start_header_id|>system<|end_header_id|>
    あなたは、回答が質問を解決するのに役立つかどうかを評価する採点者です。
    回答が質問を解決するのに役立つかどうかを示すために、バイナリースコア「yes」または「no」を付けてください。
    バイナリースコアは、前置きや説明なしで、単一のキー「score」を持つJSONとして提供してください。
     <|eot_id|><|start_header_id|>user<|end_header_id|>
    回答:
    \n ------- \n
    {generation} 
    \n ------- \n
    質問: {question}
    <|eot_id|><|start_header_id|>assistant<|end_header_id|>""",
    input_variables=["generation", "question"],
)

llm = ChatOllama(model="elyza8b",format="json",temperature=0)
answer_chain = prompt | llm | JsonOutputParser()
print(answer_chain.invoke({"question": question, "generation": generation}))
print("\n回答:\n" + generation)
print("\n質問:\n" + question)

出力結果の確認

{'score': 'yes'}

回答:
この文章は、漫画『チェンソーマン』のあらすじと設定を説明したものです。

内容は以下の通りです。

デンジは臓器提供者となったことで復活し、「チェンソーの悪魔」の力を手に入れました。公安に管理され、デビルハンターとして活動することになります。デンジは「銃の悪魔」討伐を目指す中で、人々から英雄視されるようになります。

一方、三鷹アサは両親を悪魔に殺された過去を持つ少女です。彼女は「戦争の悪魔」ヨルと契約し、蘇りました。アサはチェンソーマンを倒すことができれば肉体を返してやっても良いと告げられます。

デンジは正体を隠しながら学校生活を送る中で、アサと出会うことになります。

質問:
デンジとは?

回答が質問の役に立つと判断された場合、{'score': 'yes'}の出力がされます。

STEP
質問をリライトするChain

生成された回答が質問を解決するのに役に立つかを判定するChainを構築しています

#Rewrite_chain
re_write_prompt = PromptTemplate(
    template="""あなたは、入力された質問をベクターストアでの検索に最適化された形に書き直すライターです。\n
    最初の質問と回答を確認し、改良された質問を前置きなしで作成してください。\n
    最初の質問:\n\n {question}。
    改良された質問:\n """,
    input_variables=["question"],
)

llm = ChatOllama(model="elyza8b",temperature=0)
rewriter_chain = re_write_prompt | llm | StrOutputParser()
rewriter_chain.invoke({"question": question})

出力結果の確認

'デンジの定義や意味について'
STEP
Web検索Toolの設定

Tavily APIを利用してWEB検索を行うToolの設定をします。

# web_search_tool
from langchain_community.tools.tavily_search import TavilySearchResults

web_search_tool = TavilySearchResults(max_results=5)
question = "デンジとは?"
web_search_tool.invoke({"query": question})
コードの説明

TavilySearchResults(max_results=5)

Tavily の検索 API を利用して、指定された検索クエリに対して上位5件の検索結果を取得します。

デンジについてWeb検索した結果

[{'url': 'https://renote.net/articles/16544',
  'content': 'デンジとは、藤本タツキによるアクションスリラー漫画『チェンソーマン』の主人公でデビルハンター。「チェンソーの悪魔」ポチタと契約したことで、頭部がチェンソーの怪人に変身できるようになった。 極貧の暮らしから救ってくれたマキマに好意を抱いており、彼女と付き合いたいと ...'},
 {'url': 'https://selvy.jp/chainsawman-denji/',
  'content': 'デンジとは現在『ジャンプ+』で連載されているマンガ作品『チェンソーマン』に登場するキャラです。デンジは今作の主人公であり、父親が遺した莫大な借金を返済するためにデビルハンターとなったのです。'},
(以下省略)

LangGraphワークフローで使用する関数の定義

見出し画像

LangGraphのワークフローで使用する各種関数の定義をしていきます。

  • GraphStateの定義
  • Retriver関数
  • 回答を生成する関数
  • ドキュメントの関連性を評価する関数
  • 質問をリライトする関数
  • Web検索をする関数
  • ルーター関数
  • 質問のリライトを判断する関数
  • 「ハルシネーション」と「回答の有用性」を判定する関数
STEP
ライブラリのインポート

必要なライブラリをインポートします。

from pprint import pprint
from typing import List
from langchain_core.documents import Document
from typing_extensions import TypedDict
STEP
Graphの状態を定義

Graphを初期化したときのノードやエッジの状態を定義します。

class GraphState(TypedDict):
    question: str
    generation: str
    documents: List[str]
STEP
Retriver関数

Retriverを使用してベクトルストアからユーザーの質問に関連するドキュメントを取得します。

「question(質問)」と「documents(ドキュメント)」を次のステップで使用するため、状態 (state) を更新します。

# Retriver関数
def retrieve(state):
    print("---RETRIEVE---")
    question = state["question"]
    
    documents = retriever.get_relevant_documents(question)
    return {"documents": documents, "question": question}
STEP
回答を生成をする関数

ユーザーの質問にもとづいた回答を生成します。

「question(質問)」と「documents(ドキュメント)」、「generation(回答)」を次のステップで使用するため、状態 (state) を更新します。

# 回答を生成する関数
def generate(state):
    print("---GENERATE---")
    question = state["question"]
    documents = state["documents"]

    generation = generate_chain.invoke({"context": documents, "question": question})
    return {"documents": documents, "question": question, "generation": generation}
STEP
ドキュメントの関連性を評価する関数

取得したドキュメントが質問に関連しているかどうかを評価します。

関連性の高いドキュメントを選別し、リストに格納します。

関連性が低いドキュメントが存在する場合は何もせずに次のドキュメントの評価に進みます。

「question(質問)」、「documents(ドキュメント)」を次のステップで使用するため、状態(sate)を更新します。

# ドキュメントの関連性を評価する関数
def grade_documents(state):
    print("---CHECK DOCUMENT RELEVANCE TO QUESTION---")
    question = state["question"]
    documents = state["documents"]

    filtered_docs = []
    for d in documents:
        score = retrieval_chain.invoke(
            {"question": question, "documents": d.page_content}
        )
        grade = score["score"]
        if grade == "yes":
            print("---GRADE: DOCUMENT RELEVANT---")
            filtered_docs.append(d)
        else:
            print("---GRADE: DOCUMENT NOT RELEVANT---")
            continue
    return {"documents": filtered_docs, "question": question}
STEP
質問をリライトする関数

rewriter_chainを使用して、元の質問を改善した質問にリライトしています。

関連性が低いドキュメントが存在する場合、Web検索が必要なフラグを立てます。

「question(質問)」、「documents(ドキュメント)」を次のステップで使用するため、状態(sate)を更新します。

# 質問をリライトする関数
def transform_query(state):
    print("---TRANSFORM QUERY---")
    question = state["question"]
    documents = state["documents"]

    better_question = rewriter_chain.invoke({"question": question})
    return {"documents": documents, "question": better_question}
STEP
Web検索の関数

質問に基づいてウェブ検索を実行します。検索結果を取得して、それをドキュメントに追加します。

「question(質問)」と「documents(ドキュメント)」を次のステップで使用するため、状態(state)を更新します。

# Web検索の関数
def web_search(state):
    print("---WEB SEARCH---")
    question = state["question"]

    docs = web_search_tool.invoke({"query": question})
    web_results = "\n".join([d["content"] for d in docs])
    web_results = Document(page_content=web_results)
    return {"documents": web_results, "question": question}
STEP
ルーター関数

質問を分析して適切なデータソースを選択し、どの処理にルーティングするかを決定します。

この関数は「websearch」または「vectorstore」を返します。この返り値は、後続のステップで利用されます。

# ルーター関数
def route_question(state):
    print("---ROUTE QUESTION---")
    question = state["question"]
    print(question)
    source = router_chain.invoke({"question": question})
    print(source)
    print(source["datasource"])
    if source["datasource"] == "web_search":
        print("---ROUTE QUESTION TO WEB SEARCH---")
        return "web_search"
    elif source["datasource"] == "vectorstore":
        print("---ROUTE QUESTION TO RAG---")
        return "vectorstore"
STEP
質問のリライトを判断する関数

フィルタリングされたドキュメント(filtered_documents)に応じて質問をリライトするか、または回答を生成するかを決めます。

この関数は「transform_query」または「generate」を返します。この返り値は、後続のステップで利用されます。

# 質問のリライトを判断する関数
def decide_to_generate(state):
    print("---ASSESS GRADED DOCUMENTS---")
    state["question"]
    filtered_documents = state["documents"]

    if not filtered_documents:
        print(
            "---DECISION: ALL DOCUMENTS ARE NOT RELEVANT TO QUESTION, TRANSFORM QUERY---"
        )
        return "transform_query"
    else:
        print("---DECISION: GENERATE---")
        return "generate"
STEP
「ハルシネーション」と「回答の有用性」を判定する関数

生成された回答がドキュメントに基づいているか(ハルシネーション)を評価します。

ハルシネーションのチェックを通過した場合、回答が質問を解決するのに役に立つか(回答の有用性)を評価します。

この関数は「useful」または「not useful」、「not supported」を返します。この返り値は、後続のステップで利用されます。

# 「ハルシネーション」と「回答の有用性」を判定する関数
def grade_generation_v_documents_and_question(state):
    print("---CHECK HALLUCINATIONS---")
    question = state["question"]
    documents = state["documents"]
    generation = state["generation"]

    score = hallucination_chain.invoke(
        {"documents": documents, "generation": generation}
    )
    grade = score["score"]

    if grade == "yes":
        print("---DECISION: GENERATION IS GROUNDED IN DOCUMENTS---")
        print("---GRADE GENERATION vs QUESTION---")
        score = answer_chain.invoke({"question": question, "generation": generation})
        grade = score["score"]
        if grade == "yes":
            print("---DECISION: GENERATION ADDRESSES QUESTION---")
            return "useful"
        else:
            print("---DECISION: GENERATION DOES NOT ADDRESS QUESTION---")
            return "not useful"
    else:
        pprint("---DECISION: GENERATION IS NOT GROUNDED IN DOCUMENTS, RE-TRY---")
        return "not supported"

LangGraphワークフローの構築

下図のようにLangGraphのワークフローを構築していきます。

workflow
ワークフローの説明
  • ルーター:質問に応じてベクトルストアを使うか、Web検索するかを選択する
  • Retriver:質問をもとにベクトルストアからドキュメントを取得する
  • Web検索:取得したドキュメントで役に立つ回答が得られないときにWeb検索を行う
  • ドキュメントの関連性評価:取得したドキュメントが質問に関連しているか評価する
  • リライトの判定:関連性評価に応じて、回答を生成するか、質問をリライトするか判定する
  • 回答の生成:取得したドキュメントをもとにRAGで回答を生成する
  • 質問のリライト:取得したドキュメントが質問に関連していないとき、質問をリライトする
  • ハルシネーションの判定:生成結果が事実に基づいているかを評価する
  • 有用性の判定:回答が質問の役に立つかを評価する
STEP
ライブラリのインポート

必要なライブラリをインポートします。

from langgraph.graph import END, StateGraph, START
from IPython.display import Image, display
from langchain_core.runnables.graph import CurveStyle, MermaidDrawMethod, NodeStyles
STEP
Graphの初期化とノードの追加

Graphのワークフローを作成し、各ノードを追加します。

#Graphのワークフローを作成
workflow = StateGraph(GraphState)

#ノードの追加
workflow.add_node("web_search", web_search)
workflow.add_node("retrieve", retrieve)
workflow.add_node("grade_documents", grade_documents)
workflow.add_node("generate", generate)
workflow.add_node("transform_query", transform_query) 
コードの説明

StateGraph(GraphState)

StateGraphは、複数の処理ステップ(ノード)をグラフ構造として扱い、それぞれのノード間で状態を渡しながら処理を進めることができます。

workflow.add_node(“web_search”, web_search)

websearchという名前のノードがワークフローに追加され、web_search関数がそのノードで実行されます。

STEP
エッジの追加

ワークフローにおけるエッジ(ノード間の接続)の設定を行います。

エッジは、各ノードをどの順序で実行するか、どのような条件で次のノードに進むかを定義します。

# Edgeの追加
workflow.add_conditional_edges(
    START,
    route_question,
    {
        "web_search": "web_search",
        "vectorstore": "retrieve",
    },
)
workflow.add_edge("web_search", "generate")
workflow.add_edge("retrieve", "grade_documents")
workflow.add_conditional_edges(
    "grade_documents",
    decide_to_generate,
    {
        "transform_query": "transform_query",
        "generate": "generate",
    },
)
workflow.add_edge("transform_query", "retrieve")
workflow.add_conditional_edges(
    "generate",
    grade_generation_v_documents_and_question,
    {
        "not supported": "generate",
        "useful": END,
        "not useful": "transform_query",
    },
)
コードの説明

workflow.add_conditional_edges()

LangGraphワークフロー内で条件付きエッジを設定するために使用されます。このメソッドは、特定のノードが実行された後に、次に進むべきノードを条件に基づいて選択します。

workflow.add_conditional_edges(
START,
route_question,
{
"websearch": "websearch",
"vectorstore": "retrieve",
},
)

START はワークフローの開始点を示します。すべてのワークフローはこのノードから始まります。

route_question関数の実行結果にもとづいて、ワークフローはwebsearchまたはvectorstoreにルーティングされるべきかを判断します。websearchにルーティングされた場合、ワークフローはwebsearchノードに進みます。vectorstoreにルーティングされた場合、ワークフローはretrieveノードに進みます。

workflow.add_conditional_edges(
"generate",
grade_generation_v_documents_and_question,
{
"not supported": "generate",
"useful": END,
"not useful": "transform_query",
},
)

generateノードは、質問に対して応答を生成するステップです。

grade_generation_v_documents_and_question関数は次の3つの条件分岐をします。

1)生成された応答がドキュメントに基づいていない、または信頼性が低いと判断された場合、not supportedが返され、ワークフローは再度generateノードに戻り、新しい応答が生成されます。

2)生成された応答が質問に対して有用であると評価された場合、usefulが返され、ワークフローはENDに進み、プロセスが完了します。

3)生成された応答が質問に対して有用でないと判断された場合、not usefulが返され、ワークフローは再度transform_queryノードに戻り、質問をリライトします。

workflow.add_edge()

LangGraphワークフロー内で、特定のノードから次のノードへ直線的な接続するために使われます。

例えば、workflow.add_edge("retrieve", "grade_documents") では、retrieveノードの処理が完了したあとにgrade_documentsノードの処理に進みます。

STEP
ワークフローのコンパイル

全体のワークフローをコンパイルして、実行可能な形にします。

# コンパイル
app = workflow.compile()
STEP
ワークフローの可視化

定義したLangGraphワークフローのグラフ構造を、Mermaid形式で可視化します。

# ワークフローの可視化
display(
    Image(
        app.get_graph().draw_mermaid_png(
            draw_method=MermaidDrawMethod.API,
        )
    )
)
workflow

Adaptive-RAGエージェントに『チェンソーマン』について聞く

見出し画像

構築したRAGエージェントに『ポチタは犬ですか?』について質問をしてみます。

RAGエージェントに質問する(1)

「ポチタは犬ですか?」と質問してみます。

from pprint import pprint

inputs = {"question": "ポチタは犬ですか?"}
for output in app.stream(inputs):
    for key, value in output.items():
        pprint(f"Node '{key}':")
    pprint("\n---\n")
pprint(value["generation"])

ポチタは犬ですか?

—ROUTE QUESTION—
ポチタは犬ですか?
{‘datasource’: ‘vectorstore’}
vectorstore
—ROUTE QUESTION TO RAG—
—RETRIEVE—
“Node ‘retrieve’:”
‘\n—\n’
—CHECK DOCUMENT RELEVANCE TO QUESTION—
—GRADE: DOCUMENT RELEVANT—
—GRADE: DOCUMENT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—ASSESS GRADED DOCUMENTS—
—DECISION: GENERATE—
“Node ‘grade_documents’:”
‘\n—\n’
—GENERATE—
—CHECK HALLUCINATIONS—
—DECISION: GENERATION IS GROUNDED IN DOCUMENTS—
—GRADE GENERATION vs QUESTION—
—DECISION: GENERATION ADDRESSES QUESTION—
“Node ‘generate’:”
‘\n—\n’
‘ポチタは、チェンソーの悪魔で子犬のような見た目をしています。普段は「ワン」としか鳴かないが、稀に相手の意識内で会話することがあります。’

ポチタは悪魔でした!

RAGエージェントに質問する(2)

「早川アキが契約している悪魔は何ですか?」と質問してみます。

from pprint import pprint

inputs = {"question": "早川アキが契約している悪魔は何ですか?"}
for output in app.stream(inputs):
    for key, value in output.items():
        pprint(f"Node '{key}':")
    pprint("\n---\n")
pprint(value["generation"])

早川アキが契約している悪魔は何ですか?

—ROUTE QUESTION—
早川アキが契約している悪魔は何ですか?
{‘datasource’: ‘vectorstore’}
vectorstore
—ROUTE QUESTION TO RAG—
—RETRIEVE—
“Node ‘retrieve’:”
‘\n—\n’
—CHECK DOCUMENT RELEVANCE TO QUESTION—
—GRADE: DOCUMENT RELEVANT—
—GRADE: DOCUMENT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—ASSESS GRADED DOCUMENTS—
—DECISION: GENERATE—
“Node ‘grade_documents’:”
‘\n—\n’
—GENERATE—
—CHECK HALLUCINATIONS—
—DECISION: GENERATION IS GROUNDED IN DOCUMENTS—
—GRADE GENERATION vs QUESTION—
—DECISION: GENERATION ADDRESSES QUESTION—
“Node ‘generate’:”
‘\n—\n’
‘早川アキが契約している悪魔は「狐の悪魔」です。’

コン!

RAGエージェントに質問する(3)

「パワーが飼っている猫の名前は?」と質問してみます。

from pprint import pprint

inputs = {"question": "パワーが飼っている猫の名前は?"}
for output in app.stream(inputs):
    for key, value in output.items():
        pprint(f"Node '{key}':")
    pprint("\n---\n")
pprint(value["generation"])

質問:
パワーが飼っている猫の名前は?

—ROUTE QUESTION—
パワーが飼っている猫の名前は?
{‘datasource’: ‘vectorstore’}
vectorstore
—ROUTE QUESTION TO RAG—
—RETRIEVE—
“Node ‘retrieve’:”
‘\n—\n’
—CHECK DOCUMENT RELEVANCE TO QUESTION—
—GRADE: DOCUMENT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—GRADE: DOCUMENT NOT RELEVANT—
—ASSESS GRADED DOCUMENTS—
—DECISION: GENERATE—
“Node ‘grade_documents’:”
‘\n—\n’
—GENERATE—
—CHECK HALLUCINATIONS—
—DECISION: GENERATION IS GROUNDED IN DOCUMENTS—
—GRADE GENERATION vs QUESTION—
—DECISION: GENERATION ADDRESSES QUESTION—
“Node ‘generate’:”
‘\n—\n’
‘パワーが飼っている猫の名前は「ニャーコ」です。’

わかりやすい名前ですね!

生成AI・LLMのコストでお困りなら

GPUのスペック不足で生成AIの開発が思うように進まないことはありませんか?

そんなときには、高性能なGPUをリーズナブルな価格で使えるGPUクラウドサービスがおすすめです!

GPUSOROBAN
GPUSOROBAN

GPUSOROBANは、生成AI・LLM向けの高速GPUを業界最安級の料金で使用することができます。

インターネット環境さえあれば、クラウド環境のGPUサーバーをすぐに利用可能です。

大規模な設備投資の必要がなく、煩雑なサーバー管理からも解放されます。

よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!
EdgeHUBロゴ

メールマガジン登録

Stable Diffusion・LLM・RAGに関する最新情報をいち早くお届けします。

無料メルマガの配信をご希望の方は、下記フォームよりご登録ください。

    EdgeHUB編集部からのお知らせ

    無料ウェビナーのお知らせ

    11/27ウェビナー

    RAG×日本語LLMの無料オンラインウェビナー第2弾!

    開催日時:
    2024年11月27日(水) 14:00~15:00

    内容:

    • 「LlamaIndex」と「Llama-3.1-Swallow」でRAG環境を構築
    • 生成AI開発の強い味方「GPUSOROBAN」の活用方法

    このウェビナーでは、オープンソース「LlamaIndex」で構築したRAGの使い方や、デモを紹介します。

    生成AIに関心のある方、AI技術をビジネスに活かしたい方は、ぜひこの貴重な機会にご参加ください!

    こんな方におすすめ!

    • 日本語LLMの利用を検討している方
    • AI開発に携わっている方
    • 最新のAI技術やトレンドに興味がある方
    • GPUスペック不足に悩んでいる方

    \簡単1分で申し込み!/

    この記事を書いた人

    EdgeHUBは、NVIDIAクラウドパートナーである株式会社ハイレゾが運営しています。「AIと共にある未来へ繋ぐ」をテーマに、画像生成AI、文章生成AI、動画生成AI、機械学習・LLM、Stable Diffusionなど、最先端の生成AI技術の使い方をわかりやすく紹介します。

    目次