はじめに
オラクルカード占いとAI技術の融合により、従来の占い体験を大きく進化させることができます。本記事では、OpenAIのFunction Calling機能を活用して、インテリジェントなオラクルカード占いシステムを開発する方法を解説します。
実装例はこちらでご覧いただけます。
オラクルカードとは?
オラクルカードは、タロットカードとは異なり、より直感的で自由度の高い占いツールです。固定された78枚の構造を持つタロットに対し、オラクルカードは枚数やテーマが自由で、日常的な瞑想や内なる知恵の啓発に適しています。
なぜFunction Callingが必要か?
従来のチャットボットでは、以下の課題がありました:
# 従来の方法:出力が不安定
response = openai.ChatCompletion.create(
model="gpt-3.5-turbo",
messages=[
{"role": "user", "content": "恋愛について占ってください"}
]
)
# 結果:構造化されていない自由形式のテキスト
Function Callingを使用することで:
- カード選択のタイミングを正確に制御
- 構造化されたデータの取得
- ユーザーの意図に応じた動的な処理
システムアーキテクチャ
実装詳細
1. Function定義
from openai import OpenAI
from typing import List, Dict
import json
client = OpenAI(api_key="your-api-key")
# オラクルカード関連のFunction定義
oracle_tools = [
{
"type": "function",
"function": {
"name": "drawOracleCard",
"description": "ユーザーが導きや占いを求めた時にオラクルカードを引く",
"parameters": {
"type": "object",
"properties": {
"question": {
"type": "string",
"description": "ユーザーの質問や悩み"
},
"cardCount": {
"type": "integer",
"description": "引くカードの枚数(デフォルト: 1)",
"default": 1
}
},
"required": ["question"]
}
}
},
{
"type": "function",
"function": {
"name": "createPersonalOracleCard",
"description": "ユーザーのために個人的なオラクルカードを作成",
"parameters": {
"type": "object",
"properties": {
"cardName": {
"type": "string",
"description": "カードの名前"
},
"cardMeaning": {
"type": "string",
"description": "カードの意味とメッセージ"
},
"visualDescription": {
"type": "string",
"description": "カードの視覚的描写(画像生成用)"
},
"keywords": {
"type": "array",
"items": {"type": "string"},
"description": "カードに関連するキーワード"
}
},
"required": ["cardName", "cardMeaning", "visualDescription"]
}
}
}
]
2. オラクルカードデータベース
# オラクルカードのデータ構造
oracle_cards_jp = [
{
"id": 1,
"name": "新しい始まり",
"message": "新たな冒険があなたを待っています。過去を手放し、新鮮な視点で前進する時です。",
"keywords": ["新スタート", "チャンス", "勇気", "解放"],
"element": "風"
},
{
"id": 2,
"name": "内なる知恵",
"message": "答えはすでにあなたの中にあります。静寂の中で内なる声に耳を傾けましょう。",
"keywords": ["直感", "瞑想", "自己信頼", "洞察"],
"element": "水"
},
{
"id": 3,
"name": "豊かさの流れ",
"message": "宇宙の豊かさがあなたに向かって流れています。受け取る準備をしましょう。",
"keywords": ["豊穣", "感謝", "受容", "繁栄"],
"element": "土"
},
# ... 他のカードデータ
]
def get_oracle_card_by_energy(question: str, cards: List[Dict]) -> Dict:
"""
質問のエネルギーに基づいてカードを選択
実際の実装では、埋め込みベクトルなどを使用してより高度なマッチングが可能
"""
import random
# 質問のキーワード抽出(簡易版)
love_keywords = ["恋愛", "愛", "パートナー", "結婚", "関係"]
work_keywords = ["仕事", "キャリア", "転職", "成功", "目標"]
spiritual_keywords = ["成長", "魂", "使命", "目的", "スピリチュアル"]
# キーワードに基づいてカードをフィルタリング
filtered_cards = cards
if any(keyword in question for keyword in love_keywords):
filtered_cards = [c for c in cards if any(k in ["愛", "関係", "調和"] for k in c.get("keywords", []))]
elif any(keyword in question for keyword in work_keywords):
filtered_cards = [c for c in cards if any(k in ["成功", "行動", "目標"] for k in c.get("keywords", []))]
# フィルタリングされたカードから選択、なければ全体から
return random.choice(filtered_cards if filtered_cards else cards)
3. メインの処理フロー
async def process_oracle_reading(user_message: str, conversation_history: List[Dict]) -> Dict:
"""
オラクルカード占いのメイン処理
"""
try:
# OpenAI APIにリクエスト
response = await client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[
{
"role": "system",
"content": """あなたは経験豊富なオラクルカードリーダーです。
ユーザーの質問や悩みに対して、適切なタイミングでカードを引き、
深い洞察と実践的なアドバイスを提供します。
カードの解釈は、ユーザーの具体的な状況に合わせてパーソナライズしてください。"""
},
*conversation_history,
{"role": "user", "content": user_message}
],
tools=oracle_tools,
tool_choice="auto"
)
message = response.choices[0].message
# Function Callの処理
if message.tool_calls:
tool_responses = []
for tool_call in message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
if function_name == "drawOracleCard":
# カードを引く
drawn_card = await draw_oracle_card(
question=function_args.get("question"),
count=function_args.get("cardCount", 1)
)
tool_responses.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps(drawn_card, ensure_ascii=False)
})
elif function_name == "createPersonalOracleCard":
# パーソナルカードを作成
personal_card = await create_personal_card(
card_name=function_args.get("cardName"),
card_meaning=function_args.get("cardMeaning"),
visual_description=function_args.get("visualDescription"),
keywords=function_args.get("keywords", [])
)
tool_responses.append({
"tool_call_id": tool_call.id,
"role": "tool",
"content": json.dumps(personal_card, ensure_ascii=False)
})
# ツールの結果を含めて再度APIを呼び出し
final_response = await client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[
*conversation_history,
{"role": "user", "content": user_message},
message,
*tool_responses
],
temperature=0.7
)
return {
"message": final_response.choices[0].message.content,
"tools_used": [tc.function.name for tc in message.tool_calls]
}
return {
"message": message.content,
"tools_used": []
}
except Exception as e:
print(f"Error in oracle reading: {e}")
raise
4. カード引きの実装
async def draw_oracle_card(question: str, count: int = 1) -> Dict:
"""
オラクルカードを引く処理
"""
drawn_cards = []
for _ in range(count):
# エネルギーマッチングによるカード選択
card = get_oracle_card_by_energy(question, oracle_cards_jp)
# カードの向き(正位置/逆位置)を決定
is_upright = random.random() > 0.3 # 70%の確率で正位置
drawn_cards.append({
"card": card,
"position": "正位置" if is_upright else "逆位置",
"interpretation_hint": get_interpretation_hint(card, is_upright, question)
})
return {
"cards": drawn_cards,
"spread_type": get_spread_type(count),
"timestamp": datetime.now().isoformat()
}
def get_interpretation_hint(card: Dict, is_upright: bool, question: str) -> str:
"""
カードの解釈のヒントを生成
"""
if not is_upright:
return f"{card['name']}の逆位置は、{card['message']}の裏面を示唆しています。障害や遅延、内面的な課題に注目してください。"
return f"{card['message']} 特に「{question}」という質問に対して、{card['keywords'][0]}の視点から考えてみましょう。"
5. パーソナルカード作成
async def create_personal_card(
card_name: str,
card_meaning: str,
visual_description: str,
keywords: List[str]
) -> Dict:
"""
AIを使用してパーソナルなオラクルカードを作成
"""
try:
# DALL-E 3で画像生成
image_response = await client.images.generate(
model="dall-e-3",
prompt=f"""
オラクルカードのイラストを作成してください:
タイトル: {card_name}
スタイル: 神秘的で幻想的な水彩画、金色のアクセント、神聖幾何学模様
要素: {visual_description}
雰囲気: 瞑想的、スピリチュアル、希望に満ちた
""",
size="1024x1024",
quality="standard",
n=1
)
image_url = image_response.data[0].url
# カードデータを構造化
personal_card = {
"id": f"personal_{int(datetime.now().timestamp())}",
"name": card_name,
"message": card_meaning,
"keywords": keywords,
"image_url": image_url,
"created_at": datetime.now().isoformat(),
"type": "personal"
}
# データベースに保存(実装は省略)
# await save_personal_card(personal_card)
return {
"success": True,
"card": personal_card,
"message": f"「{card_name}」のパーソナルカードを作成しました。"
}
except Exception as e:
return {
"success": False,
"error": str(e),
"message": "カードの作成中にエラーが発生しました。"
}
6. ストリーミング対応
async def stream_oracle_reading(user_message: str, conversation_history: List[Dict]):
"""
ストリーミングレスポンス対応
"""
stream = await client.chat.completions.create(
model="gpt-4-turbo-preview",
messages=[
{"role": "system", "content": "オラクルカードリーダーのシステムプロンプト"},
*conversation_history,
{"role": "user", "content": user_message}
],
tools=oracle_tools,
tool_choice="auto",
stream=True
)
collected_messages = []
async for chunk in stream:
delta = chunk.choices[0].delta
if delta.content:
yield {"type": "content", "data": delta.content}
if delta.tool_calls:
for tool_call in delta.tool_calls:
if tool_call.function.name == "drawOracleCard":
# カード引きの準備を通知
yield {
"type": "tool_preparing",
"data": {"tool": "drawOracleCard", "status": "preparing"}
}
実装時の注意点
1. エラーハンドリング
class OracleReadingError(Exception):
"""オラクルリーディング専用のエラークラス"""
pass
def validate_card_request(question: str) -> bool:
"""カードリクエストの検証"""
if not question or len(question.strip()) < 3:
raise OracleReadingError("質問が短すぎます。もう少し詳しく教えてください。")
if len(question) > 500:
raise OracleReadingError("質問が長すぎます。要点を絞って質問してください。")
return True
2. レート制限とキャッシング
from functools import lru_cache
import asyncio
class RateLimiter:
def __init__(self, max_requests: int, time_window: int):
self.max_requests = max_requests
self.time_window = time_window
self.requests = []
async def check_rate_limit(self, user_id: str) -> bool:
current_time = time.time()
self.requests = [
(uid, t) for uid, t in self.requests
if current_time - t < self.time_window
]
user_requests = sum(1 for uid, _ in self.requests if uid == user_id)
if user_requests >= self.max_requests:
return False
self.requests.append((user_id, current_time))
return True
# 使用例
rate_limiter = RateLimiter(max_requests=10, time_window=3600) # 1時間に10回
3. 多言語対応
translations = {
"ja": {
"welcome": "オラクルカードへようこそ",
"draw_card": "カードを引く",
"your_card": "あなたのカード",
"interpretation": "解釈"
},
"en": {
"welcome": "Welcome to Oracle Cards",
"draw_card": "Draw a Card",
"your_card": "Your Card",
"interpretation": "Interpretation"
}
}
def get_localized_cards(locale: str = "ja") -> List[Dict]:
"""ロケールに応じたカードデータを取得"""
card_files = {
"ja": "oracle_cards_jp.json",
"en": "oracle_cards_en.json",
"es": "oracle_cards_es.json"
}
file_path = card_files.get(locale, card_files["ja"])
with open(file_path, "r", encoding="utf-8") as f:
return json.load(f)
パフォーマンス最適化
1. 非同期処理
async def batch_process_readings(requests: List[Dict]) -> List[Dict]:
"""複数のリーディングリクエストを並列処理"""
tasks = []
for request in requests:
task = process_oracle_reading(
request["message"],
request["history"]
)
tasks.append(task)
results = await asyncio.gather(*tasks, return_exceptions=True)
return [
result if not isinstance(result, Exception) else {"error": str(result)}
for result in results
]
2. カードデータのインデックス化
class OracleCardIndex:
def __init__(self, cards: List[Dict]):
self.cards = cards
self.keyword_index = self._build_keyword_index()
self.element_index = self._build_element_index()
def _build_keyword_index(self) -> Dict[str, List[int]]:
"""キーワードによるインデックスを構築"""
index = {}
for i, card in enumerate(self.cards):
for keyword in card.get("keywords", []):
if keyword not in index:
index[keyword] = []
index[keyword].append(i)
return index
def search_by_keyword(self, keyword: str) -> List[Dict]:
"""キーワードでカードを検索"""
indices = self.keyword_index.get(keyword, [])
return [self.cards[i] for i in indices]
まとめ
OpenAIのFunction Callingを活用することで、従来の静的なオラクルカード占いを、AIによる動的で知的なシステムへと進化させることができました。
主な利点:
- ユーザーの意図を正確に理解し、適切なタイミングでカードを引く
- 個人の状況に合わせたパーソナライズされた解釈
- AIによるオリジナルカードの作成機能
- 構造化されたデータ処理による安定した動作
このシステムは、スピリチュアルな体験とテクノロジーを融合させた新しい形のサービスとして、実際に稼働しています。
今後の展望
- 音声インターフェース: Whisper APIを使用した音声入力対応
- AR体験: カードを3D空間に表示するAR機能
- コミュニティ機能: ユーザー作成カードの共有プラットフォーム
- AI解釈の進化: より深い心理学的洞察を提供するファインチューニング
オラクルカード占いとAI技術の融合は、まだ始まったばかりです。Function Callingという強力なツールを使いこなすことで、より豊かなユーザー体験を創造できる可能性が広がっています。
参考リンク
タグ: #OpenAI #FunctionCalling #オラクルカード #AI占い #Python #GPT4