はじめに
AWS Skill Builderで生成AIアプリケーションの学習をしていたところ、『Building Generative AI Applications Using Amazon Bedrock』というコースに出会いました。このコースでは、実際のコードでアプリケーションを動かせるデモがあり、アプリケーション開発の理解にとても役立ちます。
しかし、まだ英語版のみなので、紹介がてら日本語で内容をまとめてみようと思います。
本記事の説明
- コース上のデモの内容に基づき、デモで扱われる知識・技術の説明をします
- コースにある資料の完全な日本語訳ではありません
- デモの概要の説明はしますが、デモの具体的な実施やコードの記載はありません
- 無料のコースなので、Skill Builderに登録すれば、すぐにデモのリソースにアクセスできます
- この題のトピックは「その1~その3」の3部構成です
その 1(FM編):この記事
その 2(RAG編):投稿済み
その 3(Agent編):投稿済み
コースの概要
コースのコンテンツは、
- Module 1~4
- BedrockやLangChainに関するレッスン
- Demo 1
- LLMを用いた簡単なデモ
- Module 5
- RAGに関するレッスン
- Demo 2
- RAGを用いたデモ
- Module 6
- エージェントに関するレッスン
- Demo 3
- エージェントのデモ
となっており、「知識の習得 → それに基づくデモ」という流れが3つある構成です。(記事もそれに倣います。)一応コースレベルとしては、Advancedとなっており、生成AIやAWSに関する予備知識が多少必要です。
生成AIの基盤モデルに関しては以前こちらの記事でまとめています
デモでは、実際にAWSのリソースを作成し、Amazon SageMaker上のJupyter環境でコードを実行します。コースは無料ですが、デモ内で利用するAWSリソースには普通に料金が発生します。
自分は費用をなるべく抑えたかったので、コース上の手順とは別の方法を試しました。それらも補足情報として記載したいと思います。

↑私がコースのデモを完了するまでにかかったコストは、$0.19でした
(追記)
PineconeのAPIKeyの保存に使用したSecrets Managerが無料枠だったらしく、加算に漏れていました。執筆時点で1シークレットあたり、$0.40 / 月(日割り有)でその分の課金も来ます。
BedrockとLangChainについて
その1の記事では、コースのDemo 1で登場する知識をまとめます。
APIメソッド
InvokeModel
Amazon Bedrockのモデルにリクエストを送り、生成された回答を得るには、InvokeModel
メソッドを使用します。
import boto3
import json
bedrock_client = boto3.client('bedrock-runtime')
prompt = "What is LLM?" # LLMに送るユーザーの質問
body = json.dumps({
"inputText": prompt,
"textGenerationConfig": {
"maxTokenCount": 8192,
"stopSequences": [],
"temperature": 0,
"topP": 1
}
})
response = bedrock_client.invoke_model(
body = body,
modelId = 'amazon.titan-text-express-v1', # 回答生成させるモデルのID
accept = 'application/json',
contentType = 'application/json'
)
response_body = json.loads(response.get('body').read())
print(response_body.get('results')[0].get('outputText'))
LLM stands for Large Language Model. It is a neural network architecture that is trained on large amounts of text data to generate human-like text. LLMs are used in a variety of applications, including chatbots, virtual assistants, and language translation.
ドキュメント → invoke_model - Boto3 1.36.21 documentation
InvokeModelWithResponseStream
InvokeModel
メソッドと同様に、モデルの呼び出しを行うメソッドですが、InvokeModelWithResponseStream
では、レスポンスがストリーム形式で返ってきます。この形式は、モデルの出力を早く取得したい場合や、より長い文章を生成させる場合に役立つそうです。
以下の例のように、回答がいくつかのチャンクに分割され、ひとつのチャンクの出力は途中で途切れています。
prompt = "Write a tech blog on LLM usin 10 sentences."
# inputの他の設定はinvoke_modelと同じなので省略
response = bedrock_client.invoke_model_with_response_stream(...)
stream = response.get('body')
if stream:
for event in stream:
chunk = event.get('chunk')
if chunk:
output = json.loads(chunk.get('bytes').decode()).get('outputText')
print(output)
Artificial intelligence (AI) has made significant strides in recent years, with one of the most exciting developments being the rise of large language models (LLMs). LLMs are computer programs that can understand and generate
ドキュメント → invoke_model_with_response_stream - Boto3 1.37.4 documentation
推論のパラメータ
基盤モデル(FM)にリクエストを送る際、いくつかの推論パラメータを設定することでFMのレスポンスを調整することができます。ここでは5つの代表的なパラメータを紹介します。
名前 | 説明 | タイプ | デフォルト値 |
---|---|---|---|
Temperature | 単語選択のランダム性を決める値で、高いほど回答が予想しづらい | Float |
0 |
トップ P | 次に選択されるであろうトークンのうち、出力確率が上位何%までを候補にするかを決める値 | Float |
1 |
トップ K | 次に選択されるであろうトークンのうち、出力確率が上位何個までを候補にするかを決める値 | Float |
- |
レスポンスの最大長 | 生成される回答のトークンの最大の長さ | Integer |
512 |
停止シーケンス | ここで列挙された単語をモデルが生成すると、そこで生成を停止する | Array of strings |
- |
参考 → Amazon Bedrock Influence response generation with inference parameters
すべてのFMがすべてのパラメータに対応している訳ではなく、モデルによって利用可能なパラメータは違う。
アーキテクチャパターン
大規模なテキスト要約
非常に長いドキュメントの要約をプロンプトに入れてFMに渡そうとすると、コンテキストの制限を超えてしまいリクエストできない、という問題が起こり得ます。それを回避する解決策として、以下のような要約パターンがあります。
-
map_reduce
- 長いドキュメントをいくつかのチャンクに分割し、それぞれを要約した後、すべてのチャンクの要約をまとめて要約する
-
refine
- ドキュメントをチャンクに分けた後、最初のチャンクを要約し、次にその要約と2番目のチャンクの内容を合わせて要約する
- 上記のプロセスをすべてのチャンクが要約されるまで繰り返す
チャット履歴を持つAIアシスタント
このアーキテクチャパターンでは、AIアシスタントがユーザーとのチャット内容をチャット履歴としてストアに保存します。すると、FMはユーザーとの過去のチャット内容がプロンプトから与えられ、それに基づいて回答を生成することができます。
LangChain
概要
LangChainとは、LLMを利用したAIアプリケーションを、構築するためのフレームワークです。簡単にLLMによる機能を開発するための、クラスやメソッドが提供されています。
LangChainは、Python/Typescript/Javascriptの3つのプログラミング言語に対応しています。
また、AWSなどのLLMを提供する多くのプロバイダーごとに、独立した統合パッケージがあります。AWSの場合には、langchain-awsライブラリが用意されているので、そちらを使っていきます。
基本的な使い方
from langchain_aws import BedrockLLM
llm = BedrockLLM(
model_id="amazon.titan-text-express-v1", credentials_profile_name="default"
)
response = llm.invoke(input="Where is the capital of Japan?")
プロンプトテンプレート
LangChainのPromptTemplate
を用いると、プロンプトのベースとなるテキストを定義し、後から埋め込まれた変数を代入することで動的にプロンプトを作成できます。これにより、プロンプトの再利用や効率的なLLMの呼び出しが可能になります。
from langchain_core.prompts import PromptTemplate
prompt_template = PromptTemplate.from_template(
"Tell me a information about {topic} in {sentence_num} sentences."
)
prompt = prompt_template.invoke({"topic": "cats", "sentence_num": 3})
# → text='Tell me a information about cats in 3 sentences.'
...
response = llm.invoke(prompt)
チャット履歴
アーキテクチャパターンのセクションでも登場した、チャット履歴を持つAIアシスタントが、LangChainのライブラリを用いることで実装することができます。
コース内で紹介されるLLMChain
やlangchain.memory
を用いた実装は、最新のv0.3では非推奨のやり方です。
How to migrate to LangGraph memory | 🦜️🔗 LangChain
LangGraph memoryのコード例
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import MemorySaver
from langgraph.graph import START, MessagesState, StateGraph
from langchain_aws import BedrockLLM
llm = BedrockLLM(
model_id="amazon.titan-text-express-v1", credentials_profile_name="default"
)
# Define the function that calls the model
def call_model(state: MessagesState):
response = llm.invoke(state["messages"])
return { "messages": response }
# Define a new graph
workflow = StateGraph(state_schema=MessagesState)
workflow.add_edge(START, "model")
workflow.add_node("model", call_model)
memory = MemorySaver()
app = workflow.compile(
checkpointer = memory
)
# The thread id is a unique key that identifies this particular conversation.
config = { "configurable": { "thread_id": "abc123" } }
input_message = HumanMessage(content="hi! I'm Bob")
for event in app.stream({ "messages": [input_message] }, config, stream_mode="values"):
event["messages"][-1].pretty_print()
input_message = HumanMessage(content="what was my name?")
for event in app.stream({ "messages": [input_message] }, config, stream_mode="values"):
event["messages"][-1].pretty_print()
Bot: Hello, Bob! How can I help you today?
...
Bot: Your name is Bob.
ベクトルストア
LangChainを用いることで、簡単にドキュメントを埋め込みしたベクトルをベクトルストアに保存できます。LLMはそのドキュメントを元に回答を生成します。
-
ドキュメントの内容を分割
example.pyfrom langchain_community.document_loaders import TextLoader from langchain_text_splitters import CharacterTextSplitter text_documents = TextLoader('programming.txt').load() text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=0) documents = text_splitter.split_documents(text_documents)
-
ドキュメントを埋め込みして、ベクトルストアに保存
※FAISSというライブラリのベクトルストアを用いた例example.pyfrom langchain_aws import BedrockEmbeddings from langchain_community.vectorstores import FAISS embedding = BedrockEmbeddings(model_id="amazon.titan-embed-text-v1") vector_store = FAISS.from_documents(documents=documents, embedding=embedding)
-
ベクトルストアを使ってLLMに推論させる
example.pyfrom langchain.indexes.vectorstore import VectorStoreIndexWrapper from langchain_aws import ChatBedrock index = VectorStoreIndexWrapper(vectorstore=vector_store) chat_llm = ChatBedrock( model_id="amazon.titan-text-express-v1", credentials_profile_name="default" ) output = index.query("What is Python?", chat_llm)
チェイン
チェインによって、メソッドチェインのように、続けて呼び出される一連の処理を定義できます。以下コード内の|
は、pipe operatorといい、チェインの定義に使います。
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_aws import ChatBedrock
prompt = ChatPromptTemplate.from_template(
"Tell me a information about {topic} in 1 sentence."
)
model = ChatBedrock(
model_id="amazon.titan-text-express-v1",
credentials_profile_name="default"
)
chain = prompt | model | StrOutputParser()
chain.invoke({"topic": "LangChain"})
# → '\nLangChain is a platform that allows users to create and interact with AI agents using natural language.'
エージェント
AIアプリケーションとしてのエージェントとは、定義されたアクションやワークフローを使って複雑なタスクをこなすシステムを指します。エージェントは、関数やAPI、RAG、コーディングなどのツールが渡され、ユーザーからのインプットや状況に応じてそれらを使い分け、求められている回答を自律的に生成します。
コースのレッスンにある、AgentExecutor
を使用した実装はレガシーな手法らしいので1、推奨されているLangGraphを用いたコード例を紹介します。
from langchain_aws import ChatBedrock
from langchain_core.tools import tool
from langgraph.prebuilt import create_react_agent
@tool
def magic_function(input: int) -> int:
"""Applies a particular processing to an input.
Use this function to make sure of an output."""
if (input % 2) == 0:
return input // 2
else:
return input * 2
tools = [magic_function]
chat_model = ChatBedrock(
model_id = "amazon.nova-lite-v1:0",
credentials_profile_name = "dev"
)
langgraph_agent_executor = create_react_agent(chat_model, tools)
query = "what is the output of magic_function(4)?"
messages = langgraph_agent_executor.invoke({"messages": [("human", query)]})
print(messages["messages"][-1].content)
<thinking> The tool has provided the output for magic_function(4). I can now provide this information to the User. </thinking>
The output of magic_function(4) is 2.
Demo 1
構成
コースのDemo 1には、大きく分けて以下の6つの実装例が含まれています。
- テキスト生成
a. Bedrockの呼び出しの基礎
b. プロンプトテンプレート - テキスト要約
a. テキスト要約の基礎
b. 大規模なテキスト要約 - QA回答システムの基礎
- チャット履歴やベクトルストアを使ったチャットボット
- コード生成の基礎
- LangGraphを使ったエージェント
コースには、「セットアップ」と「デモの実施」でそれぞれ手順を示す説明動画があるので、それを見ながら進めることになります。
補足事項
-
デモのコードをローカルで実行する
Skill Builderのコースでは、Jupyter NotebookのファイルをAmazon SageMaker AI上の環境で実行します。一部無料枠もありますが、それを超過すれば料金が発生します。
そのため、自分はJupyter Notebookファイルをローカルの環境で実行し、FMの呼び出し料金のみに済ませました。環境構築の方法はこちらの別記事にまとめてますので、参考にしてください。
-
Claude 3 Sonnetのフォーム
デモの環境準備の手順で、BedrockのFMを有効化します。デモの説明動画内にはありませんが、現在ではClaude 3 Sonnetモデルを有効化する際、フォームにユースケースの詳細を記載する必要があります。
-
Amazon Titanモデルの有効化
前述の記事内でも言及していますが、Amazon Titanモデルは一度アクセスを有効化すると、取り消すことはできないです。
おわり
コード例のためにいい感じの質問を考えるのが難しかったです。
その2に続きます。
参考リンク