feat: 危险命令确认改为会话级——保留单次确认,新增「全部确认」选项(approve_all)授权后本会话不再询问
This commit is contained in:
parent
bda9f31a1b
commit
41232c8295
@ -64,6 +64,7 @@ class AgentExecutor:
|
||||
role: str = "", # 产线内角色(可选,如 develop/design;驾驶舱 agent 为空)
|
||||
base_url: str = "", # 请求 base_url(scheme://host/),供 slash 命令生成 widget 绝对 URL
|
||||
generic: bool = False, # True = 纯通用会话(不解析项目、不挂产线能力)
|
||||
session=None, # GatewaySession(会话级状态:approve_all/pending_confirm)
|
||||
):
|
||||
self.config = config
|
||||
self.project_id = project_id
|
||||
@ -72,6 +73,7 @@ class AgentExecutor:
|
||||
self.pipeline_id = "" # loaded from project context(产线能力包 key)
|
||||
self.role = role # 产线内角色(技能/工具/prompt 按角色加载)
|
||||
self.generic = generic # 是否纯通用会话
|
||||
self._session = session # GatewaySession(approve_all/pending_confirm 会话级状态)
|
||||
self.space = GENERAL_SPACE # 项目空间键:generic→'general',产线→真实 pipeline_id
|
||||
self.workspace_dir = workspace_dir or WORKSPACE_BASE
|
||||
self.model_name = model_name or config.model_name
|
||||
@ -146,6 +148,39 @@ class AgentExecutor:
|
||||
self._msgs.extend(hist_msgs)
|
||||
self._msgs.append({"role": "user", "content": user_input})
|
||||
|
||||
# Step 4.5: 危险命令确认回复短路(pending_confirm + 确认/全部确认/取消)
|
||||
confirm_decision = self._detect_confirm_decision(user_input)
|
||||
if confirm_decision and self._session and self._session.pending_confirm:
|
||||
pending = self._session.pending_confirm
|
||||
if confirm_decision == "cancel":
|
||||
self._session.pending_confirm = None
|
||||
yield json.dumps({"type": "reply", "message": "已取消该操作。"}, ensure_ascii=False) + "\n"
|
||||
await self._save_turn(user_input, "已取消该操作。")
|
||||
return
|
||||
if confirm_decision == "approve_all":
|
||||
self._session.approve_all = True
|
||||
self._session.pending_confirm = None
|
||||
pending_tool = pending.get("tool", "")
|
||||
pending_params = pending.get("params") or {}
|
||||
# 回填 assistant tool_call → 执行 → 回填 tool result,随后继续 Tool Loop
|
||||
fake_id = f"call_{int(time.time() * 1000)}"
|
||||
self._msgs.append({
|
||||
"role": "assistant",
|
||||
"content": None,
|
||||
"tool_calls": [{
|
||||
"id": fake_id,
|
||||
"type": "function",
|
||||
"function": {"name": pending_tool, "arguments": json.dumps(pending_params, ensure_ascii=False)},
|
||||
}],
|
||||
})
|
||||
yield json.dumps({"type": "tool_call", "tool": pending_tool, "params": pending_params}, ensure_ascii=False) + "\n"
|
||||
result = await self._execute_tool(pending_tool, pending_params)
|
||||
if not result.startswith("未知工具") and not result.startswith("ERROR"):
|
||||
self._tool_call_count += 1
|
||||
yield json.dumps({"type": "tool_result", "tool": pending_tool, "result": result[:500]}, ensure_ascii=False) + "\n"
|
||||
self._msgs.append({"role": "tool", "tool_call_id": fake_id, "content": str(result)})
|
||||
# 不 return,继续进入 Tool Loop,让 LLM 基于工具结果继续后续步骤
|
||||
|
||||
# Step 5: Tool Loop
|
||||
yield json.dumps({"type": "progress", "message": "思考中..."}, ensure_ascii=False) + "\n"
|
||||
|
||||
@ -179,8 +214,10 @@ class AgentExecutor:
|
||||
except Exception:
|
||||
tool_params = {}
|
||||
|
||||
# 需要确认的工具
|
||||
# 需要确认的工具:记录待确认并暂停,等待用户确认/全部确认/取消
|
||||
if self._needs_confirmation(tool_name, tool_params):
|
||||
if self._session:
|
||||
self._session.pending_confirm = {"tool": tool_name, "params": tool_params}
|
||||
yield json.dumps({
|
||||
"type": "confirm", "tool": tool_name, "params": tool_params,
|
||||
}, ensure_ascii=False) + "\n"
|
||||
@ -239,8 +276,10 @@ class AgentExecutor:
|
||||
tool_name = act.get("tool", "")
|
||||
tool_params = act.get("params", {})
|
||||
|
||||
# 需要确认的工具
|
||||
# 需要确认的工具:记录待确认并暂停,等待用户确认/全部确认/取消
|
||||
if self._needs_confirmation(tool_name, tool_params):
|
||||
if self._session:
|
||||
self._session.pending_confirm = {"tool": tool_name, "params": tool_params}
|
||||
yield json.dumps({
|
||||
"type": "confirm",
|
||||
"tool": tool_name,
|
||||
@ -1268,13 +1307,37 @@ class AgentExecutor:
|
||||
)
|
||||
return any(d in c for d in dangerous)
|
||||
|
||||
def _detect_confirm_decision(self, user_input: str) -> str:
|
||||
"""检测用户对危险命令确认的回复。返回 approve / approve_all / cancel / ''。"""
|
||||
t = (user_input or "").strip().lower()
|
||||
if not t:
|
||||
return ""
|
||||
for kw in ("全部确认", "确认全部", "全部同意", "都确认", "都同意", "一律确认", "approve all", "yes to all", "always"):
|
||||
if kw in t:
|
||||
return "approve_all"
|
||||
for kw in ("取消", "放弃", "不执行", "别执行", "cancel", "abort", "不要"):
|
||||
if kw in t:
|
||||
return "cancel"
|
||||
for kw in ("确认", "同意", "执行", "可以", "approve", "confirm", "yes", "ok", "继续"):
|
||||
if kw in t:
|
||||
return "approve"
|
||||
return ""
|
||||
|
||||
def _needs_confirmation(self, tool_name: str, params: dict = None) -> bool:
|
||||
"""检查工具是否需要用户确认。
|
||||
|
||||
已关闭确认机制:agent 会话应能理解和接受全部确认,执行任务时
|
||||
不再打断用户要求确认(危险命令也自动执行,不再弹「请回复确认」)。
|
||||
如需恢复按命令危险度确认,改回读取 tool.requires_confirmation + _is_dangerous_command。
|
||||
会话级「全部确认」(approve_all)时直接放行;否则 run_command 只对
|
||||
危险命令确认,普通命令自动执行,其他 requires_confirmation 工具一律确认。
|
||||
"""
|
||||
if self._session and getattr(self._session, "approve_all", False):
|
||||
return False
|
||||
if self._tool_registry:
|
||||
tool = self._tool_registry.get(tool_name)
|
||||
if tool and tool.requires_confirmation:
|
||||
if tool_name == "run_command":
|
||||
cmd = (params or {}).get("command", "")
|
||||
return self._is_dangerous_command(cmd)
|
||||
return True
|
||||
return False
|
||||
|
||||
|
||||
|
||||
@ -36,6 +36,8 @@ class GatewaySession:
|
||||
project_name: str = ""
|
||||
created_at: float = 0.0
|
||||
updated_at: float = 0.0
|
||||
approve_all: bool = False # 会话级"全部确认":用户评估后授权,本会话不再询问危险命令
|
||||
pending_confirm: Optional[dict] = None # 待确认工具 {"tool":..., "params":...}(危险命令等待用户确认)
|
||||
|
||||
|
||||
class Gateway:
|
||||
@ -155,7 +157,7 @@ class Gateway:
|
||||
config.model_name = ctx["default_llm_name"]
|
||||
executor = AgentExecutor(
|
||||
config=config, project_id=ctx["pid"], user_id=user_id, role=role,
|
||||
base_url=base_url, generic=generic)
|
||||
base_url=base_url, generic=generic, session=sess)
|
||||
|
||||
# 4. 转发标准化事件流
|
||||
async for chunk in executor.run(content, history=history):
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user