7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

【AWS×Rails】Bedrock AgentCore MemoryをDBの代わりにして画面へチャット履歴を表示する

Last updated at Posted at 2026-01-01

どうもこんにちは。今回の記事では、前回の記事で構築したMemoryに保存した記憶をWebアプリ側へ呼び出して画面へレンダリングしてみようと思います。

AIエージェント側へのMemory実装は以下の記事をご参照ください。

今回は何をする?

  • AIエージェントとのやりとり(Memory)をAWS SDKから呼び出す
  • 呼び出したMemoryを画面へ表示する

RailsアプリケーションとAIエージェントの関係

以下のように、AWS SDKを通じて、RailsアプリケーションからAIエージェントを呼び出しています。

image.png

また、AIチャット画面はチャットルームの閲覧画面のパスに実装をしています。ルーティングは以下のようになっています。

get '/ai_chat_rooms' => 'ai_chat_rooms#index'
get '/ai_chat_rooms/new' => 'ai_chat_rooms#new'
post '/ai_chat_rooms' => 'ai_chat_rooms#create'
get '/ai_chat_rooms/:id' => 'ai_chat_rooms#show'
get '/ai_chat_rooms/:id/edit' => 'ai_chat_rooms#edit'
patch '/ai_chat_rooms/:id' => 'ai_chat_rooms#update'
delete '/ai_chat_rooms/:id' => 'ai_chat_rooms#destroy'
get '/ai_chat_rooms/:id/chat' => 'ai_chat_rooms#chat'   # ユーザからメッセージを送信 → 受信
get '/ai_chat_rooms/:id/get_chat_history' => 'ai_chat_rooms#get_chat_history'   # 履歴を受信

# 上記を簡潔に記載すると、以下のようになります。

resources :ai_chat_rooms do
    member do
        get :chat
        get :get_chat_history
    end
end

Railsアプリケーション側のDB構成

スキーマファイルは以下のようになっています。

session_idnullを許容しているのは、チャットルームを新規作成したタイミングではsession_idを発行せず、チャット開始時に発行されたsession_idを格納するように設計したためです。

create_table "ai_chat_rooms", charset: "utf8", force: :cascade do |t|
    t.string "name", null: false
    t.integer "user_id", null: false
    t.string "session_id"
    t.text "description"
    t.datetime "created_at", null: false
    t.datetime "updated_at", null: false
    t.index ["session_id"], name: "index_ai_chat_rooms_on_session_id", unique: true
    t.index ["user_id"], name: "index_ai_chat_rooms_on_user_id"
end

RailsアプリでAIチャットを実装するために

RailsアプリでAIチャットを実装するために、ActionController::Liveを採用しています。
これをincludeすることによって、ストリーミングに対応することができます。

class AiChatController < ApplicationController
    include ActionController::Live

    #...中略...

リクエスト&レスポンスの処理の流れ

処理は以下の流れで行います。

  1. ユーザがテキストボックスにリクエスト文を入力し、送信ボタンをクリックする
  2. クリックイベントが発火し、非同期でRailsサーバへ通知される
  3. RailsサーバからAWS SDK経由でBedrock AgentCore Runtime APIが呼ばれる
  4. (Bedrock AgentCore Runtime側でやりとり履歴がMemoryに保存される)
  5. Railsサーバがレスポンスを受け取る
  6. 画面にレスポンスを表示する

APIリクエストを送信する際に、「どのユーザのやりとりなのか」「どのセッションのやりとりなのか」をパラメータに含める必要があります。

どのユーザのやりとりなのかは、ログインユーザのusernameを使用します。どのセッションのやりとりなのかは、チャットルームに保存しているsession_idを使用します。初回リクエストの場合は、その場でsesison_idを発行します。

これを踏まえると、以下のようなコードでAPIリクエストを送信することになります。

# チャットルームIDを取得
room_id = params[:room_id]

# チャットルームObjectを取得
room_obj = Room.find(room_id)

# セッションIDを取得
session_id = if room_obj.session_id.present?
               room_obj.session_id
             else
               SecureRandom.hex(24)
             end

# ログインユーザのusernameを取得
user_name = current_user.username

# リクエスト文を取得
text = params.require(:text)

# AWS Bedrock AgentCore Runtime クライアントの初期化
region = ENV.fetch('BEDROCK_AGENTCORE_AWS_REGION')
client = if Rails.env.development?
           creds = Aws::Credentials.new(
             ENV['BEDROCK_AGENTCORE_AWS_ACCESS_KEY'],
             ENV['BEDROCK_AGENTCORE_AWS_SECRET_ACCESS_KEY']
           )
           Aws::BedrockAgentCore::Client.new(
             region: region,
             credentials: creds,
             http_read_timeout: 900,
             http_open_timeout: 30
           )
         else
           Aws::BedrockAgentCore::Client.new(
             region: region,
             http_read_timeout: 900,
             http_open_timeout: 30
           )
         end

# ペイロードを生成
payload = {
  input: {
    prompt: text
  },
  session_data: {
    session_id: session_id,
    actor_id: user_name
  }
}.to_json

# リクエストを送信
resp = client.invoke_agent_runtime(
  agent_runtime_arn: ENV['BEDROCK_AGENTCORE_AGENT_RUNTIME_ARN'],
  runtime_session_id: session_id,
  payload: payload,
  qualifier: 'DEFAULT',
  content_type: 'application/json',
  accept: 'application/json'
)

上記のようなコードでAPIリクエストを送信します。AIエージェント側でペイロードを受け取れるように実装をしておいてください。

画面が更新されたり、他の画面から遷移してきた時の処理の流れ

これに加えて、画面が更新された時に、過去のやりとりも画面に表示されている必要があります。その場合は以下の流れで処理を行います。

  1. 画面を更新する
  2. RailsサーバからAWS SDK経由でBedrock AgentCore Memory APIが呼ばれる
  3. Railsサーバがレスポンス(履歴データ)を受け取る
  4. 画面にレスポンス(履歴データ)を表示する

今回は、画面が更新されたタイミングで、JS側からRailsサーバへ履歴取得のアクションを実行します。

  const getChatHistory = async () => {
    try {
      // 下に記載している`get_chat_history`を呼び出し
      const response = await fetch(`/ai_chat_rooms/${roomId}/get_chat_history`);
      if (!response.ok) throw new Error(`HTTP error! status: ${response.status}`);
      const data = await response.json();
      if (data.messages && Array.isArray(data.messages)) {
        data.messages.forEach(entry => {
          if (entry.role === 'USER') {
            appendUserMessage(entry.text);           // ユーザのメッセージを描画するための関数
          } else if (entry.role === 'ASSISTANT') {
            appendAssistantMessage(entry.text);      // AIのメッセージを描画するための関数
          }
        });
      }
    } catch (err) {
      console.error('[AI_CHAT] Failed to fetch chat history:', err);
    }
  };

  getChatHistory();
  before_action :set_ai_chat_room, only: %i[show edit update destroy get_chat_history]

  def get_chat_history
    session_id = @ai_chat_room.session_id

    return if session_id.blank?

    # AWS SDKで履歴を取得(Short-term memory)
    res = @client.list_events({
      memory_id: "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
      actor_id: current_user.username,
      session_id: session_id
    })

    # 時系列順にソート
    events = res.events.sort_by(&:event_timestamp)

    # 受信した履歴を整理
    messages = []
    events.each do |event|
      event.payload.each do |payload|
        if payload.conversational
          messages << {
            role: payload.conversational.role,
            text: payload.conversational.content.text
          }
        end
      end
    end

    # JSへ返却
    respond_to do |format|
      format.json { render json: { status: 'success', messages: messages } }
    end
  end

  private

  def set_ai_chat_room
    @ai_chat_room = current_user.ai_chat_rooms.find(params[:id])
  end

上記のように処理を行うことで、「画面を更新した時になかなか画面が表示されない」という問題は解消されます。

自分が初心者エンジニアの時は、showアクションに履歴取得のコードを書いていたと思います。
showに書いてしまうと、「大量の履歴を取得してHTMLの生成が完了するまで画面が表示されない」という問題が発生します。
そうならないように、非同期で実装することをお勧めします。

list_eventsで履歴の取得ができない場合

以下の点を確認してみてください。

  • AWS SDKのAPI呼び出しで失敗していないか
    • この場合、Rails側のシンタックスエラーなどではないか確認する
    • Rails側でない場合、AIエージェント側のエラーである可能性が高い
  • AIエージェント側のエラーである場合
    • ペイロードの受け取りはできているか
    • Memoryの保存はできているか

AIエージェント側のエラーであるかは、Bedrock AgentCore Observabilityの設定ができていないと難しかったりします。もしくは、AIエージェントのagent.pyなどのprintflush=trueを設定していないことが原因だったりします。

まとめ

履歴を保存するためのテーブルを作成しなくて良いのが楽!

画面表示が遅くなるデメリットも非同期処理にすればひとまずOKかなぁ。

7
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?