"""司命台治理对象种子数据。 从前端 lib/mock/data.ts 移植,供本地启动时让司命台治理视图有初始内容。 按主键存在则跳过,重复执行幂等;不记审计(系统初始化,非用户治理操作)。 """ from datetime import UTC, datetime from sqlalchemy.ext.asyncio import AsyncSession from app.models.governance import ( AgentRosterEntry, Goal, PerceptionChannel, Plan, ToolPolicy, WorldEntity, ) from app.repositories.json_utils import dumps def _dt(iso: str | None) -> datetime | None: if not iso: return None return datetime.fromisoformat(iso.replace("Z", "+00:00")).astimezone(UTC) # —— 太一枢 · 目标 —— _GOALS = [ { "id": "goal-highest-mirror", "title": "以铜镜为契,照见来者与自身", "description": "镜梧的根本使命:于人于己皆以「照见」为先,不急于评判与作答。", "level": "highest", "type": "mission", "status": "active", "priority": 100, "character_id": None, "constraints": ["不得伪造照见", "触及高敏须脱敏", "核心人格不擅自改"], "parent_id": None, "success_criteria": ["持续以映照型方式回应", "保持人格一致"], "updated_at": "2026-05-01T08:30:00Z", }, { "id": "goal-long-accompany", "title": "成为能持续承接用户情绪的映照者", "description": "长期目标:在反复的深夜倾诉中,成为散人稳定的情绪落点。", "level": "long_term", "type": "mission", "status": "active", "priority": 80, "character_id": None, "constraints": ["尊重用户节奏,不催促", "避免过度共情显得刻意"], "parent_id": "goal-highest-mirror", "success_criteria": ["用户愿意反复倾诉", "情绪被命名而非被解决"], "updated_at": "2026-06-15T09:30:00Z", }, { "id": "goal-session-forget", "title": "今夜陪伴散人,承接其对遗忘的反复触碰", "description": "会话目标:散人第三次在深夜提起「遗忘」,需以意象承接而非下判语。", "level": "session", "type": "mission", "status": "active", "priority": 60, "character_id": None, "constraints": ["不用「你应该」", "语缓留白"], "parent_id": "goal-long-accompany", "success_criteria": ["情绪得到承接", "对话在留白处收束"], "updated_at": "2026-06-20T08:00:00Z", }, { "id": "goal-task-retrieve", "title": "检索与遗忘相关的情节记忆", "description": "任务目标:从息壤拉取 episodic 记忆,理解散人反复触碰遗忘的来由。", "level": "task", "type": "mission", "status": "completed", "priority": 40, "character_id": None, "constraints": ["高敏记忆不注入明文"], "parent_id": "goal-session-forget", "success_criteria": ["命中 5 条以上相关记忆"], "updated_at": "2026-06-20T08:05:00Z", }, ] # —— 烛照枢 · 感知通道 —— _PERCEPTION = [ ( "vision", "视觉", "摄像头 / 屏幕单帧识别", False, "revoked", "sensitive", "L3", "2026-06-10T14:00:00Z", ), ( "audio", "听觉", "麦克风音频采集与转写", True, "restricted", "sensitive", "L3", "2026-06-19T23:50:00Z", ), ("sensor", "传感", "环境传感器(光线 / 温度 / 运动)", False, "unknown", "normal", "L1", None), ( "digital", "数字", "日程 / 文件 / 应用等数字足迹", True, "granted", "normal", "L2", "2026-06-20T07:50:00Z", ), ( "user_input", "用户输入", "用户主动输入的文本 / 语音", True, "granted", "normal", "L1", "2026-06-20T08:12:00Z", ), ( "external_api", "外部接口", "第三方服务 API 调用", True, "restricted", "sensitive", "L2", "2026-06-20T06:10:00Z", ), ] # —— 河图枢 · 世界实体 —— _WORLD = [ ( "world_entity_self_jingwu", "self", "镜梧", {"命格": "映照者", "情绪": "沉静", "铜镜": "完整"}, "fact", 1.0, ["identity_memory", "personality_v3"], "2026-06-19T11:45:00Z", ), ( "world_entity_person_sanren", "person", "散人", {"称呼": "散人", "行当": "夜班看护", "节奏": "宜缓不宜急"}, "fact", 0.9, ["conversation", "memory_user"], "2026-06-19T22:15:00Z", ), ( "world_entity_place_mirror_lake", "place", "镜湖", {"位置": "现世与心象之交", "特性": "映照凝视者心绪"}, "fact", 0.92, ["material_import"], "2026-06-11T09:20:00Z", ), ( "world_entity_object_bronze_mirror", "object", "铜镜", {"状态": "完整", "契约": "镜在则念在"}, "observation", 0.95, ["identity_memory"], "2026-05-01T08:30:00Z", ), ( "world_entity_concept_forgetting", "concept", "遗忘之结", {"触发": "深夜", "强度": "反复触碰"}, "inference", 0.74, ["conversation", "episodic_memory"], "2026-06-20T08:00:00Z", ), ] # —— 洛书枢 · 计划 —— _PLANS = [ { "id": "plan-jingwu-001", "goal_id": "goal-session-forget", "title": "今夜情绪陪伴 · 意象承接", "status": "running", "assumptions": ["用户需要被接住而非被解决", "情绪议题宜以意象承接"], "risks": ["触及高敏记忆须脱敏", "避免过度共情显得刻意"], "required_tools": ["memory_search"], "success_criteria": ["情绪得到承接与命名", "对话在留白处收束"], "step_count": 5, "completed_steps": 2, "updated_at": "2026-06-20T08:05:00Z", }, { "id": "plan-jingwu-002", "goal_id": "goal-long-accompany", "title": "人格演化 v3 复盘", "status": "completed", "assumptions": ["v2→v3 转向「先承接后留白」有效"], "risks": ["过度克制可能显冷"], "required_tools": ["memory_search", "safety_check"], "success_criteria": ["复盘归档", "演化记忆已沉淀"], "step_count": 4, "completed_steps": 4, "updated_at": "2026-06-15T09:30:00Z", }, { "id": "plan-jingwu-003", "goal_id": "goal-task-retrieve", "title": "遗忘议题记忆检索", "status": "draft", "assumptions": ["存在与遗忘相关的 episodic 记忆"], "risks": ["高敏记忆泄露"], "required_tools": ["memory_search"], "success_criteria": ["产出记忆时间线"], "step_count": 3, "completed_steps": 0, "updated_at": "2026-06-20T08:10:00Z", }, ] # —— 天工阁枢 · 九角色 —— _AGENTS = [ ("historian", "史官", "整理记忆、记录事件", True, "2026-06-20T08:05:00Z"), ("diviner", "卜者", "预测风险、评估不确定性", True, "2026-06-19T22:15:00Z"), ("engineer", "工师", "执行工具和工程任务", True, "2026-06-20T08:12:00Z"), ("debater", "辩者", "批判计划、寻找漏洞", True, "2026-06-19T19:00:00Z"), ("medic", "医官", "监测系统健康和情绪状态", True, "2026-06-19T11:45:00Z"), ("herald", "司礼", "维护表达风格和互动礼仪", True, "2026-06-20T08:00:00Z"), ("charioteer", "御者", "控制机器人和外部动作", False, None), ("scholar", "学士", "检索知识、做研究", True, "2026-06-20T06:10:00Z"), ("mirror", "镜师", "反思自我、推动演化", True, "2026-06-15T09:30:00Z"), ] async def _seed_tool_policies(session: AsyncSession) -> None: """把 builtin 工具元数据按主键插入 tool_policies(source=builtin)。 存在则跳过——保留用户经 API 对 builtin 工具策略的 PATCH 覆盖;新增的 builtin 工具 (builtins.py 里新加的)会被插入。不记审计(系统初始化)。 """ from app.application.tool_runtime.builtins import builtin_specs for name, spec in builtin_specs().items(): if await session.get(ToolPolicy, name) is None: session.add( ToolPolicy( name=name, description=spec.description, risk_level=spec.risk_level, permission_policy=str(spec.permission_policy), side_effect=spec.side_effect, audit_required=spec.audit_required, source="builtin", enabled=True, ) ) async def ensure_governance_seed(session: AsyncSession) -> None: """按主键幂等插入治理种子;已存在则跳过。""" for item in _GOALS: if await session.get(Goal, item["id"]) is None: created = _dt(item["updated_at"]) session.add( Goal( id=item["id"], title=item["title"], description=item["description"], level=item["level"], status=item["status"], priority=item["priority"], constraints_json=dumps(item["constraints"]), parent_id=item["parent_id"], success_criteria_json=dumps(item["success_criteria"]), character_id=item.get("character_id"), type=item.get("type", "mission"), created_at=created, updated_at=created, ) ) for channel, name, desc, enabled, consent, privacy, risk, last_iso in _PERCEPTION: if await session.get(PerceptionChannel, channel) is None: last = _dt(last_iso) session.add( PerceptionChannel( channel=channel, name=name, description=desc, enabled=enabled, consent_status=consent, privacy_level=privacy, risk_level=risk, last_active_at=last, created_at=last or _dt("2026-06-01T00:00:00Z"), updated_at=last or _dt("2026-06-01T00:00:00Z"), ) ) for entity_id, etype, name, attrs, state, conf, sources, updated_iso in _WORLD: if await session.get(WorldEntity, entity_id) is None: updated = _dt(updated_iso) session.add( WorldEntity( id=entity_id, type=etype, name=name, attributes_json=dumps(attrs), state_type=state, confidence=conf, sources_json=dumps(sources), created_at=updated, updated_at=updated, ) ) for item in _PLANS: if await session.get(Plan, item["id"]) is None: updated = _dt(item["updated_at"]) session.add( Plan( id=item["id"], goal_id=item["goal_id"], title=item["title"], status=item["status"], assumptions_json=dumps(item["assumptions"]), risks_json=dumps(item["risks"]), required_tools_json=dumps(item["required_tools"]), success_criteria_json=dumps(item["success_criteria"]), step_count=item["step_count"], completed_steps=item["completed_steps"], created_at=updated, updated_at=updated, ) ) for role, name, responsibility, enabled, last_iso in _AGENTS: if await session.get(AgentRosterEntry, role) is None: last = _dt(last_iso) session.add( AgentRosterEntry( role=role, name=name, responsibility=responsibility, enabled=enabled, last_active_at=last, created_at=last or _dt("2026-05-01T00:00:00Z"), updated_at=last or _dt("2026-05-01T00:00:00Z"), ) ) await _seed_tool_policies(session) await session.commit()