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.
 

10 KiB

AI Service 快速开始指南

状态:数据库层已就绪
日期:2026-01-29

概述

AI Service 的数据库层已完全实现并可立即使用。本指南帮助您快速开始使用 AI Service。

当前状态

已完成

  • 数据库表结构(4 张表)
  • Models 定义(符合 Jointo 技术栈规范)
  • 数据库迁移脚本
  • 索引和约束
  • 触发器和函数

⚠️ 待完成

  • Credit Service 集成
  • AI Providers 实现
  • Celery Tasks 实现
  • 单元测试和集成测试

数据库表

1. ai_models(AI 模型配置)

存储可用的 AI 模型及其定价信息。

from app.models.ai_model import AIModel, AIModelType, AIProvider, UnitType

# 枚举值
AIModelType.TEXT    # 1 - 文本模型
AIModelType.IMAGE   # 2 - 图片模型
AIModelType.VIDEO   # 3 - 视频模型
AIModelType.AUDIO   # 4 - 音频模型

AIProvider.OPENAI      # 1
AIProvider.ANTHROPIC   # 2
AIProvider.GOOGLE      # 3
AIProvider.STABILITY   # 4
AIProvider.RUNWAY      # 5
AIProvider.PIKA        # 6
AIProvider.ELEVENLABS  # 7
AIProvider.AZURE       # 8
AIProvider.BAIDU       # 9
AIProvider.ALIYUN      # 10
AIProvider.CUSTOM      # 99

UnitType.TOKEN    # 1 - Token(文本模型)
UnitType.IMAGE    # 2 - 图片(图片模型)
UnitType.SECOND   # 3 - 秒(视频/音频模型)
UnitType.REQUEST  # 4 - 请求(通用计费单位)

2. ai_jobs(AI 任务)

存储所有 AI 生成任务的状态和结果。

from app.models.ai_job import AIJob, AIJobType, AIJobStatus

# 任务类型
AIJobType.IMAGE              # 1 - 图片生成
AIJobType.VIDEO              # 2 - 视频生成
AIJobType.SOUND              # 3 - 音效生成
AIJobType.VOICE              # 4 - 配音生成
AIJobType.SUBTITLE           # 5 - 字幕生成
AIJobType.TEXT_PROCESSING    # 6 - 文本处理
AIJobType.RESOURCE           # 7 - 资源生成
AIJobType.STORYBOARD_SCRIPT  # 8 - 分镜脚本生成
AIJobType.SCRIPT_GENERATION  # 9 - 剧本生成

# 任务状态
AIJobStatus.PENDING      # 1 - 等待处理
AIJobStatus.PROCESSING   # 2 - 处理中
AIJobStatus.COMPLETED    # 3 - 已完成
AIJobStatus.FAILED       # 4 - 失败
AIJobStatus.CANCELLED    # 5 - 已取消

3. ai_usage_logs(使用日志)

记录每次 AI 调用的详细使用情况。

from app.models.ai_usage_log import AIUsageLog

4. ai_quotas(配额管理)

管理用户的 AI 使用配额和限流。

from app.models.ai_quota import AIQuota, QuotaPeriod

QuotaPeriod.DAILY    # 1 - 每日配额
QuotaPeriod.MONTHLY  # 2 - 每月配额
QuotaPeriod.TOTAL    # 3 - 总配额

使用示例

1. 创建 AI 模型配置

from app.models.ai_model import AIModel, AIModelType, AIProvider, UnitType
from app.utils.id_generator import generate_uuid
from datetime import datetime, timezone

# 创建 GPT-4 模型配置
gpt4_model = AIModel(
    model_id=generate_uuid(),
    model_name="gpt-4",
    display_name="GPT-4",
    description="OpenAI 最强大的语言模型",
    provider=AIProvider.OPENAI,
    model_type=AIModelType.TEXT,
    cost_per_unit=0.03,
    unit_type=UnitType.TOKEN,
    credits_per_unit=1,
    config={
        "max_tokens": 8000,
        "temperature": 0.7
    },
    is_active=True,
    is_beta=False
)

# 保存到数据库
db.add(gpt4_model)
await db.commit()

2. 创建 AI 任务

from app.models.ai_job import AIJob, AIJobType, AIJobStatus
from app.utils.id_generator import generate_uuid

# 创建图片生成任务
job = AIJob(
    ai_job_id=generate_uuid(),
    user_id=user_id,
    job_type=AIJobType.IMAGE,
    status=AIJobStatus.PENDING,
    model_id=model_id,
    model_name="stable-diffusion",
    input_data={
        "prompt": "一只可爱的猫咪在花园里玩耍",
        "width": 1024,
        "height": 1024,
        "style": "realistic"
    },
    credits_used=10
)

db.add(job)
await db.commit()

3. 更新任务状态

# 标记为处理中
job.status = AIJobStatus.PROCESSING
job.started_at = datetime.now(timezone.utc)
job.progress = 50
await db.commit()

# 标记为完成
job.status = AIJobStatus.COMPLETED
job.completed_at = datetime.now(timezone.utc)
job.progress = 100
job.output_data = {
    "image_url": "https://storage.jointo.ai/images/123.png",
    "thumbnail_url": "https://storage.jointo.ai/images/123_thumb.png"
}
await db.commit()

4. 查询任务

from sqlalchemy import select

# 查询用户的所有任务
result = await db.execute(
    select(AIJob)
    .where(AIJob.user_id == user_id)
    .order_by(AIJob.created_at.desc())
)
jobs = result.scalars().all()

# 查询待处理的任务
result = await db.execute(
    select(AIJob)
    .where(AIJob.status == AIJobStatus.PENDING)
    .order_by(AIJob.created_at.asc())
    .limit(10)
)
pending_jobs = result.scalars().all()

5. 创建用户配额

from app.models.ai_quota import AIQuota, QuotaPeriod
from datetime import datetime, timezone, timedelta

# 创建每日图片生成配额
quota = AIQuota(
    quota_id=generate_uuid(),
    user_id=user_id,
    quota_type="image_generation",
    period=QuotaPeriod.DAILY,
    total_quota=50,
    used_quota=0,
    reset_at=datetime.now(timezone.utc) + timedelta(days=1)
)

db.add(quota)
await db.commit()

6. 记录使用日志

from app.models.ai_usage_log import AIUsageLog

log = AIUsageLog(
    usage_log_id=generate_uuid(),
    user_id=user_id,
    ai_job_id=job.ai_job_id,
    model_id=model_id,
    units_used=1000,  # 使用了 1000 tokens
    unit_type=UnitType.TOKEN,
    cost=0.03,
    credits_used=10,
    extra_metadata={
        "prompt_tokens": 100,
        "completion_tokens": 900
    }
)

db.add(log)
await db.commit()

Repository 使用

现有的 Repository 已经提供了基础的 CRUD 操作:

from app.repositories.ai_job_repository import AIJobRepository
from app.repositories.ai_model_repository import AIModelRepository

# 使用 Repository
job_repo = AIJobRepository(db)
model_repo = AIModelRepository(db)

# 创建任务
job = await job_repo.create({
    'job_type': AIJobType.IMAGE,
    'status': AIJobStatus.PENDING,
    'user_id': user_id,
    'input_data': {...}
})

# 获取任务
job = await job_repo.get_by_id(job_id)

# 更新任务
job = await job_repo.update(job_id, {
    'status': AIJobStatus.COMPLETED,
    'output_data': {...}
})

# 获取用户任务列表
jobs = await job_repo.get_user_jobs(
    user_id=user_id,
    job_type=AIJobType.IMAGE,
    status=AIJobStatus.COMPLETED,
    limit=20
)

API 使用

现有的 API 端点已经可以使用(需要完成 Service 层集成):

# 生成图片
POST /api/v1/ai/generate-image
{
  "prompt": "一只可爱的猫咪",
  "width": 1024,
  "height": 1024,
  "style": "realistic"
}

# 查询任务状态
GET /api/v1/ai/jobs/{job_id}

# 取消任务
POST /api/v1/ai/jobs/{job_id}/cancel

# 获取使用统计
GET /api/v1/ai/usage/stats?startDate=2026-01-01&endDate=2026-01-31

# 获取可用模型
GET /api/v1/ai/models?type=2  # 2 = IMAGE

数据库操作

查看表结构

docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\d ai_jobs"

查看索引

docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "\di ai_*"

查看触发器

docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "
SELECT tgname, tgrelid::regclass 
FROM pg_trigger 
WHERE tgname LIKE 'update_ai%';
"

手动重置配额

docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "
SELECT reset_expired_quotas();
"

查询统计

# 任务统计
docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "
SELECT 
    job_type,
    status,
    COUNT(*) as count
FROM ai_jobs
GROUP BY job_type, status
ORDER BY job_type, status;
"

# 模型统计
docker exec jointo-server-postgres psql -U jointoAI -d jointo -c "
SELECT 
    provider,
    model_type,
    COUNT(*) as count,
    SUM(CASE WHEN is_active THEN 1 ELSE 0 END) as active_count
FROM ai_models
GROUP BY provider, model_type;
"

下一步

立即可做

  1. 使用 Repository 进行 CRUD 操作
  2. 创建测试数据
  3. 查询和统计分析
  4. 编写数据迁移脚本

需要完成

  1. ⚠️ Credit Service 集成(积分预扣、确认、退还)
  2. ⚠️ AI Providers 实现(OpenAI, Stability, Runway 等)
  3. ⚠️ Celery Tasks 实现(异步任务处理)
  4. ⚠️ 单元测试和集成测试

注意事项

应用层引用完整性

所有关联字段都没有物理外键,必须在 Service 层验证:

# ❌ 错误:直接创建任务,不验证关联
job = AIJob(user_id=user_id, model_id=model_id, ...)

# ✅ 正确:先验证关联是否存在
from app.repositories.user_repository import UserRepository
from app.repositories.ai_model_repository import AIModelRepository

user_repo = UserRepository(db)
model_repo = AIModelRepository(db)

if not await user_repo.exists(user_id):
    raise ValidationError("用户不存在")

if not await model_repo.exists(model_id):
    raise ValidationError("模型不存在")

job = AIJob(user_id=user_id, model_id=model_id, ...)

时间戳使用

所有时间戳字段使用 TIMESTAMPTZ:

from datetime import datetime, timezone

# ✅ 正确
job.started_at = datetime.now(timezone.utc)

# ❌ 错误
job.started_at = datetime.now()  # 缺少时区信息

枚举值使用

使用 IntEnum 而非字符串:

# ✅ 正确
job.job_type = AIJobType.IMAGE  # 存储为 1
job.status = AIJobStatus.PENDING  # 存储为 1

# ❌ 错误
job.job_type = "image"  # 类型错误
job.status = "pending"  # 类型错误

相关文档

支持

如有问题,请参考:

  1. 设计文档中的详细说明
  2. Changelog 中的变更记录
  3. 数据库表注释
  4. 代码中的类型提示和文档字符串