Files
hunjing/backend/app/repositories/governance.py
T
chenxin 684261f4b4 M5:百工与司命——能力注册表、授契行契请命、禁域裁决与观察标证
- 百工域 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 个预存环境失败不变)
2026-08-27 19:17:21 +08:00

167 lines
4.7 KiB
Python

"""司命台治理对象数据访问(api-spec §19.4)。
目标 / 感知通道 / 世界实体 / 计划 / 天工阁角色的 CRUD 数据访问;
均为本地单用户全局对象,不挂 character_id。
"""
from sqlalchemy import func, select
from sqlalchemy.ext.asyncio import AsyncSession
from app.core.time import utc_now
from app.models.governance import (
AgentRosterEntry,
Goal,
PerceptionChannel,
Plan,
WorldEntity,
)
# —— 太一枢 · 目标 ——
async def list_goals(session: AsyncSession) -> list[Goal]:
result = await session.execute(
select(Goal).order_by(Goal.priority.desc(), Goal.updated_at.desc())
)
return list(result.scalars().all())
async def get_goal(session: AsyncSession, goal_id: str) -> Goal | None:
return await session.get(Goal, goal_id)
async def add_goal(session: AsyncSession, goal: Goal) -> Goal:
session.add(goal)
await session.flush()
return goal
async def list_goals_by_character(
session: AsyncSession,
character_id: str,
status_filter: str | None = None,
) -> list[Goal]:
"""按 character_id 获取目标列表;可选的 status 过滤。"""
stmt = select(Goal).where(Goal.character_id == character_id)
if status_filter:
stmt = stmt.where(Goal.status == status_filter)
stmt = stmt.order_by(Goal.priority.desc(), Goal.updated_at.desc())
result = await session.execute(stmt)
return list(result.scalars().all())
async def update_goal_progress(
session: AsyncSession,
goal_id: str,
progress: float,
) -> Goal | None:
"""更新目标的 progress 与 updated_at 字段。"""
goal = await session.get(Goal, goal_id)
if goal is None:
return None
goal.progress = progress
goal.updated_at = utc_now()
await session.flush()
return goal
async def list_active_session_goal(session: AsyncSession, character_id: str) -> Goal | None:
"""查找指定人物活跃的 session 级目标(各人物至多一个活跃 session 目标)。"""
result = await session.execute(
select(Goal)
.where(
Goal.character_id == character_id,
Goal.level == "session",
Goal.status == "active",
)
.limit(1)
)
return result.scalar_one_or_none()
# —— 烛照枢 · 感知通道 ——
async def list_perception_channels(session: AsyncSession) -> list[PerceptionChannel]:
result = await session.execute(select(PerceptionChannel).order_by(PerceptionChannel.channel))
return list(result.scalars().all())
async def get_perception_channel(session: AsyncSession, channel: str) -> PerceptionChannel | None:
return await session.get(PerceptionChannel, channel)
async def add_perception_channel(
session: AsyncSession, channel: PerceptionChannel
) -> PerceptionChannel:
session.add(channel)
await session.flush()
return channel
# —— 河图枢 · 世界实体 ——
async def list_world_entities(session: AsyncSession) -> list[WorldEntity]:
result = await session.execute(select(WorldEntity).order_by(WorldEntity.updated_at.desc()))
return list(result.scalars().all())
async def get_world_entity(session: AsyncSession, entity_id: str) -> WorldEntity | None:
return await session.get(WorldEntity, entity_id)
async def add_world_entity(session: AsyncSession, entity: WorldEntity) -> WorldEntity:
session.add(entity)
await session.flush()
return entity
# —— 洛书枢 · 计划 ——
async def list_plans(session: AsyncSession) -> list[Plan]:
result = await session.execute(select(Plan).order_by(Plan.updated_at.desc()))
return list(result.scalars().all())
async def get_plan(session: AsyncSession, plan_id: str) -> Plan | None:
return await session.get(Plan, plan_id)
async def add_plan(session: AsyncSession, plan: Plan) -> Plan:
session.add(plan)
await session.flush()
return plan
# —— 天工阁枢 · 角色在岗 ——
async def list_agent_roster(session: AsyncSession) -> list[AgentRosterEntry]:
result = await session.execute(select(AgentRosterEntry).order_by(AgentRosterEntry.role))
return list(result.scalars().all())
async def get_agent(session: AsyncSession, role: str) -> AgentRosterEntry | None:
return await session.get(AgentRosterEntry, role)
async def add_agent(session: AsyncSession, agent: AgentRosterEntry) -> AgentRosterEntry:
session.add(agent)
await session.flush()
return agent
# —— 百工枢 · 工具策略(§15/§16 共享)——
# —— 计数(供九枢健康度聚合)——
async def count(session: AsyncSession, model: type) -> int:
result = await session.execute(select(func.count()).select_from(model))
return int(result.scalar_one())