- 百工域 domains/baigong:capabilities(统一能力契约:执行类型 builtin/qiankun/mcp/http + 四维风险标注 + 来源版本)、mandates(授契 grant / 行契 charter 预算时间窗,撤销立即生效)、tool_calls(净化摘要 + 观察标证);请命复用 pending_confirmations 加 expires_at/brief_json(超时=拒绝执行 HJ-1302,六要素说明) - 司命裁决器 siming.py:四维取最高档(能力/数据/后果/情境);禁域为代码常量不可配置(递归删根/格式化/写裸设备/fork 炸弹、file:// 协议、密钥材料外发)——专项测试断言恶意标注与篡改对象仍拒 HJ-1301 - 执行链:禁域→裁决(HJ-1303 未授契拒且双留痕)→请命→执行(乾坤经 SandboxClient HMAC;MCP 经 call_tool;内置无副作用)→净化标证(观察非事实,敏感遮蔽+体量截断)→行动账 - MCP 登记/启停/刷新自动派生与撤下 capabilities(博闻登记接入点);内置能力种子(echo/datetime/qiankun.shell/web.fetch) - 端点:/capabilities /mandates /petitions /tool-calls + /tools 兼容投影;前端百工页四区(能力目录四维标注/授契行契/请命批准拒绝/行动账观察标证) - 退役:modules/safety|skills|tool_runtime|agent、application/tool_runtime、services/skills|safety、tool_policies/skills/model_endpoint_overrides 表(迁移 006)、前端 SkillManager/ToolsConsole/旧 tools 页 - 验收(test_baigong_siming 四用例):未授契被拒留痕、请命超时不执行、禁域不可关闭、结果为观察不入忆;后端 454、前端 220、BFF 4、神识 5 包全绿(2 个预存环境失败不变)
428 lines
14 KiB
Python
428 lines
14 KiB
Python
"""司命台治理对象种子数据。
|
|
|
|
从前端 lib/mock/data.ts 移植,供本地启动时让司命台治理视图有初始内容。
|
|
按主键存在则跳过,重复执行幂等;不记审计(系统初始化,非用户治理操作)。
|
|
"""
|
|
|
|
from datetime import UTC, datetime
|
|
|
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
|
|
from app.domains.baigong.models import Capability
|
|
from app.models.governance import (
|
|
AgentRosterEntry,
|
|
Goal,
|
|
PerceptionChannel,
|
|
Plan,
|
|
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"),
|
|
]
|
|
|
|
|
|
_BUILTIN_CAPABILITIES = [
|
|
# name, 描述, 执行类型, 执行器, 四维风险, 数据范围, 目的地, consent
|
|
(
|
|
"builtin.echo",
|
|
"回显输入(无副作用演示能力)",
|
|
"builtin",
|
|
"echo",
|
|
{"capability": "L0", "data": "L0", "consequence": "L0", "context": "L0"},
|
|
"",
|
|
"",
|
|
"allow",
|
|
),
|
|
(
|
|
"builtin.datetime",
|
|
"读取当前时间",
|
|
"builtin",
|
|
"datetime.now",
|
|
{"capability": "L0", "data": "L0", "consequence": "L0", "context": "L0"},
|
|
"",
|
|
"",
|
|
"allow",
|
|
),
|
|
(
|
|
"qiankun.shell",
|
|
"在乾坤沙箱中执行命令(隔离执行,HMAC 授契)",
|
|
"qiankun",
|
|
"",
|
|
{"capability": "L2", "data": "L1", "consequence": "L3", "context": "L1"},
|
|
"沙箱工作目录",
|
|
"本机沙箱进程",
|
|
"ask",
|
|
),
|
|
(
|
|
"web.fetch",
|
|
"抓取指定 URL 内容(受限出站)",
|
|
"http",
|
|
"",
|
|
{"capability": "L1", "data": "L1", "consequence": "L2", "context": "L1"},
|
|
"目标 URL 响应体",
|
|
"目标站点",
|
|
"allow",
|
|
),
|
|
]
|
|
|
|
|
|
async def _seed_capabilities(session: AsyncSession) -> None:
|
|
"""内置能力按 (owner, name) 幂等登记(source=builtin,四维风险标注随代码演进)。"""
|
|
import json as _json
|
|
|
|
from sqlalchemy import select
|
|
|
|
existing = set(
|
|
(
|
|
await session.execute(
|
|
select(Capability.name).where(Capability.source == "builtin")
|
|
)
|
|
)
|
|
.scalars()
|
|
.all()
|
|
)
|
|
for name, desc, executor_type, ref, dims, scope, dests, consent in _BUILTIN_CAPABILITIES:
|
|
if name in existing:
|
|
continue
|
|
session.add(
|
|
Capability(
|
|
name=name,
|
|
owner_scope="local-user",
|
|
description=desc,
|
|
executor_type=executor_type,
|
|
executor_ref=ref,
|
|
risk_dims_json=_json.dumps(dims),
|
|
data_scope=scope,
|
|
destinations=dests,
|
|
consent_mode=consent,
|
|
source="builtin",
|
|
)
|
|
)
|
|
|
|
|
|
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_capabilities(session)
|
|
await session.commit()
|