StepFunctionsとCommandR+でコードもプロンプトも書かずにRAGを作る

Command R+ の登場で「プロンプトを書かないRAG」が実現できるようになったので、StepFunctionsの「コードを書かないRAG」と組み合わせて「コードもプロンプトも書かないRAG」を作ってみます。Retrieverとしてはナレッジベースを使用しこちらもノーコードです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/2c4d1d16-b92a-b97c-1c65-7b9c0bef4382.png)

# 実行例

### 入力

```JSON
{
  "query": "Claude 3 と Command R のモデルIDは？"
}
```

### 出力
```JSON
  "result": 
  "Anthropic Claude 3にはいくつかのモデルIDがあります。その一部を次に示します。\n\n
  - anthropic.claude-3-haiku-20240307-v1:0\n
  - anthropic.claude-3-opus-20240229-v1:0\n\n
  Cohere Command RのモデルIDは次のとおりです。\n\n
  - cohere.command-r-v1:0"
```

# 1ステップ目

1ステップ目ではユーザー入力を元にRetrieverの検索クエリを生成します。
Bedrock の InvokeModelを配置し、設定タブを以下の様にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/4965922f-eb18-69d5-81c1-2158c810a90a.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/9c80964a-6abc-0085-e70d-b053a1eb86b8.png)

基盤モデルとして Command R+ を指定し、モデルパラメーターを以下の様にします。
```JSON
{
  "message.$": "$.query",
  "search_queries_only": true
}
```
出力タブを以下の様にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/f6eb05f9-c671-d253-0a20-823257adc807.png)

`ResultSelector を使用して結果を変換`にチェックを入れ、以下の様に入力します。
```JSON
{
  "result.$": "$.Body.search_queries[*].text"
}
```
生成結果（複数件）から`text`を配列で引っこ抜きます。

`ResultPath を使用して元の入力を出力に追加`にチェックを入れ、以下の様に入力します。
```
$.step1
```
これで元の入力を維持したまま、このステップの実行結果を`step1`に格納します。

このステップの最終的な出力の例は以下です。
```
{
  "query": "Claude 3 と Command R のモデルIDは？",
  "step1": {
    "result": [
      "Claude 3 model ID",
      "Command R model ID"
    ]
  }
}
```
`step1`の`result`配列の中に検索クエリが配列で格納されます。

※英語に存在する単語は英語になっちゃうかー。。。

# 2ステップ目

1ステップ目（検索クエリ）の件数だけループしてナレッジベースを呼び出す為、Map を配置し、設定タブを以下の様にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/b33adc4a-06bd-d587-6e55-f736b34a0d27.png)

`項目配列へのパスを指定`にチェックを入れ、以下の様に入力します。
```
$.step1.result
```
`step1`の`result`配列の内容でループします。

出力タブを以下の様に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/85dbaeca-2a91-c191-f1cb-c3e894778765.png)

`ResultPath を使用して元の入力を出力に追加`にチェックを入れ、以下の様に入力します。
```
$.step2
```
これで元の入力を維持したまま、このステップの実行結果を`step2`に格納します。

## 3ステップ目

Retrieverとしてナレッジベースを使用して検索を行います。
Map の中に Bedrock Runtime Agents: Retrieve を配置し、以下の様に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/91437eca-2ffe-570e-b2e4-baa3e3897ff9.png)

APIパラメータを以下の様に設定します。
```JSON
{
  "KnowledgeBaseId": "各自のナレッジベースID 10桁",
  "RetrievalQuery": {
    "Text.$": "$"
  }
}
```

これで、`step1`の`result`配列の内容を元にナレッジベースの検索が実行されます。

出力タブを以下の様に設定します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/7b6c926e-8e7a-421e-e581-7fe5899cb933.png)

`ResultSelector を使用して結果を変換`にチェックを入れ、以下の様に入力します。
```JSON
{
  "result.$": "$.RetrievalResults[*].Content"
}
```
ナレッジベースの実行結果（検索結果）から、とりあえず`Content`のみ抜き出して（contentの中に検索結果が`text`として複数件（デフォルト5件）格納されています）`result`に格納します。
2ステップ目と3ステップ目の最終的な例は以下です。

```JSON
{
  "query": "Claude 3 と Command R のモデルIDは？",
  "step1": {
    "result": [
      "Claude 3 model ID",
      "Command R model ID"
    ]
  },
  "step2": [
    {
      "result": [
        {"Text": ...},
        {"Text": ...},
        {"Text": ...},
        {"Text": ...},
        {"Text": ...}
      ]
    },
    {
      "result": [
        {"Text": ...},
        {"Text": ...},
        {"Text": ...},
        {"Text": ...},
        {"Text": ...}
      ]
    }
  ]
}
```

検索クエリが複数件だった場合、ナレッジベースの検索が複数実行され、`result`が複数格納されます。

# 4ステップ目

Bedrock の InvokeModelを配置し、設定タブを以下の様にします。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/cf74ab68-78b0-d2b6-93c8-3f467865d947.png)

基盤モデルとして Command R+ を指定し、モデルパラメーターを以下の様にします。

```JSON
{
  "message.$": "$.query",
  "documents.$": "$.step2[*].result[*]"
}
```
大元の入力を`message`に設定します。
`step2`にはナレッジベースの検索結果（複数件）が格納されているので、まとめて`documents`に設定します。

出力タブを以下の様に設定します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3573242/7727ff69-4618-502c-78b9-eb6f5b511cd6.png)

`ResultSelector を使用して結果を変換`にチェックを入れ、以下の様に入力します。
```JSON
{
  "result.$": "$.Body.text"
}
```
これで最終的な生成結果のみ出力します。

最終的に実行されるプロンプトの例は以下です。
```JSON
{
  "ModelId": "arn:aws:bedrock:us-west-2::foundation-model/cohere.command-r-plus-v1:0",
  "Body": {
    "documents": [
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...},
      {"Text": ...}
    ],
    "message": "Claude 3 と Command R のモデルIDは？"
  }
}
```
実行結果の例は冒頭の以下になります。
```JSON
  "result": 
  "Anthropic Claude 3にはいくつかのモデルIDがあります。その一部を次に示します。\n\n
  - anthropic.claude-3-haiku-20240307-v1:0\n
  - anthropic.claude-3-opus-20240229-v1:0\n\n
  Cohere Command RのモデルIDは次のとおりです。\n\n
  - cohere.command-r-v1:0"
```

# まとめ

以上で、コードもプロンプトも書かずにRAG、いや、Advanced RAG が実現できました。
ノーコードでとても簡単ですね！（白目