🪨

コマンドラインからTypeScriptでBedrockを実行する

2024/11/29に公開

前提

この記事では、以下の技術を使ってコマンドからBedrockを実行する方法を記述していきます。

  • Yarn
  • TypeScript
  • AWS SDK for JavaScript v3
  • AWS Bedrock(Anthropic Claude 3.5 Sonnet v2)
  • AWS IAMユーザー(SDK認証用)

なお、この記事ではローカルで挙動確認することを目的としているため、簡易的にIAMユーザーかつBedrockフルアクセスポリシーを用いています。実運用時には適切な権限の設定を推奨します。
またBedrockの利用には基盤モデルへの事前リクエストが必要ですが、本記事では省略します。公式ドキュメントなどをご参照ください。

IAM

今回は既存のIAMユーザに実行権限を追加していきます。

ユーザー更新

  1. 左メニューでユーザーを押下
  2. 対象のユーザー名を押下
  3. 許可タブの許可を追加を押下し、展開されたメニューから許可を追加を押下
  4. 以下のとおりに設定
    • 許可のオプション:ポリシーを直接アタッチする
    • 許可ポリシー:AmazonBedrockFullAccess
  5. 次へを押下
  6. 許可を追加を押下

アクセスキー発行

IAMユーザのアクセスキーが未発行の場合のみ対応します。

  1. 左メニューでユーザーを押下
  2. 対象のユーザー名を押下
  3. セキュリティ認証情報タブ内のアクセスキーを作成を押下
  4. 以下のとおりに設定
    • ユースケース:ローカルコード
    • 上記のレコメンデーションを理解し、アクセスキーを作成します。:ON
  5. 次へを押下
  6. アクセスキーを作成を押下
  7. 画面上にアクセスキーシークレットアクセスキーが表示されるので、コピーして控えておく

プロジェクト作成

以降の手順はローカルで実行していきます。

初期設定

まずは空のディレクトリにて、パッケージマネージャとTypeScriptを設定します。今回はyarnを使用しています。

yarn init -y
yarn add typescript ts-node @aws-sdk/client-bedrock-runtime @aws-sdk/client-bedrock dotenv
yarn add --dev @types/node
npx tsc --init

環境変数設定

AWSの接続情報を環境変数から読み込むため、.envを作成します。

AWS_REGION={AWSリージョン}
IAM_ACCESS_KEY={IAMユーザのアクセスキー}
IAM_SECRET_ACCESS_KEY={IAMユーザのシークレットアクセスキー}

実装上の環境変数呼び出しは、初期設定コマンドで追加したdotenvで行います。

実装

src/index.tsを作成し、次のように実装していきます。

src/index.ts
import {
  BedrockRuntimeClient,
  InvokeModelCommand,
} from "@aws-sdk/client-bedrock-runtime";
import * as dotenv from "dotenv";

dotenv.config();

const client = new BedrockRuntimeClient({
  region: process.env.AWS_REGION,
  credentials: {
    accessKeyId: process.env.IAM_ACCESS_KEY!,
    secretAccessKey: process.env.IAM_SECRET_ACCESS_KEY!,
  },
});

const executeScript = async () => {
  try {
    const command = new InvokeModelCommand({
      modelId: "anthropic.claude-3-5-sonnet-20241022-v2:0",
      body: JSON.stringify({
        anthropic_version: "bedrock-2023-05-31",
        max_tokens: 1000,
        messages: [
          {
            role: "user",
            content: [{ type: "text", text: "こんにちは。" }],
          },
        ],
      }),
      accept: "application/json",
      contentType: "application/json",
    });
    const res = await client.send(command);

    const body = JSON.parse(Buffer.from(res.body).toString("utf-8"));
    const outputText = body.content[0].text;
    console.log(outputText);
  } catch (error) {
    console.error("Error executing script:", error);
  }
};

executeScript();

Bedrock用のクライアントを作成し、InvokeModel実行の結果テキストをコンソールに出力して終了します。

基盤モデルによる変更点

InvokeModel実行時に指定するbodyの構造は、modelIdで指定する基盤モデルによって異なります。
例えば、Titan Text modelsの場合は次のように設定します。レスポンスでテキストが格納されている項目も変更となります。

    const command = new InvokeModelCommand({
      modelId: "Amazon.titan-text-express-v1",
      body: JSON.stringify({
        inputText: "こんにちは。",
        textGenerationConfig: {
          maxTokenCount: 1000,
        },
      }),
      accept: "application/json",
      contentType: "application/json",
    });
    const res = await client.send(command);

    const body = JSON.parse(Buffer.from(res.body).toString("utf-8"));
    const outputText = body.results[0].outputText;

Claudeの指定形式についてはText Completions APIMessages APIという2種類があり、v3系が対応しているのはMessages APIのみです。
Text Completions APIを利用する場合、promptに固定文字列が必要となります。

      const command = new InvokeModelCommand({
        modelId: "anthropic.claude-v2:1",
        body: JSON.stringify({
          prompt: `Human:こんにちは。 Assistant:`,
          max_tokens_to_sample: 1000,
        }),
        accept: "application/json",
        contentType: "application/json",
      });
      const res = await client.send(command);
  
      const body = JSON.parse(Buffer.from(res.body).toString("utf-8"));
      const outputText = body.completion;

コマンド実行

次のコマンドで処理が実行されます。

yarn ts-node src/index.ts

正常終了した場合、以下のような結果が出力されます。

こんにちは!お手伝いできることがありましたらお気軽にお申し付けください。
✨  Done in 3.27s.
NCDCエンジニアブログ

Discussion