-- Existing effective style is imported, not re-distilled or falsely approved. CREATE TABLE IF NOT EXISTS yawan_legacy_agent_persona_mappings ( tenant_id BIGINT NOT NULL, agent_id VARCHAR(36) NOT NULL, source_system TEXT NOT NULL, source_organization_id BIGINT NOT NULL, source_id BIGINT NOT NULL, owner_id VARCHAR(36) NOT NULL, digital_human_id VARCHAR(36) NOT NULL, version_id VARCHAR(36) NOT NULL, binding_id VARCHAR(36) NOT NULL, source_persona JSONB NOT NULL, source_snapshot_sha256 VARCHAR(64) NOT NULL, migrated_at TIMESTAMPTZ NOT NULL DEFAULT NOW(), PRIMARY KEY (tenant_id, agent_id), UNIQUE (source_system, source_organization_id, source_id, tenant_id) ); DO $legacy_persona$ DECLARE item RECORD; human_id TEXT; version_id TEXT; binding_id TEXT; seed TEXT; prompt TEXT; persona_lines TEXT; globally_visible BOOLEAN; BEGIN IF to_regclass('yawan_legacy_asset_mappings') IS NULL THEN RETURN; END IF; FOR item IN SELECT m.*, a.created_by, a.name, a.avatar, a.config, a.visibility FROM yawan_legacy_asset_mappings m JOIN custom_agents a ON a.tenant_id = m.target_tenant_id AND a.id = m.target_id WHERE m.source_system = 'yawan-workbench' AND m.source_organization_id = 1 AND m.source_kind = 'agent' AND m.source_snapshot->>'status' = 'active' AND m.source_snapshot->'persona_json' IS NOT NULL AND m.source_snapshot->'persona_json' <> '{}'::jsonb AND COALESCE(m.source_snapshot->>'digital_human_id', '') IN ('', '0') AND a.deleted_at IS NULL AND COALESCE(a.created_by, '') <> '' AND NOT EXISTS (SELECT 1 FROM yawan_legacy_agent_persona_mappings p WHERE p.tenant_id = a.tenant_id AND p.agent_id = a.id) AND NOT EXISTS (SELECT 1 FROM digital_human_bindings b WHERE b.tenant_id = a.tenant_id AND b.agent_id = a.id) FOR UPDATE OF a LOOP IF jsonb_typeof(item.source_snapshot->'persona_json') <> 'object' THEN RAISE EXCEPTION 'Legacy Agent persona must be an object'; END IF; IF EXISTS (SELECT 1 FROM jsonb_each(item.source_snapshot->'persona_json') p WHERE jsonb_typeof(p.value) <> 'string' OR length(p.value #>> '{}') > 6000 OR p.key NOT IN ('persona_name', 'identity', 'tone', 'pace', 'vocab', 'taboo', 'opening_habit', 'emotion_style', 'interaction', 'expertise_border', 'sample_excerpt', 'writing_style', 'narrative_style', 'thinking_style', 'audience_profile', 'hook_patterns', 'pain_point_hooks', 'itch_point_hooks', 'sales_logic', 'cta_transition', 'expression_prompt', 'expression_rules')) OR (SELECT COALESCE(sum(length(value)), 0) FROM jsonb_each_text(item.source_snapshot->'persona_json')) > 30000 THEN RAISE EXCEPTION 'Legacy Agent persona requires explicit field review'; END IF; IF NOT EXISTS (SELECT 1 FROM jsonb_each_text(item.source_snapshot->'persona_json') p WHERE btrim(p.value) <> '') THEN CONTINUE; END IF; seed := item.source_system || ':' || item.source_organization_id || ':agent-persona:' || item.target_tenant_id || ':' || item.source_id; human_id := md5(seed || ':human')::uuid::text; version_id := md5(seed || ':version')::uuid::text; binding_id := md5(seed || ':binding')::uuid::text; -- A preexisting deterministic target is a conflict, never permission to overwrite it. IF EXISTS (SELECT 1 FROM digital_humans WHERE id = human_id) OR EXISTS (SELECT 1 FROM digital_human_versions WHERE id = version_id) OR EXISTS (SELECT 1 FROM digital_human_bindings WHERE id = binding_id) THEN RAISE EXCEPTION 'Legacy Agent persona target already exists without migration provenance'; END IF; SELECT string_agg(p.key || ':' || btrim(p.value) || E'\n', '' ORDER BY p.key COLLATE "C") INTO persona_lines FROM jsonb_each_text(item.source_snapshot->'persona_json') p WHERE btrim(p.value) <> ''; prompt := COALESCE(item.config->>'system_prompt', '') || E'\n\n\n' || '## 旧配置迁入数字人(未重新蒸馏):' || item.name || E'(v1)\n' || E'以下档案仅约束表达方式,不能替代任务目标、事实来源、工具权限或上文衔接约束。不得把档案样本当作新的事实依据。\n' || persona_lines || ''; INSERT INTO digital_humans (id, tenant_id, owner_id, name, description, avatar, model_id, status, latest_version, active_version, primary_agent_id) VALUES (human_id, item.target_tenant_id, item.created_by, item.name, '从原 Agent 的有效风格配置迁入,未重新蒸馏或审核。', COALESCE(item.avatar, ''), COALESCE(item.config->>'model_id', ''), 'active', 1, 1, item.target_id); INSERT INTO digital_human_versions (id, tenant_id, owner_id, digital_human_id, number, status, persona, sources, advice, review_note, reviewed_at) VALUES (version_id, item.target_tenant_id, item.created_by, human_id, 1, 'imported', item.source_snapshot->'persona_json', '[]', '', '', NULL); INSERT INTO digital_human_bindings (id, tenant_id, owner_id, digital_human_id, agent_id, base_prompt, applied_version) VALUES (binding_id, item.target_tenant_id, item.created_by, human_id, item.target_id, COALESCE(item.config->>'system_prompt', ''), 1); UPDATE custom_agents SET config = jsonb_set(config::jsonb, '{system_prompt}', to_jsonb(prompt)) WHERE tenant_id = item.target_tenant_id AND id = item.target_id; SELECT COALESCE((SELECT global FROM workbench_access_publications WHERE tenant_id = item.target_tenant_id AND kind = 'agents' AND resource_id = item.target_id), item.visibility = 'global') INTO globally_visible; INSERT INTO workbench_access_publications (tenant_id, kind, resource_id, global) VALUES (item.target_tenant_id, 'digital-humans', human_id, globally_visible); INSERT INTO workbench_access_grants (tenant_id, kind, resource_id, user_id) SELECT tenant_id, 'digital-humans', human_id, user_id FROM workbench_access_grants WHERE tenant_id = item.target_tenant_id AND kind = 'agents' AND resource_id = item.target_id; INSERT INTO yawan_legacy_agent_persona_mappings (tenant_id, agent_id, source_system, source_organization_id, source_id, owner_id, digital_human_id, version_id, binding_id, source_persona, source_snapshot_sha256) VALUES (item.target_tenant_id, item.target_id, item.source_system, item.source_organization_id, item.source_id, item.created_by, human_id, version_id, binding_id, item.source_snapshot->'persona_json', item.source_snapshot_sha256); END LOOP; END $legacy_persona$;