【Arduino】マイコンからHttpsでClaude3と話そう!
【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スタックチャン
ChatGPT-API公開(2023年3月2日)
Claude3-「Claude 3」ファミリーを発表(2023年3月4日)
【API、マイコンからはSDK無しで接続したい】
Claude3の基本情報は下記ページで[どんなことができる?][モデルの種類/精度/利用額は?][APIの認証用トークンの取得方法]が分かります。ただマイコンからのAPIの使い方は書いてないので[Claude 3 APIをオンラインで利用する]の先は、このページのソースを見てください。
【マイコンからAPIで接続している情報が見つからない】
ちょっとClaude 3 APIについてネットを調べると、SDKが推奨されているためか「OpenAIのAPI公開時のように、マイコン(ArduinoやRaspberry Pi Pico)からHttps接続しているサンプルコード情報」が見つかりませんでした。そんな時にESP32の掲示板?でChatGPTと同じくHttpsで接続できると噂を見ました。そうだ,自分で書こう!
Anthropic API の操作を容易にする Python と Typescript のライブラリを提供します
【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)
動作確認したM5社のESP32製品
サンプルコード(抜粋)
公式のcurlサンプルをHTTPClientライブラリを使ったHttpsに書き換えて動作することを確認しました。POSTで取得したClaude3の返信内容を出力しています。JSONは省略!
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"}
]
}'
#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);
}
サンプルコード(全体)
/***
- 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