1. 建築哲学
エンタープライズAIチャットボットプラットフォームは、単に「OpenAI APIを呼び出して応答を返す」だけではありません。それは一つです 分散システム リアルタイムの調整を必要とする多くのコンポーネントを含む複雑なシステム。 3 つのアーキテクチャ原則:
- モジュール性 — 各機能は独立した境界付きコンテキストであり、個別に置き換え/アップグレードできます。
- イベント駆動型 — サービスはイベントを介して通信し、結合を軽減し、監査証跡をサポートします
- AIファースト — AI ワークロードのアーキテクチャの最適化: ストリーミング、長時間実行推論、GPU 対応スケーリング
2. 境界付きコンテキスト — AI チャットボットの DDD
ドメイン駆動設計を適用してプラットフォームを分割します。 8 つの境界付きコンテキスト:
┌───────────────────────────────────────────────────────────────────┐
│ AI CHATBOT PLATFORM │
├───────────────────────────────────────────────────────────────────┤
│ │
│ ┌──────────────┐ ┌──────────────┐ ┌──────────────┐ │
│ │ CONVERSATION │ │ KNOWLEDGE │ │ AGENT │ │
│ │ CONTEXT │ │ CONTEXT │ │ CONTEXT │ │
│ │ │ │ │ │ │ │
│ │ • Session │ │ • Documents │ │ • Tools │ │
│ │ • Messages │ │ • Embeddings │ │ • Functions │ │
│ │ • Memory │ │ • Search │ │ • Workflows │ │
│ │ • Context │ │ • Sync │ │ • Planning │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────┴───────┐ ┌──────┴───────┐ ┌──────┴───────┐ │
│ │ CHANNEL │ │ AI ENGINE │ │ GUARDRAIL │ │
│ │ CONTEXT │ │ CONTEXT │ │ CONTEXT │ │
│ │ │ │ │ │ │ │
│ │ • Web Widget │ │ • LLM Router │ │ • Input Gate │ │
│ │ • Slack Bot │ │ • Streaming │ │ • Output Gate│ │
│ │ • WhatsApp │ │ • Prompt Eng │ │ • PII Mask │ │
│ │ • Mobile SDK │ │ • Model Mgmt │ │ • Toxicity │ │
│ └──────┬───────┘ └──────┬───────┘ └──────┬───────┘ │
│ │ │ │ │
│ ┌──────┴───────┐ ┌──────┴───────┐ │
│ │ ANALYTICS │ │ BILLING │ │
│ │ CONTEXT │ │ CONTEXT │ │
│ │ │ │ │ │
│ │ • Metrics │ │ • Usage │ │
│ │ • Tracing │ │ • Plans │ │
│ │ • Feedback │ │ • Invoicing │ │
│ │ • Evals │ │ • Quotas │ │
│ └──────────────┘ └──────────────┘ │
│ │
└───────────────────────────────────────────────────────────────────┘
3. コンテキスト マップ — サービス インタラクション
// Domain Events flowing between bounded contexts
type DomainEvent =
| { type: 'conversation.started'; payload: { sessionId: string; channelType: string; tenantId: string } }
| { type: 'message.received'; payload: { sessionId: string; content: string; role: 'user' | 'assistant' } }
| { type: 'knowledge.searched'; payload: { query: string; results: number; latencyMs: number } }
| { type: 'tool.invoked'; payload: { toolName: string; params: Record<string, unknown>; success: boolean } }
| { type: 'agent.planned'; payload: { plan: string[]; model: string } }
| { type: 'guardrail.triggered'; payload: { type: string; severity: 'low' | 'medium' | 'high' | 'critical' } }
| { type: 'response.generated'; payload: { sessionId: string; tokensUsed: number; latencyMs: number } }
| { type: 'human.escalated'; payload: { sessionId: string; reason: string } }
| { type: 'feedback.submitted'; payload: { messageId: string; rating: 'positive' | 'negative'; comment?: string } };
4. 高レベルのアーキテクチャ — C4 レベル 1 (システム コンテキスト)
┌─────────────┐
│ End User │
└──────┬──────┘
│
┌────────────┼────────────┐
│ │ │
┌─────▼────┐ ┌────▼─────┐ ┌───▼──────┐
│ Web Chat │ │ Slack │ │ WhatsApp │
│ Widget │ │ Bot │ │ Bot │
└─────┬────┘ └────┬─────┘ └───┬──────┘
│ │ │
└────────────┼────────────┘
│
┌───────▼───────┐
│ API Gateway │
│ (Kong/Nginx) │
└───────┬───────┘
│
┌────────────┼────────────┐
│ │
┌────────▼─────────┐ ┌─────────▼──────────┐
│ Chatbot Platform │ │ Admin Dashboard │
│ (Core API) │ │ (Management UI) │
└────────┬─────────┘ └─────────┬──────────┘
│ │
┌─────────┼──────────┐ │
│ │ │ │
┌───▼──┐ ┌───▼──┐ ┌────▼───┐ ┌─────▼─────┐
│OpenAI│ │Claude│ │ Self- │ │PostgreSQL │
│ API │ │ API │ │ Hosted │ │ Qdrant │
│ │ │ │ │ (vLLM) │ │ Redis │
└──────┘ └──────┘ └────────┘ └───────────┘
5. C4 レベル 2 — コンテナ図
┌─────────────────────────────────────────────────────────────────────┐
│ CHATBOT PLATFORM │
├─────────────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ Channel Gateway │───▶│ Conversation Svc │───▶│ AI Engine Svc │ │
│ │ (NestJS) │ │ (NestJS) │ │ (Python/FastAPI│ │
│ │ │ │ │ │ + TypeScript) │ │
│ │ • WebSocket │ │ • Session mgmt │ │ • LLM Router │ │
│ │ • REST API │ │ • Context build │ │ • RAG Pipeline│ │
│ │ • Webhook recv │ │ • Memory mgmt │ │ • Prompt Eng │ │
│ └─────────────────┘ └──────────────────┘ │ • Streaming │ │
│ └────────────────┘ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ Agent Service │ │ Knowledge Service│ │ Guardrail Svc │ │
│ │ (Python) │ │ (Python) │ │ (Python) │ │
│ │ │ │ │ │ │ │
│ │ • Tool Registry │ │ • Doc Ingestion │ │ • Input check │ │
│ │ • Executor │ │ • Embedding │ │ • Output check│ │
│ │ • Planner │ │ • Search │ │ • PII masking │ │
│ └─────────────────┘ └──────────────────┘ └────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ ┌────────────────┐ │
│ │ Analytics Svc │ │ Billing Service │ │ Admin API │ │
│ │ (Python) │ │ (NestJS) │ │ (NestJS) │ │
│ │ │ │ │ │ │ │
│ │ • Tracing │ │ • Usage meter │ │ • Tenant mgmt │ │
│ │ • Metrics │ │ • Subscription │ │ • Config │ │
│ │ • Evals │ │ • Invoicing │ │ • Prompt mgmt │ │
│ └─────────────────┘ └──────────────────┘ └────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────────┘
6. イベント駆動型アーキテクチャ
AI チャットボットになぜイベント駆動型なのか?
- 監査証跡 — すべての会話イベントがログに記録され、コンプライアンスにとって重要です
- 非同期処理 — ナレッジの取り込み、分析、請求は非同期で実行されます
- デカップリング — AI エンジンは請求について知る必要はありません。トークン使用状況イベントのみを発行する
- リプレイ — イベントを再生して会話フローをデバッグし、モデルを再トレーニングします
// Event Bus implementation với Kafka
import { Kafka, Producer, Consumer } from 'kafkajs';
interface EventBus {
publish(topic: string, event: DomainEvent): Promise<void>;
subscribe(topic: string, handler: (event: DomainEvent) => Promise<void>): Promise<void>;
}
class KafkaEventBus implements EventBus {
private producer: Producer;
private consumers: Map<string, Consumer> = new Map();
constructor(private kafka: Kafka) {
this.producer = kafka.producer();
}
async publish(topic: string, event: DomainEvent): Promise<void> {
await this.producer.send({
topic,
messages: [{
key: event.payload.sessionId ?? crypto.randomUUID(),
value: JSON.stringify({
...event,
timestamp: new Date().toISOString(),
eventId: crypto.randomUUID(),
}),
headers: {
'event-type': event.type,
'tenant-id': event.payload.tenantId ?? 'system',
},
}],
});
}
async subscribe(
topic: string,
handler: (event: DomainEvent) => Promise<void>,
): Promise<void> {
const consumer = this.kafka.consumer({ groupId: `${topic}-consumer` });
await consumer.connect();
await consumer.subscribe({ topic, fromBeginning: false });
await consumer.run({
eachMessage: async ({ message }) => {
const event = JSON.parse(message.value!.toString()) as DomainEvent;
await handler(event);
},
});
this.consumers.set(topic, consumer);
}
}
7. リクエスト フロー — ユーザー メッセージから応答まで
User Message
│
▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Channel │────▶│ Input │────▶│Conversa- │
│ Gateway │ │ Guardrail│ │tion Svc │
└──────────┘ └──────────┘ └────┬─────┘
│
┌──────────────────┤
│ │
▼ ▼
┌──────────┐ ┌──────────┐
│ Memory │ │ Knowledge│
│ Retrieve │ │ Search │
└────┬─────┘ └────┬─────┘
│ │
└────────┬─────────┘
│
▼
┌──────────┐
│ Prompt │
│ Assembly │
└────┬─────┘
│
▼
┌──────────┐
│ AI Engine│──── Tool Calls? ───▶ Agent Svc
│ (LLM) │◀──── Results ──────┘
└────┬─────┘
│
▼
┌──────────┐ ┌──────────┐
│ Output │────▶│ Channel │──▶ User
│ Guardrail│ │ Delivery │
└──────────┘ └──────────┘
│
▼
┌──────────────┐
│ Event Bus │
│ (Analytics, │
│ Billing, │
│ Logging) │
└──────────────┘
8. コアデータモデル
// Core domain entities
interface Tenant {
id: string;
name: string;
plan: 'free' | 'pro' | 'enterprise';
config: TenantConfig;
createdAt: Date;
}
interface TenantConfig {
defaultModel: string; // e.g., 'gpt-4o'
fallbackModels: string[]; // e.g., ['claude-3-sonnet', 'gpt-4o-mini']
maxTokensPerRequest: number; // e.g., 4096
maxConversationsPerDay: number; // e.g., 10000
enabledChannels: ChannelType[]; // e.g., ['web', 'slack']
guardrailConfig: GuardrailConfig;
ragConfig: RAGConfig;
}
interface Conversation {
id: string;
tenantId: string;
channelType: ChannelType;
channelUserId: string;
status: 'active' | 'closed' | 'escalated';
metadata: Record<string, unknown>;
startedAt: Date;
lastMessageAt: Date;
}
interface Message {
id: string;
conversationId: string;
role: 'user' | 'assistant' | 'system' | 'tool';
content: string;
toolCalls?: ToolCall[];
metadata: {
model?: string;
tokensUsed?: { input: number; output: number };
latencyMs?: number;
sources?: Citation[];
};
createdAt: Date;
}
interface Citation {
documentId: string;
chunkId: string;
content: string;
score: number;
metadata: Record<string, unknown>;
}
type ChannelType = 'web' | 'slack' | 'teams' | 'whatsapp' | 'discord' | 'email' | 'api';
9. 導入トポロジ
┌─────────────────────────── Kubernetes Cluster ──────────────────────┐
│ │
│ ┌─── Namespace: chatbot-platform ──────────────────────────────┐ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Channel │ │Conversa- │ │AI Engine │ │Agent Svc │ │ │
│ │ │Gateway │ │tion Svc │ │(2 replicas│ │ │ │ │
│ │ │(3 rep.) │ │(2 rep.) │ │+ GPU pod)│ │(2 rep.) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ │ │ │
│ │ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ │ │
│ │ │Knowledge │ │Guardrail │ │Analytics │ │Billing │ │ │
│ │ │ Svc │ │ Svc │ │ Svc │ │ Svc │ │ │
│ │ │(2 rep.) │ │(2 rep.) │ │(1 rep.) │ │(1 rep.) │ │ │
│ │ └──────────┘ └──────────┘ └──────────┘ └──────────┘ │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── Namespace: data ──────────────────────────────────────────┐ │
│ │ PostgreSQL (HA) │ Qdrant │ Redis Cluster │ Kafka │ MinIO │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌─── Namespace: monitoring ────────────────────────────────────┐ │
│ │ Langfuse │ Prometheus │ Grafana │ AlertManager │ Loki │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
└──────────────────────────────────────────────────────────────────────┘
10. API 設計原則
// REST API structure
// POST /api/v1/conversations — Start conversation
// POST /api/v1/conversations/:id/messages — Send message (returns stream)
// GET /api/v1/conversations/:id/messages — Get message history
// POST /api/v1/conversations/:id/feedback — Submit feedback
// POST /api/v1/conversations/:id/escalate — Escalate to human
// GET /api/v1/conversations/:id — Get conversation details
// WebSocket for real-time
// ws://api/v1/ws?token=xxx
// Admin API
// POST /api/v1/admin/knowledge-bases — Create knowledge base
// POST /api/v1/admin/knowledge-bases/:id/documents — Upload document
// GET /api/v1/admin/analytics/conversations — Analytics dashboard
// PUT /api/v1/admin/prompts/:id — Update prompt template
// POST /api/v1/admin/tools — Register tool
レッスン 2 のまとめ
- 8 つの境界付きコンテキスト: 会話、ナレッジ、エージェント、チャネル、AI エンジン、ガードレール、分析、請求
- イベント駆動型 監査証跡 + 非同期処理 + デカップリングのための Kafka を使用したアーキテクチャ
- リクエストのフロースルー 7段階: チャネル → 入力ガード → コンテキスト ビルド → RAG + メモリ → プロンプト → LLM → 出力ガード
- それぞれのサービスは、 独立して展開可能 Kubernetes 上で
- コアエンティティ: テナント → 会話 → メッセージ → 引用
次の記事: マルチモデル ゲートウェイ — OpenAI/Claude/Gemini/セルフホスト モデル間でリクエストをルーティングする方法、フォールバック チェーン、コストの最適化、トークンの予算管理。