415 lines
15 KiB
Python
415 lines
15 KiB
Python
"""
|
||
AI对话系统 v2.0.0 - 数据库模型重构
|
||
支持:大模型池、Agent管理、渠道独立绑定、思考功能开关
|
||
"""
|
||
from sqlalchemy import create_engine, Column, Integer, String, Text, Boolean, DateTime, ForeignKey, JSON, Float
|
||
from sqlalchemy.ext.declarative import declarative_base
|
||
from sqlalchemy.orm import sessionmaker, relationship
|
||
from datetime import datetime
|
||
import os
|
||
|
||
DATABASE_URL = os.environ.get('DATABASE_URL', 'sqlite:///./ai_chat_v2.db')
|
||
|
||
engine = create_engine(DATABASE_URL, connect_args={"check_same_thread": False})
|
||
SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
|
||
Base = declarative_base()
|
||
|
||
|
||
# ==================== 大模型池 ====================
|
||
|
||
class LLMProvider(Base):
|
||
"""大模型提供商/池配置"""
|
||
__tablename__ = 'llm_providers'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
name = Column(String(100), unique=True, index=True) # 名称标识,如 "DeepSeek", "本地LLM"
|
||
api_base = Column(String(500)) # API地址
|
||
api_key = Column(String(500)) # API密钥
|
||
models = Column(JSON, default=list) # 可用模型列表 [{"id": "xxx", "name": "xxx"}]
|
||
default_model = Column(String(100)) # 默认模型
|
||
|
||
# 思考功能支持
|
||
supports_thinking = Column(Boolean, default=False) # 是否原生支持思考
|
||
thinking_model = Column(String(100), nullable=True) # 思考模式模型名(如有单独模型)
|
||
|
||
# 视觉能力支持
|
||
supports_vision = Column(Boolean, default=False) # 是否支持图片理解(多模态)
|
||
vision_model = Column(String(100), nullable=True) # 视觉模型名(如与默认模型不同)
|
||
|
||
# 配额和限制
|
||
max_tokens = Column(Integer, default=4096)
|
||
temperature = Column(Float, default=0.7)
|
||
|
||
# 状态
|
||
is_active = Column(Boolean, default=True)
|
||
priority = Column(Integer, default=0) # 优先级,用于自动选择
|
||
description = Column(Text, nullable=True)
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
# 关联
|
||
agents = relationship("Agent", back_populates="llm_provider")
|
||
|
||
|
||
# ==================== Agent ====================
|
||
|
||
class Agent(Base):
|
||
"""智能体配置"""
|
||
__tablename__ = 'agents'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
name = Column(String(100), unique=True, index=True) # Agent名称
|
||
display_name = Column(String(100)) # 显示名称
|
||
|
||
# 工具配置
|
||
tools = Column(JSON, default=list) # 可用工具列表 ["search", "calculator", ...]
|
||
|
||
# 大模型配置
|
||
llm_provider_id = Column(Integer, ForeignKey('llm_providers.id'))
|
||
model_override = Column(String(100), nullable=True) # 覆盖Provider默认模型
|
||
|
||
# 系统设定
|
||
system_prompt = Column(Text, default="你是一个有用的AI助手。") # 系统提示词
|
||
|
||
# 思考功能
|
||
enable_thinking = Column(Boolean, default=True) # 是否启用思考
|
||
thinking_prompt = Column(Text, nullable=True) # 思考提示词模板
|
||
thinking_prefix = Column(String(50), default="<think>") # 思考内容前缀标识
|
||
thinking_suffix = Column(String(50), default="</think>") # 思考内容后缀标识
|
||
|
||
# 其他配置
|
||
max_history = Column(Integer, default=20) # 最大历史消息数
|
||
temperature_override = Column(Float, nullable=True) # 覆盖温度
|
||
|
||
# 状态
|
||
is_active = Column(Boolean, default=True)
|
||
is_default = Column(Boolean, default=False) # 是否为默认Agent
|
||
description = Column(Text, nullable=True)
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
# 关联
|
||
llm_provider = relationship("LLMProvider", back_populates="agents")
|
||
channel_mappings = relationship("ChannelAgentMapping", back_populates="agent")
|
||
|
||
|
||
# ==================== 渠道 ====================
|
||
|
||
class Channel(Base):
|
||
"""渠道配置 - 网页端、Matrix端等"""
|
||
__tablename__ = 'channels'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
channel_type = Column(String(20), index=True) # web, matrix, telegram, etc.
|
||
name = Column(String(100)) # 渠道名称,如 "网页端主入口", "Matrix AI Bot"
|
||
|
||
# 渠道配置(JSON)
|
||
config = Column(JSON, default=dict)
|
||
# Matrix示例: {"homeserver": "xxx", "username": "xxx", "password": "xxx"}
|
||
# Web示例: {"require_login": false, "session_timeout": 3600}
|
||
|
||
# 状态
|
||
is_active = Column(Boolean, default=True)
|
||
is_primary = Column(Boolean, default=False) # 是否为主要渠道
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
# 关联
|
||
agent_mappings = relationship("ChannelAgentMapping", back_populates="channel", order_by="ChannelAgentMapping.priority")
|
||
conversations = relationship("Conversation", back_populates="channel")
|
||
|
||
|
||
# ==================== 渠道-Agent映射 ====================
|
||
|
||
class ChannelAgentMapping(Base):
|
||
"""渠道与Agent的映射关系"""
|
||
__tablename__ = 'channel_agent_mappings'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
channel_id = Column(Integer, ForeignKey('channels.id'))
|
||
agent_id = Column(Integer, ForeignKey('agents.id'))
|
||
|
||
# 映射配置
|
||
priority = Column(Integer, default=0) # 优先级(数字越小优先级越高)
|
||
mode = Column(String(20), default='single') # single(单Agent), round_robin(轮询), fallback(备用)
|
||
weight = Column(Integer, default=1) # 轮询权重
|
||
|
||
# 条件配置(可选)
|
||
conditions = Column(JSON, nullable=True) # 条件触发,如 {"user_type": "vip"}
|
||
|
||
is_active = Column(Boolean, default=True)
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
|
||
# 关联
|
||
channel = relationship("Channel", back_populates="agent_mappings")
|
||
agent = relationship("Agent", back_populates="channel_mappings")
|
||
|
||
|
||
# ==================== 用户和会话(保留原有结构,增加渠道关联) ====================
|
||
|
||
class User(Base):
|
||
"""用户表"""
|
||
__tablename__ = 'users'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
user_id = Column(String(100), unique=True, index=True)
|
||
display_name = Column(String(100))
|
||
user_type = Column(String(20), default='web') # web, matrix, telegram
|
||
matrix_user_id = Column(String(200), nullable=True)
|
||
|
||
# 用户级别(用于Agent选择条件)
|
||
user_level = Column(String(20), default='normal') # normal, vip, admin
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
last_active_at = Column(DateTime, default=datetime.utcnow)
|
||
is_active = Column(Boolean, default=True)
|
||
|
||
conversations = relationship("Conversation", back_populates="user")
|
||
|
||
|
||
class Conversation(Base):
|
||
"""对话会话"""
|
||
__tablename__ = 'conversations'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
conversation_id = Column(String(100), unique=True, index=True)
|
||
user_id = Column(Integer, ForeignKey('users.id'))
|
||
channel_id = Column(Integer, ForeignKey('channels.id'), nullable=True) # 新增渠道关联
|
||
|
||
title = Column(String(200), nullable=True)
|
||
|
||
# 当前使用的Agent(会话可切换Agent)
|
||
current_agent_id = Column(Integer, ForeignKey('agents.id'), nullable=True)
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
is_active = Column(Boolean, default=True)
|
||
extra_data = Column(JSON, nullable=True)
|
||
|
||
user = relationship("User", back_populates="conversations")
|
||
channel = relationship("Channel", back_populates="conversations")
|
||
messages = relationship("Message", back_populates="conversation")
|
||
|
||
|
||
class Message(Base):
|
||
"""消息表"""
|
||
__tablename__ = 'messages'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
conversation_id = Column(Integer, ForeignKey('conversations.id'))
|
||
role = Column(String(20)) # user, assistant, system, thinking(思考过程)
|
||
content = Column(Text)
|
||
source = Column(String(20)) # web, matrix
|
||
|
||
# 思考内容(如果启用了思考功能)
|
||
thinking_content = Column(Text, nullable=True) # 思考过程
|
||
|
||
# Agent信息
|
||
agent_id = Column(Integer, ForeignKey('agents.id'), nullable=True) # 生成此消息的Agent
|
||
model_used = Column(String(100), nullable=True) # 使用的模型
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
extra_data = Column(JSON, nullable=True)
|
||
|
||
conversation = relationship("Conversation", back_populates="messages")
|
||
|
||
|
||
# ==================== Matrix房间映射(保留) ====================
|
||
|
||
class MatrixRoomMapping(Base):
|
||
"""Matrix房间映射"""
|
||
__tablename__ = 'matrix_room_mapping'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
room_id = Column(String(200), unique=True, index=True)
|
||
user_id = Column(Integer, ForeignKey('users.id'))
|
||
conversation_id = Column(Integer, ForeignKey('conversations.id'))
|
||
channel_id = Column(Integer, ForeignKey('channels.id'), nullable=True)
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
|
||
|
||
# ==================== 搜索工具配置 ====================
|
||
|
||
class ToolConfig(Base):
|
||
"""工具配置(通用,支持搜索、计算器、代码执行等)"""
|
||
__tablename__ = 'tool_configs'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
name = Column(String(100)) # 工具名称,如 "Tavily Search"、"Calculator"
|
||
tool_type = Column(String(50), index=True) # 工具类型:search, calculator, code_runner, image_gen, etc.
|
||
provider = Column(String(50), nullable=True) # 提供商(可选):tavily, google, wolfram, etc.
|
||
|
||
# API配置(JSON,不同工具可能有不同配置)
|
||
config = Column(JSON, default=dict)
|
||
# search示例: {"api_key": "xxx", "max_results": 5, "search_depth": "basic"}
|
||
# calculator示例: {"api_base": "xxx"}
|
||
|
||
# 状态
|
||
is_active = Column(Boolean, default=True)
|
||
is_default = Column(Boolean, default=False) # 是否为该类型的默认工具
|
||
|
||
# 统计
|
||
total_calls = Column(Integer, default=0) # 总调用次数
|
||
success_calls = Column(Integer, default=0) # 成功次数
|
||
failed_calls = Column(Integer, default=0) # 失败次数
|
||
|
||
created_at = Column(DateTime, default=datetime.utcnow)
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
|
||
class ToolUsageLog(Base):
|
||
"""工具使用日志"""
|
||
__tablename__ = 'tool_usage_logs'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
tool_id = Column(Integer, ForeignKey('tool_configs.id'))
|
||
tool_type = Column(String(50), index=True)
|
||
|
||
# 调用信息
|
||
query = Column(Text) # 调用参数/查询内容
|
||
success = Column(Boolean, default=True)
|
||
error_message = Column(Text, nullable=True)
|
||
result_summary = Column(Text, nullable=True) # 结果摘要
|
||
|
||
# 关联信息
|
||
conversation_id = Column(String(100), nullable=True)
|
||
agent_id = Column(Integer, nullable=True)
|
||
user_id = Column(String(100), nullable=True)
|
||
|
||
# 性能
|
||
duration_ms = Column(Integer, nullable=True) # 调用耗时(毫秒)
|
||
|
||
called_at = Column(DateTime, default=datetime.utcnow)
|
||
|
||
|
||
# ==================== 系统配置(保留) ====================
|
||
|
||
class SystemConfig(Base):
|
||
"""系统配置表"""
|
||
__tablename__ = 'system_config'
|
||
|
||
id = Column(Integer, primary_key=True, index=True)
|
||
key = Column(String(100), unique=True, index=True)
|
||
value = Column(Text)
|
||
description = Column(String(500))
|
||
updated_at = Column(DateTime, default=datetime.utcnow, onupdate=datetime.utcnow)
|
||
|
||
|
||
def init_db():
|
||
"""初始化数据库"""
|
||
Base.metadata.create_all(bind=engine)
|
||
|
||
|
||
def get_db():
|
||
"""获取数据库会话"""
|
||
db = SessionLocal()
|
||
try:
|
||
yield db
|
||
finally:
|
||
db.close()
|
||
|
||
|
||
# ==================== 初始化默认数据 ====================
|
||
|
||
def init_default_data():
|
||
"""初始化默认数据:大模型池、Agent、渠道"""
|
||
db = SessionLocal()
|
||
try:
|
||
# 检查是否已有数据
|
||
if db.query(LLMProvider).count() > 0:
|
||
return
|
||
|
||
# 1. 创建默认大模型池
|
||
default_llm = LLMProvider(
|
||
name="本地LLM Proxy",
|
||
api_base="http://192.168.2.17:19007/v1",
|
||
api_key="xxxx",
|
||
models=[
|
||
{"id": "auto", "name": "auto (自动选择)"},
|
||
{"id": "qwen3.5-4b", "name": "qwen3.5-4b"},
|
||
{"id": "dsv32", "name": "dsv32"},
|
||
{"id": "glm-4", "name": "glm-4"}
|
||
],
|
||
default_model="auto",
|
||
supports_thinking=False,
|
||
is_active=True,
|
||
priority=0,
|
||
description="本地LLM代理服务"
|
||
)
|
||
db.add(default_llm)
|
||
|
||
# 2. 创建默认Agent
|
||
default_agent = Agent(
|
||
name="default",
|
||
display_name="默认助手",
|
||
llm_provider_id=1,
|
||
system_prompt="你是一个有用的AI助手。",
|
||
enable_thinking=True,
|
||
thinking_prompt="请先仔细思考这个问题,然后给出回答。",
|
||
is_active=True,
|
||
is_default=True,
|
||
description="默认AI助手"
|
||
)
|
||
db.add(default_agent)
|
||
|
||
# 3. 创建渠道
|
||
web_channel = Channel(
|
||
channel_type="web",
|
||
name="网页端",
|
||
config={"require_login": False},
|
||
is_active=True,
|
||
is_primary=True
|
||
)
|
||
db.add(web_channel)
|
||
|
||
matrix_channel = Channel(
|
||
channel_type="matrix",
|
||
name="Matrix AI Bot",
|
||
config={
|
||
"homeserver": "http://matrix.tphai.com",
|
||
"username": "@tester:matrix.tphai.com",
|
||
"password": "tester12345@!"
|
||
},
|
||
is_active=True
|
||
)
|
||
db.add(matrix_channel)
|
||
|
||
db.commit()
|
||
|
||
# 4. 创建渠道-Agent映射
|
||
web_mapping = ChannelAgentMapping(
|
||
channel_id=1,
|
||
agent_id=1,
|
||
priority=0,
|
||
mode='single'
|
||
)
|
||
db.add(web_mapping)
|
||
|
||
matrix_mapping = ChannelAgentMapping(
|
||
channel_id=2,
|
||
agent_id=1,
|
||
priority=0,
|
||
mode='single'
|
||
)
|
||
db.add(matrix_mapping)
|
||
|
||
# 5. 创建默认搜索工具配置
|
||
search_config = SearchToolConfig(
|
||
name="Tavily Search",
|
||
provider="tavily",
|
||
api_key="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",
|
||
max_results=5,
|
||
search_depth="basic",
|
||
is_active=True,
|
||
is_default=True
|
||
)
|
||
db.add(search_config)
|
||
|
||
db.commit()
|
||
print("默认数据初始化完成")
|
||
|
||
finally:
|
||
db.close() |