プロンプトエンジニアリングに最適なツールはこれだ！！？（結局方眼紙最強説）

皆さん、Bedrockしてますか？？（生成系AI活用してますか？？）

生成系AIを活用する際に必須なのがプロンプトエンジニアリングです。プロンプトエンジニアリングを行う際には試行錯誤が必要になりますが、チャットツールで行おうとすると入力欄が小さかったり😣、途中で間違えて送信してしまったり🤣と、なかなか効率が上がらないと思います。Streamlitでアプリを作成するのもちょっと面倒😥、、ということがあると思います。

そんなときの救世主を発見しました。

そう、みんな大好き **Excel方眼紙** です。🎊

---

眼の前に広がる無限の入力欄！！😍

大量の関数！！😍

Ctrl+Sでいつでも保存可能！！😍

コピペ超簡単！！😍

**メリットしかない！！！！！**

---

ということで、Excel、、、ではなくGoogleスプレッドシートでプロンプトエンジニアリングしてみました。

こちらを御覧ください。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/7dbf232c-2315-7f5d-b350-e3a30e8b751a.gif)

どうですか！ **捗ってるでしょう！！**

* ワークエリアでタスクや対象の文書、出力のトーンなどを個別に入力します。
* プロンプト行には`JOIN()`関数を使い、ワークエリアの文字列を連結した文字列がセットされるようにします。
* プロンプト文字列を選択した状態でマクロを実行すると、回答欄にAIで生成した文書が入ります。

すごい

:::note
PartyRockのリリース文書を使用しました。

https://aws.amazon.com/about-aws/whats-new/2023/11/partyrock-amazon-bedrock-playground/
:::

:::note
Bedrockで使用できるClaudeモデルのプロンプトエンジニアリング手法はこちらで学べます。

https://docs.anthropic.com/claude/docs

このような超絶わかりやすいスライドも用意されています。

![](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/ff723246-ed36-1af0-2570-785694299333.png)

:::

ということで、GoogleスプレッドシートでAmazon Bedrockを呼べるようにする方法を紹介します。
モデルはもちろんClaudeです。

## 手順

1. スプレッドシートを開きます。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/7403162e-1cf6-61f8-3101-7f648267e647.png)

1. 拡張機能メニューの`Apps Script`を選択します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/9cf82430-8dfd-16b1-20d1-69845cefb28b.png)

1. Apps Scriptのエディターが開きます。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/3cba5d44-dd1a-5f0f-298f-5e180d48eac9.png)

1. `+`ボタン（ファイルを追加）から`スクリプト`を選択します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b9a3b59b-d954-9e1d-ea8f-4362ca72d999.png)

1. ファイル名は「`AWS`」とします。拡張子は自動で付与されます。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/8e328912-7f88-b733-8e3d-41e5619447aa.png)

1. GitHubで公開されている[aws-apps-scripts](https://github.com/smithy545/aws-apps-scripts)のaws.jsをまるごとコピーし、AWS.gsに貼り付けます。

    https://github.com/smithy545/aws-apps-scripts

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/1f2b8061-291c-5561-5995-1c17f8f1e787.png)

1. エンドポイント名を外部から指定できるようにソースを修正します。

    :::note
    AWSのサービスの多くのエンドポイントは`{サービス名}`.`{リージョン}`.amazonaws.com`の書式です（例：dynamodb.us-west-2.amazonaws.com）。
    
    Bedrockは特殊で、サービス名の部分が`bedrock`、`bedrock-runtime`、`bedrock-agent`、`bedrock-agent-runtime`の4種類あります（2023年12月現在）。
    
    LLMモデルの呼び出しに使用するのは`bedrock-runtime`のため、エンドポイントは `bedrock-runtime`.`{リージョン}`.amazonaws.comとなります。
   :::

    ```diff:24行目～
        /**
         * Authenticates and sends the given parameters for an AWS api request.
         * @param {string} service - the aws service to connect to (e.g. 'ec2', 'iam', 'codecommit')
         * @param {string} region - the aws region your command will go to (e.g. 'us-east-1')
         * @param {string} action - the api action to call
         * @param {Object} [params] - the parameters to call on the action. Defaults to none.
         * @param {string} [method=GET] - the http method (e.g. 'GET', 'POST'). Defaults to GET.
         * @param {(string|object)} [payload={}] - the payload to send. Defults to ''.
         * @param {Object} [headers={Host:..., X-Amz-Date:...}] - the headers to attach to the request. Host and X-Amz-Date are premade for you.
         * @param {string} [uri='/'] - the path after the domain before the action. Defaults to '/'.
         * @param {Object} [options] - additional service specific values
    +    * @param {string} [host] - host
         * @return {string} the server response to the request
         */
    -   request: function(service, region, action, params, method, payload, headers, uri, options) {
    +   request: function(service, region, action, params, method, payload, headers, uri, options, host) {
    ```
    
    ```diff:63行目～
          var dateStringFull =  String(d.getUTCFullYear()) + addZero(d.getUTCMonth()+1) + addZero(d.getUTCDate()) + "T" + addZero(d.getUTCHours()) + addZero(d.getUTCMinutes()) + addZero(d.getUTCSeconds()) + 'Z';
          var dateStringShort = String(d.getUTCFullYear()) + addZero(d.getUTCMonth()+1) + addZero(d.getUTCDate());
          var payload = payload || '';
          var hashedPayload = Crypto.SHA256(payload);
          var method = method || "GET";
          var uri = uri || "/";
    -     var host = getHost(service, region, bucket);
    +     var host = host || getHost(service, region, bucket);
          var headers = headers || {};
          var request;
          var query;
    ```

    これでAWS.gsは完成です。

1. `コード.gs`に以下の関数を追加します。（myFunction関数は削除します）
    
    ```javascript:コード.gs
    function invokeClaudeInstantV1() {
    
      const modelId = 'anthropic.claude-instant-v1'
    
      const headers = {
        'Content-Type': 'application/json',
        'Accept': '*/*'
      }
    
      const payload = {
        "prompt": `\n\nHuman: ${getInputText()}\n\nAssistant:`,
        "max_tokens_to_sample": 1000,
        "temperature": 0.5,
        "top_k": 250,
        "top_p": 1,
        "stop_sequences": [
          "\n\nHuman:"
        ],
        "anthropic_version": "bedrock-2023-05-31"
      }
    
      const outputText = invokeBedrock(modelId, headers, payload)
      setOutputText(outputText)
    }
    
    function getInputText() {
      return SpreadsheetApp.getCurrentCell().getRichTextValue().getText()
    }
    
    function setOutputText(outputText) {
      const currentCell = SpreadsheetApp.getCurrentCell()
      currentCell.offset(1,0).setRichTextValue(
        SpreadsheetApp.newRichTextValue().setText(outputText).build()
        )
    }
    
    function invokeBedrock(modelId, headers, payload) {
    
      const accessKeyId = PropertiesService.getScriptProperties().getProperty('accessKeyId')
      const secretAccessKey = PropertiesService.getScriptProperties().getProperty('secretAccessKey')
      const region = PropertiesService.getScriptProperties().getProperty('region')
      
      AWS.init(accessKeyId, secretAccessKey)
    
      const response = AWS.request(
        'bedrock',
        region,
        '',
        undefined,
        'POST',
        payload,
        headers,
        `/model/${modelId}/invoke`,
        undefined,
        `bedrock-runtime.${region}.amazonaws.com`
      )
      const content = JSON.parse(response.getContentText())
      
      return content.completion
    }
    ```

1. パラメーターを設定します。左のメニューの`プロジェクトの設定`を選択します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/bba9b5aa-930c-ed4a-247c-0a497db443d9.png)

    スクリプトプロパティ欄に以下の値をセットします。
    
    * accessKeyId
    * secretAccessKey
    * region

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/b6ab95e2-ce92-b9df-6d8f-10e51475a4fa.png)

    :::note
    アクセスキー、シークレットキーを使用するので誤ってスプレッドシートを共有したりしないように注意しましょう。
   :::

    これでコーディングは完了です。試しに実行してみましょう。

1. スプレッドシートのセルにプロンプトを入力します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/0e8fd833-9e9a-226c-d6e7-e8ce13d7ccbf.png)

1. Apps Scriptのエディタに戻り、`実行`ボタンをクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/dd4585ea-8b5a-d15b-d66c-b2429147e916.png)

1. 初回実行時は権限の確認を求められます。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/cbab8f7b-414d-13f1-9a52-110dd2ad4493.png)

1. 内容を確認し、`許可`を選択します。

1. うまく動作したら、プロンプトのひとつ下のセルに応答が入ります。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/20006ad9-ddb7-41dd-77e5-f5a40e8b4bed.png)

    :::note
    私は一つ下のセルに出力するようにしましたがお好みで改変ください。
   :::

    あと少しです。

1. スプレッドシートの画面に戻り、`拡張機能`メニューの`マクロ`-> `マクロをインポート`を選択します。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/22ce27ce-b937-7f28-4e6b-7bd19fd6fa83.png)

1. `invokeClaudeInstantV1`の`関数を追加`をクリックします。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/1e4d50a8-ddc3-2652-88e1-c9c793483c2c.png)

    これでApps Scriptの画面に移らなくてもBedrockを呼び出すことができるようになりました。

    ![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/41574/d5108e56-ff0c-220c-3df1-9195c76e8883.png)

---

最強です。

他のLLMサービスでも同じようなことはできると思います。APIキーでアクセスできるものであればもっと簡単に実現できると思います。

---
参考サイト

https://developer.mamezou-tech.com/blogs/2023/06/28/gas-using-npm-packages/

https://zenn.dev/creationup2u/articles/9eca7883e82c0a

https://akkinoc.dev/posts/2022/05/15/aws-api-from-google-apps-script/