やったこと
LINEとAzure OpenAIで超簡単なbotを以前作ってみたのですが、ちょっとBedrockもさわってみたかったのとバックエンドをClaudeに変えてみたかったこともあって切り替えました。
困ったことと変えた後のコードを残しておきます!
困ったこと
あまりにAWS Bedrock素人すぎて以下の点がわかってなかったです。
Bedrockでテキスト生成する場合はInvokeとConverseの2種類のAPIがある
InvokeAPIはTitanとかClaudeとかそれぞれのモデルの公式なメッセージ定義通りに書き下す必要があるもの。
それに対してConverseはモデルごとの切り替えを容易にするためにBedrock上でユーザープロンプトやシステムプロンプトの定義の仕方を標準化してくれている(モデルごとの差異を吸収してくれている)ようです! なんて便利な!
ということで今回はConverseAPIを使うことにしました。
推論パラメータ
基盤モデルをそのまま使うのはClaude4とか新しいやつだとサポート外で、推論パラメータなるインターフェースを使って呼び出すのがいいみたいです。
使うときはBedrockコンソールのモデルカタログにあるモデルごとのモデルIDではなく、推論パラメータのID(Inference profile ID)を使うだけ。
簡単・・・だけど最初基盤モデルを必死に呼び出そうとして30分くらい困ってしまいました。。
Claude4だったら
システムプロンプトの入れ方
- ConverseAPIで
- HTTP(cURL)アクセス時の
- ユーザーではなくシステムプロンプトの
という3つの条件がそろった記述やドキュメントが意外と見つからずに苦戦しました。
公式ドキュメントにも記載がなく・・・
いろいろ調べた結果、HTTP POSTリクエストする際のBodyに入れるMessage
属性の中にはuser
とassistant
属性しか入れられないということに1時間くらいして気が付きました。
リクエストボディのMessage
と同レベルでSystem
の定義をすることが正解だったみたいです。
ということで完成したコード
こちらです。わかりやすいようにAzure OpenAIを使っていた時のコードをコメントで残してみています。
今回は業務報告書のレビューをLINEでしてほしかったので”業務”で始まるLINEメッセージの場合に中身を読みとってコメントしてくれるbotをLambda上で書きました。
PostmanやcURLを使って動作確認した後に、node-fetchライブラリを使ってLambda上では実装しました。
import fetch from "node-fetch/src/index.js"
const API_KEY = "Bearer ******"; //bedrock api key(IAMユーザー上で発行)
// const API_KEY = "*****"; //azure-openai api key
const LINE_TOKEN = "*****";
export const handler = async(event) => {
const reqBody = JSON.parse(event.body);
let message = {};
if (reqBody.events[0].message.type == "text" && reqBody.events[0].message.text.startsWith("業務")) {
const URL =
"https://bedrock-runtime.ap-northeast-1.amazonaws.com/model/apac.anthropic.claude-sonnet-4-20250514-v1:0/converse";
// "https://*****.openai.azure.com/openai/deployments/*****/chat/completions?api-version=2023-03-15-preview";
const data = {
"system": [
{"text" : "あなたは優秀なモチベーターです。業務報告書を受け取った場合は、「特にポジティブフィードバックができそうな点」「特に今後成長するために取り組めそうな点」をそれぞれ見つけて200字以内でコンパクトにコメントしてください。"}
],
messages: [
// {
// role: "system",
// content:"あなたは優秀なモチベーターです。業務報告書を受け取った場合は、「特にポジティブフィードバックができそうな点」「特に今後成長するために取り組めそうな点」をそれぞれ見つけて200字以内でコンパクトにコメントしてください。",
// },
{
"role": "user",
"content":[
{
"text" : reqBody.events[0].message.text
}
]
// content: reqBody.events[0].message.text,
},
],
};
const response = await fetch(URL, {
method: "POST",
body: JSON.stringify(data),
headers: {
"Content-Type": "application/json",
// "api-key": API_KEY,
"Authorization": API_KEY,
},
});
const resjson = await response.json();
message = {
type: "text",
// text: resjson.choices[0].message.content
text: resjson.output.message.content[0].text
};
await fetch("https://api.line.me/v2/bot/message/push", {
method: "POST",
headers: {
"Content-Type": "application/json",
"Authorization": `Bearer ${LINE_TOKEN}`,
},
body: JSON.stringify({
to: reqBody.events[0].source.groupId || reqBody.events[0].source.userId,
messages: [
message
],
}),
});
}
return 200;
};