muse-agent-example/数据库/迁移/V0004__任务运行与证据.sql
zizi e99a952dd0 W05 任务状态、租约、事件与恢复:任务状态机、短事务租约、事件续接、恢复控制与作用域互斥。
按 R2 串行阶段整理提交;包内文件为该阶段交付(含后续小增量),状态以工作包清单为准。
2026-09-10 19:25:40 +08:00

291 lines
14 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

-- W05:持久任务、步骤、尝试、独立作用域租约和有序事件。
-- W06 模型预算与 W08 原文生命周期随所属实现接入;仅用于尚未冻结的开发基线。
DO $migration$
DECLARE
namespace text;
BEGIN
FOREACH namespace IN ARRAY ARRAY['public', 'evaluation'] LOOP
EXECUTE format($ddl$
CREATE TABLE %1$I.muse_task (
task_id uuid PRIMARY KEY,
run_purpose public.muse_purpose NOT NULL,
command_id text NOT NULL,
author_id text NOT NULL,
request_hash text NOT NULL,
frozen_input jsonb NOT NULL,
flow_id text NOT NULL,
flow_version text NOT NULL,
flow_snapshot jsonb NOT NULL,
state text NOT NULL CHECK (state IN ('queued','running','paused','reconciling','failed','cancelled','completed')),
scope_key text,
scope_generation bigint,
last_sequence bigint NOT NULL DEFAULT 0,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
updated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
UNIQUE (run_purpose, author_id, command_id)
);
CREATE TABLE %1$I.muse_task_control (
run_purpose public.muse_purpose NOT NULL,
author_id text NOT NULL,
command_id text NOT NULL,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
request_hash text NOT NULL,
receipt jsonb,
PRIMARY KEY (run_purpose, author_id, command_id)
);
CREATE TABLE %1$I.muse_step (
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
step_id text NOT NULL,
processor_id text NOT NULL,
processor_version text NOT NULL,
dependencies text[] NOT NULL,
state text NOT NULL CHECK (state IN ('pending','running','completed','failed','unknown')),
current_attempt uuid,
checkpoint jsonb NOT NULL DEFAULT '{}',
result jsonb,
PRIMARY KEY (task_id, step_id)
);
CREATE TABLE %1$I.muse_attempt (
attempt_id uuid PRIMARY KEY,
task_id uuid NOT NULL,
step_id text NOT NULL,
worker_id text NOT NULL,
lease_token uuid NOT NULL,
lease_until timestamptz NOT NULL,
scope_generation bigint,
heartbeat_at timestamptz NOT NULL DEFAULT clock_timestamp(),
state text NOT NULL CHECK (state IN ('running','expired','paused','cancelled','completed','failed','unknown')),
call_state text NOT NULL DEFAULT 'not_sent' CHECK (call_state IN ('not_sent','sent','unknown','saved')),
call_reference text,
result jsonb,
failure_code text,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
FOREIGN KEY (task_id, step_id) REFERENCES %1$I.muse_step(task_id, step_id)
);
CREATE TABLE %1$I.muse_task_scope_lease (
run_purpose public.muse_purpose NOT NULL,
scope_key text NOT NULL,
holder_task_id uuid,
generation bigint NOT NULL CHECK (generation > 0),
lease_until timestamptz NOT NULL,
PRIMARY KEY (run_purpose, scope_key)
);
CREATE TABLE %1$I.muse_task_event (
event_id uuid PRIMARY KEY,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
step_id text,
attempt_id uuid,
sequence bigint NOT NULL,
event_type text NOT NULL,
occurred_at timestamptz NOT NULL DEFAULT clock_timestamp(),
payload_version integer NOT NULL,
payload jsonb NOT NULL,
payload_hash text NOT NULL,
UNIQUE(task_id, sequence)
);
CREATE INDEX ON %1$I.muse_task (run_purpose, state, created_at);
CREATE INDEX ON %1$I.muse_step (state, task_id);
CREATE INDEX ON %1$I.muse_attempt (task_id, state, lease_until);
$ddl$, namespace);
END LOOP;
END
$migration$;
-- W06 预算与配置:额度账户串行化预留,金额事实留在逐调用账本。
-- W08 原文:授权和租约先持久化,完整归档事务同时写入清单与字节。
DO $raw_lifecycle$
DECLARE namespace text;
BEGIN
FOREACH namespace IN ARRAY ARRAY['public', 'evaluation'] LOOP
EXECUTE format($ddl$
CREATE TABLE %1$I.muse_raw_namespace (
singleton boolean PRIMARY KEY DEFAULT true CHECK (singleton),
namespace_id uuid NOT NULL DEFAULT gen_random_uuid()
);
INSERT INTO %1$I.muse_raw_namespace (singleton) VALUES (true);
CREATE TABLE %1$I.muse_raw_orphan_cleanup (
run_purpose public.muse_purpose NOT NULL,
object_id uuid NOT NULL,
receipt_id uuid NOT NULL DEFAULT gen_random_uuid(),
state text NOT NULL CHECK (state IN ('pending','completed')),
removed boolean,
completed_at timestamptz,
PRIMARY KEY (run_purpose,object_id)
);
CREATE TABLE %1$I.muse_raw_authorization (
authorization_id uuid PRIMARY KEY,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
command_id text NOT NULL,
approved_by text NOT NULL,
source_version text NOT NULL,
content_hashes text[] NOT NULL CHECK (cardinality(content_hashes)>0),
purpose text NOT NULL,
retention_mode text NOT NULL CHECK (retention_mode IN ('temporary','archive','persistent')),
approved_at timestamptz NOT NULL,
valid_until timestamptz NOT NULL,
request_hash text NOT NULL,
revoked boolean NOT NULL DEFAULT false,
call_id text,
call_request_hash text,
response_hash text,
parent_authorization_id uuid REFERENCES %1$I.muse_raw_authorization(authorization_id),
attempt_id uuid REFERENCES %1$I.muse_attempt(attempt_id),
derivation_kind text CHECK (derivation_kind IN ('model','tool')),
evidence_refs uuid[] NOT NULL DEFAULT '{}',
CHECK ((call_id IS NULL) = (call_request_hash IS NULL)),
CHECK (response_hash IS NULL OR call_id IS NOT NULL),
UNIQUE(task_id,command_id)
);
CREATE TABLE %1$I.muse_role_session_authorization (
authorization_id uuid PRIMARY KEY REFERENCES %1$I.muse_raw_authorization(authorization_id),
step_id text NOT NULL,
stage text NOT NULL,
initial_request_hash text NOT NULL,
frozen_input_hash text NOT NULL,
max_model_calls integer NOT NULL CHECK (max_model_calls>0),
max_tool_calls integer NOT NULL CHECK (max_tool_calls>=0),
policy_hash text NOT NULL
);
CREATE TABLE %1$I.muse_raw_lease (
lease_id uuid PRIMARY KEY,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
authorization_id uuid NOT NULL REFERENCES %1$I.muse_raw_authorization(authorization_id),
command_id text NOT NULL,
created_at timestamptz NOT NULL,
retain_until timestamptz NOT NULL,
state text NOT NULL CHECK (state IN ('open','closed','migrating','migrated')),
archive_id uuid,
archive_authorization_id uuid REFERENCES %1$I.muse_raw_authorization(authorization_id),
cleanup_receipt uuid,
failure_code text,
UNIQUE(task_id,command_id),
CHECK (retain_until>created_at AND retain_until<=created_at+interval '24 hours')
);
CREATE TABLE %1$I.muse_raw_archive (
archive_id uuid PRIMARY KEY,
lease_id uuid UNIQUE REFERENCES %1$I.muse_raw_lease(lease_id),
legacy_ref text UNIQUE,
legacy_manifest jsonb,
CHECK ((lease_id IS NULL) <> (legacy_ref IS NULL)),
authorization_id uuid NOT NULL REFERENCES %1$I.muse_raw_authorization(authorization_id),
tree_hash text NOT NULL,
entry_count integer NOT NULL CHECK (entry_count>0),
total_bytes bigint NOT NULL CHECK (total_bytes>=0),
receipt_id uuid NOT NULL,
created_at timestamptz NOT NULL
);
CREATE TABLE %1$I.muse_raw_archive_item (
archive_id uuid NOT NULL REFERENCES %1$I.muse_raw_archive(archive_id),
content_hash text NOT NULL,
content bytea NOT NULL,
PRIMARY KEY(archive_id,content_hash)
);
CREATE TABLE %1$I.muse_runtime_evidence (
evidence_id uuid PRIMARY KEY,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
attempt_id uuid NOT NULL REFERENCES %1$I.muse_attempt(attempt_id),
kind text NOT NULL CHECK (kind IN ('model_input','model_response','tool_result','failure')),
reference_id text NOT NULL,
content_hash text NOT NULL,
content bytea,
authorization_id uuid REFERENCES %1$I.muse_raw_authorization(authorization_id),
outcome text NOT NULL CHECK (outcome IN ('completed','failed','partial')),
metadata jsonb NOT NULL,
revision integer NOT NULL DEFAULT 1,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
UNIQUE(attempt_id,kind,reference_id),
CHECK (content IS NULL OR authorization_id IS NOT NULL)
);
$ddl$, namespace);
EXECUTE format('REVOKE INSERT,UPDATE,DELETE ON %I.muse_raw_namespace FROM muse_app,muse_eval', namespace);
END LOOP;
END
$raw_lifecycle$;
DO $budget_config$
DECLARE
namespace text;
BEGIN
FOREACH namespace IN ARRAY ARRAY['public', 'evaluation'] LOOP
EXECUTE format($ddl$
CREATE TABLE %1$I.muse_budget_account (
run_purpose public.muse_purpose NOT NULL,
account_id text NOT NULL,
policy_version text NOT NULL,
policy jsonb NOT NULL,
policy_hash text NOT NULL,
PRIMARY KEY (run_purpose, account_id)
);
CREATE TABLE %1$I.muse_task_budget (
task_id uuid PRIMARY KEY REFERENCES %1$I.muse_task(task_id),
account_id text NOT NULL,
plan jsonb NOT NULL,
plan_hash text NOT NULL,
stopped boolean NOT NULL DEFAULT false
);
CREATE TABLE %1$I.muse_budget_reservation (
run_purpose public.muse_purpose NOT NULL,
call_id text NOT NULL,
account_id text NOT NULL,
task_id uuid NOT NULL REFERENCES %1$I.muse_task(task_id),
attempt_id uuid NOT NULL REFERENCES %1$I.muse_attempt(attempt_id),
role_id text NOT NULL,
request_hash text NOT NULL,
state text NOT NULL CHECK (state IN ('reserved','in_flight','unknown','settled','released')),
reserved_amount numeric(24,6) NOT NULL CHECK (reserved_amount >= 0),
actual_amount numeric(24,6) CHECK (actual_amount >= 0),
window_start timestamptz NOT NULL,
window_end timestamptz NOT NULL,
expires_at timestamptz NOT NULL,
sent_at timestamptz,
receipt_id text,
reason text,
over_budget boolean NOT NULL DEFAULT false,
PRIMARY KEY (run_purpose, call_id),
CHECK ((state = 'settled') = (actual_amount IS NOT NULL))
);
CREATE INDEX ON %1$I.muse_budget_reservation (run_purpose,account_id,window_start);
CREATE INDEX ON %1$I.muse_budget_reservation (task_id,role_id);
CREATE TABLE %1$I.muse_runtime_config_version (
run_purpose public.muse_purpose NOT NULL,
config_id text NOT NULL,
version text NOT NULL,
content jsonb NOT NULL,
content_hash text NOT NULL,
created_at timestamptz NOT NULL DEFAULT clock_timestamp(),
PRIMARY KEY (run_purpose,config_id,version)
);
CREATE TABLE %1$I.muse_runtime_config_validation (
receipt_id uuid PRIMARY KEY,
run_purpose public.muse_purpose NOT NULL,
config_id text NOT NULL,
version text NOT NULL,
content_hash text NOT NULL,
validator_id text NOT NULL,
evidence_refs jsonb NOT NULL,
validated_at timestamptz NOT NULL DEFAULT clock_timestamp(),
FOREIGN KEY (run_purpose,config_id,version)
REFERENCES %1$I.muse_runtime_config_version(run_purpose,config_id,version)
);
CREATE TABLE %1$I.muse_runtime_config_active (
run_purpose public.muse_purpose NOT NULL,
config_id text NOT NULL,
version text,
validation_receipt uuid REFERENCES %1$I.muse_runtime_config_validation(receipt_id),
approval_ref text,
generation bigint NOT NULL DEFAULT 0,
PRIMARY KEY (run_purpose,config_id)
);
CREATE TABLE %1$I.muse_task_config_binding (
task_id uuid PRIMARY KEY REFERENCES %1$I.muse_task(task_id),
config_id text NOT NULL,
version text NOT NULL,
content_hash text NOT NULL,
content jsonb NOT NULL,
validation_receipt uuid NOT NULL REFERENCES %1$I.muse_runtime_config_validation(receipt_id)
);
$ddl$, namespace);
END LOOP;
END
$budget_config$;