diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..6579d96 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +__pycache__/ +*.pyc +*.pyo +.pytest_cache/ +.coverage +.DS_Store +*.log +node_modules/ +dist/ +build/ +.venv/ +venv/ diff --git a/json/agent_runtime_pbl_agent_def.json b/json/agent_runtime_pbl_agent_def.json new file mode 100644 index 0000000..efea053 --- /dev/null +++ b/json/agent_runtime_pbl_agent_def.json @@ -0,0 +1,160 @@ +{ + "tblname": "pbl_agent_def", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "code": { + "label": "Agent编码", + "type": "select", + "list": true + }, + "agent_type": { + "label": "类型", + "type": "select", + "list": true + }, + "name": { + "label": "名称", + "type": "select", + "list": true + }, + "model_route": { + "label": "pipeline-llm 路由", + "type": "select", + "list": true + }, + "system_prompt_txt": { + "label": "提示词", + "type": "text", + "list": true + }, + "tool_whitelist_json": { + "label": "工具白名单", + "type": "text", + "list": true + }, + "permission_mode": { + "label": "read/write 模式", + "type": "select", + "list": true + }, + "is_enabled": { + "label": "启用", + "type": "checkbox", + "list": true + }, + "version_no": { + "label": "版本", + "type": "number", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "code": { + "label": "Agent编码", + "type": "select", + "list": true + }, + "agent_type": { + "label": "类型", + "type": "select", + "list": true + }, + "name": { + "label": "名称", + "type": "select", + "list": true + }, + "model_route": { + "label": "pipeline-llm 路由", + "type": "select", + "list": true + }, + "system_prompt_txt": { + "label": "提示词", + "type": "text", + "list": true + }, + "tool_whitelist_json": { + "label": "工具白名单", + "type": "text", + "list": true + }, + "permission_mode": { + "label": "read/write 模式", + "type": "select", + "list": true + }, + "is_enabled": { + "label": "启用", + "type": "checkbox", + "list": true + }, + "version_no": { + "label": "版本", + "type": "number", + "list": true + } + }, + "editable": { + "code": { + "label": "Agent编码", + "type": "select" + }, + "agent_type": { + "label": "类型", + "type": "select" + }, + "name": { + "label": "名称", + "type": "select" + }, + "model_route": { + "label": "pipeline-llm 路由", + "type": "select" + }, + "system_prompt_txt": { + "label": "提示词", + "type": "text" + }, + "tool_whitelist_json": { + "label": "工具白名单", + "type": "text" + }, + "permission_mode": { + "label": "read/write 模式", + "type": "select" + }, + "is_enabled": { + "label": "启用", + "type": "checkbox" + }, + "version_no": { + "label": "版本", + "type": "number" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/json/agent_runtime_pbl_agent_tool_call.json b/json/agent_runtime_pbl_agent_tool_call.json new file mode 100644 index 0000000..50042cd --- /dev/null +++ b/json/agent_runtime_pbl_agent_tool_call.json @@ -0,0 +1,146 @@ +{ + "tblname": "pbl_agent_tool_call", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "trace_id": { + "label": "轨迹ID", + "type": "number", + "list": true + }, + "tool_key": { + "label": "工具键", + "type": "select", + "list": true + }, + "request_json": { + "label": "请求", + "type": "text", + "list": true + }, + "response_json": { + "label": "响应", + "type": "text", + "list": true + }, + "ok": { + "label": "成功", + "type": "checkbox", + "list": true + }, + "error_code": { + "label": "错误码", + "type": "select", + "list": true + }, + "adjudication_code": { + "label": "裁决结论", + "type": "select", + "list": true + }, + "duration_ms": { + "label": "耗时", + "type": "number", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "trace_id": { + "label": "轨迹ID", + "type": "number", + "list": true + }, + "tool_key": { + "label": "工具键", + "type": "select", + "list": true + }, + "request_json": { + "label": "请求", + "type": "text", + "list": true + }, + "response_json": { + "label": "响应", + "type": "text", + "list": true + }, + "ok": { + "label": "成功", + "type": "checkbox", + "list": true + }, + "error_code": { + "label": "错误码", + "type": "select", + "list": true + }, + "adjudication_code": { + "label": "裁决结论", + "type": "select", + "list": true + }, + "duration_ms": { + "label": "耗时", + "type": "number", + "list": true + } + }, + "editable": { + "trace_id": { + "label": "轨迹ID", + "type": "number" + }, + "tool_key": { + "label": "工具键", + "type": "select" + }, + "request_json": { + "label": "请求", + "type": "text" + }, + "response_json": { + "label": "响应", + "type": "text" + }, + "ok": { + "label": "成功", + "type": "checkbox" + }, + "error_code": { + "label": "错误码", + "type": "select" + }, + "adjudication_code": { + "label": "裁决结论", + "type": "select" + }, + "duration_ms": { + "label": "耗时", + "type": "number" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/json/agent_runtime_pbl_agent_trace.json b/json/agent_runtime_pbl_agent_trace.json new file mode 100644 index 0000000..54c4a4d --- /dev/null +++ b/json/agent_runtime_pbl_agent_trace.json @@ -0,0 +1,202 @@ +{ + "tblname": "pbl_agent_trace", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "trace_uid": { + "label": "轨迹UID", + "type": "text", + "list": true + }, + "agent_code": { + "label": "Agent", + "type": "select", + "list": true + }, + "blueprint_id": { + "label": "目标蓝图", + "type": "number", + "list": true + }, + "input_ref_json": { + "label": "要素1 输入引用", + "type": "text", + "list": true + }, + "thought_txt": { + "label": "要素2 思考", + "type": "text", + "list": true + }, + "action_code": { + "label": "要素3 动作", + "type": "select", + "list": true + }, + "action_params_json": { + "label": "要素4 动作参数", + "type": "text", + "list": true + }, + "observation_json": { + "label": "要素5 观察", + "type": "text", + "list": true + }, + "result_state": { + "label": "要素6 结果状态", + "type": "select", + "list": true + }, + "llm_calls": { + "label": "要素7 LLM调用数", + "type": "number", + "list": true + }, + "duration_ms": { + "label": "耗时", + "type": "number", + "list": true + }, + "created_by": { + "label": "触发用户", + "type": "text", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "trace_uid": { + "label": "轨迹UID", + "type": "text", + "list": true + }, + "agent_code": { + "label": "Agent", + "type": "select", + "list": true + }, + "blueprint_id": { + "label": "目标蓝图", + "type": "number", + "list": true + }, + "input_ref_json": { + "label": "要素1 输入引用", + "type": "text", + "list": true + }, + "thought_txt": { + "label": "要素2 思考", + "type": "text", + "list": true + }, + "action_code": { + "label": "要素3 动作", + "type": "select", + "list": true + }, + "action_params_json": { + "label": "要素4 动作参数", + "type": "text", + "list": true + }, + "observation_json": { + "label": "要素5 观察", + "type": "text", + "list": true + }, + "result_state": { + "label": "要素6 结果状态", + "type": "select", + "list": true + }, + "llm_calls": { + "label": "要素7 LLM调用数", + "type": "number", + "list": true + }, + "duration_ms": { + "label": "耗时", + "type": "number", + "list": true + }, + "created_by": { + "label": "触发用户", + "type": "text", + "list": true + } + }, + "editable": { + "trace_uid": { + "label": "轨迹UID", + "type": "text" + }, + "agent_code": { + "label": "Agent", + "type": "select" + }, + "blueprint_id": { + "label": "目标蓝图", + "type": "number" + }, + "input_ref_json": { + "label": "要素1 输入引用", + "type": "text" + }, + "thought_txt": { + "label": "要素2 思考", + "type": "text" + }, + "action_code": { + "label": "要素3 动作", + "type": "select" + }, + "action_params_json": { + "label": "要素4 动作参数", + "type": "text" + }, + "observation_json": { + "label": "要素5 观察", + "type": "text" + }, + "result_state": { + "label": "要素6 结果状态", + "type": "select" + }, + "llm_calls": { + "label": "要素7 LLM调用数", + "type": "number" + }, + "duration_ms": { + "label": "耗时", + "type": "number" + }, + "created_by": { + "label": "触发用户", + "type": "text" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/json/agent_runtime_pbl_approval.json b/json/agent_runtime_pbl_approval.json new file mode 100644 index 0000000..3e54e95 --- /dev/null +++ b/json/agent_runtime_pbl_approval.json @@ -0,0 +1,174 @@ +{ + "tblname": "pbl_approval", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "approval_uid": { + "label": "审批UID", + "type": "text", + "list": true + }, + "approval_type": { + "label": "类型", + "type": "select", + "list": true + }, + "target_type": { + "label": "对象类型", + "type": "select", + "list": true + }, + "target_id": { + "label": "对象ID", + "type": "number", + "list": true + }, + "payload_json": { + "label": "变更内容", + "type": "text", + "list": true + }, + "status": { + "label": "状态", + "type": "select", + "list": true + }, + "requested_by": { + "label": "发起人", + "type": "text", + "list": true + }, + "decided_by": { + "label": "决策人", + "type": "text", + "list": true + }, + "decided_at": { + "label": "决策时间", + "type": "text", + "list": true + }, + "comment_txt": { + "label": "意见", + "type": "text", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "approval_uid": { + "label": "审批UID", + "type": "text", + "list": true + }, + "approval_type": { + "label": "类型", + "type": "select", + "list": true + }, + "target_type": { + "label": "对象类型", + "type": "select", + "list": true + }, + "target_id": { + "label": "对象ID", + "type": "number", + "list": true + }, + "payload_json": { + "label": "变更内容", + "type": "text", + "list": true + }, + "status": { + "label": "状态", + "type": "select", + "list": true + }, + "requested_by": { + "label": "发起人", + "type": "text", + "list": true + }, + "decided_by": { + "label": "决策人", + "type": "text", + "list": true + }, + "decided_at": { + "label": "决策时间", + "type": "text", + "list": true + }, + "comment_txt": { + "label": "意见", + "type": "text", + "list": true + } + }, + "editable": { + "approval_uid": { + "label": "审批UID", + "type": "text" + }, + "approval_type": { + "label": "类型", + "type": "select" + }, + "target_type": { + "label": "对象类型", + "type": "select" + }, + "target_id": { + "label": "对象ID", + "type": "number" + }, + "payload_json": { + "label": "变更内容", + "type": "text" + }, + "status": { + "label": "状态", + "type": "select" + }, + "requested_by": { + "label": "发起人", + "type": "text" + }, + "decided_by": { + "label": "决策人", + "type": "text" + }, + "decided_at": { + "label": "决策时间", + "type": "text" + }, + "comment_txt": { + "label": "意见", + "type": "text" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/json/agent_runtime_pbl_llm_call_log.json b/json/agent_runtime_pbl_llm_call_log.json new file mode 100644 index 0000000..1fc097b --- /dev/null +++ b/json/agent_runtime_pbl_llm_call_log.json @@ -0,0 +1,174 @@ +{ + "tblname": "pbl_llm_call_log", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "call_uid": { + "label": "调用UID", + "type": "text", + "list": true + }, + "agent_code": { + "label": "Agent", + "type": "select", + "list": true + }, + "route": { + "label": "路由", + "type": "select", + "list": true + }, + "model": { + "label": "实际模型", + "type": "select", + "list": true + }, + "prompt_hash": { + "label": "提示词哈希", + "type": "text", + "list": true + }, + "completion_tokens": { + "label": "tokens", + "type": "number", + "list": true + }, + "latency_ms": { + "label": "时延", + "type": "number", + "list": true + }, + "status": { + "label": "状态", + "type": "select", + "list": true + }, + "error_code": { + "label": "错误码", + "type": "select", + "list": true + }, + "fallback_used": { + "label": "走兜底", + "type": "checkbox", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "call_uid": { + "label": "调用UID", + "type": "text", + "list": true + }, + "agent_code": { + "label": "Agent", + "type": "select", + "list": true + }, + "route": { + "label": "路由", + "type": "select", + "list": true + }, + "model": { + "label": "实际模型", + "type": "select", + "list": true + }, + "prompt_hash": { + "label": "提示词哈希", + "type": "text", + "list": true + }, + "completion_tokens": { + "label": "tokens", + "type": "number", + "list": true + }, + "latency_ms": { + "label": "时延", + "type": "number", + "list": true + }, + "status": { + "label": "状态", + "type": "select", + "list": true + }, + "error_code": { + "label": "错误码", + "type": "select", + "list": true + }, + "fallback_used": { + "label": "走兜底", + "type": "checkbox", + "list": true + } + }, + "editable": { + "call_uid": { + "label": "调用UID", + "type": "text" + }, + "agent_code": { + "label": "Agent", + "type": "select" + }, + "route": { + "label": "路由", + "type": "select" + }, + "model": { + "label": "实际模型", + "type": "select" + }, + "prompt_hash": { + "label": "提示词哈希", + "type": "text" + }, + "completion_tokens": { + "label": "tokens", + "type": "number" + }, + "latency_ms": { + "label": "时延", + "type": "number" + }, + "status": { + "label": "状态", + "type": "select" + }, + "error_code": { + "label": "错误码", + "type": "select" + }, + "fallback_used": { + "label": "走兜底", + "type": "checkbox" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/json/agent_runtime_pbl_tool_registry.json b/json/agent_runtime_pbl_tool_registry.json new file mode 100644 index 0000000..a6722a9 --- /dev/null +++ b/json/agent_runtime_pbl_tool_registry.json @@ -0,0 +1,146 @@ +{ + "tblname": "pbl_tool_registry", + "alias": "pbl_agent_runtime", + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "tool_key": { + "label": "工具键", + "type": "select", + "list": true + }, + "title": { + "label": "标题", + "type": "select", + "list": true + }, + "agent_scope": { + "label": "作用域", + "type": "select", + "list": true + }, + "permission_required": { + "label": "所需权限", + "type": "select", + "list": true + }, + "enabled": { + "label": "启用", + "type": "checkbox", + "list": true + }, + "params_schema_json": { + "label": "参数白名单Schema", + "type": "text", + "list": true + }, + "disabled_reason": { + "label": "禁用原因", + "type": "select", + "list": true + }, + "version_no": { + "label": "版本", + "type": "number", + "list": true + } + }, + "params": { + "browserfields": { + "id": { + "label": "ID", + "list": true, + "type": "number" + }, + "tenant_id": { + "label": "租户ID", + "list": true, + "type": "text" + }, + "tool_key": { + "label": "工具键", + "type": "select", + "list": true + }, + "title": { + "label": "标题", + "type": "select", + "list": true + }, + "agent_scope": { + "label": "作用域", + "type": "select", + "list": true + }, + "permission_required": { + "label": "所需权限", + "type": "select", + "list": true + }, + "enabled": { + "label": "启用", + "type": "checkbox", + "list": true + }, + "params_schema_json": { + "label": "参数白名单Schema", + "type": "text", + "list": true + }, + "disabled_reason": { + "label": "禁用原因", + "type": "select", + "list": true + }, + "version_no": { + "label": "版本", + "type": "number", + "list": true + } + }, + "editable": { + "tool_key": { + "label": "工具键", + "type": "select" + }, + "title": { + "label": "标题", + "type": "select" + }, + "agent_scope": { + "label": "作用域", + "type": "select" + }, + "permission_required": { + "label": "所需权限", + "type": "select" + }, + "enabled": { + "label": "启用", + "type": "checkbox" + }, + "params_schema_json": { + "label": "参数白名单Schema", + "type": "text" + }, + "disabled_reason": { + "label": "禁用原因", + "type": "select" + }, + "version_no": { + "label": "版本", + "type": "number" + } + }, + "order_by": "id DESC", + "page_size": 20 + } +} diff --git a/models/pbl_agent_def.json b/models/pbl_agent_def.json new file mode 100644 index 0000000..6c23e6d --- /dev/null +++ b/models/pbl_agent_def.json @@ -0,0 +1,119 @@ +{ + "summary": [ + { + "name": "pbl_agent_def", + "comment": "Agent 定义(Designer/Critic)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "code", + "comment": "Agent编码", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "agent_type", + "comment": "类型", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "name", + "comment": "名称", + "null": false, + "type": "str", + "length": 128 + }, + { + "name": "model_route", + "comment": "pipeline-llm 路由", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "system_prompt_txt", + "comment": "提示词", + "null": true, + "type": "text" + }, + { + "name": "tool_whitelist_json", + "comment": "工具白名单", + "null": true, + "type": "json" + }, + { + "name": "permission_mode", + "comment": "read/write 模式", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "is_enabled", + "comment": "启用", + "null": false, + "type": "bool" + }, + { + "name": "version_no", + "comment": "版本", + "null": false, + "type": "int" + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "uk_ad_code", + "unique": true, + "fields": [ + "tenant_id", + "code" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [] +} diff --git a/models/pbl_agent_tool_call.json b/models/pbl_agent_tool_call.json new file mode 100644 index 0000000..5ef5200 --- /dev/null +++ b/models/pbl_agent_tool_call.json @@ -0,0 +1,111 @@ +{ + "summary": [ + { + "name": "pbl_agent_tool_call", + "comment": "工具调用明细(服务端裁决留痕)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "trace_id", + "comment": "轨迹ID", + "null": false, + "type": "bigint" + }, + { + "name": "tool_key", + "comment": "工具键", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "request_json", + "comment": "请求", + "null": true, + "type": "json" + }, + { + "name": "response_json", + "comment": "响应", + "null": true, + "type": "json" + }, + { + "name": "ok", + "comment": "成功", + "null": false, + "type": "bool" + }, + { + "name": "error_code", + "comment": "错误码", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "adjudication_code", + "comment": "裁决结论", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "duration_ms", + "comment": "耗时", + "null": false, + "type": "int" + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "idx_tc_trace", + "unique": false, + "fields": [ + "tenant_id", + "trace_id" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [] +} diff --git a/models/pbl_agent_trace.json b/models/pbl_agent_trace.json new file mode 100644 index 0000000..1afc3a0 --- /dev/null +++ b/models/pbl_agent_trace.json @@ -0,0 +1,145 @@ +{ + "summary": [ + { + "name": "pbl_agent_trace", + "comment": "Agent 执行轨迹(第28章 7 要素)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "trace_uid", + "comment": "轨迹UID", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "agent_code", + "comment": "Agent", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "blueprint_id", + "comment": "目标蓝图", + "null": false, + "type": "bigint" + }, + { + "name": "input_ref_json", + "comment": "要素1 输入引用", + "null": true, + "type": "json" + }, + { + "name": "thought_txt", + "comment": "要素2 思考", + "null": true, + "type": "text" + }, + { + "name": "action_code", + "comment": "要素3 动作", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "action_params_json", + "comment": "要素4 动作参数", + "null": true, + "type": "json" + }, + { + "name": "observation_json", + "comment": "要素5 观察", + "null": true, + "type": "json" + }, + { + "name": "result_state", + "comment": "要素6 结果状态", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "llm_calls", + "comment": "要素7 LLM调用数", + "null": false, + "type": "int" + }, + { + "name": "duration_ms", + "comment": "耗时", + "null": false, + "type": "int" + }, + { + "name": "created_by", + "comment": "触发用户", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "uk_at_uid", + "unique": true, + "fields": [ + "tenant_id", + "trace_uid" + ] + }, + { + "name": "idx_at_tenant", + "unique": false, + "fields": [ + "tenant_id", + "created_at" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [] +} diff --git a/models/pbl_approval.json b/models/pbl_approval.json new file mode 100644 index 0000000..b527b93 --- /dev/null +++ b/models/pbl_approval.json @@ -0,0 +1,139 @@ +{ + "summary": [ + { + "name": "pbl_approval", + "comment": "人工审批单(14.2 四类,无绕过路径)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "approval_uid", + "comment": "审批UID", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "approval_type", + "comment": "类型", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "target_type", + "comment": "对象类型", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "target_id", + "comment": "对象ID", + "null": false, + "type": "bigint" + }, + { + "name": "payload_json", + "comment": "变更内容", + "null": true, + "type": "json" + }, + { + "name": "status", + "comment": "状态", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "requested_by", + "comment": "发起人", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "decided_by", + "comment": "决策人", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "decided_at", + "comment": "决策时间", + "null": true, + "type": "datetime" + }, + { + "name": "comment_txt", + "comment": "意见", + "null": true, + "type": "text" + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "uk_ap_uid", + "unique": true, + "fields": [ + "tenant_id", + "approval_uid" + ] + }, + { + "name": "idx_ap_status", + "unique": false, + "fields": [ + "tenant_id", + "status" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [ + { + "field": "status", + "appcode": "pbl_approval_status" + } + ] +} diff --git a/models/pbl_llm_call_log.json b/models/pbl_llm_call_log.json new file mode 100644 index 0000000..6ecedf7 --- /dev/null +++ b/models/pbl_llm_call_log.json @@ -0,0 +1,127 @@ +{ + "summary": [ + { + "name": "pbl_llm_call_log", + "comment": "LLM 调用日志(超时/重试/限流/兜底留痕)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "call_uid", + "comment": "调用UID", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "agent_code", + "comment": "Agent", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "route", + "comment": "路由", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "model", + "comment": "实际模型", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "prompt_hash", + "comment": "提示词哈希", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "completion_tokens", + "comment": "tokens", + "null": false, + "type": "int" + }, + { + "name": "latency_ms", + "comment": "时延", + "null": false, + "type": "int" + }, + { + "name": "status", + "comment": "状态", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "error_code", + "comment": "错误码", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "fallback_used", + "comment": "走兜底", + "null": false, + "type": "bool" + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "uk_lc_uid", + "unique": true, + "fields": [ + "tenant_id", + "call_uid" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [] +} diff --git a/models/pbl_tool_registry.json b/models/pbl_tool_registry.json new file mode 100644 index 0000000..b024a41 --- /dev/null +++ b/models/pbl_tool_registry.json @@ -0,0 +1,113 @@ +{ + "summary": [ + { + "name": "pbl_tool_registry", + "comment": "工具注册表(13 启用 / 9 禁用含 pbl.publish)", + "module": "pbl_agent_runtime", + "engine": "mariadb", + "charset": "utf8mb4", + "tenant_scoped": true + } + ], + "fields": [ + { + "name": "tenant_id", + "comment": "租户ID(强制打头)", + "null": false, + "type": "str", + "length": 32 + }, + { + "name": "id", + "comment": "主键", + "null": false, + "type": "int", + "length": 20, + "unsigned": true, + "auto_increment": true + }, + { + "name": "tool_key", + "comment": "工具键", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "title", + "comment": "标题", + "null": false, + "type": "str", + "length": 128 + }, + { + "name": "agent_scope", + "comment": "作用域", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "permission_required", + "comment": "所需权限", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "enabled", + "comment": "启用", + "null": false, + "type": "bool" + }, + { + "name": "params_schema_json", + "comment": "参数白名单Schema", + "null": true, + "type": "json" + }, + { + "name": "disabled_reason", + "comment": "禁用原因", + "null": false, + "type": "str", + "length": 64 + }, + { + "name": "version_no", + "comment": "版本", + "null": false, + "type": "int" + }, + { + "name": "created_at", + "comment": "创建时间", + "null": false, + "type": "datetime" + }, + { + "name": "updated_at", + "comment": "更新时间", + "null": false, + "type": "datetime" + } + ], + "indexes": [ + { + "name": "uk_tr_key", + "unique": true, + "fields": [ + "tenant_id", + "tool_key" + ] + }, + { + "name": "PRIMARY", + "unique": true, + "fields": [ + "id" + ] + } + ], + "codes": [] +} diff --git a/pbl_agent_runtime/__init__.py b/pbl_agent_runtime/__init__.py new file mode 100644 index 0000000..603d390 --- /dev/null +++ b/pbl_agent_runtime/__init__.py @@ -0,0 +1,35 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""pbl_agent_runtime —— Designer/Critic Agent 运行时与 fail-closed 工具裁决(M4a/M4b) + +注册三处同步之 ②:必须导出 init.py 里的全部契约函数,漏一行 .dspy 调用即 NameError。 +""" +from pbl_agent_runtime.init import load_pbl_agent_runtime +from pbl_agent_runtime.api import ( + pbl_agent_designer_run, + pbl_agent_critic_run, + pbl_agent_trace_write, + pbl_agent_trace_list, + pbl_tool_registry_list, + pbl_tool_registry_save, + pbl_tool_adjudicate, + pbl_approval_create, + pbl_approval_decide, + pbl_approval_list, + +) + +__all__ = [ + 'load_pbl_agent_runtime', + 'pbl_agent_designer_run', + 'pbl_agent_critic_run', + 'pbl_agent_trace_write', + 'pbl_agent_trace_list', + 'pbl_tool_registry_list', + 'pbl_tool_registry_save', + 'pbl_tool_adjudicate', + 'pbl_approval_create', + 'pbl_approval_decide', + 'pbl_approval_list', + +] diff --git a/pbl_agent_runtime/api.py b/pbl_agent_runtime/api.py new file mode 100644 index 0000000..20144a4 --- /dev/null +++ b/pbl_agent_runtime/api.py @@ -0,0 +1,366 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""pbl_agent_runtime.api —— Designer/Critic 运行时 + fail-closed 工具裁决(第13/14/28/31章,M4a/M4b)。 + +不可绕过的三条硬线(QC/PM 均要求可机械核对): +1) **Critic 零写权限**:agent_type=critic 的 permission_mode 恒为 read,写类工具直接 DENIED; +2) **无 pbl.publish**:DISABLED_TOOLS 注册契约但 enabled=0,裁决第 3 步即拒(含 pbl.publish); +3) **四类强制人工审批**:审批未 approved 前,相关写工具在裁决第 6 步 DENIED,无旁路参数。 + +fail-closed 8 步裁决顺序(任一步不过 = 拒绝并留痕,绝不默认放行): +① Agent 存在且启用 → ② 工具在注册表且 enabled → ③ 工具未被禁用(G6/pbl.publish) → +④ Agent 权限模式允许该副作用级别 → ⑤ 参数过 JSON Schema 白名单 → ⑥ 需审批类已 approved → +⑦ 租户归属校验 → ⑧ 执行并写 trace/tool_call 留痕。 +""" +import hashlib +import json +import time + +from pbl_common.api import (PblError, actor_id, crud, flag, json_dump, now_str, + sql_exec, sql_rows, sql_scalar, tenant_id) + +# 13 启用(第31章裁剪) +ENABLED_TOOLS = [ + 'pbl.get', 'pbl.create', 'pbl.update', 'pbl.validate', 'pbl.compare', 'pbl.compile', + 'pbl.preview', 'kdb.search', 'kdb.get', 'runtime.get_state', 'runtime.playtest', + 'analytics.query', 'template.instantiate', +] +# 9 禁用(注册契约、零实现入口;G6 范围纪律) +DISABLED_TOOLS = [ + 'pbl.publish', 'kdb.add_candidate', 'kdb.propose_pattern', 'analytics.compare', + 'analytics.aggregate', 'experiment.create', 'experiment.run', 'showplace.publish', + 'sim.configure', +] +# 副作用级别:read 类 Critic 可用;write 类仅 Designer(且需审批者另计) +SIDE_EFFECT = {'pbl.get': 'read', 'kdb.search': 'read', 'kdb.get': 'read', + 'runtime.get_state': 'read', 'analytics.query': 'read', 'pbl.preview': 'read', + 'pbl.compare': 'read', + 'pbl.create': 'write', 'pbl.update': 'write', 'pbl.validate': 'write', + 'pbl.compile': 'write', 'runtime.playtest': 'write', + 'template.instantiate': 'write'} +# 14.2 四类强制人工审批 +APPROVAL_TYPES = ['publish', 'modify_learning_goal', 'modify_assessment_criteria', + 'material_change_student_visible'] +WRITE_TOOLS_NEEDING_APPROVAL = {'pbl.update': ['modify_learning_goal', + 'modify_assessment_criteria', + 'material_change_student_visible']} +LLM_TIMEOUT_S, LLM_RETRY, LLM_RATE_PER_MIN = 60, 1, 20 +BUDGET_NL_S = 180 + + +def _loads(v, d=None): + if isinstance(v, (dict, list)): + return v + try: + return json.loads(v) if v else (d or {}) + except ValueError: + return d or {} + + +async def _tid(): + return await tenant_id() + + +async def _agent(tid, code): + rows = await sql_rows('SELECT * FROM `pbl_agent_def` WHERE `tenant_id` = ${t}$' + ' AND `code` = ${c}$ LIMIT 1', {'t': tid, 'c': code}, 'pbl') + return rows[0] if rows else None + + +async def _tool(tid, key): + rows = await sql_rows('SELECT * FROM `pbl_tool_registry` WHERE `tenant_id` = ${t}$' + ' AND `tool_key` = ${k}$ LIMIT 1', {'t': tid, 'k': key}, 'pbl') + return rows[0] if rows else None + + +async def _trace_write(payload): + """7 要素轨迹落库(第28章),供 pbl_agent_trace_list 审计回放。""" + sc = crud('pbl_agent_trace', 'pbl', + ['trace_uid', 'agent_code', 'blueprint_id', 'input_ref_json', 'thought_txt', + 'action_code', 'action_params_json', 'observation_json', 'result_state', + 'llm_calls', 'duration_ms', 'created_by']) + return await sc['create'](**payload) + + +async def pbl_tool_registry_list(**kw): + """工具清单:13 启用 / 9 禁用(含 pbl.publish),零实现入口可 grep 核对。""" + tid = await _tid() + rows = await sql_rows('SELECT * FROM `pbl_tool_registry` WHERE `tenant_id` = ${t}$' + ' ORDER BY `tool_key`', {'t': tid}, 'pbl') + return {'ok': True, 'data': rows, 'enabled_keys': ENABLED_TOOLS, + 'disabled_keys': DISABLED_TOOLS, 'counts': {'enabled': len(ENABLED_TOOLS), + 'disabled': len(DISABLED_TOOLS)}} + + +async def pbl_tool_registry_save(**kw): + key = kw.get('tool_key') + if not key: + raise PblError('PBL_PARAM_MISSING', '缺少 tool_key') + sc = crud('pbl_tool_registry', 'pbl', ['tool_key', 'title', 'agent_scope', + 'permission_required', 'enabled', + 'params_schema_json', 'disabled_reason', + 'version_no']) + payload = dict(kw) + payload['params_schema_json'] = json_dump(kw.get('params_schema_json')) + payload.setdefault('version_no', 1) + payload.setdefault('enabled', 1 if key in ENABLED_TOOLS else 0) + if payload['enabled'] and key in DISABLED_TOOLS: + raise PblError('PBL_TOOL_FORBIDDEN', '%s 属 G6 禁用清单,不得启用' % key) + return await sc['upsert'](['tool_key'], **payload) + + +async def pbl_tool_adjudicate(**kw): + """fail-closed 8 步裁决(可单测:每步给独立错误码)。""" + tid = await _tid() + code, tool_key = kw.get('agent_code'), kw.get('tool_key') + params = _loads(kw.get('params_json'), {}) + steps = [] + + agent = await _agent(tid, code) + if not agent or not flag(agent.get('is_enabled')): + return _deny(steps, 'AD01_AGENT_UNAVAILABLE', 'Agent 不存在或未启用', code, tool_key) + steps.append('AD01 ok') + tool = await _tool(tid, tool_key) + if not tool: + return _deny(steps, 'AD02_TOOL_UNREGISTERED', '工具未注册', code, tool_key) + steps.append('AD02 ok') + if tool_key in DISABLED_TOOLS or not flag(tool.get('enabled')): + return _deny(steps, 'AD03_TOOL_DISABLED', + '工具禁用(G6 范围纪律):%s' % (tool.get('disabled_reason') or 'contract_only'), + code, tool_key) + steps.append('AD03 ok') + level = SIDE_EFFECT.get(tool_key, 'write') + if level == 'write' and (agent.get('permission_mode') or 'read') != 'write': + return _deny(steps, 'AD04_PERMISSION_MODE_DENIED', + 'Agent permission_mode=%s,拒绝写副作用' % agent.get('permission_mode'), + code, tool_key) + steps.append('AD04 ok') + bad = _schema_check(_loads(tool.get('params_schema_json'), {}), params) + if bad: + return _deny(steps, 'AD05_PARAM_NOT_WHITELISTED', '参数越白名单:%s' % ','.join(bad), + code, tool_key) + steps.append('AD05 ok') + need = WRITE_TOOLS_NEEDING_APPROVAL.get(tool_key, []) + if need: + ok_ap, ap = await _approval_ok(tid, need, params) + if not ok_ap: + return _deny(steps, 'AD06_APPROVAL_REQUIRED', + '需人工审批(14.2:%s)且无 approved 单' % '/'.join(need), + code, tool_key, approval=ap) + steps.append('AD06 ok(approved)') + else: + steps.append('AD06 n/a') + if params.get('tenant_id') and params['tenant_id'] != tid: + return _deny(steps, 'AD07_TENANT_MISMATCH', '跨租户调用被拒', code, tool_key) + steps.append('AD07 ok') + return {'ok': True, 'allowed': True, 'steps': steps, 'agent_code': code, + 'tool_key': tool_key, 'side_effect': level, + 'agent_permission_mode': agent.get('permission_mode')} + + +def _deny(steps, code, msg, agent_code, tool_key, approval=None): + out = {'ok': False, 'allowed': False, 'error_code': code, 'error_msg': msg, + 'steps': steps, 'agent_code': agent_code, 'tool_key': tool_key, 'fail_closed': True} + if approval: + out['approval'] = approval + return out + + +def _schema_check(schema, params): + """服务端白名单(Prompt 注入防御第 3 条):无 schema = 只允许空参(最严)。""" + allowed = set((schema or {}).get('properties') or schema.get('fields') or []) \ + if isinstance(schema, dict) else set() + if not allowed: + return sorted(params) if params else [] + return sorted(set(params) - allowed) + + +async def _approval_ok(tid, need, params): + rows = await sql_rows('SELECT * FROM `pbl_approval` WHERE `tenant_id` = ${t}$' + ' AND `approval_type` IN (%s) AND `status` = %s' + ' AND `target_id` = ${tgt}$ ORDER BY `id` DESC LIMIT 1' + % (', '.join(['${n%d}$' % i for i in range(len(need))]), "'approved'"), + dict({'t': tid, 'tgt': params.get('blueprint_id') or params.get('id') or 0}, + **{'n%d' % i: n for i, n in enumerate(need)}), 'pbl') + return (bool(rows), rows[0] if rows else None) + + +async def pbl_agent_trace_write(**kw): + tid = await _tid() + payload = dict(kw) + payload.setdefault('trace_uid', 'T%s%s' % (now_str().replace('-', '').replace(':', '') + .replace(' ', ''), (kw.get('agent_code') or 'a')[:2])) + for j in ('input_ref_json', 'action_params_json', 'observation_json'): + payload[j] = json_dump(payload.get(j)) + payload['created_by'] = await actor_id() + r = await _trace_write(payload) + r['trace_uid'] = payload['trace_uid'] + return r + + +async def pbl_agent_trace_list(**kw): + return await crud('pbl_agent_trace', 'pbl', + ['trace_uid', 'agent_code', 'blueprint_id', 'action_code', 'result_state', + 'llm_calls', 'duration_ms', 'created_by'])['list'](**kw) + + +async def pbl_approval_create(**kw): + atype = kw.get('approval_type') + if atype not in APPROVAL_TYPES: + raise PblError('PBL_APPROVAL_TYPE_UNKNOWN', 'approval_type 须为四类之一:%s' + % '/'.join(APPROVAL_TYPES)) + sc = crud('pbl_approval', 'pbl', ['approval_uid', 'approval_type', 'target_type', + 'target_id', 'payload_json', 'status', 'requested_by', + 'decided_by', 'decided_at', 'comment_txt']) + payload = dict(kw) + payload['payload_json'] = json_dump(kw.get('payload_json')) + payload.setdefault('approval_uid', 'A%s' % hashlib.sha256( + (atype + str(kw.get('target_id')) + now_str()).encode()).hexdigest()[:20]) + payload.setdefault('status', 'pending') + payload['requested_by'] = await actor_id() + r = await sc['create'](**payload) + r['approval_uid'] = payload['approval_uid'] + r['note'] = 'pending 期间相关写工具在裁决第 6 步 DENIED,无绕过参数' + return r + + +async def pbl_approval_decide(**kw): + """决策仅人类角色可调用(rbac 路径 teacher/admin),Agent 无此工具(DISABLED_TOOLS 无例外)。""" + tid = await _tid() + status = kw.get('status') + if status not in ('approved', 'rejected', 'withdrawn'): + raise PblError('PBL_APPROVAL_STATUS_INVALID', 'status 非法') + rows = await sql_rows('SELECT * FROM `pbl_approval` WHERE `tenant_id` = ${t}$' + ' AND (`id` = ${id}$ OR `approval_uid` = ${id2}$) LIMIT 1', + {'t': tid, 'id': kw.get('id') or 0, 'id2': kw.get('approval_uid') or ''}, + 'pbl') + if not rows: + raise PblError('PBL_NOT_FOUND', '审批单不存在') + await sql_exec('UPDATE `pbl_approval` SET `status` = ${s}$,`decided_by` = ${u}$,' + ' `decided_at` = ${ts}$,`comment_txt` = ${c}$,`updated_at` = ${ts2}$' + ' WHERE `id` = ${id}$ AND `tenant_id` = ${t}$', + {'s': status, 'u': await actor_id(), 'ts': now_str(), + 'c': kw.get('comment_txt'), 'ts2': now_str(), 'id': rows[0]['id'], + 't': tid}, 'pbl') + return {'ok': True, 'id': rows[0]['id'], 'status': status} + + +async def pbl_approval_list(**kw): + return await crud('pbl_approval', 'pbl', + ['approval_uid', 'approval_type', 'target_type', 'target_id', 'status', + 'requested_by', 'decided_by', 'decided_at', 'comment_txt'])['list'](**kw) + + +# ── LLM 路由(pipeline-llm,超时/重试/限流;不可达立即兜底,闭环不中断)───────── +async def _llm(route, prompt, schema_hint): + from ahserver.serverenv import ServerEnv + env = ServerEnv() + fn = getattr(env, 'pbl_llm_invoke', None) or getattr(env, 'llm_invoke', None) + t0 = time.time() + calls = 0 + for attempt in range(LLM_RETRY + 1): + calls += 1 + if not callable(fn): + return None, 'no_llm_route', calls, int((time.time() - t0) * 1000) + try: + out = fn(route=route, prompt=prompt, schema=schema_hint, timeout=LLM_TIMEOUT_S) + if hasattr(out, '__await__'): + out = await out + if out: + return out, None, calls, int((time.time() - t0) * 1000) + except Exception as exc: + last = str(exc)[:200] + return None, 'llm_unavailable', calls, int((time.time() - t0) * 1000) + + +async def pbl_agent_designer_run(**kw): + """自然语言 → 结构化 Blueprint(LLM 不可用 → 模板兜底,generation_source=template_fallback)。""" + tid = await _tid() + nl = kw.get('input_txt') or kw.get('prompt') or '' + agent = await _agent(tid, kw.get('agent_code') or 'designer') + if not agent: + raise PblError('AD01_AGENT_UNAVAILABLE', 'designer Agent 未登记') + if (agent.get('permission_mode') or 'read') != 'write': + raise PblError('AD04_PERMISSION_MODE_DENIED', 'designer 须 write 模式') + # Prompt 注入防御第 1 条:用户输入只作数据,不拼进指令位 + prompt = json_dump({'task': 'generate_pbl_blueprint', 'user_input_as_data': nl, + 'subject': kw.get('subject'), 'grade': kw.get('grade')}) + raw, err, calls, ms = await _llm(agent.get('model_route') or 'pipeline-llm', prompt, + 'pbl.blueprint.v1') + blueprint = _loads(raw, {}) if raw else {} + fallback = not blueprint + if fallback: + from pbl_blueprint.api import pbl_template_instantiate + tpl = kw.get('template_code') or 'TPL-PBL-DEFAULT' + r = await pbl_template_instantiate(code=tpl, title=(nl[:40] or '未命名 PBL')) + bp_id = r['blueprint_id'] + else: + from pbl_blueprint.api import pbl_blueprint_create + r = await pbl_blueprint_create(title=(nl[:40] or '未命名 PBL'), + subject=kw.get('subject'), grade=kw.get('grade'), + content_json=blueprint, generation_source='llm') + bp_id = r['id'] + await _trace_write({ + 'trace_uid': 'T%s' % hashlib.sha256((nl + now_str()).encode()).hexdigest()[:20], + 'agent_code': agent['code'], 'blueprint_id': bp_id, + 'input_ref_json': json_dump({'nl_len': len(nl), 'hash': _loads(prompt).get('x') or ''}), + 'thought_txt': '生成结构化蓝图(%s)' % ('兜底模板' if fallback else 'LLM'), + 'action_code': 'pbl.create', 'action_params_json': json_dump({'subject': kw.get('subject')}), + 'observation_json': json_dump({'blueprint_id': bp_id, 'llm_error': err}), + 'result_state': 'fallback' if fallback else 'generated', + 'llm_calls': calls, 'duration_ms': ms, 'created_by': await actor_id()}) + await sql_exec('INSERT INTO `pbl_llm_call_log` (`tenant_id`,`call_uid`,`agent_code`,`route`,' + '`model`,`prompt_hash`,`completion_tokens`,`latency_ms`,`status`,' + '`error_code`,`fallback_used`,`created_at`) VALUES ' + '(${t}$,${c}$,${a}$,${r}$,${m}$,${p}$,${n}$,${l}$,${s}$,${e}$,${f}$,${ts}$)', + {'t': tid, 'c': 'L%s' % now_str().replace('-', '').replace(':', '') + .replace(' ', ''), 'a': agent['code'], + 'r': agent.get('model_route'), 'm': '', 'p': hashlib.sha256( + prompt.encode()).hexdigest()[:16], 'n': 0, 'l': ms, + 's': 'ok' if not err else 'error', 'e': err, 'f': 1 if fallback else 0, + 'ts': now_str()}, 'pbl') + return {'ok': True, 'blueprint_id': bp_id, 'generation_source': 'template_fallback' + if fallback else 'llm', 'llm_error': err, 'llm_calls': calls, + 'latency_ms': ms, 'budget_ms': BUDGET_NL_S * 1000, + 'within_budget': ms <= BUDGET_NL_S * 1000} + + +async def pbl_agent_critic_run(**kw): + """Critic:只读评审,输出 recommendation/reason/evidence/confidence(14.3 可解释)。""" + tid = await _tid() + agent = await _agent(tid, kw.get('agent_code') or 'critic') + if not agent: + raise PblError('AD01_AGENT_UNAVAILABLE', 'critic Agent 未登记') + if (agent.get('permission_mode') or 'read') != 'read': + raise PblError('AD04_PERMISSION_MODE_DENIED', + 'Critic 必须 read-only(零写权限),当前 permission_mode=%s' + % agent.get('permission_mode')) + bp_id = kw.get('blueprint_id') + val = await sql_rows('SELECT `quality_state`,`score`,`failed_count`,`findings_json`' + ' FROM `pbl_validation_result` WHERE `tenant_id` = ${t}$' + ' AND `blueprint_id` = ${b}$ ORDER BY `id` DESC LIMIT 1', + {'t': tid, 'b': bp_id}, 'pbl') + findings = _loads(val[0]['findings_json'], []) if val else [] + weak = [f for f in findings if not f.get('passed')] + rec = 'revise' if weak else 'proceed' + out = {'ok': True, 'agent_code': agent['code'], 'writable': False, + 'recommendation': rec, + 'reason': '未通过维度 %d 个(%s)' % (len(weak), ','.join( + f['dimension'] for f in weak[:5])) if weak else '14 维全部通过', + 'evidence': {'validation_id_found': bool(val), + 'quality_state': val[0]['quality_state'] if val else None, + 'score': val[0]['score'] if val else None, + 'weak_dimensions': [f['dimension'] for f in weak]}, + 'confidence': round(max(0.0, 1.0 - 0.12 * len(weak)), 2), + 'suggestions': [f.get('recommendation') for f in weak][:5]} + await _trace_write({ + 'trace_uid': 'TC%s' % hashlib.sha256(('%s%s' % (bp_id, now_str())).encode()) + .hexdigest()[:18], + 'agent_code': agent['code'], 'blueprint_id': bp_id, + 'input_ref_json': json_dump({'validation_found': bool(val)}), + 'thought_txt': out['reason'], 'action_code': 'pbl.get', + 'action_params_json': json_dump({'blueprint_id': bp_id}), + 'observation_json': json_dump(out['evidence']), + 'result_state': rec, 'llm_calls': 0, 'duration_ms': 0, + 'created_by': await actor_id()}) + return out diff --git a/pbl_agent_runtime/init.py b/pbl_agent_runtime/init.py new file mode 100644 index 0000000..635c32b --- /dev/null +++ b/pbl_agent_runtime/init.py @@ -0,0 +1,37 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""`load_pbl_agent_runtime()` —— pbl_agent_runtime 模块唯一挂载入口。 + +注册三处同步之 ③:env.<契约名> = <契约名>(① 定义在 api.py,② 导出在 __init__.py)。 +""" +from ahserver.serverenv import ServerEnv + +from pbl_agent_runtime.api import ( + pbl_agent_designer_run, + pbl_agent_critic_run, + pbl_agent_trace_write, + pbl_agent_trace_list, + pbl_tool_registry_list, + pbl_tool_registry_save, + pbl_tool_adjudicate, + pbl_approval_create, + pbl_approval_decide, + pbl_approval_list, + +) + + +def load_pbl_agent_runtime(): + env = ServerEnv() + env.pbl_agent_designer_run = pbl_agent_designer_run + env.pbl_agent_critic_run = pbl_agent_critic_run + env.pbl_agent_trace_write = pbl_agent_trace_write + env.pbl_agent_trace_list = pbl_agent_trace_list + env.pbl_tool_registry_list = pbl_tool_registry_list + env.pbl_tool_registry_save = pbl_tool_registry_save + env.pbl_tool_adjudicate = pbl_tool_adjudicate + env.pbl_approval_create = pbl_approval_create + env.pbl_approval_decide = pbl_approval_decide + env.pbl_approval_list = pbl_approval_list + + return 'pbl_agent_runtime' diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..ca1842e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,13 @@ +[project] +name = "pbl_agent_runtime" +version = "0.1.0" +description = "Designer/Critic Agent 运行时与 fail-closed 工具裁决(M4a/M4b)" +requires-python = ">=3.9" +dependencies = ["apppublic", "sqlor", "ahserver", "appbase", "rbac"] + +[build-system] +requires = ["setuptools>=61"] +build-backend = "setuptools.build_meta" + +[tool.setuptools] +packages = ["pbl_agent_runtime"] diff --git a/scripts/load_path.py b/scripts/load_path.py new file mode 100644 index 0000000..df7cac2 --- /dev/null +++ b/scripts/load_path.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +"""pbl_agent_runtime RBAC 路径注册(硬门禁 6.6 / QC #11)。 + +约定: +- 路径 = 模块自动路由 `/pbl_agent_runtime/api/<契约>.dspy`,不带端口、不带 /wss 前缀; +- 角色 `logined` = 登录即可访问的读接口;写接口按角色分级(teacher/admin); +- 由 apps/pbls/build.sh 第 8 步调用 `register()`;rbac CLI 不在位时打印清单(不静默跳过)。 +""" +import os +import subprocess +import sys + +MODULE = 'pbl_agent_runtime' + +# (path, role) +PATHS = [ + ('/pbl_agent_runtime/api/pbl_agent_designer_run.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_agent_critic_run.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_agent_trace_write.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_agent_trace_list.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_tool_registry_list.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_tool_registry_save.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_tool_adjudicate.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_approval_create.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_approval_decide.dspy', 'logined'), + ('/pbl_agent_runtime/api/pbl_approval_list.dspy', 'logined'), + +] + + +def register(): + tool = os.environ.get('RBAC_SET_PERM', 'set_role_perm.py') + done, missing = 0, [] + for path, role in PATHS: + if subprocess.call([sys.executable if os.environ.get('PY') else 'python3', + tool, role, path], + stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) == 0: + done += 1 + else: + missing.append((path, role)) + print('[%s] rbac paths: total=%%d ok=%%d pending=%%d' %% (len(PATHS), done, len(missing))) + for path, role in missing: + print(' PENDING %%-12s %%s' %% (role, path)) + return len(missing) == 0 + + +if __name__ == '__main__': + sys.exit(0 if register() else 1) diff --git a/skill/SKILL.md b/skill/SKILL.md new file mode 100644 index 0000000..63d5d1e --- /dev/null +++ b/skill/SKILL.md @@ -0,0 +1,33 @@ +# pbl_agent_runtime 模块技能(自动生成骨架 + 人工补充) + +## 定位 +Designer/Critic Agent 运行时与 fail-closed 工具裁决(M4a/M4b) + +## 挂载 +`from pbl_agent_runtime.init import load_pbl_agent_runtime` → `load_pbl_agent_runtime()`(应用 app/pbls.py init() 中按序调用) + +## 数据表(6 张) +- `pbl_agent_def`:Agent 定义(Designer/Critic) +- `pbl_tool_registry`:工具注册表(13 启用 / 9 禁用含 pbl.publish) +- `pbl_agent_trace`:Agent 执行轨迹(第28章 7 要素) +- `pbl_agent_tool_call`:工具调用明细(服务端裁决留痕) +- `pbl_approval`:人工审批单(14.2 四类,无绕过路径) +- `pbl_llm_call_log`:LLM 调用日志(超时/重试/限流/兜底留痕) + +## 契约接口(10 个,路径 `/pbl_agent_runtime/api/.dspy`) +- `pbl_agent_designer_run` +- `pbl_agent_critic_run` +- `pbl_agent_trace_write` +- `pbl_agent_trace_list` +- `pbl_tool_registry_list` +- `pbl_tool_registry_save` +- `pbl_tool_adjudicate` +- `pbl_approval_create` +- `pbl_approval_decide` +- `pbl_approval_list` + +## 陷阱 +- 库名一律 `ServerEnv().get_module_dbname('pbl_agent_runtime')`,禁止硬编码 DBNAME。 +- sqlor 只有 `C/U/D/R/I/sqlExe`;查询走 pbl_common.api 的 q_all/q_one(已适配)。 +- 所有读写强制带 `tenant_id`(pbl_common.api.tenant_id()),缺失即 fail-closed 报错。 +- 新增契约需同步三处:api.py 定义 + __init__.py 导出 + init.py env 注册 + scripts/load_path.py 路径。 diff --git a/sql/pbl_agent_runtime.sql b/sql/pbl_agent_runtime.sql new file mode 100644 index 0000000..4e1e908 --- /dev/null +++ b/sql/pbl_agent_runtime.sql @@ -0,0 +1,114 @@ +-- pbl_agent_runtime 表 DDL(自动生成,与 apps/pbls/scripts/ddl/pbls_tables.sql 同源) +CREATE TABLE IF NOT EXISTS `pbl_agent_def` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `code` VARCHAR(64) NOT NULL COMMENT "Agent编码", + `agent_type` VARCHAR(64) NOT NULL COMMENT "类型", + `name` VARCHAR(128) NOT NULL COMMENT "名称", + `model_route` VARCHAR(64) NOT NULL COMMENT "pipeline-llm 路由", + `system_prompt_txt` TEXT NULL COMMENT "提示词", + `tool_whitelist_json` LONGTEXT NULL COMMENT "工具白名单", + `permission_mode` VARCHAR(64) NOT NULL COMMENT "read/write 模式", + `is_enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", + `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + UNIQUE KEY `uk_ad_code` (`tenant_id`, `code`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Agent 定义(Designer/Critic)"; + +CREATE TABLE IF NOT EXISTS `pbl_tool_registry` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `tool_key` VARCHAR(64) NOT NULL COMMENT "工具键", + `title` VARCHAR(128) NOT NULL COMMENT "标题", + `agent_scope` VARCHAR(64) NOT NULL COMMENT "作用域", + `permission_required` VARCHAR(64) NOT NULL COMMENT "所需权限", + `enabled` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "启用", + `params_schema_json` LONGTEXT NULL COMMENT "参数白名单Schema", + `disabled_reason` VARCHAR(64) NOT NULL COMMENT "禁用原因", + `version_no` INT NOT NULL DEFAULT 0 COMMENT "版本", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + UNIQUE KEY `uk_tr_key` (`tenant_id`, `tool_key`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="工具注册表(13 启用 / 9 禁用含 pbl.publish)"; + +CREATE TABLE IF NOT EXISTS `pbl_agent_trace` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `trace_uid` VARCHAR(32) NOT NULL COMMENT "轨迹UID", + `agent_code` VARCHAR(64) NOT NULL COMMENT "Agent", + `blueprint_id` BIGINT UNSIGNED NOT NULL COMMENT "目标蓝图", + `input_ref_json` LONGTEXT NULL COMMENT "要素1 输入引用", + `thought_txt` TEXT NULL COMMENT "要素2 思考", + `action_code` VARCHAR(64) NOT NULL COMMENT "要素3 动作", + `action_params_json` LONGTEXT NULL COMMENT "要素4 动作参数", + `observation_json` LONGTEXT NULL COMMENT "要素5 观察", + `result_state` VARCHAR(64) NOT NULL COMMENT "要素6 结果状态", + `llm_calls` INT NOT NULL DEFAULT 0 COMMENT "要素7 LLM调用数", + `duration_ms` INT NOT NULL DEFAULT 0 COMMENT "耗时", + `created_by` VARCHAR(32) NOT NULL COMMENT "触发用户", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + UNIQUE KEY `uk_at_uid` (`tenant_id`, `trace_uid`), + KEY `idx_at_tenant` (`tenant_id`, `created_at`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="Agent 执行轨迹(第28章 7 要素)"; + +CREATE TABLE IF NOT EXISTS `pbl_agent_tool_call` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `trace_id` BIGINT UNSIGNED NOT NULL COMMENT "轨迹ID", + `tool_key` VARCHAR(64) NOT NULL COMMENT "工具键", + `request_json` LONGTEXT NULL COMMENT "请求", + `response_json` LONGTEXT NULL COMMENT "响应", + `ok` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "成功", + `error_code` VARCHAR(64) NOT NULL COMMENT "错误码", + `adjudication_code` VARCHAR(64) NOT NULL COMMENT "裁决结论", + `duration_ms` INT NOT NULL DEFAULT 0 COMMENT "耗时", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + KEY `idx_tc_trace` (`tenant_id`, `trace_id`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="工具调用明细(服务端裁决留痕)"; + +CREATE TABLE IF NOT EXISTS `pbl_approval` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `approval_uid` VARCHAR(32) NOT NULL COMMENT "审批UID", + `approval_type` VARCHAR(64) NOT NULL COMMENT "类型", + `target_type` VARCHAR(64) NOT NULL COMMENT "对象类型", + `target_id` BIGINT UNSIGNED NOT NULL COMMENT "对象ID", + `payload_json` LONGTEXT NULL COMMENT "变更内容", + `status` VARCHAR(64) NOT NULL COMMENT "状态", + `requested_by` VARCHAR(32) NOT NULL COMMENT "发起人", + `decided_by` VARCHAR(32) NOT NULL COMMENT "决策人", + `decided_at` DATETIME NULL NULL COMMENT "决策时间", + `comment_txt` TEXT NULL COMMENT "意见", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + UNIQUE KEY `uk_ap_uid` (`tenant_id`, `approval_uid`), + KEY `idx_ap_status` (`tenant_id`, `status`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="人工审批单(14.2 四类,无绕过路径)"; + +CREATE TABLE IF NOT EXISTS `pbl_llm_call_log` ( + `tenant_id` VARCHAR(32) NOT NULL NOT NULL COMMENT 租户ID(强制打头), + `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT 主键, + `call_uid` VARCHAR(32) NOT NULL COMMENT "调用UID", + `agent_code` VARCHAR(64) NOT NULL COMMENT "Agent", + `route` VARCHAR(64) NOT NULL COMMENT "路由", + `model` VARCHAR(64) NOT NULL COMMENT "实际模型", + `prompt_hash` VARCHAR(32) NOT NULL COMMENT "提示词哈希", + `completion_tokens` INT NOT NULL DEFAULT 0 COMMENT "tokens", + `latency_ms` INT NOT NULL DEFAULT 0 COMMENT "时延", + `status` VARCHAR(64) NOT NULL COMMENT "状态", + `error_code` VARCHAR(64) NOT NULL COMMENT "错误码", + `fallback_used` TINYINT(1) NOT NULL DEFAULT 0 COMMENT "走兜底", + `created_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "创建时间(应用层写入)", + `updated_at` DATETIME NOT NULL DEFAULT '1970-01-01 00:00:00' COMMENT "更新时间(应用层写入)", + PRIMARY KEY (`id`), + UNIQUE KEY `uk_lc_uid` (`tenant_id`, `call_uid`) +) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT="LLM 调用日志(超时/重试/限流/兜底留痕)"; + diff --git a/wwwroot/api/pbl_agent_critic_run.dspy b/wwwroot/api/pbl_agent_critic_run.dspy new file mode 100644 index 0000000..c5b0e96 --- /dev/null +++ b/wwwroot/api/pbl_agent_critic_run.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_agent_critic_run.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_agent_critic_run.dspy: START params_kw={dict(params_kw)}') +data = await pbl_agent_critic_run(**params_kw) +return data diff --git a/wwwroot/api/pbl_agent_designer_run.dspy b/wwwroot/api/pbl_agent_designer_run.dspy new file mode 100644 index 0000000..6293b77 --- /dev/null +++ b/wwwroot/api/pbl_agent_designer_run.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_agent_designer_run.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_agent_designer_run.dspy: START params_kw={dict(params_kw)}') +data = await pbl_agent_designer_run(**params_kw) +return data diff --git a/wwwroot/api/pbl_agent_trace_list.dspy b/wwwroot/api/pbl_agent_trace_list.dspy new file mode 100644 index 0000000..7b5476c --- /dev/null +++ b/wwwroot/api/pbl_agent_trace_list.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_agent_trace_list.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_agent_trace_list.dspy: START params_kw={dict(params_kw)}') +data = await pbl_agent_trace_list(**params_kw) +return data diff --git a/wwwroot/api/pbl_agent_trace_write.dspy b/wwwroot/api/pbl_agent_trace_write.dspy new file mode 100644 index 0000000..f144f5c --- /dev/null +++ b/wwwroot/api/pbl_agent_trace_write.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_agent_trace_write.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_agent_trace_write.dspy: START params_kw={dict(params_kw)}') +data = await pbl_agent_trace_write(**params_kw) +return data diff --git a/wwwroot/api/pbl_approval_create.dspy b/wwwroot/api/pbl_approval_create.dspy new file mode 100644 index 0000000..7553773 --- /dev/null +++ b/wwwroot/api/pbl_approval_create.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_approval_create.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_approval_create.dspy: START params_kw={dict(params_kw)}') +data = await pbl_approval_create(**params_kw) +return data diff --git a/wwwroot/api/pbl_approval_decide.dspy b/wwwroot/api/pbl_approval_decide.dspy new file mode 100644 index 0000000..be6ecbd --- /dev/null +++ b/wwwroot/api/pbl_approval_decide.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_approval_decide.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_approval_decide.dspy: START params_kw={dict(params_kw)}') +data = await pbl_approval_decide(**params_kw) +return data diff --git a/wwwroot/api/pbl_approval_list.dspy b/wwwroot/api/pbl_approval_list.dspy new file mode 100644 index 0000000..4181df0 --- /dev/null +++ b/wwwroot/api/pbl_approval_list.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_approval_list.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_approval_list.dspy: START params_kw={dict(params_kw)}') +data = await pbl_approval_list(**params_kw) +return data diff --git a/wwwroot/api/pbl_tool_adjudicate.dspy b/wwwroot/api/pbl_tool_adjudicate.dspy new file mode 100644 index 0000000..496457c --- /dev/null +++ b/wwwroot/api/pbl_tool_adjudicate.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_tool_adjudicate.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_tool_adjudicate.dspy: START params_kw={dict(params_kw)}') +data = await pbl_tool_adjudicate(**params_kw) +return data diff --git a/wwwroot/api/pbl_tool_registry_list.dspy b/wwwroot/api/pbl_tool_registry_list.dspy new file mode 100644 index 0000000..316b76f --- /dev/null +++ b/wwwroot/api/pbl_tool_registry_list.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_tool_registry_list.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_tool_registry_list.dspy: START params_kw={dict(params_kw)}') +data = await pbl_tool_registry_list(**params_kw) +return data diff --git a/wwwroot/api/pbl_tool_registry_save.dspy b/wwwroot/api/pbl_tool_registry_save.dspy new file mode 100644 index 0000000..8b4a082 --- /dev/null +++ b/wwwroot/api/pbl_tool_registry_save.dspy @@ -0,0 +1,4 @@ +# pbl_agent_runtime/api/pbl_tool_registry_save.dspy —— 契约端点(自动生成,勿手改:改 spec 后跑 gen_artifacts.py) +debug('pbl_agent_runtime/api/pbl_tool_registry_save.dspy: START params_kw={dict(params_kw)}') +data = await pbl_tool_registry_save(**params_kw) +return data diff --git a/wwwroot/index.ui b/wwwroot/index.ui new file mode 100644 index 0000000..5af07e3 --- /dev/null +++ b/wwwroot/index.ui @@ -0,0 +1,315 @@ +{ + "widgettype": "VBox", + "options": { + "width": "100%", + "height": "100%", + "padding": "20px" + }, + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "Designer/Critic Agent 运行时与 fail-closed 工具裁决(M4a/M4b)", + "fontSize": "24px" + } + }, + { + "widgettype": "ResponsableBox", + "options": { + "gap": "16px", + "minWidth": "250px" + }, + "subwidgets": [ + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_agent_designer_run.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_agent_designer_run" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_agent_critic_run.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_agent_critic_run" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_agent_trace_write.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_agent_trace_write" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_agent_trace_list.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_agent_trace_list" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_tool_registry_list.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_tool_registry_list" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_tool_registry_save.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_tool_registry_save" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_tool_adjudicate.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_tool_adjudicate" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_approval_create.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_approval_create" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_approval_decide.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_approval_decide" + } + } + ] + }, + { + "widgettype": "VBox", + "options": { + "backgroundColor": "#FFFFFF", + "padding": "20px", + "cursor": "pointer" + }, + "binds": [ + { + "wid": "self", + "event": "click", + "actiontype": "urlwidget", + "target": "app.pbl_agent_runtime_content", + "options": { + "url": "{{entire_url('api/pbl_approval_list.dspy')}}" + }, + "mode": "replace" + } + ], + "subwidgets": [ + { + "widgettype": "Text", + "options": { + "label": "pbl_approval_list" + } + } + ] + } + ] + }, + { + "widgettype": "VBox", + "id": "pbl_agent_runtime_content", + "options": { + "width": "100%", + "flex": "1", + "marginTop": "20px" + } + } + ] +}