from django.core.management.base import BaseCommand, CommandError from django.db import transaction from django.utils import timezone from apps.agents.models import AgentDefinition, AgentStepBinding, AgentVersion from apps.modelhub.models import ModelDeployment from apps.modelhub.structured_output import StructuredOutputError, ensure_compatible from apps.skills.models import SkillDefinition, SkillVersion from apps.skills.services import validate_skill_version TICKET_FIELDS = { "type": "object", "properties": { "title": {"type": "string"}, "description": {"type": "string"}, "category_code": {"type": "string"}, "priority": {"type": "string", "enum": ["low", "medium", "high", "urgent"]}, "problem_performance": {"type": "string"}, "problem_object": {"type": "string"}, "submitter_name": {"type": "string"}, "submitter_org": {"type": "string"}, "impact_scope": {"type": "string"}, "environment": {"type": "string"}, "related_system": {"type": "string"}, }, "required": [ "title", "description", "category_code", "priority", "problem_performance", "problem_object", "submitter_name", "submitter_org", "impact_scope", "environment", "related_system", ], "additionalProperties": False, } AGENT_INPUT_SCHEMA = { "type": "object", "properties": { "source_text": {"type": "string"}, "current_fields": {"type": "object"}, "messages": {"type": "array", "items": {"type": "object"}}, "image_refs": {"type": "array", "items": {"type": "object"}}, }, "required": ["source_text", "current_fields", "messages", "image_refs"], } OUTPUT_SCHEMA = { "type": "object", "properties": { "fields": TICKET_FIELDS, "missing_fields": {"type": "array", "items": {"type": "string"}}, "questions": {"type": "array", "items": {"type": "string"}}, }, "required": ["fields", "missing_fields", "questions"], "additionalProperties": False, } class Command(BaseCommand): help = "Create and publish the single-step ticket intake Agent and Skill." def add_arguments(self, parser): parser.add_argument("--deployment-id", type=int) @transaction.atomic def handle(self, *args, **options): deployments = ModelDeployment.objects.filter(enabled=True, provider__enabled=True) deployment_id = options.get("deployment_id") deployment = ( deployments.filter(id=deployment_id).first() if deployment_id else deployments.first() ) if deployment is None: raise CommandError("请先配置并启用模型渠道和模型配置") try: ensure_compatible( mode=deployment.structured_output_mode, test_status=deployment.structured_output_test_status, enforcement="native_json_schema", ) except StructuredOutputError as error: raise CommandError(f"{error.code}: {error}") from error skill, _ = SkillDefinition.objects.get_or_create( code="ticket-draft", defaults={"name": "可信建单草稿", "description": "从描述与追问答案生成工单草稿。"}, ) skill_version = skill.versions.filter(status="published").order_by("-version").first() if skill_version is None: next_version = ( skill.versions.order_by("-version").values_list("version", flat=True).first() or 0 ) + 1 skill_version = SkillVersion.objects.create( definition=skill, version=next_version, status="published", prompt_template=( "根据用户原始描述 {{ input.source_text }}、" "当前字段 {{ input.current_fields }}、" "逐问记录 {{ input.messages }} 以及随消息提供的可选图片,生成完整工单草稿。" "分类代码只能使用 business_issue、operation_issue、" "data_issue、technical_issue。" "无法可靠判断的值使用空字符串并列入 missing_fields,同时为缺失项生成简洁问题。" "只输出符合 Schema 的 JSON。" ), input_schema=AGENT_INPUT_SCHEMA, output_schema=OUTPUT_SCHEMA, output_enforcement="native_json_schema", example_input={ "source_text": "生产环境采集任务两小时没有更新", "current_fields": {}, "messages": [], "image_refs": [], }, published_at=timezone.now(), ) validate_skill_version(skill_version) skill.active_version = skill_version skill.save(update_fields=["active_version", "updated_at"]) agent, _ = AgentDefinition.objects.get_or_create( code="ticket-intake", defaults={"name": "可信建单 Agent", "description": "单步生成可审核的结构化工单草稿。"}, ) version = agent.versions.filter(status="published").order_by("-version").first() if version is None: next_version = ( agent.versions.order_by("-version").values_list("version", flat=True).first() or 0 ) + 1 version = AgentVersion.objects.create( definition=agent, version=next_version, status="published", model_deployment=deployment, system_prompt=( "你是企业技术支持建单助手。不得创建工单,只生成待用户审核的 JSON 草稿;" "不得臆造机构、联系人、环境或影响范围。" ), input_schema=AGENT_INPUT_SCHEMA, example_input={ "source_text": "生产环境采集任务两小时没有更新", "current_fields": {}, "messages": [], "image_refs": [], }, published_at=timezone.now(), ) AgentStepBinding.objects.create( agent_version=version, code="draft", order=1, skill_version=skill_version, input_mapping={ "source_text": "$input.source_text", "current_fields": "$input.current_fields", "messages": "$input.messages", "image_refs": "$input.image_refs", }, ) agent.active_version = version agent.save(update_fields=["active_version", "updated_at"]) self.stdout.write( self.style.SUCCESS( f"Published ticket-intake v{version.version} with deployment {deployment.id}." ) )