こんにちは!この記事は、APIの経験がまったくない完全な初心者の方に向けて、AIの流式応答(リアルタイムで文字が次々と表示される機能)をゼロから実装する方法を優しく説明します。

実は、私自身もかつては「流式応答なんて専門知識が必要そう...」と思っていた一人です。しかし、やってみれば驚くほどシンプルに実装できることを発見しました。この記事を読み終えれば、あなたも自分のウェブサイトやアプリにChatGPTのようなリアルタイム応答機能を追加できるようになります。

流式応答とは?为什么要用它?

まず、「流式応答(Streaming Response)」について説明します。従来のAI応答では、ユーザーが質問を送ってから、AIが全文を生成し終わるまで数秒〜数十秒待つ必要がありました。しかし、流式応答では、AIが文字を生成するたびに少しずつ結果を返していくため、まるで人間が目を見ながら話しているようなスムーズな体験を実現できます。

この技術を実現するのがServer-Sent Events(SSE)です。SSEは、サーバーからクライアントへリアルタイムでデータを推送できる仕組みで особенно AI応答の場面に力を发挥します。

HolySheep AI とは

流式応答を実装するには、まずAPIを提供するサービスが必要です。私は複数のAPIサービスを比較検討しましたが、HolySheep AIが最も優れています。その理由は以下の通りです:

特にDeepSeek V3.2が$0.42/MTokという破格の安さで、コストパフォーマンスに優れた選択肢となっています。

Step 1:必要な準備をしよう

APIキーの取得

まずはHolySheep AIに新規登録してください。登録が完了すると、ダッシュボードからAPIキーが取得できます。このAPIキーは他人に教えてはいけません。自分のウェブサイトやアプリのみで利用してください。

ヒント:ダッシュボード画面には「API Keys」という項目があります。そこをクリックして新しいキーを作成してください。キーは「sk-...」で始まる長い文字列になります。

必要な道具

この教程では、以下のものを使います。すべて無料 инструментовです:

Step 2:HTMLで基本的な画面を作ろう

まずは、流式応答を表示するためのシンプルなHTMLページを作成しましょう。以下のコードを「index.html」というファイル名で保存してください。

<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Claude 流式応答デモ</title>
    <style>
        body {
            font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
            max-width: 800px;
            margin: 0 auto;
            padding: 20px;
            background-color: #f5f5f5;
        }
        h1 {
            color: #333;
            text-align: center;
        }
        #chat-container {
            background: white;
            border-radius: 10px;
            padding: 20px;
            box-shadow: 0 2px 10px rgba(0,0,0,0.1);
            min-height: 400px;
        }
        #message-input {
            width: 100%;
            padding: 15px;
            border: 2px solid #ddd;
            border-radius: 5px;
            font-size: 16px;
            margin-top: 20px;
            box-sizing: border-box;
        }
        #send-button {
            width: 100%;
            padding: 15px;
            background-color: #4CAF50;
            color: white;
            border: none;
            border-radius: 5px;
            font-size: 16px;
            cursor: pointer;
            margin-top: 10px;
            transition: background-color 0.3s;
        }
        #send-button:hover {
            background-color: #45a049;
        }
        #send-button:disabled {
            background-color: #cccccc;
            cursor: not-allowed;
        }
        .user-message {
            background-color: #007bff;
            color: white;
            padding: 10px 15px;
            border-radius: 10px;
            margin: 10px 0;
            max-width: 80%;
            margin-left: auto;
        }
        .ai-message {
            background-color: #e9ecef;
            color: #333;
            padding: 10px 15px;
            border-radius: 10px;
            margin: 10px 0;
            max-width: 80%;
            white-space: pre-wrap;
            word-wrap: break-word;
        }
        .typing-indicator {
            color: #666;
            font-style: italic;
        }
    </style>
</head>
<body>
    <h1>🤖 Claude 流式応答デモ</h1>
    <div id="chat-container">
        <div class="ai-message">こんにちは!何か質問してください。AIがリアルタイムで回答します。</div>
    </div>
    <input type="text" id="message-input" placeholder="質問を入力してください..." />
    <button id="send-button">送信</button>

    <script>
        const chatContainer = document.getElementById('chat-container');
        const messageInput = document.getElementById('message-input');
        const sendButton = document.getElementById('send-button');

        // メッセージを追加する関数
        function addMessage(content, className) {
            const messageDiv = document.createElement('div');
            messageDiv.className = className;
            messageDiv.textContent = content;
            chatContainer.appendChild(messageDiv);
            chatContainer.scrollTop = chatContainer.scrollHeight;
            return messageDiv;
        }

        // 送信ボタンがクリックされた時の処理
        sendButton.addEventListener('click', sendMessage);
        messageInput.addEventListener('keypress', function(e) {
            if (e.key === 'Enter') sendMessage();
        });

        async function sendMessage() {
            const message = messageInput.value.trim();
            if (!message) return;

            // ユーザーのメッセージを表示
            addMessage(message, 'user-message');
            messageInput.value = '';
            sendButton.disabled = true;

            // AIの返答を格納する要素を作成
            const aiMessageDiv = document.createElement('div');
            aiMessageDiv.className = 'ai-message typing-indicator';
            aiMessageDiv.textContent = '考え中...';
            chatContainer.appendChild(aiMessageDiv);

            try {
                // ★★★ ここにSSEのコードを実装します ★★★
                const response = await fetch('https://api.holysheep.ai/v1/chat/completions', {
                    method: 'POST',
                    headers: {
                        'Content-Type': 'application/json',
                        'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'
                    },
                    body: JSON.stringify({
                        model: 'claude-sonnet-4-20250514',
                        messages: [{ role: 'user', content: message }],
                        stream: true  // これがポイント!
                    })
                });

                const reader = response.body.getReader();
                const decoder = new TextDecoder();
                let aiResponse = '';

                aiMessageDiv.className = 'ai-message';
                aiMessageDiv.textContent = '';

                while (true) {
                    const { done, value } = await reader.read();
                    if (done) break;

                    const chunk = decoder.decode(value);
                    const lines = chunk.split('\n');

                    for (const line of lines) {
                        if (line.startsWith('data: ')) {
                            const data = line.slice(6);
                            if (data === '[DONE]') continue;

                            try {
                                const json = JSON.parse(data);
                                const content = json.choices?.[0]?.delta?.content;
                                if (content) {
                                    aiResponse += content;
                                    aiMessageDiv.textContent = aiResponse;
                                    chatContainer.scrollTop = chatContainer.scrollHeight;
                                }
                            } catch (e) {
                                // 最初の行が '0' の 경우など、無視して続行
                            }
                        }
                    }
                }
            } catch (error) {
                aiMessageDiv.textContent = 'エラーが発生しました: ' + error.message;
                aiMessageDiv.className = 'ai-message';
            } finally {
                sendButton.disabled = false;
            }
        }
    </script>
</body>
</html>

ヒント:HTMLファイルをダブルクリックすると、ブラウザで開いて動作確認できます。「YOUR_HOLYSHEEP_API_KEY」の部分を自分のAPIキーに置き換えるのを忘れないでください!

Step 3:Pythonでバックエンドサーバーを作ろう

実際のプロジェクトでは、フロントエンドとバックエンドを分離することが一般的です。Pythonを使ってAPIを中継するサーバーを作る方法を説明します。

# stream_server.py

Pythonで作成したSSE中継サーバー

from flask import Flask, request, Response import requests import json app = Flask(__name__) @app.route('/stream', methods=['POST']) def stream_chat(): """HolySheep APIにリクエストを転送し、SSEとして返す""" # フロントエンドから受け取ったデータ data = request.get_json() user_message = data.get('message', '') # HolySheep APIへのリクエスト headers headers = { 'Content-Type': 'application/json', 'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY' } # HolySheep APIへのリクエスト body(stream=Trueを設定) payload = { 'model': 'claude-sonnet-4-20250514', 'messages': [ {'role': 'user', 'content': user_message} ], 'stream': True } # HolySheep APIにリクエストを送信 api_response = requests.post( 'https://api.holysheep.ai/v1/chat/completions', headers=headers, json=payload, stream=True, timeout=60 ) def generate(): """APIからのストリーミング応答を転送""" for line in api_response.iter_lines(): if line: # SSE形式に加工して返す decoded = line.decode('utf-8') if decoded.startswith('data: '): yield decoded + '\n' return Response( generate(), mimetype='text/event-stream', headers={ 'Cache-Control': 'no-cache', 'Connection': 'keep-alive', 'X-Accel-Buffering': 'no' } ) if __name__ == '__main__': print("🚀 流式応答サーバーを起動中...") print("http://localhost:5000 でアクセス可能") app.run(port=5000, debug=True)

このPythonサーバーを起動するには、コマンドプロンプトまたはターミナルで以下を実行します:

# pip install flask requests を先に実行してください
pip install flask requests

サーバーを起動

python stream_server.py

ヒント:ターミナルに「Running on http://127.0.0.1:5000」と表示されたら成功です。ブラウザで「http://localhost:5000」にアクセスしてテストしましょう。

Step 4:SSE応答の見分け方

SSEで流れてくるデータを見てみましょう。実際の応答は以下のようになります:

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"claude-sonnet-4-20250514","choices":[{"index":0,"delta":{"content":"こ"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"claude-sonnet-4-20250514","choices":[{"index":0,"delta":{"content":"ん"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"claude-sonnet-4-20250514","choices":[{"index":0,"delta":{"content":"に"},"finish_reason":null}]}

data: {"id":"chatcmpl-xxx","object":"chat.completion.chunk","created":1234567890,"model":"claude-sonnet-4-20250514","choices":[{"index":0,"delta":{"content":"ち"},"finish_reason":null}]}

data: [DONE]

ポイントはこの部分です:

私のはっきりとした記憶では、初めてこのデータの流れを見た時、「たった一文字ずつなのに...これがリアルタイムか!」と感动しました。

Step 5:応用編 - より高度な実装

ここからは、より实务的な例として、typingインジケーター(中打ちdots)を追加したり、エラー処理を充実させたりしましょう:

// enhanced_stream.js
// より高度なSSE実装

class AIStreamHandler {
    constructor(apiKey, baseUrl = 'https://api.holysheep.ai/v1') {
        this.apiKey = apiKey;
        this.baseUrl = baseUrl;
    }

    async *streamCompletion(messages, model = 'claude-sonnet-4-20250514') {
        const response = await fetch(${this.baseUrl}/chat/completions, {
            method: 'POST',
            headers: {
                'Content-Type': 'application/json',
                'Authorization': Bearer ${this.apiKey}
            },
            body: JSON.stringify({
                model: model,
                messages: messages,
                stream: true
            })
        });

        if (!response.ok) {
            const error = await response.json().catch(() => ({}));
            throw new AIStreamError(
                APIエラー: ${response.status},
                response.status,
                error
            );
        }

        const reader = response.body.getReader();
        const decoder = new TextDecoder();
        let buffer = '';

        while (true) {
            const { done, value } = await reader.read();
            if (done) break;

            buffer += decoder.decode(value, { stream: true });
            const lines = buffer.split('\n');
            buffer = lines.pop() || '';

            for (const line of lines) {
                if (line.startsWith('data: ')) {
                    const data = line.slice(6);
                    if (data === '[DONE]') {
                        yield { type: 'done', content: '' };
                        return;
                    }

                    try {
                        const json = JSON.parse(data);
                        const content = json.choices?.[0]?.delta?.content;
                        if (content) {
                            yield { type: 'content', content: content };
                        }
                    } catch (e) {
                        console.warn('JSON解析エラー:', e);
                    }
                }
            }
        }
    }
}

// カスタムエラー kelas
class AIStreamError extends Error {
    constructor(message, statusCode, responseBody) {
        super(message);
        this.name = 'AIStreamError';
        this.statusCode = statusCode;
        this.responseBody = responseBody;
    }
}

// 使用例
async function main() {
    const handler = new AIStreamHandler('YOUR_HOLYSHEEP_API_KEY');
    const messages = [
        { role: 'user', content: '日本の四季について教えてください' }
    ];

    let fullResponse = '';
    
    try {
        for await (const chunk of handler.streamCompletion(messages)) {
            if (chunk.type === 'content') {
                fullResponse += chunk.content;
                process.stdout.write(chunk.content); // リアルタイム表示
            } else if (chunk.type === 'done') {
                console.log('\n\n応答完了!');
            }
        }
        console.log('\n全文:', fullResponse);
    } catch (error) {
        if (error instanceof AIStreamError) {
            console.error(エラー発生 (${error.statusCode}):, error.message);
            console.error('詳細:', error.responseBody);
        } else {
            console.error('不明なエラー:', error);
        }
    }
}

// 実行
main();

よくあるエラーと対処法

SSE実装中に私が実際に遭遇したエラーとその解決策をまとめました。エラーに直面しても慌てないでください!

エラー1:401 Unauthorized(認証エラー)

{
  "error": {
    "message": "Invalid authentication credential",
    "type": "invalid_request_error",
    "code": "invalid_api_key"
  }
}

原因:APIキーが正しく設定されていない、または無効なキーを使っている

解決方法:

// ❌  잘못った例(キーが空またはプレースホルダー)
headers: {
    'Authorization': 'Bearer YOUR_HOLYSHEEP_API_KEY'  // そのまま使ってしまった
}

// ✅ 正しい例
headers: {
    'Authorization': 'Bearer sk-xxxxxxxxxxxxxxxxxxxx'  // 実際のキーに置き換える
}

// APIキーの確認方法
const apiKey = 'YOUR_HOLYSHEEP_API_KEY';
if (apiKey.includes('YOUR_')) {
    console.error('⚠️  APIキーが設定されていません!');
    alert('APIキーを設定してください');
}

ヒント:APIキーはダッシュボードで確認できます。コピー&ペーストする際に、余分なスペースが入らないようにしましょう。

エラー2:CORSエラー

Access to fetch at 'https://api.holysheep.ai/v1/chat/completions' 
from origin 'http://localhost:3000' has been blocked by CORS policy

原因:ブラウザのセキュリティポリシーにより、異なるドメイン間のリクエストが拒否される

解決方法:

// 方法1:バックエンドサーバーを挟んでプロキシ化する(推奨)
// Python Flaskサーバー(stream_server.py)
@app.route('/proxy/chat', methods=['POST'])
def proxy_chat():
    # リクエストをHolySheep APIに転送
    response = requests.post(
        'https://api.holysheep.ai/v1/chat/completions',
        headers={
            'Content-Type': 'application/json',
            'Authorization': f'Bearer {YOUR_API_KEY}'
        },
        json=request.get_json(),
        stream=True
    )
    return Response(response.iter_content(), 
                    mimetype='application/json')

// 方法2:Fetch APIでmodeを変更する(開発時のみ)
const response = await fetch('http://localhost:5000/proxy/chat', {
    method: 'POST',
    mode: 'cors',  // サーバー側でCORS設定が必要
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify(payload)
});

// 方法3:Chrome拡張機能を使う(開発時のみ)
// コマンドラインでCORSを無効にして起動
// chrome.exe --disable-web-security --user-data-dir="C:\temp"

私自身の经验では、バックエンドサーバーを挟む方法(方法1)が最も確実で、実運用でもこの構成 推荐しています。

エラー3:stream: true を忘れた

{
  "error": {
    "message": "stream option must be set to true",
    "type": "invalid_request_error",
    "code": "stream_required"
  }
}

原因:リクエストボディに stream: true を指定していない

解決方法:

// ❌ 忘れた場合(全文が返ってくる)
const payload = {
    model: 'claude-sonnet-4-20250514',
    messages: [{ role: 'user', content: message }]
    // stream: true がない!
};

// ✅ 正しい場合
const payload = {
    model: 'claude-sonnet-4-20250514',
    messages: [{ role: 'user', content: message }],
    stream: true  // ★ これ必須!
};

// streamオプションを確認してからリクエスト
if (!payload.stream) {
    console.error('streamオプションを設定してください!');
}

エラー4:タイムアウト

Error: Timeout executing HTTP request
AbortError: The operation was aborted

原因:AIの応答に時間がかかりすぎる、または接続が切れた

解決方法:

// 方法1:AbortControllerでタイムアウトを設定
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 60000); // 60秒

try {
    const response = await fetch(url, {
        method: 'POST',
        headers: headers,
        body: JSON.stringify(payload),
        signal: controller.signal
    });
    clearTimeout(timeoutId);
} catch (error) {
    if (error.name === 'AbortError') {
        console.error('リクエストがタイムアウトしました');
    }
}

// 方法2:リクエスト設定でタイムアウトを調整
// HolySheep API侧の設定(stream_server.py)
response = requests.post(
    url,
    headers=headers,
    json=payload,
    stream=True,
    timeout=(10, 120)  // (接続タイムアウト, 読み取りタイムアウト)
)

// 方法3:再試行ロジックを追加
async function fetchWithRetry(url, options, maxRetries = 3) {
    for (let i = 0; i < maxRetries; i++) {
        try {
            const response = await fetch(url, options);
            return response;
        } catch (error) {
            if (i === maxRetries - 1) throw error;
            console.log(${i + 1}回目失敗、${i + 1}秒後に再試行...);
            await new Promise(r => setTimeout(r, 1000 * (i + 1)));
        }
    }
}

エラー5:JSON解析エラー

SyntaxError: Unexpected token 'd', "[DONE]" is not valid JSON

原因:[DONE] 信号をJSONとして解析しようとしている

解決方法:

// すべてのJSON解析前に[DONE]チェックを入れる
for (const line of lines) {
    if (line.startsWith('data: ')) {
        const data = line.slice(6);
        
        // ★★★ [DONE]を先にチェック ★★★
        if (data === '[DONE]') {
            console.log('ストリーミング完了');
            break;
        }
        
        try {
            const json = JSON.parse(data);
            const content = json.choices?.[0]?.delta?.content;
            if (content) {
                fullText += content;
            }
        } catch (e) {
            console.warn('JSON解析スキップ:', line.substring(0, 50));
        }
    }
}

// 安全なJSON解析関数
function safeJsonParse(str) {
    try {
        return JSON.parse(str);
    } catch (e) {
        return null;
    }
}

Step 6:成本の最適化

AI APIを使う际、コスト管理も重要です。HolySheep AI の料金 테이블を再確認しておきましょう:

  • Claude Sonnet 4.5: $15/MTok(高品质な文章生成に 적합)
  • GPT-4.1: $8/MTok(バランス型)
  • Gemini 2.5 Flash: $2.50/MTok(コスト重視)
  • DeepSeek V3.2: $0.42/MTok(最安値・日常的なタスクに最適)

流式応答を実装하면、応答が生成されている最中に用户に表示,可以让用户感受到“正在処理中”,从而避免因等待时间长而产生的焦虑,实际上也提升了用户体验!

まとめ

以上で、Server-Sent Events(SSE)を使ったClaude流式応答の実装説明を終わります。重要なポイントをまとめましょう:

  • SSEはサーバーからクライアントへリアルタイムでデータを推送する技術
  • stream: trueオプションでHolySheep APIから流式応答を取得
  • data: プレフィックスついた行をdelta.content
  • [DONE]で応答完了を感知
  • APIキー管理、CORS対応、エラー処理を忘れない

最初は難しく感じるかもしれませんが、少しずつ試해보면必ず身につきます。私がそうだったように、あなたのプロジェクトにもきっとこの技術を活かせる場面があるはずです。

HolySheep AI の<50msという低遅延と¥1=$1という优惠なレートなら、コストを気にせず思う存分 实验できます。WeChat PayやAlipayにも対応しているので、日本からの登録も簡単です。

👉 HolySheep AI に登録して無料クレジットを獲得