feat(v3): T28 操作审批流(dsh 式 allow-once/deny,fail-closed)
- ToolLoop 增 approval_hook:工具执行前挂起等待用户裁决,拒绝/异常折叠为
失败结果回喂模型(可改道),fail-closed
- 策略 agent.approval_policy:off | dangerous(写/编辑/命令询问,只读放行,默认)| all;
approval_timeout_s 超时自动拒绝(轮询实现,规避 portal 循环下 wait_for 定时器不可靠)
- POST /agent/{id}/approve 裁决端点;approval_request/decided 事件对进 SSE 与审计
- 前端:运行中审批卡(工具名+参数预览+拒绝/允许一次),composer 审批策略 chip
- 测试 +8(策略矩阵/拒绝回喂/允许执行/fail-closed/超时/端点分支),全量 289 passed
This commit is contained in:
@@ -602,6 +602,8 @@ try:
|
||||
executor_chat=executor_chat,
|
||||
max_handoffs=int(agent_cfg.get("max_handoffs", 2)),
|
||||
session=session,
|
||||
approval_policy=str(agent_cfg.get("approval_policy", "dangerous")),
|
||||
approval_timeout_s=int(agent_cfg.get("approval_timeout_s", 120)),
|
||||
)
|
||||
except Exception as exc:
|
||||
import traceback
|
||||
@@ -638,6 +640,23 @@ try:
|
||||
service._write_status(info)
|
||||
return {"ok": True}
|
||||
|
||||
@app.post("/agent/{request_id}/approve", tags=["agent"])
|
||||
async def agent_approve(request_id: str, req: dict):
|
||||
"""裁决待审批操作:{"approval_id", "allowed"}(dsh 式 allow-once / deny)。"""
|
||||
from gateway.agent import get_agent_service
|
||||
info = get_agent_service().get(request_id)
|
||||
if info is None:
|
||||
raise HTTPException(status_code=404, detail=f"智能体任务不存在: {request_id}")
|
||||
approval_id = str((req or {}).get("approval_id") or "")
|
||||
allowed = bool((req or {}).get("allowed", False))
|
||||
mgr = getattr(info, "_approval_manager", None)
|
||||
if mgr is None:
|
||||
raise HTTPException(status_code=409, detail="该任务无审批流程")
|
||||
ok = mgr.decide(approval_id, allowed)
|
||||
if not ok:
|
||||
raise HTTPException(status_code=404, detail=f"审批单不存在或已裁决: {approval_id}")
|
||||
return {"ok": True, "approval_id": approval_id, "allowed": allowed}
|
||||
|
||||
# ---------------- 会话(dsh 式多轮对话) ----------------
|
||||
@app.post("/agent/sessions", tags=["agent"])
|
||||
async def agent_session_create(req: dict = None):
|
||||
|
||||
Reference in New Issue
Block a user