266 lines
13 KiB
Python
266 lines
13 KiB
Python
# -*- coding: utf-8 -*-
|
||
"""
|
||
pbl_agent_runtime M4a — 对外统一门面(facade)
|
||
|
||
装配:
|
||
from pbl_agent_runtime.m4a import load_m4a, M4aApi
|
||
api = load_m4a(tenant_id="T001") # 幂等:建表 + seed_agents + seed_tools
|
||
|
||
对外契约(modules/pbl_agent_runtime.md §5):
|
||
Designer : designer_generate / designer_modify / designer_clarify
|
||
Critic : critic_review / get_critic_report
|
||
工具裁决 : invoke_tool / adjudicate / register_tool / list_tools /
|
||
set_tool_status / seed_tools
|
||
轨迹 : start_trace / append_trace / get_trace / list_traces /
|
||
trace_completeness
|
||
审批 : request_approval / decide_approval / get_approval /
|
||
list_pending_approvals / mandatory_approval_matrix
|
||
"""
|
||
|
||
from .m4a_kernel import (
|
||
PblError, TenantContext, get_store, set_store, reset_store, fail,
|
||
MemoryStore, AppendOnlyGuard, canonical_json, now_ts, gen_no,
|
||
ADJUDICATION_STEPS, TRACE_ELEMENTS,
|
||
E_TENANT_MISSING, E_FORBIDDEN, E_STATE_ILLEGAL, E_NOT_FOUND, E_VALIDATION,
|
||
E_DUPLICATE, E_APPEND_ONLY, E_MODEL_UNAVAILABLE, E_BACKEND_UNAVAILABLE,
|
||
E_TEMPLATE_NONE,
|
||
ACTOR_USER, ACTOR_AGENT, ACTOR_SYSTEM,
|
||
)
|
||
from .m4a_tables import TABLES, APPEND_ONLY_TABLES, to_sql, all_sql, ensure_tables
|
||
from .m4a_registry import (
|
||
AGENT_DEFS, TOOL_SEED, ENABLED_TOOL_CODES, DISABLED_TOOL_CODES,
|
||
APPROVAL_REQUIRED_TOOLS, APPROVAL_TYPES, DESIGNER, CRITIC,
|
||
PERM_AUTHORING, PERM_AGENT_TOOLS, PERM_PUBLISHING, PERM_KDB,
|
||
PERM_PLATFORM_ADMIN,
|
||
seed_agents, seed_tools, get_agent_def, list_agents,
|
||
register_tool, get_tool, list_tools, set_tool_status, registry_stats,
|
||
)
|
||
from .m4a_adjudicate import (
|
||
Verdict, adjudicate, invoke_tool, validate_args, adjudicate_report,
|
||
explain_chain,
|
||
)
|
||
from .m4a_trace import (
|
||
start_trace, append_trace, finish_trace, get_trace, list_traces,
|
||
list_trace_stages, trace_completeness, try_mutate_trace,
|
||
)
|
||
from .m4a_approval import (
|
||
MANDATORY_APPROVAL_TYPES, APPROVAL_PUBLISH, APPROVAL_COMPILE,
|
||
APPROVAL_BLUEPRINT, APPROVAL_REGISTRY,
|
||
request_approval, decide_approval, get_approval, list_pending_approvals,
|
||
list_approvals, find_approved, mandatory_approval_matrix, normalize_action,
|
||
)
|
||
from .m4a_designer import (
|
||
designer_generate, designer_modify, designer_clarify, parse_intent,
|
||
instruction_to_changes, build_clarifications, set_model_runner,
|
||
CLARIFY_FIELDS, MAX_CLARIFY_ROUNDS,
|
||
)
|
||
from .m4a_critic import (
|
||
critic_review, get_critic_report, assert_suggestion, critic_write_attempt,
|
||
set_critic_runner, CRITIC_READONLY_TOOLS, SUGGESTION_FIELDS,
|
||
)
|
||
from .m4a_backend import execute as execute_tool_backend, backend_availability
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 上下文构造辅助
|
||
# ---------------------------------------------------------------------------
|
||
def make_agent_ctx(tenant_id, agent_code=DESIGNER, permissions=None, org_id=None):
|
||
"""构造 Agent 调用上下文(actor_type=agent,不可审批)。"""
|
||
perms = set(permissions or ())
|
||
if not perms:
|
||
# Agent 默认权限域:authoring + agent_tools + kdb(只读)+ publishing。
|
||
# 注意:持有 publishing 只代表"可提案"发布(S5 通过),
|
||
# 真正执行仍被 S7 人工审批门禁拦住(14.2 Publish 必须人工审批)。
|
||
perms = {PERM_AUTHORING, PERM_AGENT_TOOLS, PERM_KDB, PERM_PUBLISHING}
|
||
return TenantContext(tenant_id=tenant_id, actor_type=ACTOR_AGENT,
|
||
actor_id=agent_code, permissions=perms, org_id=org_id)
|
||
|
||
|
||
def make_user_ctx(tenant_id, user_id, permissions=None, org_id=None):
|
||
"""构造人类用户上下文(actor_type=user,可审批 / 可治理注册表)。"""
|
||
perms = set(permissions or ())
|
||
if not perms:
|
||
perms = {PERM_AUTHORING, PERM_PUBLISHING, PERM_AGENT_TOOLS}
|
||
return TenantContext(tenant_id=tenant_id, actor_type=ACTOR_USER,
|
||
actor_id=user_id, permissions=perms, org_id=org_id)
|
||
|
||
|
||
def make_admin_ctx(tenant_id, user_id="admin"):
|
||
"""Platform Admin(工具注册表治理 + 全量轨迹可读)。"""
|
||
return TenantContext(tenant_id=tenant_id, actor_type=ACTOR_USER,
|
||
actor_id=user_id,
|
||
permissions={PERM_PLATFORM_ADMIN, PERM_AUTHORING,
|
||
PERM_PUBLISHING, PERM_AGENT_TOOLS,
|
||
PERM_KDB})
|
||
|
||
|
||
# ---------------------------------------------------------------------------
|
||
# 装配
|
||
# ---------------------------------------------------------------------------
|
||
def load_m4a(tenant_id=None, store=None, force_memory=False):
|
||
"""
|
||
幂等装配:建表 → seed_agents(designer/critic)→ seed_tools(13+9)。
|
||
返回 M4aApi 实例。build.sh / 应用 init() 调用。
|
||
"""
|
||
st = store or get_store(force_memory=force_memory)
|
||
ensure_tables(st)
|
||
ctx = TenantContext(tenant_id=tenant_id or "__platform__",
|
||
actor_type=ACTOR_SYSTEM, actor_id="seed",
|
||
permissions={PERM_PLATFORM_ADMIN})
|
||
agents = seed_agents(ctx=ctx, store=st)
|
||
tools = seed_tools(ctx=ctx, store=st)
|
||
return M4aApi(tenant_id=tenant_id or "__platform__", store=st,
|
||
seed_result={"agents": agents, "tools": tools})
|
||
|
||
|
||
class M4aApi(object):
|
||
"""按租户绑定的 M4a 门面(避免每个调用都传 store/tenant)。"""
|
||
|
||
def __init__(self, tenant_id, store=None, seed_result=None):
|
||
self.tenant_id = tenant_id
|
||
self.store = store or get_store()
|
||
self.seed_result = seed_result or {}
|
||
|
||
# --- 上下文 ---
|
||
def agent_ctx(self, agent_code=DESIGNER, permissions=None):
|
||
return make_agent_ctx(self.tenant_id, agent_code, permissions)
|
||
|
||
def user_ctx(self, user_id, permissions=None):
|
||
return make_user_ctx(self.tenant_id, user_id, permissions)
|
||
|
||
def admin_ctx(self, user_id="admin"):
|
||
return make_admin_ctx(self.tenant_id, user_id)
|
||
|
||
# --- Designer ---
|
||
def designer_generate(self, intent_text, owner_teacher_id=None, class_id=None,
|
||
session_no=None, model_runner=None):
|
||
return designer_generate(intent_text, owner_teacher_id=owner_teacher_id,
|
||
class_id=class_id, session_no=session_no,
|
||
ctx=self.agent_ctx(DESIGNER), store=self.store,
|
||
model_runner=model_runner)
|
||
|
||
def designer_modify(self, blueprint_id, instruction, session_no=None,
|
||
version_no=None):
|
||
return designer_modify(blueprint_id, instruction, session_no=session_no,
|
||
ctx=self.agent_ctx(DESIGNER), store=self.store,
|
||
version_no=version_no)
|
||
|
||
def designer_clarify(self, blueprint_id, missing_fields, session_no=None,
|
||
round_no=None):
|
||
return designer_clarify(blueprint_id, missing_fields,
|
||
ctx=self.agent_ctx(DESIGNER), store=self.store,
|
||
session_no=session_no, round_no=round_no)
|
||
|
||
# --- Critic ---
|
||
def critic_review(self, blueprint_id, version_no=None, session_no=None):
|
||
return critic_review(blueprint_id, version_no=version_no,
|
||
ctx=self.agent_ctx(CRITIC), store=self.store,
|
||
session_no=session_no)
|
||
|
||
def get_critic_report(self, trace_no):
|
||
return get_critic_report(trace_no, ctx=self.admin_ctx(), store=self.store)
|
||
|
||
# --- 工具裁决 ---
|
||
def invoke_tool(self, agent_code, tool_code, args=None, trace_no=None,
|
||
session_no=None, approval_no=None):
|
||
return invoke_tool(self.agent_ctx(agent_code), agent_code, tool_code,
|
||
args=args, trace_no=trace_no, session_no=session_no,
|
||
approval_no=approval_no, store=self.store)
|
||
|
||
def adjudicate(self, agent_code, tool_code, args=None):
|
||
return adjudicate_report(self.agent_ctx(agent_code), agent_code, tool_code,
|
||
args=args, store=self.store)
|
||
|
||
def list_tools(self, status=None, group=None):
|
||
return list_tools(ctx=self.admin_ctx(), status=status, group=group,
|
||
store=self.store)
|
||
|
||
def registry_stats(self):
|
||
return registry_stats(ctx=self.admin_ctx(), store=self.store)
|
||
|
||
def set_tool_status(self, tool_code, status, reason=None, admin_id="admin",
|
||
approval_no=None):
|
||
return set_tool_status(self.admin_ctx(admin_id), tool_code, status,
|
||
reason=reason or approval_no, store=self.store)
|
||
|
||
# --- 轨迹 ---
|
||
def get_trace(self, trace_no):
|
||
return get_trace(trace_no, ctx=self.admin_ctx(), store=self.store)
|
||
|
||
def list_traces(self, filters=None, page=1, size=20):
|
||
return list_traces(filters, page=page, size=size, ctx=self.admin_ctx(),
|
||
store=self.store)
|
||
|
||
def trace_completeness(self, trace_no):
|
||
return trace_completeness(trace_no, ctx=self.admin_ctx(), store=self.store)
|
||
|
||
# --- 审批 ---
|
||
def request_approval(self, action_type, object_id=None, agent_code=DESIGNER,
|
||
tool_code=None, object_type=None, action_payload=None,
|
||
approver_id=None, trace_no=None, trace_id=None):
|
||
return request_approval(self.agent_ctx(agent_code), trace_id=trace_id,
|
||
action_type=action_type, agent_code=agent_code,
|
||
tool_code=tool_code, object_type=object_type,
|
||
object_id=object_id,
|
||
action_payload=action_payload,
|
||
approver_id=approver_id, trace_no=trace_no,
|
||
store=self.store)
|
||
|
||
def decide_approval(self, approval_no, status, user_id, comment=None):
|
||
return decide_approval(self.user_ctx(user_id), approval_no, status,
|
||
comment=comment, store=self.store)
|
||
|
||
def list_pending_approvals(self, approver_id):
|
||
return list_pending_approvals(self.user_ctx(approver_id),
|
||
approver_id=approver_id, store=self.store)
|
||
|
||
# --- 自检 ---
|
||
def self_check(self):
|
||
"""M4a 装配自检:Agent 定义 / 工具计数 / 审批矩阵 / 裁决链步数。"""
|
||
agents = list_agents(ctx=self.admin_ctx(), store=self.store)
|
||
stats = self.registry_stats()
|
||
return {
|
||
"tenant_id": self.tenant_id,
|
||
"agents": [{"agent_code": a["agent_code"],
|
||
"write_allowed": a.get("write_allowed"),
|
||
"status": a.get("status"),
|
||
"allowed_tools": a.get("allowed_tools")} for a in agents],
|
||
"agent_count": len(agents),
|
||
"critic_write_allowed": [a["agent_code"] for a in agents
|
||
if a["agent_code"] == CRITIC
|
||
and a.get("write_allowed") in (1, True)],
|
||
"tools": stats,
|
||
"adjudication_steps": [s[0] for s in ADJUDICATION_STEPS],
|
||
"adjudication_step_count": len(ADJUDICATION_STEPS),
|
||
"mandatory_approvals": [m["action_type"]
|
||
for m in mandatory_approval_matrix()],
|
||
"trace_elements": list(TRACE_ELEMENTS),
|
||
"append_only_tables": list(APPEND_ONLY_TABLES),
|
||
"backend": backend_availability(),
|
||
}
|
||
|
||
|
||
__all__ = [
|
||
"load_m4a", "M4aApi", "make_agent_ctx", "make_user_ctx", "make_admin_ctx",
|
||
"TenantContext", "PblError", "get_store", "set_store", "reset_store",
|
||
"MemoryStore", "AppendOnlyGuard", "ensure_tables", "TABLES", "to_sql",
|
||
"all_sql", "APPEND_ONLY_TABLES",
|
||
"seed_agents", "seed_tools", "get_agent_def", "list_agents",
|
||
"register_tool", "get_tool", "list_tools", "set_tool_status",
|
||
"registry_stats", "AGENT_DEFS", "TOOL_SEED", "ENABLED_TOOL_CODES",
|
||
"DISABLED_TOOL_CODES", "APPROVAL_REQUIRED_TOOLS", "APPROVAL_TYPES",
|
||
"adjudicate", "adjudicate_report", "invoke_tool", "validate_args",
|
||
"explain_chain", "Verdict", "ADJUDICATION_STEPS",
|
||
"start_trace", "append_trace", "finish_trace", "get_trace", "list_traces",
|
||
"list_trace_stages", "trace_completeness", "try_mutate_trace",
|
||
"TRACE_ELEMENTS",
|
||
"request_approval", "decide_approval", "get_approval",
|
||
"list_pending_approvals", "list_approvals", "find_approved",
|
||
"mandatory_approval_matrix", "MANDATORY_APPROVAL_TYPES",
|
||
"designer_generate", "designer_modify", "designer_clarify", "parse_intent",
|
||
"instruction_to_changes", "set_model_runner",
|
||
"critic_review", "get_critic_report", "assert_suggestion",
|
||
"critic_write_attempt", "set_critic_runner",
|
||
"backend_availability", "DESIGNER", "CRITIC",
|
||
]
|