2026-09-17 23:44:00 +08:00

266 lines
13 KiB
Python
Raw 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.

# -*- 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_agentsdesigner/critic→ seed_tools13+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",
]