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 个预存环境失败不变)
This commit is contained in:
1 parent
144415247e
commit
684261f4b4
97 files changed
+3075
-5554
No files matched your search
@@ -14,7 +14,6 @@ from app.models.governance import (
|
||||
Goal,
|
||||
PerceptionChannel,
|
||||
Plan,
|
||||
ToolPolicy,
|
||||
WorldEntity,
|
||||
)
|
||||
from app.modules.runtime.events import Event
|
||||
@@ -39,11 +38,7 @@ from app.schemas.admin import (
|
||||
WorldEntityResponse,
|
||||
)
|
||||
from app.schemas.safety import (
|
||||
CreateToolPolicyRequest,
|
||||
SafetyPolicyItem,
|
||||
ToolCallDetail,
|
||||
ToolPolicyResponse,
|
||||
UpdateToolPolicyRequest,
|
||||
)
|
||||
from app.services.audit import record_audit_event
|
||||
from app.services.serializers import (
|
||||
@@ -51,8 +46,6 @@ from app.services.serializers import (
|
||||
goal_to_response,
|
||||
perception_channel_to_response,
|
||||
plan_to_response,
|
||||
tool_policy_to_response,
|
||||
tool_policy_to_safety_item,
|
||||
world_entity_to_response,
|
||||
)
|
||||
|
||||
@@ -81,28 +74,17 @@ async def load_active_goals_context(character_id: str) -> str | None:
|
||||
|
||||
目标按优先级排序(``prioritize_active_goals``),最高优先级在前。
|
||||
"""
|
||||
from app.modules.goal_orchestrator import prioritize_active_goals
|
||||
from app.modules.runtime.goals import build_goals_context
|
||||
|
||||
async with AsyncSessionLocal() as session:
|
||||
goals = await governance_repo.list_goals_by_character(
|
||||
session, character_id, status_filter="active"
|
||||
)
|
||||
if not goals:
|
||||
return None
|
||||
goals_dicts = [
|
||||
{
|
||||
"title": g.title,
|
||||
"level": g.level,
|
||||
"status": g.status,
|
||||
"priority": g.priority,
|
||||
"progress": g.progress,
|
||||
}
|
||||
for g in goals
|
||||
lines = [
|
||||
f"- [{g.level}] {g.title}(优先级 {g.priority},进度 {g.progress}%)"
|
||||
for g in sorted(goals, key=lambda g: -(g.priority or 0))
|
||||
]
|
||||
# 优先级排序后注入运行时,使最高优先级目标最先被看到
|
||||
sorted_goals = prioritize_active_goals(goals_dicts)
|
||||
return build_goals_context(sorted_goals)
|
||||
return "【当前目标】\n" + "\n".join(lines)
|
||||
|
||||
|
||||
async def emit_goal_event(
|
||||
@@ -520,107 +502,6 @@ async def delete_agent(role: str) -> None:
|
||||
# —— 百工枢 · 工具策略(§15 / §16 共享)——
|
||||
|
||||
|
||||
def _builtin_tool_names() -> set[str]:
|
||||
"""builtin 工具名集合(惰性 import,避免 application→services 循环依赖)。"""
|
||||
from app.application.tool_runtime.builtins import builtin_specs
|
||||
|
||||
return set(builtin_specs().keys())
|
||||
|
||||
|
||||
async def list_tool_policies() -> list[ToolPolicyResponse]:
|
||||
"""GET /tools:完整元数据,executable 按 builtin 派生。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
policies = await governance_repo.list_tool_policies(session)
|
||||
builtins = _builtin_tool_names()
|
||||
return [tool_policy_to_response(p, executable=p.name in builtins) for p in policies]
|
||||
|
||||
|
||||
async def list_safety_policies() -> list[SafetyPolicyItem]:
|
||||
"""GET /safety/policies:策略子集。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
policies = await governance_repo.list_tool_policies(session)
|
||||
return [tool_policy_to_safety_item(p) for p in policies]
|
||||
|
||||
|
||||
async def create_tool_policy(payload: CreateToolPolicyRequest) -> ToolPolicyResponse:
|
||||
"""POST /tools、POST /safety/policies:注册工具策略(source=custom)。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
if await governance_repo.get_tool_policy(session, payload.name) is not None:
|
||||
raise AppError("invalid_request", f"工具 {payload.name} 已存在。")
|
||||
policy = ToolPolicy(
|
||||
name=payload.name,
|
||||
description=payload.description,
|
||||
risk_level=payload.risk_level,
|
||||
permission_policy=payload.permission_policy,
|
||||
side_effect=payload.side_effect,
|
||||
audit_required=payload.audit_required,
|
||||
source="custom",
|
||||
enabled=payload.enabled,
|
||||
)
|
||||
await governance_repo.add_tool_policy(session, policy)
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="tool.policy_created",
|
||||
actor="user",
|
||||
risk_level=payload.risk_level,
|
||||
input_summary=payload.name,
|
||||
resource_type="tool_policy",
|
||||
resource_id=policy.name,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(policy)
|
||||
return tool_policy_to_response(policy, executable=policy.name in _builtin_tool_names())
|
||||
|
||||
|
||||
async def update_tool_policy(name: str, payload: UpdateToolPolicyRequest) -> ToolPolicyResponse:
|
||||
"""PATCH /tools/{name}、PATCH /safety/policies/{tool}:编辑策略元数据。"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
policy = await governance_repo.get_tool_policy(session, name)
|
||||
if policy is None:
|
||||
raise AppError("not_found", f"工具 {name} 不存在。")
|
||||
updates = payload.model_dump(exclude_unset=True)
|
||||
if not updates:
|
||||
raise AppError("invalid_request", "至少需要提供一个待更新字段。")
|
||||
for key, value in updates.items():
|
||||
setattr(policy, key, value)
|
||||
policy.updated_at = utc_now()
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="tool.policy_updated",
|
||||
actor="user",
|
||||
risk_level=policy.risk_level,
|
||||
input_summary=policy.name,
|
||||
resource_type="tool_policy",
|
||||
resource_id=policy.name,
|
||||
)
|
||||
await session.commit()
|
||||
await session.refresh(policy)
|
||||
return tool_policy_to_response(policy, executable=policy.name in _builtin_tool_names())
|
||||
|
||||
|
||||
async def delete_tool_policy(name: str) -> None:
|
||||
"""DELETE /tools/{name}、DELETE /safety/policies/{tool}:注销策略。
|
||||
|
||||
builtin 工具的策略记录不可删除(执行实现仍在代码层);只允许注销 custom 工具。
|
||||
"""
|
||||
async with AsyncSessionLocal() as session:
|
||||
policy = await governance_repo.get_tool_policy(session, name)
|
||||
if policy is None:
|
||||
raise AppError("not_found", f"工具 {name} 不存在。")
|
||||
if policy.source == "builtin":
|
||||
raise AppError("invalid_request", f"内置工具 {name} 不可删除,可改用 PATCH 禁用。")
|
||||
await session.delete(policy)
|
||||
await record_audit_event(
|
||||
session,
|
||||
action="tool.policy_deleted",
|
||||
actor="user",
|
||||
input_summary=policy.name,
|
||||
resource_type="tool_policy",
|
||||
resource_id=policy.name,
|
||||
)
|
||||
await session.commit()
|
||||
|
||||
|
||||
async def get_tool_call(tool_call_id: str) -> ToolCallDetail:
|
||||
"""GET /tools/calls/{tool_call_id}:单条 tool.* 事件详情。
|
||||
|
||||
|
||||
Reference in new issue
Block a user