Skip to content

要件定義AIエージェントサービスのVercelデプロイとSupabase実装 #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
68 changes: 68 additions & 0 deletions examples/requirements_agent/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
# 要件定義AIエージェント (Requirements Definition AI Agent)

このサンプルは、OpenAI Agents Python SDKを使用して要件定義を支援するAIエージェントを実装したものです。ユーザーの入力から要件を抽出し、優先度やカテゴリを付与して整理します。

## 機能

- チャットインターフェースによる要件の収集
- 要件の自動抽出と分類
- 優先度の自動設定
- Supabaseによるユーザー認証と履歴管理(準備中)
- Vercelへのデプロイ対応

## 構成

- `backend/`: FastAPIを使用したバックエンドAPI
- `app/api/`: APIエンドポイント
- `app/models/`: データモデル
- `app/agents/`: AIエージェント定義
- `app/services/`: Supabase連携などのサービス
- `frontend/`: Reactを使用したフロントエンド
- `src/components/`: UIコンポーネント
- `src/services/`: APIとの通信
- `src/types/`: 型定義

## 使用方法

### バックエンド

```bash
cd backend
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
uvicorn app.main:app --reload
```

### フロントエンド

```bash
cd frontend
npm install
npm run dev
```

## 環境変数

バックエンドの `.env` ファイルに以下の環境変数を設定してください:

```
OPENAI_API_KEY=your-api-key
SUPABASE_URL=your-supabase-url
SUPABASE_KEY=your-supabase-key
```

## Vercelへのデプロイ

バックエンドとフロントエンドは、それぞれVercelにデプロイすることができます。

```bash
# バックエンド
cd backend
vercel deploy --prod

# フロントエンド
cd frontend
npm run build
vercel deploy --prod
```
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
from pydantic import BaseModel
from typing import List, Optional
import os

from agents import Agent, function_tool
from ..models.requirement import Requirement, RequirementsExtraction

@function_tool
def save_requirement(requirement: Requirement) -> str:
"""
新しい要件を保存します。

Args:
requirement: 保存する要件
"""
print(f"要件を保存: {requirement}")
return f"要件が保存されました: {requirement.description}"

@function_tool
def categorize_requirements(requirements: list[Requirement]) -> list[Requirement]:
"""
要件をカテゴリー別に整理します。

Args:
requirements: カテゴリー分けする要件リスト
"""
for req in requirements:
if not req.category:
req.category = "未分類"
return requirements

@function_tool
def prioritize_requirements(requirements: list[Requirement]) -> list[Requirement]:
"""
要件に優先順位を付けます。

Args:
requirements: 優先順位付けする要件リスト
"""
priorities = ["高", "中", "低"]
for i, req in enumerate(requirements):
if not req.priority:
req.priority = priorities[i % len(priorities)]
return requirements

requirements_gathering_agent = Agent(
name="要件収集エージェント",
instructions="""
あなたは要件定義の専門家です。ユーザーとの会話から具体的なソフトウェア要件を抽出します。

会話を分析し、明示的および暗黙的な要件を見つけてください。
各要件は明確で具体的なものにしてください。
要件が曖昧な場合は、明確にするための質問をしてください。

要件を収集したら、save_requirement関数を使用して保存してください。
複数の要件をまとめて整理する必要がある場合は、categorize_requirements関数を使用してください。
要件に優先順位を付ける場合は、prioritize_requirements関数を使用してください。
""",
tools=[save_requirement, categorize_requirements, prioritize_requirements],
output_type=RequirementsExtraction
)
Empty file.
78 changes: 78 additions & 0 deletions examples/requirements_agent/backend/app/api/chat.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
from fastapi import APIRouter, HTTPException, Depends
from typing import List, Dict, Any, Optional
import asyncio
from datetime import datetime
import os

from agents import Runner

from ..models.message import Message, ChatRequest, ChatResponse
from ..models.requirement import Requirement, RequirementsExtraction
from ..agents.requirements_agent import requirements_gathering_agent
from ..services.supabase_service import store_message, store_requirement

router = APIRouter()

@router.post("/chat", response_model=ChatResponse)
async def process_chat(request: ChatRequest):
try:
last_user_message = next((msg.content for msg in reversed(request.messages) if msg.role == "user"), None)
if not last_user_message:
raise HTTPException(status_code=400, detail="ユーザーメッセージが見つかりません")

if not os.environ.get("OPENAI_API_KEY") or os.environ.get("OPENAI_API_KEY") == "sk-your-api-key":
mock_requirements = [
Requirement(
description="ユーザー認証機能の実装",
priority="高",
category="認証",
notes="ユーザー登録、ログイン、パスワードリセット機能を含む"
),
Requirement(
description="レスポンシブデザインの対応",
priority="中",
category="UI/UX",
notes="モバイル、タブレット、デスクトップに対応したレイアウト"
),
Requirement(
description="データのバックアップ機能",
priority="低",
category="データ管理",
notes="定期的なバックアップとリストア機能"
)
]

mock_response = ChatResponse(
message="要件を分析しました。主要な要件として、ユーザー認証機能、レスポンシブデザイン対応、データバックアップ機能が必要です。これらの要件は優先度に応じて実装計画を立てることをお勧めします。",
requirements=mock_requirements
)

return mock_response

result = await Runner.run(
requirements_gathering_agent,
last_user_message,
)

assistant_message = Message(
role="assistant",
content=result.final_output.summary,
created_at=datetime.now()
)

if request.user_id:
conversation_id = request.project_id or "default"
await store_message(assistant_message, request.user_id, conversation_id)

for req in result.final_output.requirements:
req.user_id = request.user_id
await store_requirement(req, request.user_id)

response = ChatResponse(
message=result.final_output.summary,
requirements=result.final_output.requirements
)
return response

except Exception as e:
raise HTTPException(status_code=500, detail=f"チャット処理エラー: {str(e)}")
36 changes: 36 additions & 0 deletions examples/requirements_agent/backend/app/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware
import os
from dotenv import load_dotenv

from .api import chat # APIルーターのインポート

load_dotenv() # 環境変数の読み込み

app = FastAPI(title="要件定義AIエージェント")

origins = [
"http://localhost:3000",
"http://localhost:5173",
"http://127.0.0.1:5173",
"https://requirements-ai-agent.vercel.app",
"*", # 開発中は全てのオリジンを許可
]

app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

app.include_router(chat.router, prefix="/api")

@app.get("/")
async def root():
return {"message": "要件定義AIエージェント API"}

@app.get("/health")
async def health_check():
return {"status": "healthy"}
Empty file.
17 changes: 17 additions & 0 deletions examples/requirements_agent/backend/app/models/message.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
from pydantic import BaseModel
from typing import List, Optional
from datetime import datetime

class Message(BaseModel):
role: str # "user" または "assistant"
content: str
created_at: Optional[datetime] = None

class ChatRequest(BaseModel):
messages: list[Message]
user_id: Optional[str] = None
project_id: Optional[str] = None

class ChatResponse(BaseModel):
message: str
requirements: list = []
16 changes: 16 additions & 0 deletions examples/requirements_agent/backend/app/models/requirement.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
from pydantic import BaseModel
from typing import List, Optional, Dict, Any
from datetime import datetime

class Requirement(BaseModel):
id: Optional[str] = None
description: str
priority: Optional[str] = None
category: Optional[str] = None
notes: Optional[str] = None
created_at: Optional[datetime] = None
user_id: Optional[str] = None

class RequirementsExtraction(BaseModel):
requirements: list[Requirement]
summary: str
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import os
from supabase import create_client, Client
from dotenv import load_dotenv
from typing import List, Dict, Any, Optional

from ..models.requirement import Requirement
from ..models.message import Message

load_dotenv()

url = os.environ.get("SUPABASE_URL")
key = os.environ.get("SUPABASE_KEY")

supabase: Optional[Client] = None
if url and key:
supabase = create_client(url, key)

async def store_message(message: Message, user_id: str, conversation_id: str) -> Dict[str, Any]:
"""メッセージをSupabaseに保存する"""
if not supabase:
print("Supabase設定が完了していません。メッセージ保存をスキップします。")
return {"status": "skipped", "message": "Supabase not configured"}

print(f"メッセージを保存: {message}")
return {"status": "success", "message": "Message stored"}

async def store_requirement(requirement: Requirement, user_id: str) -> Dict[str, Any]:
"""要件をSupabaseに保存する"""
if not supabase:
print("Supabase設定が完了していません。要件保存をスキップします。")
return {"status": "skipped", "requirement": requirement.dict()}

print(f"要件を保存: {requirement}")
return {"status": "success", "requirement": requirement.dict()}

async def get_conversation_history(user_id: str, conversation_id: Optional[str] = None) -> List[Message]:
"""会話履歴を取得する"""
if not supabase:
print("Supabase設定が完了していません。空の履歴を返します。")
return []

return []
15 changes: 15 additions & 0 deletions examples/requirements_agent/backend/vercel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"version": 2,
"builds": [
{
"src": "app/main.py",
"use": "@vercel/python"
}
],
"routes": [
{
"src": "/(.*)",
"dest": "app/main.py"
}
]
}
13 changes: 13 additions & 0 deletions examples/requirements_agent/frontend/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8" />
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>要件定義AIエージェント</title>
</head>
<body>
<div id="root"></div>
<script type="module" src="/src/main.tsx"></script>
</body>
</html>
22 changes: 22 additions & 0 deletions examples/requirements_agent/frontend/src/App.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Chat } from './components/Chat';

function App() {
return (
<div className="container">
<header className="header">
<h1>要件定義AIエージェント</h1>
<p>AIを活用した要件収集・分析ツール</p>
</header>

<main className="main">
<Chat />
</main>

<footer className="footer">
&copy; 2023 要件定義AIエージェント
</footer>
</div>
);
}

export default App;
Loading
Loading