feat: 接入模型动态 Fallback 与调用审计

This commit is contained in:
hh
2026-07-21 10:35:50 +08:00
parent 9429133d04
commit d0c3690d47
45 changed files with 9253 additions and 173 deletions
+54
View File
@@ -174,6 +174,60 @@ class AITask(TeamOwnedModel):
return f"{self.task_type}:{self.status}:{self.id}"
class AIModelAttempt(TimeStampedModel):
"""AITask 下的一次真实模型请求审计;不参与积分预留、扣费或任务生命周期。"""
class Status(models.TextChoices):
STARTED = "started", "Started"
SUCCEEDED = "succeeded", "Succeeded"
FAILED = "failed", "Failed"
task = models.ForeignKey(AITask, on_delete=models.CASCADE, related_name="model_attempts")
sequence = models.PositiveIntegerField()
provider = models.ForeignKey(ModelProvider, on_delete=models.SET_NULL, null=True, blank=True)
model_config = models.ForeignKey(ModelConfig, on_delete=models.SET_NULL, null=True, blank=True)
# 名称均保存历史快照,后续后台改名或删除配置也不影响旧任务审计。
provider_name = models.CharField(max_length=128)
provider_display_name = models.CharField(max_length=128, blank=True)
model_name = models.CharField(max_length=128)
model_display_name = models.CharField(max_length=128, blank=True)
public_model_name = models.CharField(max_length=128, blank=True)
capability = models.CharField(max_length=32)
operation = models.CharField(max_length=64)
status = models.CharField(max_length=24, choices=Status.choices, default=Status.STARTED)
is_retry = models.BooleanField(default=False)
is_fallback = models.BooleanField(default=False)
previous_attempt = models.ForeignKey(
"self",
on_delete=models.SET_NULL,
null=True,
blank=True,
related_name="next_attempts",
)
provider_task_id = models.CharField(max_length=255, blank=True)
started_at = models.DateTimeField()
finished_at = models.DateTimeField(null=True, blank=True)
duration_ms = models.PositiveBigIntegerField(null=True, blank=True)
error_type = models.CharField(max_length=64, blank=True)
provider_error_code = models.CharField(max_length=128, blank=True)
raw_error = models.TextField(blank=True)
safe_error_summary = models.TextField(blank=True)
usage = models.JSONField(default=dict, blank=True)
platform_cost = models.DecimalField(max_digits=12, decimal_places=4, default=0)
request_summary = models.JSONField(default=dict, blank=True)
response_summary = models.JSONField(default=dict, blank=True)
class Meta:
ordering = ["sequence"]
constraints = [
models.UniqueConstraint(fields=["task", "sequence"], name="ai_attempt_task_sequence_unique"),
]
indexes = [models.Index(fields=["task", "status"], name="ai_attempt_task_status_idx")]
def __str__(self) -> str:
return f"{self.task_id}:{self.sequence}:{self.status}"
class QualityWord(TimeStampedModel):
"""平台单层质量词配置:按生成阶段(stage)+ 槽位(slot)挂若干质量词,
生成侧拼提示词时优先读取本配置;**无配置则回落各 builder 的写死值**(零回归)。