|
|
<!--
|
|
|
CO_OP_TRANSLATOR_METADATA:
|
|
|
{
|
|
|
"original_hash": "46d665af66e51524598af34a42b9b663",
|
|
|
"translation_date": "2025-10-24T15:00:00+00:00",
|
|
|
"source_file": "9-chat-project/README.md",
|
|
|
"language_code": "ja"
|
|
|
}
|
|
|
-->
|
|
|
# AIチャットアシスタントを作成しよう
|
|
|
|
|
|
1960年代の『スター・トレック』で、乗組員が船のコンピュータと気軽に会話し、複雑な質問をして思慮深い回答を得ていたシーンを覚えていますか?当時は純粋なSFのように思えたものが、今ではあなたが既に知っているウェブ技術を使って構築できるものになっています。
|
|
|
|
|
|
このレッスンでは、HTML、CSS、JavaScript、そしてバックエンド統合を使用してAIチャットアシスタントを作成します。これまで学んできたスキルが、文脈を理解し、意味のある回答を生成する強力なAIサービスとどのように接続できるかを発見することができます。
|
|
|
|
|
|
AIは、膨大な情報を検索するだけでなく、それを統合して特定の質問に合わせた一貫した回答を提供する広大な図書館にアクセスするようなものだと考えてください。何千ものページを検索する代わりに、直接的で文脈に沿った回答を得ることができます。
|
|
|
|
|
|
統合は、馴染みのあるウェブ技術が連携することで実現します。HTMLはチャットインターフェースを作成し、CSSは視覚デザインを担当し、JavaScriptはユーザーとのやり取りを管理し、バックエンドAPIがすべてをAIサービスに接続します。これは、オーケストラの異なるセクションが協力して交響曲を作り上げるようなものです。
|
|
|
|
|
|
私たちは、自然な人間のコミュニケーションと機械処理の間に橋を架けることを目指しています。AIサービス統合の技術的な実装と、直感的に感じられるインタラクションを実現するデザインパターンの両方を学びます。
|
|
|
|
|
|
このレッスンの終わりまでには、AI統合が神秘的なプロセスではなく、扱いやすいAPIの一つとして感じられるようになります。ChatGPTやClaudeのようなアプリケーションを支える基礎的なパターンを理解し、これまで学んできたウェブ開発の原則を活用していきます。
|
|
|
|
|
|
完成したプロジェクトは以下のようになります:
|
|
|
|
|
|

|
|
|
|
|
|
## AIを理解する:謎から習得へ
|
|
|
|
|
|
コードに取り掛かる前に、まず何を扱っているのかを理解しましょう。APIを使用したことがあるなら、基本的なパターンはご存じでしょう:リクエストを送信し、レスポンスを受け取る。
|
|
|
|
|
|
AI APIも同様の構造を持っていますが、データベースから事前に保存されたデータを取得する代わりに、膨大なテキストから学んだパターンに基づいて新しい回答を生成します。これは、図書館のカタログシステムと、複数の情報源から情報を統合できる知識豊富な司書との違いのようなものです。
|
|
|
|
|
|
### 「生成AI」とは何か?
|
|
|
|
|
|
ロゼッタストーンが知られている言語と未知の言語の間のパターンを見つけることで学者がエジプトの象形文字を理解できるようになったように、AIモデルも同様に膨大なテキストのパターンを見つけて言語の仕組みを理解し、新しい質問に対して適切な回答を生成します。
|
|
|
|
|
|
**簡単な比較で説明します:**
|
|
|
- **従来のデータベース**:出生証明書を求めるようなもの。同じ文書を毎回取得します。
|
|
|
- **検索エンジン**:司書に猫に関する本を探してもらうようなもの。利用可能なものを見せてくれます。
|
|
|
- **生成AI**:知識豊富な友人に猫について尋ねるようなもの。あなたが知りたいことに合わせて自分の言葉で面白いことを教えてくれます。
|
|
|
|
|
|
```mermaid
|
|
|
graph LR
|
|
|
A[Your Question] --> B[AI Model]
|
|
|
B --> C[Pattern Recognition]
|
|
|
C --> D[Content Generation]
|
|
|
D --> E[Contextual Response]
|
|
|
|
|
|
F[Training Data<br/>Books, Articles, Web] --> B
|
|
|
```
|
|
|
|
|
|
### AIモデルの学習方法(簡単版)
|
|
|
|
|
|
AIモデルは、書籍、記事、会話などの膨大なデータセットに触れることで学習します。このプロセスを通じて以下のパターンを特定します:
|
|
|
- 書かれたコミュニケーションで思考がどのように構成されているか
|
|
|
- どの単語が一般的に一緒に現れるか
|
|
|
- 会話が通常どのように進行するか
|
|
|
- フォーマルなコミュニケーションとインフォーマルなコミュニケーションの文脈的な違い
|
|
|
|
|
|
**これは考古学者が古代の言語を解読する方法に似ています**:文法、語彙、文化的文脈を理解するために何千もの例を分析し、学んだパターンを使って新しいテキストを解釈できるようになります。
|
|
|
|
|
|
### GitHub Modelsを選ぶ理由
|
|
|
|
|
|
GitHub Modelsを使用する理由は非常に実用的です。自分でAIインフラを構築する必要がなく、エンタープライズレベルのAIにアクセスできるからです(今すぐ自分で構築するのはお勧めしません!)。これは天気APIを使うのと同じで、全国に気象観測所を設置して天気を予測しようとする代わりに利用するようなものです。
|
|
|
|
|
|
これは基本的に「AI-as-a-Service」であり、最良の部分は無料で始められることです。実験をしても高額な請求を心配する必要がありません。
|
|
|
|
|
|
```mermaid
|
|
|
graph LR
|
|
|
A[Frontend Chat UI] --> B[Your Backend API]
|
|
|
B --> C[GitHub Models API]
|
|
|
C --> D[AI Model Processing]
|
|
|
D --> C
|
|
|
C --> B
|
|
|
B --> A
|
|
|
```
|
|
|
|
|
|
GitHub Modelsをバックエンド統合に使用します。これにより、開発者に優しいインターフェースを通じてプロフェッショナルなAI機能にアクセスできます。[GitHub Models Playground](https://github.com/marketplace/models/azure-openai/gpt-4o-mini/playground)は、異なるAIモデルを試し、その能力を理解してコードに実装する前に実験できるテスト環境として機能します。
|
|
|
|
|
|

|
|
|
|
|
|
**Playgroundが便利な理由:**
|
|
|
- **異なるAIモデルを試す**:GPT-4o-mini、Claudeなど(すべて無料!)
|
|
|
- **アイデアやプロンプトをテストする**:コードを書く前に試せます
|
|
|
- **お気に入りのプログラミング言語で使えるコードスニペットを取得する**
|
|
|
- **設定を調整する**:創造性レベルや応答の長さを変更して出力にどのように影響するか確認できます
|
|
|
|
|
|
少し試してみたら、「Code」タブをクリックしてプログラミング言語を選択し、必要な実装コードを取得します。
|
|
|
|
|
|

|
|
|
|
|
|
## Pythonバックエンド統合のセットアップ
|
|
|
|
|
|
次に、Pythonを使用してAI統合を実装します。Pythonはその簡潔な構文と強力なライブラリのため、AIアプリケーションに最適です。GitHub Models Playgroundからコードを取得し、それを再利用可能で本番環境対応の関数にリファクタリングします。
|
|
|
|
|
|
### 基本的な実装を理解する
|
|
|
|
|
|
PlaygroundからPythonコードを取得すると、以下のようなものが得られます。最初は多く感じるかもしれませんが、部分ごとに説明していきます:
|
|
|
|
|
|
```python
|
|
|
"""Run this model in Python
|
|
|
|
|
|
> pip install openai
|
|
|
"""
|
|
|
import os
|
|
|
from openai import OpenAI
|
|
|
|
|
|
# To authenticate with the model you will need to generate a personal access token (PAT) in your GitHub settings.
|
|
|
# Create your PAT token by following instructions here: https://docs.github.com/en/authentication/keeping-your-account-and-data-secure/managing-your-personal-access-tokens
|
|
|
client = OpenAI(
|
|
|
base_url="https://models.github.ai/inference",
|
|
|
api_key=os.environ["GITHUB_TOKEN"],
|
|
|
)
|
|
|
|
|
|
```python
|
|
|
response = client.chat.completions.create(
|
|
|
messages=[
|
|
|
{
|
|
|
"role": "system",
|
|
|
"content": "",
|
|
|
},
|
|
|
{
|
|
|
"role": "user",
|
|
|
"content": "What is the capital of France?",
|
|
|
}
|
|
|
],
|
|
|
model="openai/gpt-4o-mini",
|
|
|
temperature=1,
|
|
|
max_tokens=4096,
|
|
|
top_p=1
|
|
|
)
|
|
|
|
|
|
print(response.choices[0].message.content)
|
|
|
```
|
|
|
|
|
|
**このコードで何が行われているか:**
|
|
|
- **必要なツールをインポート**:環境変数を読み取るための`os`とAIと通信するための`OpenAI`
|
|
|
- **OpenAIクライアントを設定**:GitHubのAIサーバーを直接OpenAIではなく指すように設定
|
|
|
- **認証を行う**:特別なGitHubトークンを使用(これについては後ほど詳しく説明します!)
|
|
|
- **会話を構成する**:異なる「役割」を設定することで、舞台のシーンを作るようなもの
|
|
|
- **リクエストをAIに送信**:微調整パラメータを含めて送信
|
|
|
- **戻ってきたデータから実際の応答テキストを抽出**
|
|
|
**FastAPIが私たちのプロジェクトに最適な理由:**
|
|
|
- **デフォルトで非同期**: 複数のAIリクエストを同時に処理し、詰まることがない
|
|
|
- **自動ドキュメント生成**: `/docs`にアクセスするだけで、美しいインタラクティブなAPIドキュメントページが無料で手に入る
|
|
|
- **組み込みのバリデーション**: 問題を引き起こす前にエラーをキャッチ
|
|
|
- **超高速**: Pythonフレームワークの中でも最速の一つ
|
|
|
- **モダンなPython**: 最新かつ最高のPython機能を活用
|
|
|
|
|
|
**なぜバックエンドが必要なのか:**
|
|
|
|
|
|
**セキュリティ**: AI APIキーはパスワードのようなものです。フロントエンドのJavaScriptにキーを埋め込むと、ウェブサイトのソースコードを閲覧した誰でもそれを盗んでAIクレジットを使用できてしまいます。バックエンドは機密情報を安全に保ちます。
|
|
|
|
|
|
**リクエスト制限と管理**: バックエンドを使用することで、ユーザーがリクエストを送信する頻度を制御したり、ユーザー認証を実装したり、使用状況を追跡するためのログを追加したりできます。
|
|
|
|
|
|
**データ処理**: 会話を保存したり、不適切なコンテンツをフィルタリングしたり、複数のAIサービスを組み合わせたりする場合があります。これらのロジックはバックエンドで処理されます。
|
|
|
|
|
|
**アーキテクチャはクライアントサーバーモデルに似ています:**
|
|
|
- **フロントエンド**: ユーザーインターフェース層
|
|
|
- **バックエンドAPI**: リクエスト処理とルーティング層
|
|
|
- **AIサービス**: 外部計算と応答生成
|
|
|
- **環境変数**: 安全な設定と資格情報の保存
|
|
|
|
|
|
### リクエストとレスポンスの流れを理解する
|
|
|
|
|
|
ユーザーがメッセージを送信したときに何が起こるかを追跡してみましょう:
|
|
|
|
|
|
```mermaid
|
|
|
sequenceDiagram
|
|
|
participant User as 👤 User
|
|
|
participant Frontend as 🌐 Frontend
|
|
|
participant API as 🔧 FastAPI Server
|
|
|
participant AI as 🤖 AI Service
|
|
|
|
|
|
User->>Frontend: Types "Hello AI!"
|
|
|
Frontend->>API: POST /hello {"message": "Hello AI!"}
|
|
|
Note over API: Validates request<br/>Adds system prompt
|
|
|
API->>AI: Sends formatted request
|
|
|
AI->>API: Returns AI response
|
|
|
Note over API: Processes response<br/>Logs conversation
|
|
|
API->>Frontend: {"response": "Hello! How can I help?"}
|
|
|
Frontend->>User: Displays AI message
|
|
|
```
|
|
|
|
|
|
**各ステップの理解:**
|
|
|
1. **ユーザーの操作**: チャットインターフェースに入力
|
|
|
2. **フロントエンド処理**: JavaScriptが入力をキャプチャし、JSON形式にフォーマット
|
|
|
3. **APIバリデーション**: FastAPIがPydanticモデルを使用してリクエストを自動的に検証
|
|
|
4. **AI統合**: バックエンドがコンテキスト(システムプロンプト)を追加し、AIサービスを呼び出す
|
|
|
5. **レスポンス処理**: APIがAIの応答を受け取り、必要に応じて修正
|
|
|
6. **フロントエンド表示**: JavaScriptがチャットインターフェースに応答を表示
|
|
|
|
|
|
### APIアーキテクチャの理解
|
|
|
|
|
|
```mermaid
|
|
|
sequenceDiagram
|
|
|
participant Frontend
|
|
|
participant FastAPI
|
|
|
participant AI Function
|
|
|
participant GitHub Models
|
|
|
|
|
|
Frontend->>FastAPI: POST /hello {"message": "Hello AI!"}
|
|
|
FastAPI->>AI Function: call_llm(message, system_prompt)
|
|
|
AI Function->>GitHub Models: API request
|
|
|
GitHub Models->>AI Function: AI response
|
|
|
AI Function->>FastAPI: response text
|
|
|
FastAPI->>Frontend: {"response": "Hello! How can I help?"}
|
|
|
```
|
|
|
|
|
|
### FastAPIアプリケーションの作成
|
|
|
|
|
|
APIを段階的に構築していきましょう。`api.py`というファイルを作成し、以下のFastAPIコードを記述します:
|
|
|
|
|
|
```python
|
|
|
# api.py
|
|
|
from fastapi import FastAPI, HTTPException
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
from pydantic import BaseModel
|
|
|
from llm import call_llm
|
|
|
import logging
|
|
|
|
|
|
# Configure logging
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
# Create FastAPI application
|
|
|
app = FastAPI(
|
|
|
title="AI Chat API",
|
|
|
description="A high-performance API for AI-powered chat applications",
|
|
|
version="1.0.0"
|
|
|
)
|
|
|
|
|
|
# Configure CORS
|
|
|
app.add_middleware(
|
|
|
CORSMiddleware,
|
|
|
allow_origins=["*"], # Configure appropriately for production
|
|
|
allow_credentials=True,
|
|
|
allow_methods=["*"],
|
|
|
allow_headers=["*"],
|
|
|
)
|
|
|
|
|
|
# Pydantic models for request/response validation
|
|
|
class ChatMessage(BaseModel):
|
|
|
message: str
|
|
|
|
|
|
class ChatResponse(BaseModel):
|
|
|
response: str
|
|
|
|
|
|
@app.get("/")
|
|
|
async def root():
|
|
|
"""Root endpoint providing API information."""
|
|
|
return {
|
|
|
"message": "Welcome to the AI Chat API",
|
|
|
"docs": "/docs",
|
|
|
"health": "/health"
|
|
|
}
|
|
|
|
|
|
@app.get("/health")
|
|
|
async def health_check():
|
|
|
"""Health check endpoint."""
|
|
|
return {"status": "healthy", "service": "ai-chat-api"}
|
|
|
|
|
|
@app.post("/hello", response_model=ChatResponse)
|
|
|
async def chat_endpoint(chat_message: ChatMessage):
|
|
|
"""Main chat endpoint that processes messages and returns AI responses."""
|
|
|
try:
|
|
|
# Extract and validate message
|
|
|
message = chat_message.message.strip()
|
|
|
if not message:
|
|
|
raise HTTPException(status_code=400, detail="Message cannot be empty")
|
|
|
|
|
|
logger.info(f"Processing message: {message[:50]}...")
|
|
|
|
|
|
# Call AI service (note: call_llm should be made async for better performance)
|
|
|
ai_response = await call_llm_async(message, "You are a helpful and friendly assistant.")
|
|
|
|
|
|
logger.info("AI response generated successfully")
|
|
|
return ChatResponse(response=ai_response)
|
|
|
|
|
|
except HTTPException:
|
|
|
raise
|
|
|
except Exception as e:
|
|
|
logger.error(f"Error processing chat message: {str(e)}")
|
|
|
raise HTTPException(status_code=500, detail="Internal server error")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
import uvicorn
|
|
|
uvicorn.run(app, host="0.0.0.0", port=5000, reload=True)
|
|
|
```
|
|
|
|
|
|
**FastAPI実装の理解:**
|
|
|
- **インポート**: FastAPIのモダンなウェブフレームワーク機能とPydanticのデータ検証機能
|
|
|
- **自動APIドキュメント生成**: サーバー実行時に`/docs`で利用可能
|
|
|
- **CORSミドルウェアの有効化**: 異なるオリジンからのフロントエンドリクエストを許可
|
|
|
- **Pydanticモデルの定義**: 自動リクエスト/レスポンス検証とドキュメント化
|
|
|
- **非同期エンドポイントの使用**: 同時リクエスト処理のパフォーマンス向上
|
|
|
- **適切なHTTPステータスコードとエラーハンドリングの実装**: HTTPExceptionを使用
|
|
|
- **構造化されたログの追加**: モニタリングとデバッグのため
|
|
|
- **ヘルスチェックエンドポイントの提供**: サービス状態のモニタリング
|
|
|
|
|
|
**従来のフレームワークに対するFastAPIの主な利点:**
|
|
|
- **自動バリデーション**: Pydanticモデルが処理前にデータの整合性を保証
|
|
|
- **インタラクティブなドキュメント**: `/docs`で自動生成されたテスト可能なAPIドキュメントを利用可能
|
|
|
- **型安全性**: Pythonの型ヒントがランタイムエラーを防ぎ、コード品質を向上
|
|
|
- **非同期サポート**: 複数のAIリクエストを同時に処理可能
|
|
|
- **パフォーマンス**: リアルタイムアプリケーション向けの高速なリクエスト処理
|
|
|
|
|
|
### CORS: ウェブのセキュリティガードを理解する
|
|
|
|
|
|
CORS(クロスオリジンリソース共有)は、建物のセキュリティガードのようなもので、訪問者が許可されているかどうかを確認します。この重要性とアプリケーションへの影響を理解しましょう。
|
|
|
|
|
|
#### CORSとは何か、なぜ存在するのか
|
|
|
|
|
|
**問題点**: もしどんなウェブサイトでもあなたの銀行のウェブサイトに許可なくリクエストを送れるとしたら、それはセキュリティの悪夢です!ブラウザはデフォルトでこれを防ぐために「同一オリジンポリシー」を使用します。
|
|
|
|
|
|
**同一オリジンポリシー**: ブラウザは、読み込まれたドメイン、ポート、プロトコルと同じオリジンに対してのみリクエストを許可します。
|
|
|
|
|
|
**現実世界の例え**: アパートのセキュリティのようなもので、居住者(同一オリジン)のみがデフォルトで建物にアクセスできます。友人(異なるオリジン)を訪問させたい場合は、セキュリティに明示的に許可を伝える必要があります。
|
|
|
|
|
|
#### 開発環境でのCORS
|
|
|
|
|
|
開発中、フロントエンドとバックエンドは異なるポートで動作します:
|
|
|
- フロントエンド: `http://localhost:3000`(またはHTMLを直接開く場合はfile://)
|
|
|
- バックエンド: `http://localhost:5000`
|
|
|
|
|
|
これらは同じコンピュータ上にあっても「異なるオリジン」と見なされます!
|
|
|
|
|
|
```python
|
|
|
from fastapi.middleware.cors import CORSMiddleware
|
|
|
|
|
|
app = FastAPI(__name__)
|
|
|
CORS(app) # This tells browsers: "It's okay for other origins to make requests to this API"
|
|
|
```
|
|
|
|
|
|
**CORS設定が実際に行うこと:**
|
|
|
- **特別なHTTPヘッダーを追加**: APIレスポンスに「このクロスオリジンリクエストは許可されている」とブラウザに伝える
|
|
|
- **「プリフライト」リクエストを処理**: ブラウザが実際のリクエストを送る前に許可を確認する場合がある
|
|
|
- **ブラウザコンソールでの「CORSポリシーによるブロック」エラーを防止**
|
|
|
|
|
|
#### CORSセキュリティ: 開発と本番環境の違い
|
|
|
|
|
|
```python
|
|
|
# 🚨 Development: Allows ALL origins (convenient but insecure)
|
|
|
CORS(app)
|
|
|
|
|
|
# ✅ Production: Only allow your specific frontend domain
|
|
|
CORS(app, origins=["https://yourdomain.com", "https://www.yourdomain.com"])
|
|
|
|
|
|
# 🔒 Advanced: Different origins for different environments
|
|
|
if app.debug: # Development mode
|
|
|
CORS(app, origins=["http://localhost:3000", "http://127.0.0.1:3000"])
|
|
|
else: # Production mode
|
|
|
CORS(app, origins=["https://yourdomain.com"])
|
|
|
```
|
|
|
|
|
|
**重要性**: 開発中は`CORS(app)`がフロントドアを開けっぱなしにするようなもので、便利ですが安全ではありません。本番環境では、APIと通信できるウェブサイトを正確に指定する必要があります。
|
|
|
|
|
|
#### 一般的なCORSシナリオと解決策
|
|
|
|
|
|
| シナリオ | 問題 | 解決策 |
|
|
|
|----------|---------|----------|
|
|
|
| **ローカル開発** | フロントエンドがバックエンドに到達できない | FastAPIにCORSMiddlewareを追加 |
|
|
|
| **GitHub Pages + Heroku** | デプロイされたフロントエンドがAPIに到達できない | GitHub PagesのURLをCORSオリジンに追加 |
|
|
|
| **カスタムドメイン** | 本番環境でのCORSエラー | CORSオリジンをドメインに合わせて更新 |
|
|
|
| **モバイルアプリ** | アプリがウェブAPIに到達できない | アプリのドメインを追加するか、慎重に`*`を使用 |
|
|
|
|
|
|
**プロのヒント**: ブラウザの開発者ツールのネットワークタブでCORSヘッダーを確認できます。レスポンス内の`Access-Control-Allow-Origin`などのヘッダーを探してください。
|
|
|
|
|
|
### エラーハンドリングとバリデーション
|
|
|
|
|
|
APIが適切なエラーハンドリングを含んでいることに注目してください:
|
|
|
|
|
|
```python
|
|
|
# Validate that we received a message
|
|
|
if not message:
|
|
|
return jsonify({"error": "Message field is required"}), 400
|
|
|
```
|
|
|
|
|
|
**バリデーションの重要な原則:**
|
|
|
- **必須フィールドをチェック**: リクエストを処理する前に
|
|
|
- **意味のあるエラーメッセージをJSON形式で返す**
|
|
|
- **適切なHTTPステータスコードを使用**: (400は不正なリクエスト用)
|
|
|
- **フロントエンド開発者が問題をデバッグしやすいように明確なフィードバックを提供**
|
|
|
|
|
|
## バックエンドのセットアップと実行
|
|
|
|
|
|
AI統合とFastAPIサーバーが準備できたので、すべてを実行してみましょう。セットアッププロセスには、Python依存関係のインストール、環境変数の設定、開発サーバーの起動が含まれます。
|
|
|
|
|
|
### Python環境のセットアップ
|
|
|
|
|
|
Python開発環境をセットアップしましょう。仮想環境はマンハッタン計画の区分化されたアプローチのようなもので、各プロジェクトが特定のツールと依存関係を持つ独自の空間を得ることで、異なるプロジェクト間の競合を防ぎます。
|
|
|
|
|
|
```bash
|
|
|
# Navigate to your backend directory
|
|
|
cd backend
|
|
|
|
|
|
# Create a virtual environment (like creating a clean room for your project)
|
|
|
python -m venv venv
|
|
|
|
|
|
# Activate it (Linux/Mac)
|
|
|
source ./venv/bin/activate
|
|
|
|
|
|
# On Windows, use:
|
|
|
# venv\Scripts\activate
|
|
|
|
|
|
# Install the good stuff
|
|
|
pip install openai fastapi uvicorn python-dotenv
|
|
|
```
|
|
|
|
|
|
**今行ったこと:**
|
|
|
- **独自のPythonバブルを作成**: パッケージをインストールしても他のものに影響を与えない
|
|
|
- **アクティブ化**: ターミナルがこの特定の環境を使用するように設定
|
|
|
- **必須項目をインストール**: OpenAI(AIマジック用)、FastAPI(ウェブAPI用)、Uvicorn(実行用)、python-dotenv(安全な秘密管理用)
|
|
|
|
|
|
**主要な依存関係の説明:**
|
|
|
- **FastAPI**: モダンで高速なウェブフレームワーク、自動APIドキュメント付き
|
|
|
- **Uvicorn**: FastAPIアプリケーションを実行する超高速ASGIサーバー
|
|
|
- **OpenAI**: GitHubモデルとOpenAI API統合用の公式ライブラリ
|
|
|
- **python-dotenv**: `.env`ファイルから安全に環境変数を読み込む
|
|
|
|
|
|
### 環境設定: 秘密を安全に保つ
|
|
|
|
|
|
APIを開始する前に、ウェブ開発で最も重要な教訓の一つについて話しましょう: 秘密を本当に秘密に保つ方法。環境変数は、アプリケーションだけがアクセスできる安全な金庫のようなものです。
|
|
|
|
|
|
#### 環境変数とは何か?
|
|
|
|
|
|
**環境変数は金庫のようなもの**です。貴重品をそこに入れておけば、あなただけ(とアプリ)が鍵を持ってアクセスできます。コードに直接機密情報を書き込む(誰でも見られる場所に置く)のではなく、安全に環境に保存します。
|
|
|
|
|
|
**違いは次の通りです:**
|
|
|
- **間違った方法**: パスワードを付箋に書いてモニターに貼る
|
|
|
- **正しい方法**: パスワードを安全なパスワードマネージャーに保存し、あなただけがアクセスできるようにする
|
|
|
|
|
|
#### 環境変数が重要な理由
|
|
|
|
|
|
```python
|
|
|
# 🚨 NEVER DO THIS - API key visible to everyone
|
|
|
client = OpenAI(
|
|
|
api_key="ghp_1234567890abcdef...", # Anyone can steal this!
|
|
|
base_url="https://models.github.ai/inference"
|
|
|
)
|
|
|
|
|
|
# ✅ DO THIS - API key stored securely
|
|
|
client = OpenAI(
|
|
|
api_key=os.environ["GITHUB_TOKEN"], # Only your app can access this
|
|
|
base_url="https://models.github.ai/inference"
|
|
|
)
|
|
|
```
|
|
|
|
|
|
**秘密をハードコードするとどうなるか:**
|
|
|
1. **バージョン管理の露出**: Gitリポジトリにアクセスできる人は誰でもAPIキーを確認できる
|
|
|
2. **公開リポジトリ**: GitHubにプッシュすると、キーがインターネット全体に公開される
|
|
|
3. **チーム共有**: プロジェクトに取り組む他の開発者が個人のAPIキーにアクセスできる
|
|
|
4. **セキュリティ侵害**: 誰かがAPIキーを盗むと、AIクレジットを使用される可能性がある
|
|
|
|
|
|
#### 環境ファイルの設定
|
|
|
|
|
|
バックエンドディレクトリに`.env`ファイルを作成します。このファイルは秘密をローカルに保存します:
|
|
|
|
|
|
```bash
|
|
|
# .env file - This should NEVER be committed to Git
|
|
|
GITHUB_TOKEN=your_github_personal_access_token_here
|
|
|
FASTAPI_DEBUG=True
|
|
|
ENVIRONMENT=development
|
|
|
```
|
|
|
|
|
|
**.envファイルの理解:**
|
|
|
- **1行に1つの秘密**: `KEY=value`形式
|
|
|
- **等号の周りにスペースなし**
|
|
|
- **通常は値に引用符不要**
|
|
|
- **コメント**は`#`で始まる
|
|
|
|
|
|
#### GitHubの個人アクセストークンの作成
|
|
|
|
|
|
GitHubトークンは、アプリケーションにGitHubのAIサービスを使用する権限を与える特別なパスワードのようなものです:
|
|
|
|
|
|
**トークン作成の手順:**
|
|
|
1. **GitHub設定に移動** → Developer settings → Personal access tokens → Tokens (classic)
|
|
|
2. **「Generate new token (classic)」をクリック**
|
|
|
3. **有効期限を設定**(テスト用に30日、運用用に長期間)
|
|
|
4. **スコープを選択**: 「repo」と必要な権限をチェック
|
|
|
5. **トークンを生成**し、すぐにコピー(後で確認できません!)
|
|
|
6. **.envファイルに貼り付け**
|
|
|
|
|
|
```bash
|
|
|
# Example of what your token looks like (this is fake!)
|
|
|
GITHUB_TOKEN=ghp_1A2B3C4D5E6F7G8H9I0J1K2L3M4N5O6P7Q8R
|
|
|
```
|
|
|
|
|
|
#### Pythonで環境変数を読み込む
|
|
|
|
|
|
```python
|
|
|
import os
|
|
|
from dotenv import load_dotenv
|
|
|
|
|
|
# Load environment variables from .env file
|
|
|
load_dotenv()
|
|
|
|
|
|
# Now you can access them securely
|
|
|
api_key = os.environ.get("GITHUB_TOKEN")
|
|
|
if not api_key:
|
|
|
raise ValueError("GITHUB_TOKEN not found in environment variables!")
|
|
|
|
|
|
client = OpenAI(
|
|
|
api_key=api_key,
|
|
|
base_url="https://models.github.ai/inference"
|
|
|
)
|
|
|
```
|
|
|
|
|
|
**このコードが行うこと:**
|
|
|
- **.envファイルを読み込み**: 変数をPythonで利用可能にする
|
|
|
- **必要なトークンが存在するか確認**: (良いエラーハンドリング!)
|
|
|
- **トークンがない場合は明確なエラーを発生**
|
|
|
- **コード内でトークンを安全に使用**: 露出を防ぐ
|
|
|
|
|
|
#### Gitのセキュリティ: .gitignoreファイル
|
|
|
|
|
|
`.gitignore`ファイルは、Gitに追跡やアップロードをしないファイルを指示します:
|
|
|
|
|
|
```bash
|
|
|
# .gitignore - Add these lines
|
|
|
.env
|
|
|
*.env
|
|
|
.env.local
|
|
|
.env.production
|
|
|
__pycache__/
|
|
|
venv/
|
|
|
.vscode/
|
|
|
```
|
|
|
|
|
|
**これが重要な理由**: `.env`を`.gitignore`に追加すると、Gitが環境ファイルを無視し、秘密をGitHubに誤ってアップロードするのを防ぎます。
|
|
|
|
|
|
#### 異なる環境、異なる秘密
|
|
|
|
|
|
プロフェッショナルなアプリケーションは、異なる環境に異なるAPIキーを使用します:
|
|
|
|
|
|
```bash
|
|
|
# .env.development
|
|
|
GITHUB_TOKEN=your_development_token
|
|
|
DEBUG=True
|
|
|
|
|
|
# .env.production
|
|
|
GITHUB_TOKEN=your_production_token
|
|
|
DEBUG=False
|
|
|
```
|
|
|
|
|
|
**これが重要な理由**: 開発中の実験が本番環境のAI使用量に影響を与えないようにし、異なる環境に異なるセキュリティレベルを設定します。
|
|
|
|
|
|
### 開発サーバーの起動: FastAPIを稼働させる
|
|
|
|
|
|
いよいよエキサイティングな瞬間です – FastAPI開発サーバーを起動し、AI統合が動作する様子を確認しましょう!FastAPIは、非同期Pythonアプリケーション専用に設計された超高速ASGIサーバーであるUvicornを使用します。
|
|
|
|
|
|
#### FastAPIサーバー起動プロセスの理解
|
|
|
|
|
|
```bash
|
|
|
# Method 1: Direct Python execution (includes auto-reload)
|
|
|
python api.py
|
|
|
|
|
|
# Method 2: Using Uvicorn directly (more control)
|
|
|
uvicorn api:app --host 0.0.0.0 --port 5000 --reload
|
|
|
```
|
|
|
|
|
|
このコマンドを実行すると、以下のことが舞台裏で行われます:
|
|
|
|
|
|
**1. PythonがFastAPIアプリケーションを読み込む**:
|
|
|
- 必要なライブラリ(FastAPI、Pydantic、OpenAIなど)をインポート
|
|
|
- `.env`ファイルから環境変数を読み込む
|
|
|
- 自動ドキュメント付きのFastAPIアプリケーションインスタンスを作成
|
|
|
|
|
|
**2. UvicornがASGIサーバーを設定**:
|
|
|
- 非同期リクエスト処理機能を備えたポート5000にバインド
|
|
|
- 自動バリデーション付きのリクエストルーティングを設定
|
|
|
- 開発用ホットリロードを有効化(ファイル変更時に再起動)
|
|
|
- インタラクティブなAPIドキュメントを生成
|
|
|
|
|
|
**3. サーバーがリスニングを開始**:
|
|
|
- ターミナルに`INFO: Uvicorn running on http://0.0.0.0:5000`と表示
|
|
|
- サーバーが複数の同時AIリクエストを処理可能
|
|
|
- APIが`http://localhost:5000/docs`で自動ドキュメント付きで準備完了
|
|
|
|
|
|
#### すべてが正常に動作した場合に見るべきもの
|
|
|
|
|
|
```bash
|
|
|
$ python api.py
|
|
|
INFO: Will watch for changes in these directories: ['/your/project/path']
|
|
|
INFO: Uvicorn running on http://0.0.0.0:5000 (Press CTRL+C to quit)
|
|
|
INFO: Started reloader process [12345] using WatchFiles
|
|
|
INFO: Started server process [12346]
|
|
|
INFO: Waiting for application startup.
|
|
|
INFO: Application startup complete.
|
|
|
```
|
|
|
|
|
|
**FastAPI出力の理解:**
|
|
|
- **変更を監視**: 開発用の自動リロードが有効
|
|
|
- **Uvicornが稼働中**: 高性能ASGIサーバ
|
|
|
```python
|
|
|
# test_api.py - Create this file to test your API
|
|
|
import requests
|
|
|
import json
|
|
|
|
|
|
# Test the API endpoint
|
|
|
url = "http://localhost:5000/hello"
|
|
|
data = {"message": "Tell me a joke about programming"}
|
|
|
|
|
|
response = requests.post(url, json=data)
|
|
|
if response.status_code == 200:
|
|
|
result = response.json()
|
|
|
print("AI Response:", result['response'])
|
|
|
else:
|
|
|
print("Error:", response.status_code, response.text)
|
|
|
```
|
|
|
|
|
|
#### よくある起動時の問題のトラブルシューティング
|
|
|
|
|
|
| エラーメッセージ | 意味 | 修正方法 |
|
|
|
|------------------|------|----------|
|
|
|
| `ModuleNotFoundError: No module named 'fastapi'` | FastAPIがインストールされていない | 仮想環境で`pip install fastapi uvicorn`を実行してください |
|
|
|
| `ModuleNotFoundError: No module named 'uvicorn'` | ASGIサーバーがインストールされていない | 仮想環境で`pip install uvicorn`を実行してください |
|
|
|
| `KeyError: 'GITHUB_TOKEN'` | 環境変数が見つからない | `.env`ファイルと`load_dotenv()`の呼び出しを確認してください |
|
|
|
| `Address already in use` | ポート5000が使用中 | ポート5000を使用している他のプロセスを終了するか、ポートを変更してください |
|
|
|
| `ValidationError` | リクエストデータがPydanticモデルと一致しない | リクエスト形式が期待されるスキーマと一致しているか確認してください |
|
|
|
| `HTTPException 422` | 処理できないエンティティ | リクエストの検証に失敗しました。正しい形式については`/docs`を確認してください |
|
|
|
| `OpenAI API error` | AIサービスの認証に失敗 | GitHubトークンが正しく、適切な権限を持っていることを確認してください |
|
|
|
|
|
|
#### 開発のベストプラクティス
|
|
|
|
|
|
**ホットリロード**: FastAPIとUvicornを使用すると、Pythonファイルを保存するたびに自動的にリロードされます。これにより、コードを変更してすぐにテストでき、手動で再起動する必要がありません。
|
|
|
|
|
|
```python
|
|
|
# Enable hot reloading explicitly
|
|
|
if __name__ == "__main__":
|
|
|
app.run(host="0.0.0.0", port=5000, debug=True) # debug=True enables hot reload
|
|
|
```
|
|
|
|
|
|
**開発用のログ記録**: ログを追加して、何が起きているかを把握しましょう:
|
|
|
|
|
|
```python
|
|
|
import logging
|
|
|
|
|
|
# Set up logging
|
|
|
logging.basicConfig(level=logging.INFO)
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
@app.route("/hello", methods=["POST"])
|
|
|
def hello():
|
|
|
data = request.get_json()
|
|
|
message = data.get("message", "")
|
|
|
|
|
|
logger.info(f"Received message: {message}")
|
|
|
|
|
|
if not message:
|
|
|
logger.warning("Empty message received")
|
|
|
return jsonify({"error": "Message field is required"}), 400
|
|
|
|
|
|
try:
|
|
|
response = call_llm(message, "You are a helpful and friendly assistant.")
|
|
|
logger.info(f"AI response generated successfully")
|
|
|
return jsonify({"response": response})
|
|
|
except Exception as e:
|
|
|
logger.error(f"AI API error: {str(e)}")
|
|
|
return jsonify({"error": "AI service temporarily unavailable"}), 500
|
|
|
```
|
|
|
|
|
|
**ログが役立つ理由**: 開発中に、どのリクエストが来ているのか、AIが何を返しているのか、エラーがどこで発生しているのかを正確に確認できます。これによりデバッグが非常に迅速になります。
|
|
|
|
|
|
### GitHub Codespacesの設定: クラウド開発を簡単に
|
|
|
|
|
|
GitHub Codespacesは、ブラウザからアクセスできるクラウド上の強力な開発環境のようなものです。Codespacesで作業する場合、バックエンドをフロントエンドにアクセス可能にするためにいくつか追加の手順が必要です。
|
|
|
|
|
|
#### Codespacesネットワークの理解
|
|
|
|
|
|
ローカル開発環境では、すべてが同じコンピュータ上で動作します:
|
|
|
- バックエンド: `http://localhost:5000`
|
|
|
- フロントエンド: `http://localhost:3000` (またはfile://)
|
|
|
|
|
|
Codespacesでは、開発環境がGitHubのサーバー上で動作するため、「localhost」の意味が異なります。GitHubはサービス用の公開URLを自動的に作成しますが、適切に設定する必要があります。
|
|
|
|
|
|
#### Codespaces設定の手順
|
|
|
|
|
|
**1. バックエンドサーバーを起動する**:
|
|
|
```bash
|
|
|
cd backend
|
|
|
python api.py
|
|
|
```
|
|
|
|
|
|
Codespace環境内でFastAPI/Uvicornの起動メッセージが表示されます。
|
|
|
|
|
|
**2. ポートの可視性を設定する**:
|
|
|
- VS Codeの下部パネルにある「Ports」タブを探します
|
|
|
- リスト内のポート5000を見つけます
|
|
|
- ポート5000を右クリックします
|
|
|
- 「Port Visibility」→「Public」を選択します
|
|
|
|
|
|
**公開にする理由**: デフォルトでは、Codespaceのポートはプライベート(自分だけがアクセス可能)です。公開にすることで、ブラウザで動作するフロントエンドがバックエンドと通信できるようになります。
|
|
|
|
|
|
**3. 公開URLを取得する**:
|
|
|
ポートを公開にした後、次のようなURLが表示されます:
|
|
|
```
|
|
|
https://your-codespace-name-5000.app.github.dev
|
|
|
```
|
|
|
|
|
|
**4. フロントエンド設定を更新する**:
|
|
|
```javascript
|
|
|
// In your frontend app.js, update the BASE_URL:
|
|
|
this.BASE_URL = "https://your-codespace-name-5000.app.github.dev";
|
|
|
```
|
|
|
|
|
|
#### Codespace URLの理解
|
|
|
|
|
|
Codespace URLは予測可能なパターンに従います:
|
|
|
```
|
|
|
https://[codespace-name]-[port].app.github.dev
|
|
|
```
|
|
|
|
|
|
**これを分解すると**:
|
|
|
- `codespace-name`: Codespaceの一意の識別子(通常はユーザー名を含む)
|
|
|
- `port`: サービスが動作しているポート番号(FastAPIアプリの場合は5000)
|
|
|
- `app.github.dev`: Codespaceアプリケーション用のGitHubドメイン
|
|
|
|
|
|
#### Codespace設定のテスト
|
|
|
|
|
|
**1. バックエンドを直接テストする**:
|
|
|
公開URLを新しいブラウザタブで開きます。次のような画面が表示されるはずです:
|
|
|
```
|
|
|
Welcome to the AI Chat API. Send POST requests to /hello with JSON payload containing 'message' field.
|
|
|
```
|
|
|
|
|
|
**2. ブラウザの開発者ツールでテストする**:
|
|
|
```javascript
|
|
|
// Open browser console and test your API
|
|
|
fetch('https://your-codespace-name-5000.app.github.dev/hello', {
|
|
|
method: 'POST',
|
|
|
headers: {'Content-Type': 'application/json'},
|
|
|
body: JSON.stringify({message: 'Hello from Codespaces!'})
|
|
|
})
|
|
|
.then(response => response.json())
|
|
|
.then(data => console.log(data));
|
|
|
```
|
|
|
|
|
|
#### Codespacesとローカル開発の比較
|
|
|
|
|
|
| 項目 | ローカル開発 | GitHub Codespaces |
|
|
|
|------|-------------|-------------------|
|
|
|
| **セットアップ時間** | 長い(Pythonや依存関係のインストール) | 即時(事前設定済み環境) |
|
|
|
| **URLアクセス** | `http://localhost:5000` | `https://xyz-5000.app.github.dev` |
|
|
|
| **ポート設定** | 自動 | 手動(ポートを公開にする必要あり) |
|
|
|
| **ファイルの永続性** | ローカルマシン | GitHubリポジトリ |
|
|
|
| **コラボレーション** | 環境の共有が難しい | Codespaceリンクの共有が簡単 |
|
|
|
| **インターネット依存性** | AI API呼び出しのみ | すべてに必要 |
|
|
|
|
|
|
#### Codespace開発のヒント
|
|
|
|
|
|
**Codespacesでの環境変数**:
|
|
|
Codespacesでは`.env`ファイルが同じように機能しますが、環境変数を直接設定することもできます:
|
|
|
|
|
|
```bash
|
|
|
# Set environment variable for the current session
|
|
|
export GITHUB_TOKEN="your_token_here"
|
|
|
|
|
|
# Or add to your .bashrc for persistence
|
|
|
echo 'export GITHUB_TOKEN="your_token_here"' >> ~/.bashrc
|
|
|
```
|
|
|
|
|
|
**ポート管理**:
|
|
|
- Codespacesはアプリケーションがポートでリスニングを開始すると自動的に検出します
|
|
|
- 複数のポートを同時に転送可能(後でデータベースを追加する場合に便利)
|
|
|
- Codespaceが動作している限りポートはアクセス可能です
|
|
|
|
|
|
**開発ワークフロー**:
|
|
|
1. VS Codeでコードを変更
|
|
|
2. FastAPIが自動リロード(Uvicornのリロードモードのおかげ)
|
|
|
3. 公開URLを通じてすぐに変更をテスト
|
|
|
4. 準備が整ったらコミットしてプッシュ
|
|
|
|
|
|
> 💡 **プロのヒント**: 開発中にCodespaceバックエンドURLをブックマークしてください。同じCodespaceを使用している限り、URLは変更されません。
|
|
|
|
|
|
## フロントエンドチャットインターフェースの作成: 人間とAIの接点
|
|
|
|
|
|
次に、ユーザーインターフェースを構築します。これは、人々がAIアシスタントとどのようにやり取りするかを決定する部分です。オリジナルのiPhoneインターフェースのデザインのように、複雑な技術を直感的で自然に使えるようにすることに焦点を当てます。
|
|
|
|
|
|
### モダンなフロントエンドアーキテクチャの理解
|
|
|
|
|
|
私たちのチャットインターフェースは「シングルページアプリケーション(SPA)」と呼ばれるものになります。古い方法では、クリックするたびに新しいページが読み込まれますが、私たちのアプリはスムーズかつ瞬時に更新されます。
|
|
|
|
|
|
**古いウェブサイト**: 紙の本を読むようなもの – 完全に新しいページに移動します
|
|
|
**私たちのチャットアプリ**: スマートフォンを使うようなもの – すべてが流れるように更新されます
|
|
|
|
|
|
```mermaid
|
|
|
graph TD
|
|
|
A[User Types Message] --> B[JavaScript Captures Input]
|
|
|
B --> C[Validate & Format Data]
|
|
|
C --> D[Send to Backend API]
|
|
|
D --> E[Display Loading State]
|
|
|
E --> F[Receive AI Response]
|
|
|
F --> G[Update Chat Interface]
|
|
|
G --> H[Ready for Next Message]
|
|
|
```
|
|
|
|
|
|
### フロントエンド開発の3つの柱
|
|
|
|
|
|
すべてのフロントエンドアプリケーション – シンプルなウェブサイトからDiscordやSlackのような複雑なアプリまで – は3つのコア技術で構築されています。これらはウェブ上で見たり操作したりするすべての基盤です。
|
|
|
|
|
|
**HTML(構造)**: 基盤となる部分
|
|
|
- 存在する要素を決定(ボタン、テキストエリア、コンテナなど)
|
|
|
- コンテンツに意味を与える(これはヘッダー、これはフォームなど)
|
|
|
- すべてが構築される基本構造を作成
|
|
|
|
|
|
**CSS(見た目)**: インテリアデザイナーのような役割
|
|
|
- すべてを美しくする(色、フォント、レイアウト)
|
|
|
- 異なる画面サイズに対応(スマホ、ラップトップ、タブレット)
|
|
|
- スムーズなアニメーションや視覚的なフィードバックを作成
|
|
|
|
|
|
**JavaScript(動作)**: 頭脳の役割
|
|
|
- ユーザーの操作に応答(クリック、入力、スクロール)
|
|
|
- バックエンドと通信してページを更新
|
|
|
- すべてをインタラクティブで動的にする
|
|
|
|
|
|
**建築設計のように考える**:
|
|
|
- **HTML**: 構造設計図(空間と関係を定義)
|
|
|
- **CSS**: 美的および環境デザイン(視覚スタイルとユーザー体験)
|
|
|
- **JavaScript**: 機械システム(機能性とインタラクティブ性)
|
|
|
|
|
|
### モダンなJavaScriptアーキテクチャが重要な理由
|
|
|
|
|
|
私たちのチャットアプリケーションでは、プロフェッショナルなアプリケーションで見られるモダンなJavaScriptパターンを使用します。これらの概念を理解することで、開発者として成長する際に役立ちます。
|
|
|
|
|
|
**クラスベースのアーキテクチャ**: コードをクラスに整理します。これはオブジェクトの設計図を作成するようなものです
|
|
|
**Async/Await**: 時間がかかる操作(API呼び出しなど)を処理するモダンな方法
|
|
|
**イベント駆動型プログラミング**: アプリがユーザーの操作(クリック、キー入力)に応答する
|
|
|
**DOM操作**: ユーザーの操作やAPIの応答に基づいてウェブページのコンテンツを動的に更新
|
|
|
|
|
|
### プロジェクト構造のセットアップ
|
|
|
|
|
|
以下のような整理された構造でフロントエンドディレクトリを作成します:
|
|
|
|
|
|
```text
|
|
|
frontend/
|
|
|
├── index.html # Main HTML structure
|
|
|
├── app.js # JavaScript functionality
|
|
|
└── styles.css # Visual styling
|
|
|
```
|
|
|
|
|
|
**アーキテクチャの理解**:
|
|
|
- **構造(HTML)**、**動作(JavaScript)**、**見た目(CSS)**の間で関心を分離
|
|
|
- **簡単にナビゲート**および変更可能なシンプルなファイル構造を維持
|
|
|
- **ウェブ開発のベストプラクティス**に従った組織化と保守性
|
|
|
|
|
|
### HTMLの基盤を構築する: アクセシビリティのためのセマンティック構造
|
|
|
|
|
|
まずHTML構造から始めましょう。モダンなウェブ開発では「セマンティックHTML」が重視されます。これは、外観だけでなくその目的を明確に説明するHTML要素を使用することを意味します。これにより、アプリケーションがスクリーンリーダー、検索エンジン、その他のツールにアクセス可能になります。
|
|
|
|
|
|
**セマンティックHTMLが重要な理由**: チャットアプリを電話で誰かに説明すると想像してください。「タイトルがあるヘッダー、会話が表示されるメインエリア、メッセージを入力するためのフォームが下部にある」と言うでしょう。セマンティックHTMLは、この自然な説明に一致する要素を使用します。
|
|
|
|
|
|
`index.html`を以下のように構造化されたマークアップで作成します:
|
|
|
|
|
|
```html
|
|
|
<!DOCTYPE html>
|
|
|
<html lang="en">
|
|
|
<head>
|
|
|
<meta charset="UTF-8">
|
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
|
<title>AI Chat Assistant</title>
|
|
|
<link rel="stylesheet" href="styles.css">
|
|
|
</head>
|
|
|
<body>
|
|
|
<div class="chat-container">
|
|
|
<header class="chat-header">
|
|
|
<h1>AI Chat Assistant</h1>
|
|
|
<p>Ask me anything!</p>
|
|
|
</header>
|
|
|
|
|
|
<main class="chat-messages" id="messages" role="log" aria-live="polite">
|
|
|
<!-- Messages will be dynamically added here -->
|
|
|
</main>
|
|
|
|
|
|
<form class="chat-form" id="chatForm">
|
|
|
<div class="input-group">
|
|
|
<input
|
|
|
type="text"
|
|
|
id="messageInput"
|
|
|
placeholder="Type your message here..."
|
|
|
required
|
|
|
aria-label="Chat message input"
|
|
|
>
|
|
|
<button type="submit" id="sendBtn" aria-label="Send message">
|
|
|
Send
|
|
|
</button>
|
|
|
</div>
|
|
|
</form>
|
|
|
</div>
|
|
|
<script src="app.js"></script>
|
|
|
</body>
|
|
|
</html>
|
|
|
```
|
|
|
|
|
|
**各HTML要素とその目的の理解**:
|
|
|
|
|
|
#### ドキュメント構造
|
|
|
- **`<!DOCTYPE html>`**: ブラウザにこれがモダンなHTML5であることを伝える
|
|
|
- **`<html lang="en">`**: ページの言語をスクリーンリーダーや翻訳ツールに指定
|
|
|
- **`<meta charset="UTF-8">`**: 国際的なテキストのための適切な文字エンコーディングを保証
|
|
|
- **`<meta name="viewport"...>`**: ズームとスケールを制御してページをモバイル対応にする
|
|
|
|
|
|
#### セマンティック要素
|
|
|
- **`<header>`**: タイトルと説明があるトップセクションを明確に識別
|
|
|
- **`<main>`**: 主なコンテンツエリア(会話が行われる場所)を指定
|
|
|
- **`<form>`**: ユーザー入力に適したセマンティックな形式で、適切なキーボードナビゲーションを可能にする
|
|
|
|
|
|
#### アクセシビリティ機能
|
|
|
- **`role="log"`**: このエリアがメッセージの時系列ログを含むことをスクリーンリーダーに伝える
|
|
|
- **`aria-live="polite"`**: 新しいメッセージをスクリーンリーダーに中断せずに通知
|
|
|
- **`aria-label`**: フォームコントロールの説明的なラベルを提供
|
|
|
- **`required`**: ユーザーがメッセージを入力するまでブラウザが検証
|
|
|
|
|
|
#### CSSとJavaScriptの統合
|
|
|
- **`class`属性**: CSSのスタイリングフックを提供(例: `chat-container`, `input-group`)
|
|
|
- **`id`属性**: JavaScriptが特定の要素を見つけて操作できるようにする
|
|
|
- **スクリプトの配置**: JavaScriptファイルを最後に読み込むことでHTMLが先にロードされる
|
|
|
|
|
|
**この構造が機能する理由**:
|
|
|
- **論理的な流れ**: ヘッダー → メインコンテンツ → 入力フォームが自然な読み順に一致
|
|
|
- **キーボード対応**: ユーザーがすべてのインタラクティブ要素をタブで移動可能
|
|
|
- **スクリーンリーダー対応**: 視覚障害者向けに明確なランドマークと説明を提供
|
|
|
- **モバイル対応**: ビューポートメタタグがレスポンシブデザインを可能にする
|
|
|
- **漸進的な強化**: CSSやJavaScriptがロードされなくても動作する
|
|
|
|
|
|
### インタラクティブなJavaScriptの追加: モダンなウェブアプリケーションロジック
|
|
|
|
|
|
次に、チャットインターフェースを活性化するJavaScriptを構築します。ES6クラス、async/await、イベント駆動型プログラミングなど、プロフェッショナルなウェブ開発で遭遇するモダンなJavaScriptパターンを使用します。
|
|
|
|
|
|
#### モダンなJavaScriptアーキテクチャの理解
|
|
|
|
|
|
手続き型コード(順番に実行される一連の関数)を書く代わりに、**クラスベースのアーキテクチャ**を作成します。クラスはオブジェクトを作成するための設計図のようなものです。
|
|
|
|
|
|
**ウェブアプリケーションでクラスを使用する理由**:
|
|
|
- **整理**: 関連する機能がすべて一緒にグループ化される
|
|
|
- **再利用性**: 同じページで複数のチャットインスタンスを作成可能
|
|
|
- **保守性**: 特定の機能をデバッグおよび変更しやすい
|
|
|
- **プロフェッショナルな標準**: React、Vue、Angularなどのフレームワークで使用されるパターン
|
|
|
|
|
|
`app.js`を以下のようなモダンで構造化されたJavaScriptで作成します:
|
|
|
|
|
|
```javascript
|
|
|
// app.js - Modern chat application logic
|
|
|
|
|
|
class ChatApp {
|
|
|
constructor() {
|
|
|
// Get references to DOM elements we'll need to manipulate
|
|
|
this.messages = document.getElementById("messages");
|
|
|
this.form = document.getElementById("chatForm");
|
|
|
this.input = document.getElementById("messageInput");
|
|
|
this.sendButton = document.getElementById("sendBtn");
|
|
|
|
|
|
// Configure your backend URL here
|
|
|
this.BASE_URL = "http://localhost:5000"; // Update this for your environment
|
|
|
this.API_ENDPOINT = `${this.BASE_URL}/hello`;
|
|
|
|
|
|
// Set up event listeners when the chat app is created
|
|
|
this.initializeEventListeners();
|
|
|
}
|
|
|
|
|
|
initializeEventListeners() {
|
|
|
// Listen for form submission (when user clicks Send or presses Enter)
|
|
|
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
|
|
|
|
|
|
// Also listen for Enter key in the input field (better UX)
|
|
|
this.input.addEventListener("keypress", (e) => {
|
|
|
if (e.key === "Enter" && !e.shiftKey) {
|
|
|
e.preventDefault();
|
|
|
this.handleSubmit(e);
|
|
|
}
|
|
|
});
|
|
|
}
|
|
|
|
|
|
async handleSubmit(event) {
|
|
|
event.preventDefault(); // Prevent form from refreshing the page
|
|
|
|
|
|
const messageText = this.input.value.trim();
|
|
|
if (!messageText) return; // Don't send empty messages
|
|
|
|
|
|
// Provide user feedback that something is happening
|
|
|
this.setLoading(true);
|
|
|
|
|
|
// Add user message to chat immediately (optimistic UI)
|
|
|
this.appendMessage(messageText, "user");
|
|
|
|
|
|
// Clear input field so user can type next message
|
|
|
this.input.value = '';
|
|
|
|
|
|
try {
|
|
|
// Call the AI API and wait for response
|
|
|
const reply = await this.callAPI(messageText);
|
|
|
|
|
|
// Add AI response to chat
|
|
|
this.appendMessage(reply, "assistant");
|
|
|
} catch (error) {
|
|
|
console.error('API Error:', error);
|
|
|
this.appendMessage("Sorry, I'm having trouble connecting right now. Please try again.", "error");
|
|
|
} finally {
|
|
|
// Re-enable the interface regardless of success or failure
|
|
|
this.setLoading(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async callAPI(message) {
|
|
|
const response = await fetch(this.API_ENDPOINT, {
|
|
|
method: "POST",
|
|
|
headers: {
|
|
|
"Content-Type": "application/json"
|
|
|
},
|
|
|
body: JSON.stringify({ message })
|
|
|
});
|
|
|
|
|
|
if (!response.ok) {
|
|
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
|
}
|
|
|
|
|
|
const data = await response.json();
|
|
|
return data.response;
|
|
|
}
|
|
|
|
|
|
appendMessage(text, role) {
|
|
|
const messageElement = document.createElement("div");
|
|
|
messageElement.className = `message ${role}`;
|
|
|
messageElement.innerHTML = `
|
|
|
<div class="message-content">
|
|
|
<span class="message-text">${this.escapeHtml(text)}</span>
|
|
|
<span class="message-time">${new Date().toLocaleTimeString()}</span>
|
|
|
</div>
|
|
|
`;
|
|
|
|
|
|
this.messages.appendChild(messageElement);
|
|
|
this.scrollToBottom();
|
|
|
}
|
|
|
|
|
|
escapeHtml(text) {
|
|
|
const div = document.createElement('div');
|
|
|
div.textContent = text;
|
|
|
return div.innerHTML;
|
|
|
}
|
|
|
|
|
|
scrollToBottom() {
|
|
|
this.messages.scrollTop = this.messages.scrollHeight;
|
|
|
}
|
|
|
|
|
|
setLoading(isLoading) {
|
|
|
this.sendButton.disabled = isLoading;
|
|
|
this.input.disabled = isLoading;
|
|
|
this.sendButton.textContent = isLoading ? "Sending..." : "Send";
|
|
|
}
|
|
|
}
|
|
|
|
|
|
// Initialize the chat application when the page loads
|
|
|
document.addEventListener("DOMContentLoaded", () => {
|
|
|
new ChatApp();
|
|
|
});
|
|
|
```
|
|
|
|
|
|
#### 各JavaScript概念の理解
|
|
|
|
|
|
**ES6クラス構造**:
|
|
|
```javascript
|
|
|
class ChatApp {
|
|
|
constructor() {
|
|
|
// This runs when you create a new ChatApp instance
|
|
|
// It's like the "setup" function for your chat
|
|
|
}
|
|
|
|
|
|
methodName() {
|
|
|
// Methods are functions that belong to the class
|
|
|
// They can access class properties using "this"
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**Async/Awaitパターン**:
|
|
|
```javascript
|
|
|
// Old way (callback hell):
|
|
|
fetch(url)
|
|
|
.then(response => response.json())
|
|
|
.then(data => console.log(data))
|
|
|
.catch(error => console.error(error));
|
|
|
|
|
|
// Modern way (async/await):
|
|
|
try {
|
|
|
const response = await fetch(url);
|
|
|
const data = await response.json();
|
|
|
console.log(data);
|
|
|
} catch (error) {
|
|
|
console.error(error);
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**イベント駆動型プログラミング**:
|
|
|
何かが起きたかを常にチェックする代わりに、イベントを「リッスン」します:
|
|
|
```javascript
|
|
|
// When form is submitted, run handleSubmit
|
|
|
this.form.addEventListener("submit", (e) => this.handleSubmit(e));
|
|
|
|
|
|
// When Enter key is pressed, also run handleSubmit
|
|
|
this.input.addEventListener("keypress", (e) => { /* ... */ });
|
|
|
```
|
|
|
|
|
|
**DOM操作**:
|
|
|
```javascript
|
|
|
// Create new elements
|
|
|
const messageElement = document.createElement("div");
|
|
|
|
|
|
// Modify their properties
|
|
|
messageElement.className = "message user";
|
|
|
messageElement.innerHTML = "Hello world!";
|
|
|
|
|
|
// Add to the page
|
|
|
this.messages.appendChild(messageElement);
|
|
|
```
|
|
|
|
|
|
#### セキュリティとベストプラクティス
|
|
|
|
|
|
**XSS防止**:
|
|
|
```javascript
|
|
|
escapeHtml(text) {
|
|
|
const div = document.createElement('div');
|
|
|
div.textContent = text; // This automatically escapes HTML
|
|
|
return div.innerHTML;
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**これが重要な理由**: ユーザーが`<script>alert('hack')</script>`と入力した場合、この関数はそれをコードとして実行するのではなくテキストとして表示します。
|
|
|
|
|
|
**エラーハンドリング**:
|
|
|
```javascript
|
|
|
try {
|
|
|
const reply = await this.callAPI(messageText);
|
|
|
this.appendMessage(reply, "assistant");
|
|
|
} catch (error) {
|
|
|
// Show user-friendly error instead of breaking the app
|
|
|
this.appendMessage("Sorry, I'm having trouble...", "error");
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**ユーザー体験の考慮**:
|
|
|
- **楽観的UI**: サーバーの応答を待たずにユーザーメッセージをすぐに追加
|
|
|
- **ローディング状態**: ボタンを無効化し、「送信
|
|
|
このアーキテクチャは拡張性が高く、メッセージ編集、ファイルアップロード、複数の会話スレッドなどの機能をコア構造を再構築することなく簡単に追加できます。
|
|
|
|
|
|
### チャットインターフェースのスタイリング
|
|
|
|
|
|
次に、CSSを使ってモダンで視覚的に魅力的なチャットインターフェースを作成しましょう。良いスタイリングはアプリケーションをプロフェッショナルに感じさせ、全体的なユーザー体験を向上させます。Flexbox、CSS Grid、カスタムプロパティなどのモダンなCSS機能を使用して、レスポンシブでアクセシブルなデザインを作成します。
|
|
|
|
|
|
`styles.css`を作成し、以下の包括的なスタイルを追加してください:
|
|
|
|
|
|
```css
|
|
|
/* styles.css - Modern chat interface styling */
|
|
|
|
|
|
:root {
|
|
|
--primary-color: #2563eb;
|
|
|
--secondary-color: #f1f5f9;
|
|
|
--user-color: #3b82f6;
|
|
|
--assistant-color: #6b7280;
|
|
|
--error-color: #ef4444;
|
|
|
--text-primary: #1e293b;
|
|
|
--text-secondary: #64748b;
|
|
|
--border-radius: 12px;
|
|
|
--shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
|
|
|
}
|
|
|
|
|
|
* {
|
|
|
margin: 0;
|
|
|
padding: 0;
|
|
|
box-sizing: border-box;
|
|
|
}
|
|
|
|
|
|
body {
|
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
|
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
|
|
min-height: 100vh;
|
|
|
display: flex;
|
|
|
align-items: center;
|
|
|
justify-content: center;
|
|
|
padding: 20px;
|
|
|
}
|
|
|
|
|
|
.chat-container {
|
|
|
width: 100%;
|
|
|
max-width: 800px;
|
|
|
height: 600px;
|
|
|
background: white;
|
|
|
border-radius: var(--border-radius);
|
|
|
box-shadow: var(--shadow);
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
overflow: hidden;
|
|
|
}
|
|
|
|
|
|
.chat-header {
|
|
|
background: var(--primary-color);
|
|
|
color: white;
|
|
|
padding: 20px;
|
|
|
text-align: center;
|
|
|
}
|
|
|
|
|
|
.chat-header h1 {
|
|
|
font-size: 1.5rem;
|
|
|
margin-bottom: 5px;
|
|
|
}
|
|
|
|
|
|
.chat-header p {
|
|
|
opacity: 0.9;
|
|
|
font-size: 0.9rem;
|
|
|
}
|
|
|
|
|
|
.chat-messages {
|
|
|
flex: 1;
|
|
|
padding: 20px;
|
|
|
overflow-y: auto;
|
|
|
display: flex;
|
|
|
flex-direction: column;
|
|
|
gap: 15px;
|
|
|
background: var(--secondary-color);
|
|
|
}
|
|
|
|
|
|
.message {
|
|
|
display: flex;
|
|
|
max-width: 80%;
|
|
|
animation: slideIn 0.3s ease-out;
|
|
|
}
|
|
|
|
|
|
.message.user {
|
|
|
align-self: flex-end;
|
|
|
}
|
|
|
|
|
|
.message.user .message-content {
|
|
|
background: var(--user-color);
|
|
|
color: white;
|
|
|
border-radius: var(--border-radius) var(--border-radius) 4px var(--border-radius);
|
|
|
}
|
|
|
|
|
|
.message.assistant {
|
|
|
align-self: flex-start;
|
|
|
}
|
|
|
|
|
|
.message.assistant .message-content {
|
|
|
background: white;
|
|
|
color: var(--text-primary);
|
|
|
border-radius: var(--border-radius) var(--border-radius) var(--border-radius) 4px;
|
|
|
border: 1px solid #e2e8f0;
|
|
|
}
|
|
|
|
|
|
.message.error .message-content {
|
|
|
background: var(--error-color);
|
|
|
color: white;
|
|
|
border-radius: var(--border-radius);
|
|
|
}
|
|
|
|
|
|
.message-content {
|
|
|
padding: 12px 16px;
|
|
|
box-shadow: var(--shadow);
|
|
|
position: relative;
|
|
|
}
|
|
|
|
|
|
.message-text {
|
|
|
display: block;
|
|
|
line-height: 1.5;
|
|
|
word-wrap: break-word;
|
|
|
}
|
|
|
|
|
|
.message-time {
|
|
|
display: block;
|
|
|
font-size: 0.75rem;
|
|
|
opacity: 0.7;
|
|
|
margin-top: 5px;
|
|
|
}
|
|
|
|
|
|
.chat-form {
|
|
|
padding: 20px;
|
|
|
border-top: 1px solid #e2e8f0;
|
|
|
background: white;
|
|
|
}
|
|
|
|
|
|
.input-group {
|
|
|
display: flex;
|
|
|
gap: 10px;
|
|
|
align-items: center;
|
|
|
}
|
|
|
|
|
|
#messageInput {
|
|
|
flex: 1;
|
|
|
padding: 12px 16px;
|
|
|
border: 2px solid #e2e8f0;
|
|
|
border-radius: var(--border-radius);
|
|
|
font-size: 1rem;
|
|
|
outline: none;
|
|
|
transition: border-color 0.2s ease;
|
|
|
}
|
|
|
|
|
|
#messageInput:focus {
|
|
|
border-color: var(--primary-color);
|
|
|
}
|
|
|
|
|
|
#messageInput:disabled {
|
|
|
background: #f8fafc;
|
|
|
opacity: 0.6;
|
|
|
cursor: not-allowed;
|
|
|
}
|
|
|
|
|
|
#sendBtn {
|
|
|
padding: 12px 24px;
|
|
|
background: var(--primary-color);
|
|
|
color: white;
|
|
|
border: none;
|
|
|
border-radius: var(--border-radius);
|
|
|
font-size: 1rem;
|
|
|
font-weight: 600;
|
|
|
cursor: pointer;
|
|
|
transition: background-color 0.2s ease;
|
|
|
min-width: 80px;
|
|
|
}
|
|
|
|
|
|
#sendBtn:hover:not(:disabled) {
|
|
|
background: #1d4ed8;
|
|
|
}
|
|
|
|
|
|
#sendBtn:disabled {
|
|
|
background: #94a3b8;
|
|
|
cursor: not-allowed;
|
|
|
}
|
|
|
|
|
|
@keyframes slideIn {
|
|
|
from {
|
|
|
opacity: 0;
|
|
|
transform: translateY(10px);
|
|
|
}
|
|
|
to {
|
|
|
opacity: 1;
|
|
|
transform: translateY(0);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Responsive design for mobile devices */
|
|
|
@media (max-width: 768px) {
|
|
|
body {
|
|
|
padding: 10px;
|
|
|
}
|
|
|
|
|
|
.chat-container {
|
|
|
height: calc(100vh - 20px);
|
|
|
border-radius: 8px;
|
|
|
}
|
|
|
|
|
|
.message {
|
|
|
max-width: 90%;
|
|
|
}
|
|
|
|
|
|
.input-group {
|
|
|
flex-direction: column;
|
|
|
gap: 10px;
|
|
|
}
|
|
|
|
|
|
#messageInput {
|
|
|
width: 100%;
|
|
|
}
|
|
|
|
|
|
#sendBtn {
|
|
|
width: 100%;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Accessibility improvements */
|
|
|
@media (prefers-reduced-motion: reduce) {
|
|
|
.message {
|
|
|
animation: none;
|
|
|
}
|
|
|
|
|
|
* {
|
|
|
transition: none !important;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
/* Dark mode support */
|
|
|
@media (prefers-color-scheme: dark) {
|
|
|
.chat-container {
|
|
|
background: #1e293b;
|
|
|
color: #f1f5f9;
|
|
|
}
|
|
|
|
|
|
.chat-messages {
|
|
|
background: #0f172a;
|
|
|
}
|
|
|
|
|
|
.message.assistant .message-content {
|
|
|
background: #334155;
|
|
|
color: #f1f5f9;
|
|
|
border-color: #475569;
|
|
|
}
|
|
|
|
|
|
.chat-form {
|
|
|
background: #1e293b;
|
|
|
border-color: #475569;
|
|
|
}
|
|
|
|
|
|
#messageInput {
|
|
|
background: #334155;
|
|
|
color: #f1f5f9;
|
|
|
border-color: #475569;
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
**CSSアーキテクチャの理解:**
|
|
|
- **使用** CSSカスタムプロパティ(変数)でテーマの一貫性を保ち、メンテナンスを容易に
|
|
|
- **実装** Flexboxレイアウトでレスポンシブデザインと適切な配置を実現
|
|
|
- **含む** メッセージ表示のスムーズなアニメーション(気を散らさない程度に)
|
|
|
- **提供** ユーザーメッセージ、AI応答、エラーステートの視覚的な区別
|
|
|
- **対応** デスクトップとモバイルデバイスの両方で動作するレスポンシブデザイン
|
|
|
- **考慮** アクセシビリティ(動きの軽減設定や適切なコントラスト比)
|
|
|
- **提供** ユーザーのシステム設定に基づいたダークモード対応
|
|
|
|
|
|
### バックエンドURLの設定
|
|
|
|
|
|
最後のステップは、JavaScript内の`BASE_URL`をバックエンドサーバーに合わせて更新することです:
|
|
|
|
|
|
```javascript
|
|
|
// For local development
|
|
|
this.BASE_URL = "http://localhost:5000";
|
|
|
|
|
|
// For GitHub Codespaces (replace with your actual URL)
|
|
|
this.BASE_URL = "https://your-codespace-name-5000.app.github.dev";
|
|
|
```
|
|
|
|
|
|
**バックエンドURLの決定方法:**
|
|
|
- **ローカル開発**: フロントエンドとバックエンドをローカルで実行している場合は`http://localhost:5000`を使用
|
|
|
- **Codespaces**: ポート5000を公開した後、PortsタブでバックエンドURLを確認
|
|
|
- **本番環境**: ホスティングサービスにデプロイする際は実際のドメインに置き換え
|
|
|
|
|
|
> 💡 **テストのヒント**: ブラウザでルートURLにアクセスしてバックエンドを直接テストできます。FastAPIサーバーのウェルカムメッセージが表示されるはずです。
|
|
|
|
|
|
## テストとデプロイ
|
|
|
|
|
|
フロントエンドとバックエンドのコンポーネントが完成したら、すべてが正常に動作するかテストし、他の人とチャットアシスタントを共有するためのデプロイオプションを探りましょう。
|
|
|
|
|
|
### ローカルテストワークフロー
|
|
|
|
|
|
以下の手順に従って、アプリケーション全体をテストしてください:
|
|
|
|
|
|
```mermaid
|
|
|
graph TD
|
|
|
A[Start Backend Server] --> B[Configure Environment Variables]
|
|
|
B --> C[Test API Endpoints]
|
|
|
C --> D[Open Frontend in Browser]
|
|
|
D --> E[Test Chat Functionality]
|
|
|
E --> F[Debug Any Issues]
|
|
|
```
|
|
|
|
|
|
**ステップバイステップのテストプロセス:**
|
|
|
|
|
|
1. **バックエンドサーバーを起動**:
|
|
|
```bash
|
|
|
cd backend
|
|
|
source venv/bin/activate # or venv\Scripts\activate on Windows
|
|
|
python api.py
|
|
|
```
|
|
|
|
|
|
2. **APIが動作していることを確認**:
|
|
|
- ブラウザで`http://localhost:5000`を開く
|
|
|
- FastAPIサーバーのウェルカムメッセージが表示されるはずです
|
|
|
|
|
|
3. **フロントエンドを開く**:
|
|
|
- フロントエンドディレクトリに移動
|
|
|
- ブラウザで`index.html`を開く
|
|
|
- または、VS CodeのLive Server拡張機能を使用して開発体験を向上
|
|
|
|
|
|
4. **チャット機能をテスト**:
|
|
|
- 入力フィールドにメッセージを入力
|
|
|
- 「送信」をクリックするかEnterキーを押す
|
|
|
- AIが適切に応答することを確認
|
|
|
- ブラウザのコンソールでJavaScriptエラーがないか確認
|
|
|
|
|
|
### よくある問題のトラブルシューティング
|
|
|
|
|
|
| 問題 | 症状 | 解決策 |
|
|
|
|------|------|--------|
|
|
|
| **CORSエラー** | フロントエンドがバックエンドに接続できない | FastAPIのCORSMiddlewareが正しく設定されていることを確認 |
|
|
|
| **APIキーエラー** | 401 Unauthorizedの応答 | `GITHUB_TOKEN`環境変数を確認 |
|
|
|
| **接続拒否** | フロントエンドでネットワークエラー | バックエンドURLとFlaskサーバーが動作していることを確認 |
|
|
|
| **AI応答なし** | 空の応答またはエラー応答 | APIのクォータや認証問題についてバックエンドログを確認 |
|
|
|
|
|
|
**一般的なデバッグ手順:**
|
|
|
- **ブラウザの開発者ツールコンソール**でJavaScriptエラーを確認
|
|
|
- **ネットワークタブ**でAPIリクエストと応答が成功しているか確認
|
|
|
- **バックエンドのターミナル出力**でPythonエラーやAPI問題を確認
|
|
|
- **環境変数**が正しく読み込まれ、アクセス可能であることを確認
|
|
|
|
|
|
## GitHub Copilot Agentチャレンジ 🚀
|
|
|
|
|
|
Agentモードを使用して以下のチャレンジを完了してください:
|
|
|
|
|
|
**説明:** 会話履歴とメッセージの永続性を追加してチャットアシスタントを強化します。このチャレンジでは、チャットアプリケーションでの状態管理とデータ保存の実装について学びます。
|
|
|
|
|
|
**プロンプト:** チャットアプリケーションを修正して、セッション間で会話履歴を保持できるようにします。チャットメッセージをローカルストレージに保存し、ページ読み込み時に会話履歴を表示し、「履歴をクリア」ボタンを追加してください。また、タイピングインジケーターとメッセージのタイムスタンプを実装して、チャット体験をよりリアルにしてください。
|
|
|
|
|
|
[agent modeについて詳しくはこちら](https://code.visualstudio.com/blogs/2025/02/24/introducing-copilot-agent-mode)。
|
|
|
|
|
|
## 課題: 自分のAIアシスタントを構築する
|
|
|
|
|
|
チュートリアルコードを単に再現するのではなく、学んだ概念を応用して、自分の興味やユースケースに合ったものを構築する機会です。
|
|
|
|
|
|
### プロジェクト要件
|
|
|
|
|
|
クリーンで整理された構造でプロジェクトをセットアップしましょう:
|
|
|
|
|
|
```text
|
|
|
my-ai-assistant/
|
|
|
├── backend/
|
|
|
│ ├── api.py # Your FastAPI server
|
|
|
│ ├── llm.py # AI integration functions
|
|
|
│ ├── .env # Your secrets (keep this safe!)
|
|
|
│ └── requirements.txt # Python dependencies
|
|
|
├── frontend/
|
|
|
│ ├── index.html # Your chat interface
|
|
|
│ ├── app.js # The JavaScript magic
|
|
|
│ └── styles.css # Make it look amazing
|
|
|
└── README.md # Tell the world about your creation
|
|
|
```
|
|
|
|
|
|
### コア実装タスク
|
|
|
|
|
|
**バックエンド開発:**
|
|
|
- **FastAPIコードを**自分のものにカスタマイズ
|
|
|
- **ユニークなAIの個性を作成** – 例えば、役立つ料理アシスタント、創造的なライティングパートナー、または勉強の相棒?
|
|
|
- **堅牢なエラーハンドリングを追加**して、問題が発生してもアプリが壊れないように
|
|
|
- **APIの動作を理解するための**明確なドキュメントを作成
|
|
|
|
|
|
**フロントエンド開発:**
|
|
|
- **直感的で親しみやすいチャットインターフェースを構築**
|
|
|
- **他の開発者に誇れるような**クリーンでモダンなJavaScriptを書く
|
|
|
- **AIの個性を反映するカスタムスタイリングをデザイン** – 楽しくカラフル?クリーンでミニマル?完全にあなた次第!
|
|
|
- **スマートフォンとコンピュータの両方で**素晴らしく動作することを確認
|
|
|
|
|
|
**パーソナライズ要件:**
|
|
|
- **AIアシスタントのユニークな名前と個性を選択** – あなたの興味や解決したい問題を反映するもの
|
|
|
- **アシスタントの雰囲気に合った**ビジュアルデザインをカスタマイズ
|
|
|
- **人々がチャットを始めたくなるような**魅力的なウェルカムメッセージを書く
|
|
|
- **異なる種類の質問でアシスタントをテスト**して、どのように応答するか確認
|
|
|
|
|
|
### 強化アイデア(オプション)
|
|
|
|
|
|
プロジェクトを次のレベルに引き上げたいですか?以下の楽しいアイデアを探ってみてください:
|
|
|
|
|
|
| 機能 | 説明 | 練習するスキル |
|
|
|
|------|------|---------------|
|
|
|
| **メッセージ履歴** | ページ更新後も会話を記憶 | localStorageの操作、JSON処理 |
|
|
|
| **タイピングインジケーター** | AIが応答を待っている間「AIが入力中...」を表示 | CSSアニメーション、非同期プログラミング |
|
|
|
| **メッセージタイムスタンプ** | 各メッセージが送信された時間を表示 | 日付/時間フォーマット、UXデザイン |
|
|
|
| **チャットのエクスポート** | ユーザーが会話をダウンロードできるようにする | ファイル操作、データエクスポート |
|
|
|
| **テーマ切り替え** | ライト/ダークモードの切り替え | CSS変数、ユーザー設定 |
|
|
|
| **音声入力** | 音声認識機能を追加 | Web API、アクセシビリティ |
|
|
|
|
|
|
### テストとドキュメント
|
|
|
|
|
|
**品質保証:**
|
|
|
- **さまざまな入力タイプとエッジケースで**アプリケーションをテスト
|
|
|
- **異なる画面サイズで**レスポンシブデザインが機能することを確認
|
|
|
- **キーボードナビゲーションやスクリーンリーダーを使用して**アクセシビリティを確認
|
|
|
- **HTMLとCSSが**標準に準拠していることを検証
|
|
|
|
|
|
**ドキュメント要件:**
|
|
|
- **README.mdを作成して**プロジェクトと実行方法を説明
|
|
|
- **チャットインターフェースが動作しているスクリーンショットを含める**
|
|
|
- **追加したユニークな機能やカスタマイズをドキュメント化**
|
|
|
- **他の開発者向けに**明確なセットアップ手順を提供
|
|
|
|
|
|
### 提出ガイドライン
|
|
|
|
|
|
**プロジェクト成果物:**
|
|
|
1. すべてのソースコードを含む完全なプロジェクトフォルダ
|
|
|
2. プロジェクト説明とセットアップ手順を記載したREADME.md
|
|
|
3. チャットアシスタントが動作しているスクリーンショット
|
|
|
4. 学んだことや直面した課題についての簡単な振り返り
|
|
|
|
|
|
**評価基準:**
|
|
|
- **機能性**: チャットアシスタントが期待通りに動作するか?
|
|
|
- **コード品質**: コードが整理されていて、コメントがあり、保守可能か?
|
|
|
- **デザイン**: インターフェースが視覚的に魅力的で使いやすいか?
|
|
|
- **創造性**: 実装がどれだけユニークでパーソナライズされているか?
|
|
|
- **ドキュメント**: セットアップ手順が明確で完全か?
|
|
|
|
|
|
> 💡 **成功のヒント**: まず基本的な要件から始め、すべてが機能するようになったら強化機能を追加してください。高度な機能を追加する前に、洗練されたコア体験を作ることに集中しましょう。
|
|
|
|
|
|
## ソリューション
|
|
|
|
|
|
[ソリューション](./solution/README.md)
|
|
|
|
|
|
## ボーナスチャレンジ
|
|
|
|
|
|
AIアシスタントをさらに進化させたいですか?AI統合とウェブ開発の理解を深めるための高度なチャレンジに挑戦してみてください。
|
|
|
|
|
|
### 個性のカスタマイズ
|
|
|
|
|
|
AIアシスタントにユニークな個性を与えると、本当の魔法が起こります。さまざまなシステムプロンプトを試して、専門的なアシスタントを作成してみましょう:
|
|
|
|
|
|
**プロフェッショナルアシスタントの例:**
|
|
|
```python
|
|
|
call_llm(message, "You are a professional business consultant with 20 years of experience. Provide structured, actionable advice with specific steps and considerations.")
|
|
|
```
|
|
|
|
|
|
**創造的なライティングヘルパーの例:**
|
|
|
```python
|
|
|
call_llm(message, "You are an enthusiastic creative writing coach. Help users develop their storytelling skills with imaginative prompts and constructive feedback.")
|
|
|
```
|
|
|
|
|
|
**技術的なメンターの例:**
|
|
|
```python
|
|
|
call_llm(message, "You are a patient senior developer who explains complex programming concepts using simple analogies and practical examples.")
|
|
|
```
|
|
|
|
|
|
### フロントエンドの強化
|
|
|
|
|
|
以下の視覚的および機能的な改善でチャットインターフェースを変革しましょう:
|
|
|
|
|
|
**高度なCSS機能:**
|
|
|
- **スムーズなメッセージアニメーションとトランジションを実装**
|
|
|
- **CSSの形状やグラデーションを使ったカスタムチャットバブルデザインを追加**
|
|
|
- **AIが「考えている」間のタイピングインジケーターアニメーションを作成**
|
|
|
- **絵文字リアクションやメッセージ評価システムをデザイン**
|
|
|
|
|
|
**JavaScriptの強化:**
|
|
|
- **キーボードショートカットを追加**(Ctrl+Enterで送信、Escapeで入力クリア)
|
|
|
- **メッセージ検索とフィルタリング機能を実装**
|
|
|
- **会話エクスポート機能を作成**(テキストまたはJSONとしてダウンロード)
|
|
|
- **メッセージの損失を防ぐためにlocalStorageへの自動保存を追加**
|
|
|
|
|
|
### 高度なAI統合
|
|
|
|
|
|
**複数のAI個性:**
|
|
|
- **異なるAI個性を切り替えるためのドロップダウンを作成**
|
|
|
- **ユーザーの好みの個性をlocalStorageに保存**
|
|
|
- **会話の流れを維持するコンテキスト切り替えを実装**
|
|
|
|
|
|
**スマート応答機能:**
|
|
|
- **会話のコンテキスト認識を追加**(AIが以前のメッセージを記憶)
|
|
|
- **会話のトピックに基づいたスマートな提案を実装**
|
|
|
- **一般的な質問に対するクイック返信ボタンを作成**
|
|
|
|
|
|
> 🎯 **学習目標**: これらのボーナスチャレンジは、実際のアプリケーションで使用される高度なウェブ開発パターンとAI統合技術を理解するのに役立ちます。
|
|
|
|
|
|
## まとめと次のステップ
|
|
|
|
|
|
おめでとうございます!ゼロからAI搭載のチャットアシスタントを完全に構築することに成功しました。このプロジェクトを通じて、モダンなウェブ開発技術とAI統合の実践的な経験を得ることができました。これらのスキルは、今日の技術分野でますます価値が高まっています。
|
|
|
|
|
|
### 達成したこと
|
|
|
|
|
|
このレッスンを通じて、いくつかの重要な技術と概念を習得しました:
|
|
|
|
|
|
**バックエンド開発:**
|
|
|
- **GitHub Models APIと統合**してAI機能を実現
|
|
|
- **Flaskを使用してRESTful APIを構築**し、適切なエラーハンドリングを実装
|
|
|
- **環境変数を使用して**安全な認証を実現
|
|
|
- **CORSを設定**してフロントエンドとバックエンド間のクロスオリジンリクエストを許可
|
|
|
|
|
|
**フロントエンド開発:**
|
|
|
- **セマンティックHTMLを使用して**レスポンシブなチャットインターフェースを作成
|
|
|
- **async/awaitとクラスベースのアーキテクチャを使用して**モダンなJavaScriptを実装
|
|
|
- **CSS Grid、Flexbox、アニメーションを使用して**魅力的なユーザーインターフェースをデザイン
|
|
|
- **アクセシビリティ機能とレスポンシブデザイン原則を追加**
|
|
|
|
|
|
**フルスタック統合:**
|
|
|
- **HTTP APIコールを通じて**フロントエンドとバックエンドを接続
|
|
|
- **リアルタイムのユーザーインタラクションと非同期データフローを処理**
|
|
|
- **アプリケーション全体でエラーハンドリングとユーザーフィードバックを実装**
|
|
|
- **ユーザー入力からAI応答までの完全なアプリケーションワークフローをテスト**
|
|
|
|
|
|
### 主要な学習成果
|
|
|
|
|
|
```mermaid
|
|
|
mindmap
|
|
|
root((AI Chat App Skills))
|
|
|
API Integration
|
|
|
Authentication
|
|
|
Error Handling
|
|
|
Async Programming
|
|
|
Web Development
|
|
|
HTML5 Semantics
|
|
|
Modern CSS
|
|
|
ES6+ JavaScript
|
|
|
User Experience
|
|
|
Responsive Design
|
|
|
Accessibility
|
|
|
Real-time Interaction
|
|
|
AI Understanding
|
|
|
Prompt Engineering
|
|
|
Model Parameters
|
|
|
Conversation Flow
|
|
|
```
|
|
|
|
|
|
このプロジェクトは、AI搭載アプリケーションの構築の基礎を紹介しました。これはウェブ開発の未来を代表するものであり、従来のウェブアプリケーションにAI機能を統合し、知的で応答性のあるユーザー体験を作り出す方法を理解することができました。
|
|
|
|
|
|
### プロフェッショナルな応用
|
|
|
|
|
|
このレッスンで習得したスキルは、現代のソフトウェア開発キャリアに直接適用できます:
|
|
|
|
|
|
- **モダンなフレームワークとAPIを使用したフルスタックウェブ開発**
|
|
|
- **ウェブアプリケーションやモバイルアプリへのAI統合**
|
|
|
- **マイクロサービスアーキテクチャ向けのAPI設計と開発**
|
|
|
- **アクセシビリティとレスポンシブデザインに焦点
|
|
|
- **ポートフォワーディング**を使用してアプリケーションをテストする
|
|
|
|
|
|
**Codespacesが提供するもの:**
|
|
|
- ローカル環境のセットアップや構成の問題を**解消**
|
|
|
- 異なるデバイス間で**一貫した開発環境**を提供
|
|
|
- ウェブ開発向けの事前設定済みツールや拡張機能を**含む**
|
|
|
- バージョン管理やコラボレーションのためのGitHubとの**シームレスな統合**を提供
|
|
|
|
|
|
> 🚀 **プロのヒント**: CodespacesはAIアプリケーションの学習やプロトタイピングに最適です。複雑な環境設定を自動的に処理してくれるので、構成のトラブルシューティングではなく、構築や学習に集中できます。
|
|
|
|
|
|
---
|
|
|
|
|
|
**免責事項**:
|
|
|
この文書はAI翻訳サービス[Co-op Translator](https://github.com/Azure/co-op-translator)を使用して翻訳されています。正確性を追求しておりますが、自動翻訳には誤りや不正確な部分が含まれる可能性があります。元の言語で記載された文書を正式な情報源としてご参照ください。重要な情報については、専門の人間による翻訳を推奨します。この翻訳の使用に起因する誤解や誤認について、当社は一切の責任を負いません。 |