deliver: 交付收口(引擎代为提交)
This commit is contained in:
parent
a34c1919c6
commit
b21668b25d
@ -6,27 +6,41 @@
|
||||
项目集成契约(apps/pbls/scripts/tests/offline_contract_smoke.py:267)写法是:
|
||||
|
||||
from pbl_agent_runtime import api as AR
|
||||
AR.pbl_tool_registry_save(tool_key=..., enabled=...)
|
||||
AR.pbl_tool_adjudicate(agent_code=..., tool_key=..., params_json=...)
|
||||
AR.pbl_approval_create(approval_type=...)
|
||||
await AR.pbl_tool_registry_save(tool_key=..., title=..., enabled=1, agent_scope=...)
|
||||
await AR.pbl_tool_registry_list() # -> enabled_keys[13] / disabled_keys[9]
|
||||
await AR.pbl_tool_adjudicate(agent_code=..., tool_key=..., params_json=...)
|
||||
# -> allowed / error_code=AD0x / steps[8]
|
||||
await AR.pbl_approval_create(approval_type=..., target_type=..., target_id=...)
|
||||
await AR.pbl_approval_decide(id=..., status='approved', comment_txt=...)
|
||||
await AR.pbl_agent_critic_run(blueprint_id=...) # -> writable=False + 四要素
|
||||
await AR.pbl_agent_designer_run(input_txt=..., template_code='TPL-NOT-EXIST')
|
||||
await AR.pbl_agent_trace_list()
|
||||
非法审批类型 / G6 禁用工具启用 -> raise pbl_common.PblError(e.code 断言)
|
||||
|
||||
即 ``api`` 必须是**带契约函数属性的对象**;而历史 init.py 里 ``api()`` 是返回字典的工厂函数,
|
||||
两种写法互不兼容。本模块给出同时满足两者的实现:
|
||||
即 ``api`` 必须是**带契约函数属性的对象**,且函数签名/返回字段/错误码要与集成契约逐字对齐。
|
||||
历史 init.py 里 ``api()`` 是返回字典的工厂函数,两种写法互不兼容;本模块给出同时满足的实现:
|
||||
|
||||
api.pbl_tool_adjudicate(...) # 属性直调(冒烟测试写法)
|
||||
api().pbl_tool_adjudicate(...) # 先调用再属性(工厂写法)
|
||||
api['pbl_tool_adjudicate'](...) # 下标写法
|
||||
await api.pbl_tool_adjudicate(...)# async 上下文(.dspy 端点写法)
|
||||
await api.pbl_tool_adjudicate(...)# async 上下文(.dspy 端点写法,Result.__await__ 返回自身)
|
||||
|
||||
契约函数清单以 m4a_contract.CONTRACT_FUNCTIONS 为唯一真源(10 个),
|
||||
与 wwwroot/api/*.dspy、scripts/load_path.py、skill/SKILL.md 四处同步。
|
||||
实现分层(避免"改一处坏一处")
|
||||
------------------------------
|
||||
* ``m4a_toolset`` :13 启用 / 9 禁用工具、四类审批、AD0x 错误码的**唯一真源**
|
||||
* ``m4a_smoke_compat`` :集成契约口径实现(对外唯一口径,复用 m4a_contract 核心逻辑)
|
||||
* ``m4a_contract`` :内部核心(轨迹 7 要素 / LLM 留痕 / append-only 守卫 / self_check)
|
||||
|
||||
契约函数清单以 ``CONTRACT_FUNCTIONS``(10 个)为唯一真源,与 wwwroot/api/*.dspy、
|
||||
scripts/load_path.py、skill/SKILL.md 四处同步(scripts/check_contract_sync.py 机械核验)。
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from . import m4a_smoke_compat as COMPAT
|
||||
from .m4a_contract import (
|
||||
ADJUDICATION_STEPS,
|
||||
CONTRACT_FUNCTIONS,
|
||||
CONTRACT_FUNCTIONS as CORE_CONTRACT_FUNCTIONS,
|
||||
MANDATORY_APPROVAL_TYPES,
|
||||
TRACE_ELEMENTS,
|
||||
build_api,
|
||||
@ -34,6 +48,14 @@ from .m4a_contract import (
|
||||
seed_registry,
|
||||
)
|
||||
from .m4a_store import APPEND_ONLY_TABLES, PblContractError, Result
|
||||
from .m4a_toolset import (
|
||||
DISABLED_TOOLS,
|
||||
ENABLED_TOOLS,
|
||||
STEP_ERROR_CODES,
|
||||
)
|
||||
|
||||
#: 对外契约函数清单(10 个)—— 与 .dspy / load_path / SKILL.md 同源
|
||||
CONTRACT_FUNCTIONS = COMPAT.CONTRACT_FUNCTIONS
|
||||
|
||||
|
||||
class ContractApi(dict):
|
||||
@ -62,23 +84,29 @@ class ContractApi(dict):
|
||||
return list(CONTRACT_FUNCTIONS)
|
||||
|
||||
def describe(self) -> dict:
|
||||
return {
|
||||
"module": "pbl_agent_runtime",
|
||||
"contract_functions": list(CONTRACT_FUNCTIONS),
|
||||
"contract_count": len(CONTRACT_FUNCTIONS),
|
||||
"adjudication_steps": [step[0] for step in ADJUDICATION_STEPS],
|
||||
"mandatory_approval_types": list(MANDATORY_APPROVAL_TYPES),
|
||||
"trace_elements": list(TRACE_ELEMENTS),
|
||||
"append_only_tables": list(APPEND_ONLY_TABLES),
|
||||
"tables": 6,
|
||||
}
|
||||
return COMPAT.describe()
|
||||
|
||||
|
||||
def build_contract_api() -> ContractApi:
|
||||
"""构建契约对象(每次调用返回独立实例,便于测试隔离)。"""
|
||||
"""构建契约对象(每次调用返回独立实例,便于测试隔离)。
|
||||
|
||||
装配顺序:内部核心(m4a_contract.build_api)打底 → 集成契约实现覆盖同名函数。
|
||||
覆盖后对外口径唯一(AD0x 错误码 + ok/success 双布尔 + 冒烟测试字段名),
|
||||
内部核心函数以 ``core_<name>`` 保留,供自测与排障对照。
|
||||
"""
|
||||
base = build_api()
|
||||
obj = ContractApi(base)
|
||||
merged = {}
|
||||
for name, func in base.items():
|
||||
merged["core_%s" % name] = func
|
||||
merged[name] = func
|
||||
merged.update(COMPAT.build_integration_api())
|
||||
obj = ContractApi(merged)
|
||||
obj["describe"] = obj.describe
|
||||
obj["names"] = obj.names
|
||||
obj["seed_tools"] = COMPAT.seed_tools
|
||||
obj["seed_registry"] = seed_registry
|
||||
obj["self_check"] = self_check
|
||||
obj["adjudication_steps"] = COMPAT.adjudication_steps
|
||||
return obj
|
||||
|
||||
|
||||
@ -92,9 +120,13 @@ __all__ = [
|
||||
"self_check",
|
||||
"seed_registry",
|
||||
"CONTRACT_FUNCTIONS",
|
||||
"CORE_CONTRACT_FUNCTIONS",
|
||||
"ADJUDICATION_STEPS",
|
||||
"STEP_ERROR_CODES",
|
||||
"MANDATORY_APPROVAL_TYPES",
|
||||
"TRACE_ELEMENTS",
|
||||
"ENABLED_TOOLS",
|
||||
"DISABLED_TOOLS",
|
||||
"APPEND_ONLY_TABLES",
|
||||
"PblContractError",
|
||||
"Result",
|
||||
|
||||
1206
pbl_agent_runtime/m4a_smoke_compat.py
Normal file
1206
pbl_agent_runtime/m4a_smoke_compat.py
Normal file
File diff suppressed because it is too large
Load Diff
329
pbl_agent_runtime/m4a_toolset.py
Normal file
329
pbl_agent_runtime/m4a_toolset.py
Normal file
@ -0,0 +1,329 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""pbl_agent_runtime.m4a_toolset —— 工具注册表**唯一真源**(13 启用 / 9 禁用)。
|
||||
|
||||
为什么单独一个文件
|
||||
------------------
|
||||
QC 退回 #2/#3 的根因之一是「工具键口径分裂」:设计文档用 ``blueprint.create`` 一类键,
|
||||
需求 ``agent-tool-registry.md`` §2.1/§2.2 用 ``pbl.get``/``pbl.update``/``pbl.publish``,
|
||||
而项目集成契约 ``apps/pbls/scripts/tests/offline_contract_smoke.py``(E1~E11)按需求口径
|
||||
断言,且硬断言:
|
||||
|
||||
len(enabled_keys) == 13 and len(disabled_keys) == 9 and 'pbl.publish' in disabled_keys
|
||||
|
||||
本文件把三方口径收敛为**一张表**:键名取需求 §2.1/§2.2 的 tool_key(调用方契约),
|
||||
语义/禁用原因锚点取设计文档 §3.1/§3.2 与任务书(含 ``pbl.publish`` 禁用 = G6/14.2)。
|
||||
seed / registry_list / registry_save 白名单 / 裁决 S4~S6 全部只读本文件,禁止再各自硬编码。
|
||||
|
||||
裁决错误码(AD0x,与集成冒烟测试逐字对齐)
|
||||
------------------------------------------
|
||||
AD01_TENANT_MISSING S1 租户上下文缺失
|
||||
AD07_AGENT_UNREGISTERED S2 Agent 未注册
|
||||
AD08_AGENT_DISABLED S2 Agent 已停用
|
||||
AD04_PERMISSION_MODE_DENIED S3 Critic 零写:read 模式 Agent 请求写类工具
|
||||
AD02_TOOL_UNREGISTERED S4 工具未注册(default-deny)
|
||||
AD03_TOOL_DISABLED S5 工具已禁用(pbl.publish 恒拒,G6 无绕过)
|
||||
AD09_PERMISSION_DENIED S6 Agent 不在工具 agent_scope 内
|
||||
AD05_PARAM_NOT_WHITELISTED S7 入参不在服务端 params_schema 白名单
|
||||
AD06_APPROVAL_REQUIRED S8 需人工审批且无 approved 审批单
|
||||
"""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 裁决错误码(8 步 + 3 个前置/内部码)
|
||||
# ---------------------------------------------------------------------------
|
||||
AD01_TENANT_MISSING = "AD01_TENANT_MISSING"
|
||||
AD02_TOOL_UNREGISTERED = "AD02_TOOL_UNREGISTERED"
|
||||
AD03_TOOL_DISABLED = "AD03_TOOL_DISABLED"
|
||||
AD04_PERMISSION_MODE_DENIED = "AD04_PERMISSION_MODE_DENIED"
|
||||
AD05_PARAM_NOT_WHITELISTED = "AD05_PARAM_NOT_WHITELISTED"
|
||||
AD06_APPROVAL_REQUIRED = "AD06_APPROVAL_REQUIRED"
|
||||
AD07_AGENT_UNREGISTERED = "AD07_AGENT_UNREGISTERED"
|
||||
AD08_AGENT_DISABLED = "AD08_AGENT_DISABLED"
|
||||
AD09_PERMISSION_DENIED = "AD09_PERMISSION_DENIED"
|
||||
AD10_INTERNAL = "AD10_INTERNAL"
|
||||
|
||||
#: 步骤号 → 该步骤失败时的错误码(S1~S8 固定顺序,fail-closed)
|
||||
STEP_ERROR_CODES = (
|
||||
("S1", "tenant_context", AD01_TENANT_MISSING),
|
||||
("S2", "agent_registered", AD07_AGENT_UNREGISTERED),
|
||||
("S3", "agent_write_scope", AD04_PERMISSION_MODE_DENIED),
|
||||
("S4", "tool_registered", AD02_TOOL_UNREGISTERED),
|
||||
("S5", "tool_enabled", AD03_TOOL_DISABLED),
|
||||
("S6", "permission_granted", AD09_PERMISSION_DENIED),
|
||||
("S7", "params_valid", AD05_PARAM_NOT_WHITELISTED),
|
||||
("S8", "approval_granted", AD06_APPROVAL_REQUIRED),
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 四类强制人工审批(14.2 / agent-tool-registry.md §3.3 本迭代开放四类)
|
||||
# ---------------------------------------------------------------------------
|
||||
APPROVAL_TYPE_PUBLISH = "publish"
|
||||
APPROVAL_TYPE_MODIFY_GOAL = "modify_learning_goal"
|
||||
APPROVAL_TYPE_MODIFY_RUBRIC = "modify_assessment_criteria"
|
||||
APPROVAL_TYPE_MODIFY_VISIBLE = "modify_student_visible_content"
|
||||
|
||||
#: 本迭代开放的四类(第 5 类 experiment 仅注册不开放,§3.3 行 5)
|
||||
APPROVAL_TYPES = (
|
||||
APPROVAL_TYPE_PUBLISH,
|
||||
APPROVAL_TYPE_MODIFY_GOAL,
|
||||
APPROVAL_TYPE_MODIFY_RUBRIC,
|
||||
APPROVAL_TYPE_MODIFY_VISIBLE,
|
||||
)
|
||||
|
||||
#: 注册但本迭代**不开放**(创建即拒 PBL_APPROVAL_TYPE_NOT_OPEN)
|
||||
APPROVAL_TYPES_REGISTERED_NOT_OPEN = ("experiment",)
|
||||
|
||||
#: 历史别名 → 四类正名(向后兼容早期 M4a 代码,不新增审批面)
|
||||
APPROVAL_TYPE_ALIASES = {
|
||||
"auto_publish": None, # G6:不存在「自动发布」审批类型
|
||||
"compile_execute": APPROVAL_TYPE_PUBLISH,
|
||||
"blueprint_approve": APPROVAL_TYPE_MODIFY_VISIBLE,
|
||||
"change_learning_goals": APPROVAL_TYPE_MODIFY_GOAL,
|
||||
"change_assessment_criteria": APPROVAL_TYPE_MODIFY_RUBRIC,
|
||||
}
|
||||
|
||||
# 审批/工具类错误码(抛 pbl_common.errors.PblError,与冒烟测试 e.code 对齐)
|
||||
PBL_APPROVAL_TYPE_UNKNOWN = "PBL_APPROVAL_TYPE_UNKNOWN"
|
||||
PBL_APPROVAL_TYPE_NOT_OPEN = "PBL_APPROVAL_TYPE_NOT_OPEN"
|
||||
PBL_APPROVAL_NO_BYPASS = "PBL_APPROVAL_NO_BYPASS"
|
||||
PBL_APPROVAL_ALREADY_DECIDED = "PBL_APPROVAL_ALREADY_DECIDED"
|
||||
PBL_TOOL_FORBIDDEN = "PBL_TOOL_FORBIDDEN"
|
||||
PBL_TOOL_KEY_REQUIRED = "PBL_TOOL_KEY_REQUIRED"
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 13 启用工具(agent-tool-registry.md §2.1,pbl.publish 移入禁用集)
|
||||
# (tool_key, title, category, write_class, agent_scope, required_perm,
|
||||
# approval_type, params_schema)
|
||||
# ---------------------------------------------------------------------------
|
||||
_S = lambda *names: {"type": "object", "properties": {n: {} for n in names}} # noqa: E731
|
||||
|
||||
ENABLED_TOOL_DEFS = (
|
||||
("pbl.get", "读取蓝图", "blueprint", 0, "both", "pbl_authoring.read", None,
|
||||
_S("pbl_id", "blueprint_id", "version", "version_no")),
|
||||
("pbl.create", "创建蓝图草稿", "blueprint", 1, "designer", "pbl_authoring.write", None,
|
||||
_S("draft_json", "content_json", "generation_source", "title", "subject", "grade")),
|
||||
("pbl.update", "结构化修改蓝图(delta)", "blueprint", 1, "designer",
|
||||
"pbl_authoring.write", APPROVAL_TYPE_MODIFY_GOAL,
|
||||
_S("pbl_id", "blueprint_id", "delta_json", "content_json", "instruction",
|
||||
"target_changes", "version_no")),
|
||||
("pbl.validate", "触发 14 维校验", "validation", 0, "both", "pbl_authoring.write", None,
|
||||
_S("pbl_id", "blueprint_id", "version", "version_no")),
|
||||
("pbl.compare", "版本字段级 diff", "validation", 0, "both", "pbl_authoring.read", None,
|
||||
_S("pbl_id", "blueprint_id", "version_a", "version_b")),
|
||||
("pbl.compile", "触发编译(教师审批后)", "compile", 1, "designer",
|
||||
"pbl_authoring.write", APPROVAL_TYPE_PUBLISH,
|
||||
_S("pbl_id", "blueprint_id", "version", "version_no", "compiler_version")),
|
||||
("pbl.preview", "教师预览会话", "runtime", 0, "both", "pbl_authoring.read", None,
|
||||
_S("definition_id", "session_type")),
|
||||
("template.copy", "模板实例化(离线兜底落点)", "template", 1, "designer",
|
||||
"pbl_authoring.write", None,
|
||||
_S("template_id", "template_code", "owner_teacher_id", "class_id", "new_title")),
|
||||
("kdb.search", "知识检索(Q5 桩,空结果不报错)", "kdb", 0, "both", "kdb.read", None,
|
||||
_S("query", "top_k", "filters")),
|
||||
("kdb.get", "知识条目读取(Q5 桩)", "kdb", 0, "both", "kdb.read", None,
|
||||
_S("kdb_item_id", "item_id")),
|
||||
("runtime.get_state", "世界/实体状态快照(只读聚合)", "runtime", 0, "both",
|
||||
"student_data.read", None, _S("session_id")),
|
||||
("runtime.playtest", "playtest 会话", "runtime", 1, "designer", "pbl_authoring.write",
|
||||
None, _S("definition_id", "mode")),
|
||||
("analytics.query", "聚合指标查询(禁返回可识别学生字段,Q6)", "analytics", 0, "both",
|
||||
"analytics.read", None, _S("pbl_id", "blueprint_id", "metrics", "window")),
|
||||
)
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9 禁用工具(设计文档 §3.2 out_of_scope,键名取需求口径;含 pbl.publish)
|
||||
# (tool_key, title, category, disable_reason)
|
||||
# ---------------------------------------------------------------------------
|
||||
DISABLED_TOOL_DEFS = (
|
||||
("pbl.publish", "发布 PBL", "publish",
|
||||
"G6_no_publish_tool:Publish 阶段仅人类(教师/Reviewer),Agent 无 pbl.publish 权限"
|
||||
"(14.2/28 章/36 章);发布经 publish 类人工审批单由人类决策"),
|
||||
("kdb.add_candidate", "写入候选知识", "kdb",
|
||||
"Q5:本迭代不建 KDB,知识层只读,禁任何写入"),
|
||||
("kdb.propose_pattern", "提交模式建议", "kdb",
|
||||
"Q5:KDB 只读;research 层不得采集学生数据(Q6)"),
|
||||
("analytics.compare", "跨组对比分析", "analytics",
|
||||
"Phase 3(Experiment/Research,33/35 章)本迭代不做"),
|
||||
("analytics.aggregate", "自定义聚合", "analytics",
|
||||
"Phase 3(Experiment/Research)本迭代不做;仅开放 analytics.query 固定聚合"),
|
||||
("experiment.create", "创建实验", "experiments",
|
||||
"33 章 Experiment Engine 归 Phase 3;影响活跃课程的实验不开放(§3.3 行 5)"),
|
||||
("experiment.start", "启动 A/B 实验", "experiments",
|
||||
"33 章 A/B 引擎归 Phase 3,本迭代禁止自动实验"),
|
||||
("showplace.search", "Marketplace 检索", "marketplace",
|
||||
"22/23 章 Marketplace 归 Phase 4;本迭代发布仅可见范围标记"),
|
||||
("showplace.copy", "Marketplace 复制", "marketplace",
|
||||
"22/23 章 Marketplace 归 Phase 4;本迭代由 template.copy 替代"),
|
||||
)
|
||||
|
||||
#: 需求 §2.2 其余延后键:**允许登记**(保留契约,后续迭代 is_enabled=1 即可),
|
||||
#: 但**不预置**(不计入 13/9 断言口径)。
|
||||
DEFERRED_TOOL_KEYS = (
|
||||
"experiment.evaluate",
|
||||
"showplace.get",
|
||||
"runtime.preview",
|
||||
"agent.mentor_invoke",
|
||||
"world.edit_3d",
|
||||
"billing.charge",
|
||||
"curriculum.modify_auto",
|
||||
"research.collect",
|
||||
)
|
||||
|
||||
#: G6 硬禁用键:即使 Platform Admin 也不得 enabled=1(registry_save 抛 PBL_TOOL_FORBIDDEN)
|
||||
G6_FORBIDDEN_TO_ENABLE = frozenset(
|
||||
key for key, _t, _c, _r in DISABLED_TOOL_DEFS
|
||||
) | frozenset(DEFERRED_TOOL_KEYS)
|
||||
|
||||
#: registry_save 可登记的键全集(白名单外一律 PBL_TOOL_FORBIDDEN,如 sim.configure)
|
||||
SAVABLE_TOOL_KEYS = frozenset(
|
||||
[d[0] for d in ENABLED_TOOL_DEFS]
|
||||
+ [d[0] for d in DISABLED_TOOL_DEFS]
|
||||
+ list(DEFERRED_TOOL_KEYS)
|
||||
)
|
||||
|
||||
#: 键前缀白名单(第 31 章裁剪子集的命名空间;sim.* 等未裁剪命名空间一律拒)
|
||||
ALLOWED_TOOL_PREFIXES = ("pbl.", "kdb.", "analytics.", "experiment.", "showplace.",
|
||||
"runtime.", "template.", "agent.", "world.", "billing.",
|
||||
"curriculum.", "research.")
|
||||
|
||||
ENABLED_TOOLS = tuple(d[0] for d in ENABLED_TOOL_DEFS)
|
||||
DISABLED_TOOLS = tuple(d[0] for d in DISABLED_TOOL_DEFS)
|
||||
|
||||
#: Agent 定义(13.1 章:本迭代仅 designer + critic)
|
||||
AGENT_DEFS = (
|
||||
# (code, name, agent_type, permission_mode, write_scope, model_route)
|
||||
("designer", "Designer Agent", "designer", "write", "blueprint_draft", "pipeline-llm"),
|
||||
("critic", "Critic Agent", "critic", "read", "none", "pipeline-llm"),
|
||||
)
|
||||
|
||||
#: 轨迹 7 要素(28 章 / US-20)
|
||||
TRACE_ELEMENTS = (
|
||||
"input_context",
|
||||
"retrieved_knowledge",
|
||||
"tool_calls",
|
||||
"proposed_action",
|
||||
"result",
|
||||
"approval",
|
||||
"final_output",
|
||||
)
|
||||
|
||||
|
||||
def tool_def(tool_key):
|
||||
"""按 tool_key 取启用工具定义;不存在返回 None。"""
|
||||
for row in ENABLED_TOOL_DEFS:
|
||||
if row[0] == tool_key:
|
||||
return {
|
||||
"tool_key": row[0], "tool_name": row[1], "category": row[2],
|
||||
"write_class": row[3], "agent_scope": row[4],
|
||||
"required_perm": row[5], "approval_type": row[6],
|
||||
"params_schema": row[7], "status": "enabled", "reason": None,
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def disabled_def(tool_key):
|
||||
"""按 tool_key 取禁用工具定义;不存在返回 None。"""
|
||||
for row in DISABLED_TOOL_DEFS:
|
||||
if row[0] == tool_key:
|
||||
return {
|
||||
"tool_key": row[0], "tool_name": row[1], "category": row[2],
|
||||
"write_class": 1, "agent_scope": "none", "required_perm": None,
|
||||
"approval_type": None, "params_schema": {"type": "object"},
|
||||
"status": "disabled", "reason": row[3],
|
||||
}
|
||||
return None
|
||||
|
||||
|
||||
def is_savable(tool_key):
|
||||
"""registry_save 白名单判定:键全集内,或命名空间前缀在裁剪子集内。"""
|
||||
if not tool_key:
|
||||
return False
|
||||
if tool_key in SAVABLE_TOOL_KEYS:
|
||||
return True
|
||||
return tool_key.startswith(ALLOWED_TOOL_PREFIXES)
|
||||
|
||||
|
||||
def seed_rows():
|
||||
"""13 启用 + 9 禁用的幂等 seed 行(sort_no 稳定,便于 registry_list 排序)。"""
|
||||
rows = []
|
||||
for idx, row in enumerate(ENABLED_TOOL_DEFS):
|
||||
rows.append({
|
||||
"tool_key": row[0], "tool_name": row[1], "category": row[2],
|
||||
"write_class": row[3], "agent_scope": row[4], "required_perm": row[5],
|
||||
"approval_type": row[6], "params_schema": row[7],
|
||||
"status": "enabled", "reason": None, "sort_no": 10 + idx,
|
||||
})
|
||||
for idx, row in enumerate(DISABLED_TOOL_DEFS):
|
||||
rows.append({
|
||||
"tool_key": row[0], "tool_name": row[1], "category": row[2],
|
||||
"write_class": 1, "agent_scope": "none", "required_perm": None,
|
||||
"approval_type": None, "params_schema": {"type": "object"},
|
||||
"status": "disabled", "reason": row[3], "sort_no": 200 + idx,
|
||||
})
|
||||
return rows
|
||||
|
||||
|
||||
def resolve_approval_type(tool_key, params=None):
|
||||
"""按工具 + delta 命中面动态解析审批类型(agent-tool-registry.md §3.3 行 2~4)。
|
||||
|
||||
* pbl.update 的 delta 命中 rubric/assessment → modify_assessment_criteria
|
||||
* 命中 mission/task/entity(学生可见内容) → modify_student_visible_content
|
||||
* 其余(含默认学习目标) → modify_learning_goal
|
||||
"""
|
||||
definition = tool_def(tool_key)
|
||||
default_type = definition["approval_type"] if definition else None
|
||||
if default_type is None or not isinstance(params, dict):
|
||||
return default_type
|
||||
blob = repr(sorted(params.keys())).lower() + repr(params).lower()
|
||||
if default_type != APPROVAL_TYPE_MODIFY_GOAL:
|
||||
return default_type
|
||||
if "rubric" in blob or "assessment" in blob or "criteria" in blob:
|
||||
return APPROVAL_TYPE_MODIFY_RUBRIC
|
||||
if "mission" in blob or "task" in blob or "entit" in blob or "artifact" in blob:
|
||||
return APPROVAL_TYPE_MODIFY_VISIBLE
|
||||
return APPROVAL_TYPE_MODIFY_GOAL
|
||||
|
||||
|
||||
def normalize_approval_type(raw):
|
||||
"""审批类型归一化。
|
||||
|
||||
返回 ``(type_or_None, error_code_or_None)``:
|
||||
* 空 → (None, PBL_APPROVAL_TYPE_UNKNOWN)
|
||||
* 别名 auto_publish → (None, PBL_APPROVAL_TYPE_UNKNOWN) # G6 无自动发布
|
||||
* 注册未开放 experiment → (None, PBL_APPROVAL_TYPE_NOT_OPEN)
|
||||
* 四类正名/可归一别名 → (正名, None)
|
||||
"""
|
||||
key = (raw or "").strip()
|
||||
if not key:
|
||||
return None, PBL_APPROVAL_TYPE_UNKNOWN
|
||||
if key in APPROVAL_TYPES:
|
||||
return key, None
|
||||
if key in APPROVAL_TYPE_ALIASES:
|
||||
mapped = APPROVAL_TYPE_ALIASES[key]
|
||||
if mapped is None:
|
||||
return None, PBL_APPROVAL_TYPE_UNKNOWN
|
||||
return mapped, None
|
||||
if key in APPROVAL_TYPES_REGISTERED_NOT_OPEN:
|
||||
return None, PBL_APPROVAL_TYPE_NOT_OPEN
|
||||
return None, PBL_APPROVAL_TYPE_UNKNOWN
|
||||
|
||||
|
||||
__all__ = [
|
||||
"AD01_TENANT_MISSING", "AD02_TOOL_UNREGISTERED", "AD03_TOOL_DISABLED",
|
||||
"AD04_PERMISSION_MODE_DENIED", "AD05_PARAM_NOT_WHITELISTED",
|
||||
"AD06_APPROVAL_REQUIRED", "AD07_AGENT_UNREGISTERED", "AD08_AGENT_DISABLED",
|
||||
"AD09_PERMISSION_DENIED", "AD10_INTERNAL", "STEP_ERROR_CODES",
|
||||
"APPROVAL_TYPES", "APPROVAL_TYPES_REGISTERED_NOT_OPEN", "APPROVAL_TYPE_ALIASES",
|
||||
"APPROVAL_TYPE_PUBLISH", "APPROVAL_TYPE_MODIFY_GOAL",
|
||||
"APPROVAL_TYPE_MODIFY_RUBRIC", "APPROVAL_TYPE_MODIFY_VISIBLE",
|
||||
"PBL_APPROVAL_TYPE_UNKNOWN", "PBL_APPROVAL_TYPE_NOT_OPEN",
|
||||
"PBL_APPROVAL_NO_BYPASS", "PBL_APPROVAL_ALREADY_DECIDED",
|
||||
"PBL_TOOL_FORBIDDEN", "PBL_TOOL_KEY_REQUIRED",
|
||||
"ENABLED_TOOL_DEFS", "DISABLED_TOOL_DEFS", "DEFERRED_TOOL_KEYS",
|
||||
"ENABLED_TOOLS", "DISABLED_TOOLS", "SAVABLE_TOOL_KEYS",
|
||||
"G6_FORBIDDEN_TO_ENABLE", "ALLOWED_TOOL_PREFIXES",
|
||||
"AGENT_DEFS", "TRACE_ELEMENTS",
|
||||
"tool_def", "disabled_def", "is_savable", "seed_rows",
|
||||
"resolve_approval_type", "normalize_approval_type",
|
||||
]
|
||||
BIN
var/m4a_contract.sqlite3
Normal file
BIN
var/m4a_contract.sqlite3
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user