Files
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

111 lines
6.4 KiB
Python
Raw Permalink 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.
"""百工域表模型:能力注册表 / 授契与行契 / 调用记录。
设计依据 docs/domain/mandate-and-capability-model.md 与 docs/architecture/data-model.md §6:
- ``capabilities`` 统一能力契约(工具/MCP/Skill/感官/形器),保留执行类型;
- ``mandates`` 授契(单次范围)与行契(重复执行:范围/预算/时间窗),撤销立即生效;
- ``tool_calls`` 调用记录与证据链(净化后的输入输出摘要 + 观察标证);
- 请命复用 ``pending_confirmations`` 表(models/safety.py,迁移 006 增列改造)。
"""
from datetime import datetime
from sqlalchemy import JSON, DateTime, ForeignKey, Index, String, Text
from sqlalchemy.orm import Mapped, mapped_column
from app.core.ids import new_id
from app.core.time import utc_now
from app.db.base import Base
# 执行类型(data-model §6):HTTP / MCP / 乾坤 / 内置
EXECUTOR_TYPES = ("builtin", "qiankun", "mcp", "http")
# 四维风险标注(mandate-and-capability-model.md §3):能力/数据/后果/情境
RISK_DIMENSIONS = ("capability", "data", "consequence", "context")
class Capability(Base):
"""百工能力注册表:稳定标识 + 参数 Schema + 执行类型 + 四维风险标注。"""
__tablename__ = "capabilities"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("cap"))
# 稳定标识(契约词典 §5:capability_registry);同 owner 内唯一
name: Mapped[str] = mapped_column(String(120), nullable=False)
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
description: Mapped[str] = mapped_column(Text, nullable=False, default="")
# 执行类型:builtin / qiankun / mcp / http
executor_type: Mapped[str] = mapped_column(String(20), nullable=False)
# 执行器绑定(builtin 工具名 / MCP server id / 乾坤命令模板等)
executor_ref: Mapped[str] = mapped_column(String(200), nullable=False, default="")
# 参数 Schema(JSON Schema 子集;供校验与 UI 表单)
params_schema_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
# 四维风险标注:{"capability": "L1", "data": "L2", "consequence": "L0", "context": "L1"}
risk_dims_json: Mapped[str] = mapped_column(Text, nullable=False, default="{}")
# 读取/传出/修改的数据范围与外部目的地(能力契约展示)
data_scope: Mapped[str] = mapped_column(Text, nullable=False, default="")
destinations: Mapped[str] = mapped_column(Text, nullable=False, default="")
# 来源与版本:builtin / official_signed / user_custom
source: Mapped[str] = mapped_column(String(20), nullable=False, default="builtin")
version: Mapped[str] = mapped_column(String(40), nullable=False, default="1")
# 司命裁决模式:allow(授契即行)/ ask(每次请命);禁域由裁决器硬编码,不落库不可配
consent_mode: Mapped[str] = mapped_column(String(10), nullable=False, default="allow")
enabled: Mapped[bool] = mapped_column(default=True, nullable=False)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
__table_args__ = (
Index("uq_capabilities_owner_name", "owner_scope", "name", unique=True),
)
class Mandate(Base):
"""授契(grant:单次范围)与行契(charter:限定条件内重复执行)。"""
__tablename__ = "mandates"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("mdt"))
owner_scope: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
# 授予人物(空 = 用户全局,所有人物可用)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), nullable=True, index=True
)
capability_id: Mapped[str] = mapped_column(
String(80), ForeignKey("capabilities.id"), nullable=False, index=True
)
# grant(授契:范围用完即止)/ charter(行契:条件内重复执行)
kind: Mapped[str] = mapped_column(String(10), nullable=False, default="grant")
# 范围(允许的参数/数据/目标约束,JSON;行契含预算与停止条件)
scope_json: Mapped[dict] = mapped_column(JSON, nullable=False, default=dict)
# 行契:时间窗与预算(次数上限;0 = 不限次但受停止条件约束)
expires_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
budget_total: Mapped[int] = mapped_column(nullable=False, default=1)
budget_used: Mapped[int] = mapped_column(nullable=False, default=0)
status: Mapped[str] = mapped_column(String(20), nullable=False, default="active", index=True)
granted_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now)
revoked_at: Mapped[datetime | None] = mapped_column(DateTime(timezone=True), nullable=True)
class ToolCall(Base):
"""调用记录与证据链:净化后的输入输出摘要 + 观察标证(不落原文)。"""
__tablename__ = "tool_calls"
id: Mapped[str] = mapped_column(String(80), primary_key=True, default=lambda: new_id("tc"))
run_id: Mapped[str | None] = mapped_column(String(80), ForeignKey("runs.id"), index=True)
character_id: Mapped[str | None] = mapped_column(
String(80), ForeignKey("characters.id"), index=True
)
capability_id: Mapped[str] = mapped_column(String(80), nullable=False, index=True)
trace_id: Mapped[str | None] = mapped_column(String(80), index=True)
# 司命裁决:allow / ask_approved / denied_forbidden(HJ-1301) / denied_unmandated(HJ-1303)
decision: Mapped[str] = mapped_column(String(40), nullable=False)
# 命中的授契/行契(拒绝时为空)
mandate_id: Mapped[str | None] = mapped_column(String(80))
status: Mapped[str] = mapped_column(String(20), nullable=False, default="ok")
error_code: Mapped[str | None] = mapped_column(String(40))
# 净化后摘要(体量受限,不含原文全文)
input_digest: Mapped[str] = mapped_column(Text, nullable=False, default="")
output_digest: Mapped[str] = mapped_column(Text, nullable=False, default="")
# 观察标证:来源、执行条件、观察语义(非事实)——供灵台作为观察接纳
evidence_json: Mapped[str] = mapped_column(JSON, nullable=False, default=dict)
duration_ms: Mapped[int] = mapped_column(nullable=False, default=0)
created_at: Mapped[datetime] = mapped_column(DateTime(timezone=True), default=utc_now, index=True)