🦄

【Arduino】マイコンからHttpsでClaude3と話そう!

2024/04/03に公開

【API(Https接続)でマイコンから話そう】

探しても見つからなかったので、自分で書いてみました!M5Stackシリーズ(ESP32)なら、Arduino IDE環境にコピペで動くと思います。

LLMと会話するAIスタックチャン

M5Stack(ESP32を搭載)マイコンを利用して「ChatGPTと会話できるAIスタックチャン」が誕生したのが去年2023年3月2日。しかしLLMの進化は留まるところを知らず、約1年後の2024年3月4日には「ChatGPT-GPT4を超えると言われるClaude3」が発表されました。このLLMとも会話してみたい!

ChatGPT API搭載AIスタックチャン

https://youtu.be/ceujtc7mJG0

ChatGPT-API公開(2023年3月2日)

https://www.nikkei.com/article/DGXZQOGN020A20S3A300C2000000/

Claude3-「Claude 3」ファミリーを発表(2023年3月4日)

https://pc.watch.impress.co.jp/docs/news/1573937.html

【API、マイコンからはSDK無しで接続したい】

Claude3の基本情報は下記ページで[どんなことができる?][モデルの種類/精度/利用額は?][APIの認証用トークンの取得方法]が分かります。ただマイコンからのAPIの使い方は書いてないので[Claude 3 APIをオンラインで利用する]の先は、このページのソースを見てください。
https://apidog.com/jp/blog/claude-3-api-tutorial/

【マイコンからAPIで接続している情報が見つからない】

ちょっとClaude 3 APIについてネットを調べると、SDKが推奨されているためか「OpenAIのAPI公開時のように、マイコン(ArduinoやRaspberry Pi Pico)からHttps接続しているサンプルコード情報」が見つかりませんでした。そんな時にESP32の掲示板?でChatGPTと同じくHttpsで接続できると噂を見ました。そうだ,自分で書こう!

Anthropic API の操作を容易にする Python と Typescript のライブラリを提供します

https://support.anthropic.com/ja/articles/8114535-apiのサポートや支援を受けるにはどこに行けばいいですか
https://docs.anthropic.com/claude/reference/client-sdks

【Messages APIをHttps接続で記述する】

APIは2種類で「1.旧:Text Completions API」と「2.新:Messages API」があります。公式ではMessages APIが推奨されていたので、下記curlのサンプルを元にArduino-IDE環境で書いてみました。動作確認済はM5 Core2とAtom Liteです。

Messages APIのサンプルコード(curl(shell)/Python/Typescript)

https://docs.anthropic.com/claude/reference/messages_post
https://docs.anthropic.com/claude/reference/messages-examples

動作確認したM5社のESP32製品

https://docs.m5stack.com/ja/core/core2

サンプルコード(抜粋)

公式のcurlサンプルをHTTPClientライブラリを使ったHttpsに書き換えて動作することを確認しました。POSTで取得したClaude3の返信内容を出力しています。JSONは省略!

変更前:公式curlサンプル
curl https://api.anthropic.com/v1/messages \
     --header "x-api-key: $ANTHROPIC_API_KEY" \
     --header "anthropic-version: 2023-06-01" \
     --header "content-type: application/json" \
     --data \
'{
    "model": "claude-3-opus-20240229",
    "max_tokens": 1024,
    "messages": [
        {"role": "user", "content": "Hello, world"}
    ]
}'
変更後:抜粋arduinoソース
#include <HTTPClient.h>
const char* apiKey   = "Your-Setting";
const char* host     = "https://api.anthropic.com/v1/messages";
HTTPClient https;
const char* Your_Question = "\"あなたは誰?\"";

    if (https.begin(host)) {
      /* --header */
      https.addHeader("x-api-key", apiKey);
      https.addHeader("anthropic-version", "2023-06-01"); 
      https.addHeader("content-Type", "application/json"); 
      /* --data */
      String payload = String("{\"model\": \"claude-3-haiku-20240307\", \"max_tokens\": 1024\, \"messages\": [{\"role\": \"user\", \"content\": ") + Your_Question + String("}]}");

      int httpCode = https.POST(payload);
      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = https.getString();
        dataStart = payload.indexOf("text\":\"") +7;
        dataEnd = payload.indexOf("\"}],\""); 
        Claude_Answer = payload.substring(dataStart, dataEnd);
        Serial.print("Claude3 Answer is: "); Serial.println(Claude_Answer);
      }

サンプルコード(全体)

全arduinoソース(HttpsでESP32からClaude3に問い合わせ)
/***
 - Access test from ESP32 to Claude3 API (Https)
  https://docs.anthropic.com/claude/reference/messages-examples
***/
#include <Arduino.h>
#include <WiFi.h>
#include <HTTPClient.h>

/* Setting */
const char* ssid     = "Your-Setting";
const char* password = "Your-Setting";
const char* apiKey   = "Your-Setting";
const char* host     = "https://api.anthropic.com/v1/messages";

/* Define */
const char* Your_Question = "\"あなたは誰?\"";
String Claude_Answer;
uint16_t dataStart = 0;
uint16_t dataEnd = 0;

HTTPClient https;

void setup() {
  Serial.begin(115200);

  WiFi.mode(WIFI_STA);
  WiFi.disconnect();
  while(!Serial);

  // wait for WiFi connection
  WiFi.begin(ssid, password);
  Serial.print("Connecting to ");
  Serial.println(ssid);
  /* Original code: When it does not work well to connect to WiFi (ESP32)
     https://qiita.com/nak435/items/33f05988575f8a61b2a3
  */
  bool done = true;
  while (done) {
      Serial.print("WiFi connecting");
      auto last = millis();
      while (WiFi.status() != WL_CONNECTED && last + 1000 > millis()) {
          delay(500);
          Serial.print(".");
      }
      if (WiFi.status() == WL_CONNECTED) {
          done = false;
      } else {
          Serial.println("retry");
          WiFi.disconnect();
          WiFi.reconnect();
      }
  }

  Serial.println("Wifi connected");
  Serial.print("IP address: "); Serial.println(WiFi.localIP());
}

void loop() {

    Serial.println("[HTTPS] begin...");
    if (https.begin(host)) {
      /* --header */
      https.addHeader("x-api-key", apiKey);
      https.addHeader("anthropic-version", "2023-06-01"); 
      https.addHeader("content-Type", "application/json"); 
      /* --data */
      String payload = String("{\"model\": \"claude-3-haiku-20240307\", \"max_tokens\": 1024\, \"messages\": [{\"role\": \"user\", \"content\": ") + Your_Question + String("}]}"); 

      Serial.print("data: "); Serial.println(payload);
      Serial.println("[HTTPS] GET...");
      
      /* start connection and send HTTP header */
      int httpCode = https.POST(payload);

      if (httpCode == HTTP_CODE_OK || httpCode == HTTP_CODE_MOVED_PERMANENTLY) {
        String payload = https.getString();
        // Serial.println(payload);
        dataStart = payload.indexOf("text\":\"") +7;
        dataEnd = payload.indexOf("\"}],\""); 
        Claude_Answer = payload.substring(dataStart, dataEnd);
        Serial.print("Claude3 Answer is: "); Serial.println(Claude_Answer);
      } else {
        Serial.printf("[HTTPS] GET... failed, error: %s\n", https.errorToString(httpCode).c_str());
      }
      https.end();
      
    } else {
      Serial.printf("[HTTPS] Unable to connect\n");
    }

  Serial.println("Wait 1 min...");
  delay(60000);
}

【あとがき】

ツッコミどころ満載ですが、”まずは雑に公開”します。マイコン(特にM5Stack)からClaude3との会話を楽しもう!
ちなみにClaude3 Sonnetに書いてもらったプログラムは…動かないものでした。数回試しても収束しないので、短いのは自分で書いた方が早かった。

Discussion