You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
8.1 KiB
8.1 KiB
AI 模型用户可见性控制
日期: 2026-02-10
类型: 功能增强
影响范围: AI 模型管理、前端 API
背景
当前 ai_models 表只有 is_active 字段控制模型是否可用,但存在以下问题:
- 缺乏细粒度控制:
is_active同时控制系统内部可用和用户界面可见 - 内部模型暴露:有些模型仅用于内部调用(如剧本解析的 Gemini 模型),不应展示给用户选择
- 用户体验差:用户看到大量不相关的模型,难以选择
解决方案
添加 is_visible 字段,实现双层控制:
- is_active: 控制系统内部是否可用(Factory 能否创建 Provider)
- is_visible: 控制用户界面是否可见(前端 API 是否返回)
变更内容
1. 数据库迁移
文件: server/alembic/versions/20260210_1500_add_is_visible_to_ai_models.py
def upgrade() -> None:
# 添加 is_visible 字段(默认 true)
op.add_column('ai_models', sa.Column(
'is_visible',
sa.Boolean(),
nullable=False,
server_default='true',
comment='是否对用户可见'
))
# 创建部分索引(只索引可见且活跃的模型)
op.create_index(
'idx_ai_models_visible',
'ai_models',
['is_visible'],
unique=False,
postgresql_where=sa.text('is_active = true AND is_visible = true')
)
2. 模型定义更新
文件: server/app/models/ai_model.py
class AIModel(SQLModel, table=True):
# ... 其他字段 ...
# 状态
is_active: bool = Field(
default=True,
sa_column=Column(
nullable=False,
server_default='true',
index=True,
comment='是否启用'
)
)
is_visible: bool = Field(
default=True,
sa_column=Column(
nullable=False,
server_default='true',
comment='是否对用户可见'
)
)
is_beta: bool = Field(
default=False,
sa_column=Column(
nullable=False,
server_default='false',
comment='是否为测试版'
)
)
3. Repository 更新
文件: server/app/repositories/ai_model_repository.py
新增 get_visible_models() 方法:
async def get_visible_models(
self,
model_type: Optional[int] = None
) -> List[AIModel]:
"""获取所有对用户可见的模型
Args:
model_type: 模型类型(AIModelType 枚举值,可选)
"""
query = select(AIModel).where(
and_(
AIModel.is_active == True,
AIModel.is_visible == True
)
)
if model_type is not None:
query = query.where(AIModel.model_type == model_type)
query = query.order_by(AIModel.model_type, AIModel.cost_per_unit)
result = await self.db.execute(query)
return list(result.scalars().all())
保留 get_active_models() 方法供内部使用(仅查询 is_active=true)。
4. Service 更新
文件: server/app/services/ai_service.py
async def get_available_models(
self,
model_type: Optional[int] = None
) -> List[Dict[str, Any]]:
"""获取可用的 AI 模型列表(仅返回对用户可见的模型)"""
models = await self.model_repository.get_visible_models(model_type)
# ... 序列化逻辑 ...
5. 同步脚本更新
文件: server/scripts/sync_models_from_api.py
默认设置:
# 默认所有模型 is_active=False, is_visible=False
is_active = False
is_visible = False
db_model = {
'model_name': model_id,
'display_name': display_name,
'description': api_model.get('desc', ''),
'provider': provider,
'model_type': model_type,
'cost_per_unit': cost_per_unit,
'unit_type': unit_type,
'credits_per_unit': credits_per_unit,
'is_active': is_active,
'is_visible': is_visible, # 新增
'is_beta': 'beta' in model_id.lower(),
'config': config,
}
6. 配置文件更新
文件: server/scripts/models_override_config.json
简化配置规则:
- 出现在配置文件中的模型自动设置
is_active=true(无需手动配置) - 只需配置
is_visible字段控制用户可见性
{
"gemini-2.5-flash": {
"is_visible": false,
"display_name": "Gemini 2.5 Flash",
"description": "Google Gemini 2.5 Flash 模型(内部使用)"
},
"gpt-4o": {
"is_visible": true,
"display_name": "GPT-4o",
"description": "OpenAI 多模态模型,支持文本和图像"
}
}
配置语义:
- 配置文件中的模型 = 启用(
is_active=true) is_visible=true= 用户可见is_visible=false= 仅内部使用- 未配置的模型 = 完全禁用(
is_active=false, is_visible=false)
使用场景
场景 1:内部调用模型
{
"gemini-2.5-flash": {
"is_visible": false,
"description": "用于剧本解析的内部模型"
}
}
- 出现在配置文件 →
is_active=true(系统可用) is_visible=false→ 前端 API 不返回(用户不可见)- Factory 可以创建 Provider
场景 2:用户可选模型
{
"gpt-4o": {
"is_visible": true,
"description": "用户可选择的模型"
}
}
- 出现在配置文件 →
is_active=true(系统可用) is_visible=true→ 前端 API 返回(用户可见)- Factory 可以创建 Provider
场景 3:禁用模型
不在配置文件中的模型:
is_active=false(系统不可用)is_visible=false(用户不可见)- Factory 无法创建 Provider
迁移步骤
1. 执行数据库迁移
docker exec jointo-server-app python scripts/db_migrate.py upgrade
2. 更新配置文件
编辑 server/scripts/models_override_config.json:
- 移除
is_active字段(自动设置) - 为每个模型配置
is_visible字段
3. 重新同步模型
docker exec jointo-server-app python scripts/sync_models_from_api.py --force
4. 重启应用
docker-compose -f server/docker-compose.yml restart jointo-server-app
验证
1. 检查数据库
-- 查看所有模型的可见性状态
SELECT model_name, is_active, is_visible, display_name
FROM ai_models
ORDER BY is_visible DESC, is_active DESC;
-- 查看用户可见的模型
SELECT model_name, display_name
FROM ai_models
WHERE is_active = true AND is_visible = true;
-- 查看内部使用的模型
SELECT model_name, display_name
FROM ai_models
WHERE is_active = true AND is_visible = false;
2. 测试 API
# 获取用户可见的模型列表
curl -X GET "http://localhost:8000/api/v1/ai/models" \
-H "Authorization: Bearer <token>"
# 应该只返回 is_visible=true 的模型
3. 测试内部调用
# 内部代码仍然可以使用 is_visible=false 的模型
from app.services.ai_providers.factory import AIProviderFactory
# 创建内部模型的 Provider(is_active=true, is_visible=false)
provider = await AIProviderFactory.create_provider(
db=db,
model_name="gemini-2.5-flash"
)
# 应该成功创建
影响范围
前端
- 模型选择器:只显示
is_visible=true的模型 - API 调用:
GET /api/v1/ai/models只返回可见模型
后端
- 内部调用:不受影响,仍然可以使用
is_visible=false的模型 - Factory:根据
is_active判断是否可创建 Provider - Repository:新增
get_visible_models()方法
注意事项
- 默认值:新同步的模型默认
is_visible=false,需要手动配置 - 兼容性:现有模型默认
is_visible=true(迁移脚本设置) - 更新策略:
--force更新时不会覆盖is_active和is_visible,保留用户配置
相关文档
作者: Jointo 开发团队
审核: 待审核
状态: 已实施