genai-services について
OCI Generative AIサービスの実装と運用に関するリファレンススキルです。モデル選定、トークンとコストの管理、レート制限、応答検証を扱い、医療データやコンプライアンス要件を扱う開発者やチームを対象とします。
主な機能
- 01OCIモデル選定の指針
- 02トークンとコストの最適化
- 03レート制限への再試行パターン
- 04PHI/PIIのマスキング指針
- 05応答検証と監査
活用シーン
- 01GenAIサービスのコスト見積もり
- 02トークン制限やレート制限エラーへの対処
- 03医療データのマスキング設計
- 04利用前の生成応答の検証
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ acedergren/ oci-agent-skills --skill genai-servicesインストール後、Claude Code で/genai-servicesを入力して読み込まれたか確認します。
同梱ファイルmetadata.jsonreferences/oci-genai-reference.md
詳細
OCI Generative AI サービス - 専門知識
🏗️ OCI Landing Zone Terraform モジュールを使用する
車輪の再発明はしないでください。 GenAI インフラストラクチャには oracle-terraform-modules/landing-zone を使用してください。
Landing Zone で解決できること:
- ❌ 悪いプラクティス #1:汎用的なコンパートメント(Landing Zone は AI/ML ワークロード用のコンパートメントを作成します)
- ❌ 悪いプラクティス #4:不十分なセグメンテーション(Landing Zone は GenAI エンドポイントをプライベートサブネットに隔離します)
- ❌ 悪いプラクティス #10:監視機能がない(Landing Zone は GenAI の使用状況アラームを設定します)
このスキルが提供するもの:Landing Zone 内にデプロイされた GenAI のコスト最適化、レート制限、PHI/PII セキュリティ、トラブルシューティング。
⚠️ OCI CLI/API に関する知識の不足
OCI CLI コマンドや OCI API の構造については把握していません。
トレーニングデータに含まれる以下の情報は限られており、古くなっている可能性があります。
- OCI CLI の構文とパラメーター(毎月更新されます)
- OCI GenAI API のエンドポイントとリクエスト/レスポンス形式
- GenAI サービスの CLI 操作(
oci generative-ai) - 利用可能なモデル、トークン制限、料金(頻繁に変更されます)
- 最新の GenAI 機能(Agents、RAG)と API の変更
OCI の操作が必要な場合:
- このスキルのリファレンスに記載された正確な CLI コマンドを使用してください
- OCI CLI の構文やパラメーターを推測しないでください
- モデルの提供状況や料金を決めつけないでください
- 詳細な GenAI API ドキュメントについては、リファレンスファイルを読み込んでください
把握していること:
- LLM の一般的な概念とプロンプトのパターン
- トークンの推定とコンテキスト管理
- API 統合のパターン
このスキルは、OCI GenAI 固有の最新パターンや注意点を提供することで、この不足を補います。
あなたは OCI GenAI のエキスパートです。このスキルは、Claude が把握していないコスト最適化の詳細、トークン管理、レート制限への対応、PHI/PII セキュリティ、レスポンスの検証、モデル選択におけるトレードオフに関する知識を提供します。
絶対にしてはいけないこと
❌ GenAI API に PHI/PII 識別子を絶対に送信しないでください(HIPAA/GDPR 違反)
# WRONG - patient identifiers sent to external service
prompt = f"Transcribe note for patient {patient_name}, MRN {mrn}, SSN {ssn}: {note}"
# RIGHT - redact identifiers
prompt = f"Transcribe this medical note: {redacted_note}"
# Keep mapping: temp_id → real_id in secure database, not in prompts
重大な理由:GenAI サービスのログにデータが保持される可能性があり、医療関連の規制に違反します
❌ 検証せずに GenAI の出力を絶対に信頼しないでください(ハルシネーションのリスク)
# WRONG - use response directly in critical systems
diagnosis = genai_response.text
db.execute("UPDATE patients SET diagnosis = ?", diagnosis)
# RIGHT - validate structure and flag for human review
response = genai_response.text
if validate_medical_format(response):
db.execute("UPDATE patients SET ai_suggested_diagnosis = ?, status = 'PENDING_REVIEW'", response)
ハルシネーション率:事実に関する質問では 5~15%、医療や法律の分野ではさらに高くなります
❌ トークン制限を絶対に無視しないでください
- command-r-plus:128k のコンテキストウィンドウ(入力+出力)
- command-r:4k のコンテキスト(大幅に安価ですが制限があります)
- 制限超過時:リクエストが通知なく切り詰められるか、400 エラーで失敗します
❌ レート制限への対応なしに GenAI を絶対に呼び出さないでください
# WRONG - no retry logic, fails on rate limit
response = genai_client.chat(request)
# RIGHT - exponential backoff
def call_with_retry(func, max_retries=5):
for attempt in range(max_retries):
try:
return func()
except oci.exceptions.ServiceError as e:
if e.status == 429 and attempt < max_retries - 1:
wait = (2 ** attempt) + random.uniform(0, 1)
logger.warning(f"Rate limited, retry in {wait:.2f}s")
time.sleep(wait)
else:
raise
❌ 同意なしにレスポンスを絶対にキャッシュしないでください(データプライバシー)
- キャッシュはコストを削減しますが、プライバシーポリシーに違反する可能性があります
- 医療データや個人データをキャッシュする前に、ユーザーの明示的な同意を得てください
- 匿名化されたデータのみをキャッシュしてください
❌ 決定論的なタスクに GenAI を絶対に使用しないでください
- 不適切:「OCR テキストから請求書の合計金額を抽出する」(正規表現/構造化解析を使用してください)
- 不適切:「メールアドレスの形式を検証する」(検証ライブラリを使用してください)
- 適切:「患者の病歴を要約する」「レポートの説明文を生成する」(創造的なタスク)
モデルの選択:コストと性能
| モデル | コンテキスト | コスト(100 万トークンあたり) | 最適な用途 | 避けるべき用途 |
|---|---|---|---|---|
| command-r-plus | 128k | ~$15 入力、$75 出力 | 複雑な推論、長文書 | 単純なタスク(高コスト) |
| command-r | 4k | ~$1.50 入力、$7.50 出力 | チャット、短いプロンプト、大量処理 | 長文書、RAG |
| embed-english-v3 | 該当なし | 100 万あたり約 $0.10 | セマンティック検索、クラスタリング | テキスト生成 |
| llama-2-70b | 4k | ~$2 入力、$10 出力 | オープンウェイト、コスト効率 | 本番環境(サポートが限定的) |
コスト最適化の戦略:
- まず検索には埋め込みを使用する(生成より 1000 倍安価)
- 繰り返しのクエリに対するレスポンスをキャッシュする(同意を得たうえで)
- 単純なタスクには command-r を使用し、必要な場合にのみ command-r-plus を使用する
- 入力を適切に切り詰める(関連するコンテキストのみを残す)
コスト計算の例
シナリオ:医療文字起こしサービス
- 平均的なメモ:入力500トークン、出力300トークン=合計800トークン
- 1日あたり1000件のメモ=1日あたり800kトークン=1か月あたり24Mトークン
最適化なしの場合:
Model: command-r-plus
Input: 12M × ($15/1M) = $180/month
Output: 12M × ($75/1M) = $900/month
Total: $1,080/month
最適化ありの場合(キャッシュヒット率30%、簡単なメモには command-r を使用):
70% unique notes = 16.8M tokens
60% simple (command-r): 10M × $1.50 input + $7.50 output = $90
40% complex (command-r-plus): 6.72M × $15 input + $75 output = $605
Total: $695/month (36% savings)
トークン管理
モデルごとのトークン制限
| モデル | 最大コンテキスト | 最大出力 | 備考 |
|---|---|---|---|
| command-r-plus | 128k(入力+出力) | 変動 | 約4文字で1トークン(概算) |
| command-r | 4k | 2k | チャットに適しています |
| embed-english-v3 | 512 | 該当なし | 埋め込みのみ |
切り捨て戦略
def truncate_for_model(text: str, model: str = "command-r-plus", max_output: int = 2000):
"""Truncate input to fit token budget"""
# Rough estimate: 1 token ≈ 4 characters
if model == "command-r-plus":
max_input_tokens = 128000 - max_output
elif model == "command-r":
max_input_tokens = 4000 - max_output
else:
max_input_tokens = 2000
max_chars = max_input_tokens * 4
if len(text) <= max_chars:
return text
# Keep most recent content (chronological data like logs, notes)
logger.warning(f"Input exceeds {max_input_tokens} tokens, truncating")
return "...[earlier content truncated]...\n" + text[-max_chars:]
プロンプトの最適化
非効率(トークンを浪費):
Please carefully analyze the following medical record and provide a comprehensive
summary including all diagnoses, medications, allergies, and treatment plans. Be
thorough and include all relevant details from the patient's history...
[5000 word medical record]
最適化済み(トークンを40%削減):
Summarize: diagnoses, meds, allergies, treatment plan.
[5000 word medical record]
トークンの節約量:プロンプトあたり約50トークン × 1日あたり1000リクエスト=1日あたり50kトークンを節約=$2.25/日($68/月)
レート制限
OCI GenAI サービスの制限(コンパートメントあたり):
| モデル | 1分あたりのリクエスト数 | 1日あたりのリクエスト数 | リクエストあたりのトークン数 |
|---|---|---|---|
| command-r-plus | 20 | 1000 | 128k |
| command-r | 60 | 3000 | 4k |
| 埋め込み | 100 | 10000 | 512 |
エラー処理
import time
import random
from oci.exceptions import ServiceError
def generate_with_backoff(genai_client, request, max_retries=5):
"""Call GenAI with exponential backoff on rate limits"""
for attempt in range(max_retries):
try:
response = genai_client.chat(request)
return response.data.chat_response.text
except ServiceError as e:
if e.status == 429: # Rate limit
if attempt < max_retries - 1:
# Exponential backoff: 1s, 2s, 4s, 8s, 16s
wait = (2 ** attempt) + random.uniform(0, 1)
logger.warning(f"Rate limited (429), retry {attempt+1}/{max_retries} in {wait:.1f}s")
time.sleep(wait)
else:
logger.error(f"Rate limit exceeded after {max_retries} retries")
raise
elif e.status == 400: # Bad request (often token limit)
logger.error(f"Bad request (400): {e.message}")
if "token" in e.message.lower():
logger.error("Token limit exceeded - truncate input")
raise
else:
logger.error(f"GenAI error ({e.status}): {e.message}")
raise
レスポンスの検証(医療分野では重要)
def validate_medical_response(response: str) -> tuple[bool, list[str]]:
"""Validate GenAI medical response for safety"""
issues = []
# Check 1: Response not empty
if not response or len(response.strip()) < 10:
issues.append("Response too short or empty")
# Check 2: No obvious hallucination markers
hallucination_markers = [
"I don't have access",
"I cannot",
"As an AI",
"[INSERT",
"TODO",
]
for marker in hallucination_markers:
if marker.lower() in response.lower():
issues.append(f"Potential hallucination marker: {marker}")
# Check 3: Expected structure present (customize per use case)
required_sections = ["Chief Complaint", "Assessment", "Plan"]
missing_sections = [s for s in required_sections if s.lower() not in response.lower()]
if missing_sections:
issues.append(f"Missing sections: {missing_sections}")
# Check 4: No PII leak (if input was redacted)
pii_patterns = [
r'\b\d{3}-\d{2}-\d{4}\b', # SSN
r'\b[A-Z]{2}\d{6,8}\b', # MRN patterns
]
for pattern in pii_patterns:
if re.search(pattern, response):
issues.append(f"Potential PII in response: {pattern}")
is_valid = len(issues) == 0
return is_valid, issues
# Usage
response_text = genai_response.data.chat_response.text
is_valid, issues = validate_medical_response(response_text)
if is_valid:
store_for_review(response_text)
else:
logger.warning(f"Invalid response: {issues}")
flag_for_manual_review(response_text, issues)
医療分野固有の考慮事項
HIPAA 準拠
最低限の要件:
- ✅ Oracle との業務提携契約(BAA)
- ✅ GenAI に送信する前に PHI をマスキングする
- ✅ すべての GenAI API 呼び出しを監査ログに記録する
- ✅ 転送中および保存時の暗号化
- ✅ アクセス制御(GenAI を呼び出せるユーザー)
- ✅ データ保持ポリシー(プロンプト/レスポンスの保持期間)
GenAI がデフォルトで HIPAA に準拠していると決して想定しないでください — Oracle に BAA の適用範囲を確認してください
匿名化戦略
def redact_phi(text: str) -> tuple[str, dict]:
"""Remove PHI from text, return redacted text + mapping"""
mapping = {}
redacted = text
# Patient names (use NER or pattern matching)
names = extract_names(text) # Your NER function
for i, name in enumerate(names):
placeholder = f"[PATIENT_{i}]"
mapping[placeholder] = name
redacted = redacted.replace(name, placeholder)
# Medical Record Numbers
mrn_pattern = r'\b(MRN|Medical Record):?\s*([A-Z0-9]{6,10})\b'
redacted = re.sub(mrn_pattern, r'\1: [REDACTED]', redacted)
# SSN
ssn_pattern = r'\b\d{3}-\d{2}-\d{4}\b'
redacted = re.sub(ssn_pattern, '[SSN_REDACTED]', redacted)
# Dates (optional - some use cases need dates)
# date_pattern = r'\b\d{1,2}/\d{1,2}/\d{4}\b'
# redacted = re.sub(date_pattern, '[DATE]', redacted)
return redacted, mapping
# Usage
redacted_note, phi_mapping = redact_phi(patient_note)
genai_response = genai_client.chat(prompt=f"Summarize: {redacted_note}")
# Store phi_mapping securely, use to re-identify if needed
段階的な読み込みに関するリファレンス
OCI Generative AI のリファレンス(Oracle 公式ドキュメント)
次の場合に読み込みます oci-genai-reference.md:
- GenAI API の包括的なドキュメントが必要な場合
- 利用可能なすべてのモデルと機能を把握する場合
- OCI を使用して RAG(検索拡張生成)を実装する場合
- GenAI Agents に関する Oracle 公式のガイダンスが必要な場合
- ファインチューニングとカスタムモデルのデプロイについて理解する場合
次の場合は読み込まないでください:
- API の簡単な使用例を確認する場合(このスキルで説明しています)
- モデル選択のガイダンスを確認する場合(上記の判断フローを参照)
- コストを計算する場合(上記の計算式を参照)
このスキルを使用する場面
- GenAI API の実装:モデル選択、コスト見積もり、SDK の使用
- エラーのトラブルシューティング:レート制限(429)、トークン制限(400)、認証
- コストの最適化:キャッシュ戦略、モデルのダウングレード、プロンプトの最適化
- 医療/コンプライアンス:PHI の取り扱い、HIPAA の要件、監査ログ
- レスポンスの検証:幻覚の検出、構造のチェック
- 本番環境:レート制限への対応、エラーからの復旧、モニタリング