112 lines
6.6 KiB
SQL
112 lines
6.6 KiB
SQL
-- 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<!-- weknora:digital-human:' || human_id || E':start -->\n'
|
|||
|
|
|| '## 旧配置迁入数字人(未重新蒸馏):' || item.name || E'(v1)\n'
|
|||
|
|
|| E'以下档案仅约束表达方式,不能替代任务目标、事实来源、工具权限或上文衔接约束。不得把档案样本当作新的事实依据。\n'
|
|||
|
|
|| persona_lines || '<!-- weknora:digital-human:' || human_id || ':end -->';
|
|||
|
|
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$;
|