Blazor C# チュートリアル - Claude APIを使ってみる

# はじめに

タイトルの通り、C#(Blazor)でClaude APIを使う方法について説明します。
初回登録時に一定額の無料クレジットが付与されますので、無料で試すことができます。

# Claudeとは

Anthropic社が開発したAIモデル
ChatGPTのように人間と自然な会話をすることができ、質問に答えたり助言したりすることが可能です。

※会員登録が必要です。

https://claude.ai/

詳しい解説はしませんが、まだClaudeを使ったことがない方には以下の記事がわかりやすいかもしれません。

https://qiita.com/moritalous/items/8266f0c4bea6514713f7

# Claudia

非公式ながらも強力な機能を備えたClaude API clientです。
Readmeが丁寧で初心者にもやさしい！
Function Calling、Amazon Bedrockにも対応しています。

>Unofficial Anthropic Claude API client for .NET.
>
>We have built a C# API similar to the official Python SDK and TypeScript SDK. It >supports netstandard2.1, net6.0, and net8.0.
>
>In addition to the pure client SDK, it also includes a C# Source Generator for >performing Function Calling, similar to anthropic-tools.

https://github.com/Cysharp/Claudia


# 準備：APIキーの取得

会員登録が終わりましたら、APIキーを取得します。
Claudeのダッシュボードに移動し、`Get API Keys`→`Create Key`にてAPIキーを生成してください。

https://console.anthropic.com/dashboard

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/a779494a-ea12-736a-344a-53e777438cc2.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/a3ede24e-2eb0-e91c-6c6d-97e34b58f02e.png)

# チュートリアル

準備（会員登録、APIキーの取得）が終わっていればOKです。
C#(Blazor)でClaude APIを使ってみましょう。

以下のリポジトリよりプログラムを取得します。

https://github.com/Cysharp/Claudia

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/008ff683-4e6d-3bf1-146b-f1880832b78b.png)

フォルダ構成はこのようになっています。
今回は`BlazorApp1`を使用します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/2b7b2d0a-440c-010a-c794-96e867865791.png)

まずは、`appsettings.json`にAPIキーを追加しましょう。
```コピー用
"ANTHROPIC_API_KEY": "xxx",
```

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/72214132-a0e5-dc79-8349-9b91bff2286a.png)

`BlazorApp1`を選択してビルド→デバッグの開始

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/250aaec6-67c6-afee-ac4c-226e8fb1885e.png)

# アプリケーション起動画面

アプリケーションを起動すると以下のような画面が表示されます。

テキストボックスに`hello`を入力し`Send`ボタンを押して、返事が返ってくることを確認します。
連続で使用すると、一時的に使用制限がかかる場合があります。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/7a6ad56e-c452-6124-5b09-d725953a20ff.png)

公式HPにプロンプトの例がありますので、参考にしてみてください。

https://docs.anthropic.com/claude/prompt-library

# 機能追加：プロンプトを保存する

基本的なチャット機能が使えるようになりましたので、
更に踏み込んでちょっとした機能を追加してみたいと思います。

・`Home.razor`にDownload ChatMessagesボタンを追加
・`Home.razor`の`<script>`タグ内に`function downloadFromBase64String`を追加
・`Home.razor.cs`にChatMessagesをJson形式でダウンロードする処理を追加


![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/17bdf3c1-255b-e96f-ee6b-6f18c474dd1b.png)

```C#:Home.razor
        <div class="w-full mt-2">
            <button class="w-full inline-flex items-center justify-center
            rounded-md bg-green-600 
            px-3 py-2 text-sm 
            font-semibold 
            text-white 
            shadow-sm 
            focus-visible:outline 
            focus-visible:outline-2 focus-visible:outline-offset-2 
            focus-visible:outline-green-600"             
                @onclick="DownloadChatMessages" 
                disabled="@(chatMessages.Count == 0 || running == true )">
                Download Chat Messages
            </button>
        </div>
```

```diff_apache:Home.razor
<script>
    var rangeInput = document.querySelector(".range_slider");
    var rangeBubble = document.querySelector(".range_bubble");

    rangeInput.addEventListener("input", () => {
        setBubble(rangeInput, rangeBubble);
    });
    rangeInput.addEventListener("mouseover", () => {
        setBubble(rangeInput, rangeBubble);
    });
    rangeInput.addEventListener("mouseleave", () => {
        rangeBubble.style.display = "none";
    });

    function setBubble(range, bubble) {
        var val = range.value;
        var min = range.min ? range.min : 0;
        var max = range.max ? range.max : 100;
        var newVal = Number(((val - min) * 100) / (max - min));
        bubble.innerHTML = val;
        bubble.style.left = `calc(${newVal}% + (${-5 - newVal * 0.15}px))`;
        rangeBubble.style.display = "inline-block";
    }

+    function downloadFromBase64String(fileName, base64String) {
+        const a = document.createElement('a');
+        a.href = "data:application/octet-stream;base64," + base64String;
+        a.download = fileName;
+        a.click();
+        a.remove();
+    }
</script>
```

```C#:Home.razor.cs
    // ボタンを押した後の処理
    private async Task DownloadChatMessages()
    {
        if (chatMessages == null || chatMessages.Count == 0) return;

        try
        {
            // 何故か動かない
            //var json = JsonSerializer.Serialize(chatMessages, AnthropicJsonSerialzierContext.Default.Options); // Error
            //var json = JsonSerializer.SerializeToUtf8Bytes(chatMessages, AnthropicJsonSerialzierContext.Default.Options); // Error

            var json = JsonSerializer.Serialize(chatMessages); // OK
            var base64String = Convert.ToBase64String(System.Text.Encoding.UTF8.GetBytes(json));
            await JSRuntime.InvokeVoidAsync("downloadFromBase64String", "chat_messages.json", base64String);
        }
        catch (Exception ex)
        {

        }
    }
```

# 動作確認

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/649b22d6-a12c-6bc4-fc56-1feaac5331fa.png)

# 2024/04/01 追記：チャット履歴の復元
保存したJSONファイルからチャット履歴を復元する機能です。
`ファイルの選択`ボタンを押すと、ファイルダイアログが開くので`JSON`ファイルを選択します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/2330938c-f0c7-1f67-4b96-d725e0eb4bba.png)

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3343142/eea89721-8a2a-cb3f-6b65-43a132740eee.png)



```C#:Home.razor
        <div class="p-4">
            <h3>履歴ファイルの読み込み（JSON）</h3>
            <InputFile id="InputFileJson" OnChange="@LoadChatMessages" type="file" accept=".json" />
        </div>
```

```C#:Home.razor.cs
    bool isLoading = false;

    private async Task LoadChatMessages(InputFileChangeEventArgs e)
    {
        if (isLoading) return;

        isLoading = true;

        var files = e.GetMultipleFiles();

        var buffer = new byte[files[0].Size];
        await files[0].OpenReadStream().ReadAsync(buffer);

        if (buffer != null)
        {
            chatMessages = JsonSerializer.Deserialize<List<Message>>(buffer);
        }

        isLoading = false;
    }
```

# まとめ

C#とClaude両方が備わり最強に見える


# 2024/04/01 追記：Next

画像入力編を書きました。参考になれば幸いです。

https://qiita.com/firesign2023/items/fd539a7eb3a07dce2847