前言
随着AI应用的普及,对话系统从简单的单机脚本演进为复杂的分布式架构。本文介绍如何设计一个可扩展、高可用的AI对话系统。
一、架构演进
1.1 单机架构
适合日活1000以下的场景,所有组件运行在一台服务器上:
# 单机架构
Nginx (反向代理+静态文件) -> FastAPI应用 (API+业务逻辑) + SQLite/Redis + OpenAI API
# docker-compose.yml
version: '3.8'
services:
app:
build: .
ports: ["3000:3000"]
environment:
- DATABASE_URL=sqlite:///./db.sqlite
- REDIS_URL=redis://redis:6379
- OPENAI_API_KEY=sk-xxx
redis:
image: redis:7-alpine
1.2 微服务架构
当用户量增长到日活1万+,需要拆分服务:
# 微服务架构
网关(Kong) -> 消息队列(Kafka) -> 对话服务集群(FastAPI x3) + Redis + PostgreSQL
# 核心服务拆分
1. 网关服务 - 路由、限流、认证
2. 会话服务 - 管理对话上下文
3. 推理服务 - 调用LLM生成回复
4. 存储服务 - 持久化对话记录
5. 监控服务 - 日志、指标、告警
二、核心组件设计
2.1 会话管理服务
import redis
import json
from datetime import datetime
class SessionManager:
def __init__(self):
self.redis = redis.Redis(host='redis', port=6379, decode_responses=True)
self.ttl = 3600 # 1小时过期
def create_session(self, user_id: str) -> str:
import uuid
session_id = str(uuid.uuid4())
self.redis.hset(f"session:{session_id}", mapping={
"user_id": user_id,
"created_at": datetime.now().isoformat(),
"message_count": 0
})
self.redis.expire(f"session:{session_id}", self.ttl)
return session_id
def add_message(self, session_id: str, role: str, content: str):
key = f"messages:{session_id}"
msg = json.dumps({"role": role, "content": content, "ts": datetime.now().isoformat()})
self.redis.rpush(key, msg)
self.redis.expire(key, self.ttl)
self.redis.hincrby(f"session:{session_id}", "message_count", 1)
def get_history(self, session_id: str, max_turns: int = 10) -> list:
key = f"messages:{session_id}"
messages = self.redis.lrange(key, 0, -1)
parsed = [json.loads(m) for m in messages]
return parsed[-max_turns*2:] if len(parsed) > max_turns*2 else parsed
2.2 负载均衡策略
# nginx.conf - 对话服务负载均衡
upstream chat_servers {
least_conn; # 最少连接策略
server chat-1:8000 weight=3;
server chat-2:8000 weight=2;
server chat-3:8000 weight=1;
keepalive 32;
}
server {
listen 80;
location /api/chat {
proxy_pass http://chat_servers;
proxy_read_timeout 300; # LLM响应可能较慢
}
}
三、容错与降级
容错策略:
- 熔断器:当LLM API连续失败时自动熔断,返回降级响应
- 重试机制:指数退避重试,最多3次
- 降级响应:API不可用时返回缓存的常见问题答案
- 限流:每用户每分钟最多50条消息
import time
from functools import wraps
class CircuitBreaker:
def __init__(self, failure_threshold=5, reset_timeout=60):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.reset_timeout = reset_timeout
self.last_failure_time = None
self.state = "closed"
def __call__(self, func):
@wraps(func)
def wrapper(*args, **kwargs):
if self.state == "open":
if time.time() - self.last_failure_time > self.reset_timeout:
self.state = "half-open"
else:
return self.fallback(*args, **kwargs)
try:
result = func(*args, **kwargs)
if self.state == "half-open":
self.state = "closed"
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.state = "open"
return self.fallback(*args, **kwargs)
return wrapper
def fallback(self, *args, **kwargs):
return "抱歉,系统暂时繁忙,请稍后再试。"
四、Docker Compose部署
version: '3.8'
services:
gateway:
image: kong:3.6
ports: ["8000:8000"]
depends_on: [chat-1, chat-2]
chat-1:
build: ./chat-service
environment:
- REDIS_URL=redis://redis:6379
- KAFKA_BROKERS=kafka:9092
deploy:
replicas: 2
redis:
image: redis:7-alpine
volumes: [redisdata:/data]
kafka:
image: confluentinc/cp-kafka:7.6.0
environment:
KAFKA_ADVERTISED_LISTENERS: PLAINTEXT://kafka:9092
volumes:
redisdata:
总结
AI对话系统的架构设计需要考虑可扩展性、高可用性和容错能力。从单机到微服务的演进是渐进的过程,根据实际用户量选择合适的架构。
如果需要专业的AI系统架构设计服务,欢迎联系17老师。
关于17老师:AI应用 · 数字化管理 · 全栈开发 · 网络安全全栈专家。联系邮箱:j.d88888888@qq.com,微信:AFIST17