人在环与治理
一句话总结在 agent 自主行动的关键节点插入人的审批/干预,并施加策略约束与审计:审批门、权限/策略 enforcement、签名审计轨迹。是把自主 agent 安全投入生产的刹车与方向盘。
它解决什么问题
完全自主的 agent 在高风险动作(付款、删除、对外发布)上不可控。HITL 与治理提供可控性、合规与可审计,平衡自主与安全(见 design-tradeoffs 的可控-自由轴)。
设计维度 / 实现谱系
- 干预点:动作前审批 ↔ 关键节点暂停 ↔ 事后审查
- 机制:中断-恢复(依赖 state-persistence)↔ 审批回调 ↔ 策略引擎
- 治理位置:进程内 ↔ 进程外控制面(Cordum 的 pre-dispatch 策略 + 审批门 + 签名审计)
- 策略:白/黑名单工具、权限范围、速率限制
- 审计:结构化事件、不可篡改轨迹
关键要点
- HITL 在工程上常落地为:可中断的循环 + 持久化状态 + 审批回调。
- 进程外治理控制面是企业级生产的趋势(与具体框架解耦)。
- 治理是自主性光谱(见 what-is-an-agent)向生产落地的必经环节。
关联
各框架实现对比
下表汇总 41 个实现了「人在环 / 治理」的框架(源码级阅读结论)。网站上以可展开 + 源码节选呈现。
| 框架 | 实现方式 |
|---|---|
| Aeon | 设计上 no approval loop(卖点:不打扰人)。可选治理层:Fleet Watcher——每技能跑前向自托管控制面问 ALLOW/BLOCK(fail-closed),跑后回报用于污点链分析;通知通道(Telegram/Discord/Slack)双向可让用户发指令;./onboard 校验配置 |
| AG2 | human_input_mode 取 ALWAYS/NEVER/TERMINATE;check_termination_and_human_reply 作为最先执行的 reply func 拦截并征询人类(默认经控制台 IOStream,get_human_input);UserProxyAgent 是代表人类的预设 Agent;group/guardrails.py 与 safeguards/ 提供护栏 |
| Agency Swarm | 治理=SDK 的 input/output guardrails(tripwire 触发→raise_input_guardrail_error 决定抛错还是回灌引导文本,可重试 validation_attempts 次,execution_helpers.py:86);通信流的有向授权本身即一种”谁能找谁”的访问控制。无内置 tool-approval 审批闸门 |
| Agent-LLM (AGiXT) | 多租户 + RBAC:MagicalAuth.py 做认证/OAuth/角色,endpoints/Roles.py;extension 自动派生 ext: |
| AgentField | 双重:①执行级 app.pause() 把执行转 “waiting”,注册 future 后等审批 webhook 回调或超时恢复,crash-safe 可持久(execute_pause.go/webhook_approval.go);②访问治理 = 类 Okta IAM:tag-based ALLOW/DENY 访问策略(按 priority 降序求值)+ tag VC 校验 + 跨 agent 调用 Ed25519 签名 |
| Agentic Context Engine (ACE) | learn_from_feedback()/learn_from_traces() 让人提供纠错反馈或历史 trace 作为学习信号;Skillbook 可读(get_strategies)、可导出 markdown、可人工编辑后回载;CLAUDE.md 规定核心模块改动需人工批准。无运行时审批拦截 |
| AgentScope | 规则化 PermissionEngine:5 种 mode(DEFAULT/ACCEPT_EDITS/EXPLORE/BYPASS/DONT_ASK) × 4 种 behavior(ALLOW/DENY/ASK/PASSTHROUGH),工具自报 is_read_only 与 check_permissions,危险路径/命令拦截;ASK→agent 产出 RequireUserConfirmEvent 暂停等外部确认,reply() 可喂回 UserConfirmResultEvent 续跑;外部执行经 RequireExternalExecutionEvent |
| Agentset | 治理=多租户 + API key + 配额:withNamespaceApiHandler 校验 org/namespace 归属(handler/namespace.ts:50),x-tenant-id 头解析租户(tenant.ts:6),Stripe 计费/isFreePlan 限额、Webhook 通知;无 LLM 动作审批/打断式 HITL |
| AgentVerse | 大部分自动化运行无内建审批;human-in-the-loop 主要见于:① Pokemon demo 玩家可作为一个 agent 实时介入对话(README);② task-solving Evaluator 有被注释掉的 human_eval 交互式打分分支(tasksolving_env/rules/base.py:148,默认走 LLM 评估)。无系统化治理/权限框架 |
| Astron Agent | workflow 的 question_answer 节点做人在环:中断工作流等待用户,靠 EventRegistry 注册中断事件,支持 resume / ignore / abort 三种恢复事件;治理侧有 core/common/audit_system(审计)+ tenant 服务(多租户/空间隔离/配额)+ Casdoor 鉴权 |
| AutoGen | UserProxyAgent 把人类作为 agent 接入:on_messages 时调用可注入的 input_func(同步/异步均可)向人取输入,并发 UserInputRequestedEvent;group chat 中作为普通 participant 参与轮转;Handoff/HandoffTermination 可把控制权交回人 |
| Botpress | 多重钩子做 guardrail:onExit 校验/拦截退出(如转账超额 throw)、onBeforeExecution 审查/改写生成代码(封禁危险操作)、onBeforeTool/onAfterTool 改 IO;Chat 模式 ListenExit 让位用户;平台侧有 HITL 插件(plugins/hitl) |
| ConnectOnion | tool_approval/shell_approval 插件在 before_each_tool 拦截危险操作请求审批(bashlex 解析命令);ask_user 工具+agent.io 与前端交互;plan_mode 工具 |
| Cordum | 核心重点。① Safety Kernel 返回 5 类裁决 ALLOW/DENY/REQUIRE_HUMAN/THROTTLE/ALLOW_WITH_CONSTRAINTS(safety_client.go:235),Scheduler 在 dispatch 前据此分流:REQUIRE_APPROVAL→置 JobStateApproval 阻塞等待人审(engine.go:1596)、DENY→入 DLQ(engine.go:1608)、THROTTLE→延迟重排(engine.go:1549);② DENY-uncrossable 优先级(Global 不可被 Workflow 放宽)safetykernel/global_policy_tiers.go:92;③ 服务端 risk-tag 派生防客户端伪造低危标签(kernel.go:741);④ Edge 审批生命周期 pending/approved/rejected/expired/invalidated(core/edge/approval.go);⑤ ProvenanceGate:销毁性动作/requires_provenance 标签必须有已解析的审批记录+匹配审计事件,“approved by CFO” 之类纯文本声明一律 DENY(core/policy/actiongates/provenance_gate.go:68);⑥ Velocity/速率治理 safetykernel/velocity.go;⑦ fail-open 旁路会发专门审计事件 engine.go:1580 |
| Cortex Memory | 多租户隔离(—tenant/X-Tenant-ID,tenant 后缀 collection)做数据边界;本地优先、零云依赖(MemClaw 主打隐私)。无审批/权限审查流 |
| CrewAI | Task human_input=True:agent 出终答后请求人工反馈并据此再迭代;Flow 侧 human_feedback DSL 做流程级审批;before/after_kickoff 钩子 |
| Dust | 工具按 stake 等级(never_ask/low/high,front/lib/actions/constants.ts:40)决定是否需审批;需审批时 step 循环中断等待 validateAction 用户批准后恢复(launchAgentLoopWorkflow);外加 RBAC、space/group 权限、publishing 限制、WorkOS 审计日志 |
| Haystack | Agent 支持 confirmation_strategies:按 tool 名映射 ConfirmationStrategy,工具执行前可拦截要求用户确认(BlockingConfirmationStrategy 等),含 ConfirmationPolicy/ConfirmationUI 协议,支持 web 场景注入 request-scoped 上下文(WebSocket 等);ToolExecutionDecision 记录决策 |
| hcom | 人始终在环:每个 agent 跑在可见、可滚动、可打断的真实终端。安全命令白名单免审批、危险命令(stop/kill/run/reset)需显式批准(hooks/common.rs:51)。relay 跨设备为”全有或全无”信任域:enroll 即等于给该设备 shell 权限,无分级角色/只读 peer(README:147,165) |
| Hermes Agent | 危险命令审批:DANGEROUS_PATTERNS 检测→CLI 交互/gateway 异步提示→可选辅助 LLM 智能自动批低风险→永久 allowlist 落 config.yaml;HERMES_YOLO_MODE 导入期冻结防 prompt-injection 提权;clarify 工具向用户提问;gateway DM 配对/容器隔离 |
| Hive | HITL=节点 client_facing=True 暂停问人(开放问答/多选/是非/表单),状态存盘可挂起数天后恢复(新版收敛为仅 Queen 直面用户,见 edge.py:542 弃用告警);hard/soft constraint 治理(违反 hard→escalate);budget/cost 限额由 runtime 强制 |
| LangChain | HumanInTheLoopMiddleware 用 langgraph interrupt() 在工具执行前暂停征求批准/编辑/拒绝(InterruptOnConfig);create_agent(interrupt_before/after=…) 节点级中断;ShellToolMiddleware 带 Docker/Codex 沙箱执行策略;PII 中间件 |
| Llama Agentic System (llama-stack-apps) | 治理核心=Llama Guard Shields:input_shields/output_shields 在推理前后做安全过滤(ShieldCallStep),含 code/cybersec shield 拦截工具调用代码;client.shields.list() 发现。无审批/打断式 HITL,但有人工反馈(“Ingest into Memory Bank”点赞写回) |
| LlamaIndex | 经 Workflow 的 InputRequiredEvent/HumanResponseEvent:工具内 ctx.write_event_to_stream(InputRequiredEvent) 暂停并 wait_for_event(HumanResponseEvent) 等人工输入再继续;无内置审批/权限沙箱,工具默认本进程执行 |
| llm-agents | 唯一”人在环”是 run_agent.py 启动时 input() 收集一次问题;无审批/拦截/危险操作治理——PythonREPL 直接 exec() 任意代码,无沙箱(安全风险) |
| LoongFlow | 主要是 中断治理 而非审批:AgentBase.interrupt() 取消 asyncio task,PESAgent 经 _stop_event 优雅停机并终止全部评测子进程(SIGTERM→SIGKILL,evaluator.py:427);ReAct 可注册自定义 interrupt 处理器(react_agent.py:184);ClaudeCodeAgent 有 permission_mode(prompt/acceptEdits/acceptAll)但默认自动接受;无内置工具审批/危险命令拦截层 |
| Maestro | 仅启动时 CLI 交互(目标/是否加文件/是否搜索);运行中全自动,无审批/中断/护栏;写文件无确认 |
| Mastra | suspend/resume:workflow step 与 tool 均可声明 suspendSchema/resumeSchema,执行中 suspend() 暂停并把状态落 storage,之后 resume() 携用户输入恢复(可无限期暂停);requireToolApproval 工具审批;DurableAgent 把整次 agent 运行包成可持久/可恢复的 workflow |
| MetaGPT | HumanProvider 把 is_human=True 的角色 LLM 调用替换成 input() 终端交互;Planner.ask_review(非 auto_run 时)让人审核/改计划;ActionNode.human_review 人工评审结构化产物;RoleZero.ask_human/reply_to_human 工具经 env.ask_human 向人提问 |
| nanobot | ask_user 工具(支持 choices)向渠道发问;DM 发送者 pairing 审批(每渠道持久配对码,pairing/store.py);渠道 allow-list / 安全默认拒绝;SSRF 硬边界(私网 URL 不可绕过,runner.py:1043);shell allow-list;/stop 中途取消 turn 并保留部分上下文 |
| Open Multi-Agent | onPlanReady(tasks) 在任何 agent 执行前审批整份计划(返 false 中止);onApproval(completed,next) 在每轮任务之间审批;planOnly 只看不跑;AbortSignal 运行中取消;beforeRun/afterRun 钩子改写 prompt / 后处理结果;maxTokenBudget 硬性封顶花费 |
| OpenClaw | DM pairing:未知发信人默认收到配对码、消息不被处理,openclaw pairing approve 后加入 allowlist(dmPolicy/allowFrom);沙箱:agents.defaults.sandbox.mode:“non-main” 让非 main 会话跑在 Docker/SSH/OpenShell 沙箱,默认 deny browser/canvas/nodes/cron/discord/gateway;beforeToolCall 钩子+ACP approval-classifier 对危险工具审批;openclaw doctor 体检风险配置 |
| Pilot Protocol | 互信即治理:节点默认私有,必须双向 handshake 才能被解析/连接(“no mutual trust”会拒绝 find);—trust-auto-approve 可自动批准(demo 用),否则人工 pilotctl trust 审批;policy 插件用 expr-lang 表达式对 connect/dial/datagram/join/leave 等事件做策略判定 |
| Pipecat | 实时交互而非审批治理:打断/barge-in——InterruptionFrame(携 asyncio.Event,到 sink 时 set)由用户轮次开始策略触发;轮次管理——UserTurnStrategies(start: VAD+转写; stop)判定用户起止说话;RTVIProcessor 作为客户端↔管道协议桥接收文本/音频/函数结果 |
| PraisonAI | @require_approval(risk_level=…) 标记高危工具→执行前 request_approval 走审批后端(console/自定义 callback);Guardrails(LLMGuardrail 或函数式)对输入/输出做校验+重试;Policy Engine 声明式行为控制;doom-loop 检测自动恢复 |
| Semantic Kernel | ① IAutoFunctionInvocationFilter 在工具自动调用前后拦截,可设 context.Terminate=true 中止循环、把结果交还用户审批(FunctionCallsProcessor.cs:205/225/366 消费);② 编排层 OrchestrationInteractiveCallback / GroupChatManager ShouldRequestUserInput 请求人工输入;③ FunctionChoiceBehavior.None 让模型只建议不执行 |
| Strands Agents | Interrupt/InterruptException 暂停 agent 等人类输入,经 session 持久化后 resume(agent.py:878);AfterInvocationEvent.resume 钩子可注入新输入续跑;experimental/steering 提供 LLM/ledger 引导;guardrail 触发 redactContent 自动脱敏(agent.py:1310) |
| SwarmClaw | 审批门:requestApproval/submitDecision,危险工具走 durable_wait 终端边界挂起等人审,审批后 wake 续跑;E-Stop 急停(estop);learned-skill 上线需人工审查;capability/tool 策略与权限预设(OpenClaw permission-presets);mission budget 上限(USD/token/turn/wallclock) |
| Swarms | interactive=True 进入 REPL,每轮经 formatter.console.input 收用户输入(agent.py:1871);AgentRearrange flow DSL 支持插入 -> H -> 人审步骤 + custom_human_in_the_loop 回调;无细粒度工具审批/沙箱 |
| Upsonic | HITL 经异常驱动暂停/恢复:ConfirmationPause/UserInputPause/ExternalExecutionPause(tools/hitl.py:92,100,108),由 ToolConfig.requires_confirmation 等触发,agent.continue_run()(agent.py:4946) 恢复;治理经 safety engine 策略(user/agent/tool_pre/tool_post policy + feedback loop) + PII 匿名化 |
| VoltAgent | 两条线:①Guardrails(input/output 方向,可设 severity/action 拦截校验 IO);②工具 needsApproval + Workflow suspend()/resume()(带 resumeSchema) 做审批挂起恢复(README 报销审批示例) |
各框架实现对比 · 源码级
Aeon yaml 设计上 no approval loop(卖点:不打扰人)。可选治理层:Fleet Watcher——每技能跑前向自托管控制面问 ALLOW/BLOCK(fail-closed),跑后回报用于污点链分析;通知通道(Telegram/Discord/Slack)双向可让用户发指令;./onboard 校验配置
设计上 no approval loop(卖点:不打扰人)。可选治理层:Fleet Watcher——每技能跑前向自托管控制面问 ALLOW/BLOCK(fail-closed),跑后回报用于污点链分析;通知通道(Telegram/Discord/Slack)双向可让用户发指令;./onboard 校验配置
github/workflows/aeon.yml:204github/workflows/aeon.yml:521github/workflows/messages.yml:468 # If the secrets are not set, this step no-ops — backward compatible
# with every existing AEON fork.
# ──────────────────────────────────────────────────────────────────────
- name: Fleet Watcher preflight
id: fleet_preflight
if: steps.work.outputs.mode != '' && env.FLEET_ENDPOINT != '' && env.FLEET_TOKEN != ''
env:
SKILL: ${{ steps.skill.outputs.name }}
SKILL_VAR: ${{ inputs.var }}
run: |
set -euo pipefail
OP_ID="${GITHUB_RUN_ID}-${GITHUB_RUN_ATTEMPT}-pre"
BODY=$(jq -n \ AG2 python human_input_mode 取 ALWAYS/NEVER/TERMINATE;check_termination_and_human_reply 作为最先执行的 reply func 拦截并征询人类(默认经控制台 IOStream,get_human_input);UserProxyAgent 是代表人类的预设 Agent;group/guardrails.py 与 safeguards/ 提供护栏
human_input_mode 取 ALWAYS/NEVER/TERMINATE;check_termination_and_human_reply 作为最先执行的 reply func 拦截并征询人类(默认经控制台 IOStream,get_human_input);UserProxyAgent 是代表人类的预设 Agent;group/guardrails.py 与 safeguards/ 提供护栏
conversable_agent.py:189 system_message: str | list | None = "You are a helpful AI Assistant.",
is_termination_msg: Callable[[dict[str, Any]], bool] | None = None,
max_consecutive_auto_reply: int | None = None,
human_input_mode: Literal["ALWAYS", "NEVER", "TERMINATE"] = "TERMINATE",
function_map: dict[str, Callable[..., Any]] | None = None,
code_execution_config: dict[str, Any] | Literal[False] = False,
llm_config: LLMConfig | dict[str, Any] | Literal[False] | None = None,
default_auto_reply: str | dict[str, Any] = "",
description: str | None = None,
chat_messages: dict[Agent, list[dict[str, Any]]] | None = None,
silent: bool | None = None,
context_variables: Optional["ContextVariables"] = None,
functions: list[Callable[..., Any]] | Callable[..., Any] = None, Agency Swarm python 治理=SDK 的 input/output guardrails(tripwire 触发→raise_input_guardrail_error 决定抛错还是回灌引导文本,可重试 validation_attempts 次,execution_helpers.py:86);通信流的有向授权本身即一种"谁能找谁"的访问控制。无内置 tool-approval 审批闸门
治理=SDK 的 input/output guardrails(tripwire 触发→raise_input_guardrail_error 决定抛错还是回灌引导文本,可重试 validation_attempts 次,execution_helpers.py:86);通信流的有向授权本身即一种"谁能找谁"的访问控制。无内置 tool-approval 审批闸门
agent/execution_helpers.py:115agency/core.py:83 kwargs=kwargs,
)
return run_result, master_context_for_run
except OutputGuardrailTripwireTriggered as e:
history_for_runner = append_guardrail_feedback(
agent=agent,
agency_context=agency_context,
sender_name=sender_name,
parent_run_id=parent_run_id,
run_trace_id=run_trace_id,
current_agent_run_id=current_agent_run_id,
exception=e,
include_assistant=True, Agent-LLM (AGiXT) python 多租户 + RBAC:MagicalAuth.py 做认证/OAuth/角色,endpoints/Roles.py;extension 自动派生 ext:<name>:read/execute/configure 权限作用域;CriticalEndpointProtectionMiddleware/middleware.py 端点保护;可经聊天渠道由人审批/介入。无内置逐工具调用审批 UI(待确认)
多租户 + RBAC:MagicalAuth.py 做认证/OAuth/角色,endpoints/Roles.py;extension 自动派生 ext:<name>:read/execute/configure 权限作用域;CriticalEndpointProtectionMiddleware/middleware.py 端点保护;可经聊天渠道由人审批/介入。无内置逐工具调用审批 UI(待确认)
middleware.py:258 "color": 15158332, # Red color
"timestamp": datetime.utcnow().isoformat(),
"fields": [
{
"name": "Traceback",
"value": f"```python\n{tb_str[:1000]}{'...' if len(tb_str) > 1000 else ''}\n```",
"inline": False,
}
],
"footer": {"text": f"{app_name} @ {agixt_server} | {timestamp}"},
}
]
} AgentField go 双重:①执行级 app.pause() 把执行转 "waiting",注册 future 后等审批 webhook 回调或超时恢复,crash-safe 可持久(execute_pause.go/webhook_approval.go);②访问治理 = 类 Okta IAM:tag-based ALLOW/DENY 访问策略(按 priority 降序求值)+ tag VC 校验 + 跨 agent 调用 Ed25519 签名
双重:①执行级 app.pause() 把执行转 "waiting",注册 future 后等审批 webhook 回调或超时恢复,crash-safe 可持久(execute_pause.go/webhook_approval.go);②访问治理 = 类 Okta IAM:tag-based ALLOW/DENY 访问策略(按 priority 降序求值)+ tag VC 校验 + 跨 agent 调用 Ed25519 签名
agent.py:4369control-plane/internal/services/access_policy_service.go:68 thread.daemon = True
thread.start()
async def pause(
self,
approval_request_id: str,
approval_request_url: str = "",
expires_in_hours: int = 72,
timeout: Optional[float] = None,
execution_id: Optional[str] = None,
) -> ApprovalResult:
"""Pause the current execution for external approval. Agentic Context Engine (ACE) python learn_from_feedback()/learn_from_traces() 让人提供纠错反馈或历史 trace 作为学习信号;Skillbook 可读(get_strategies)、可导出 markdown、可人工编辑后回载;CLAUDE.md 规定核心模块改动需人工批准。无运行时审批拦截
learn_from_feedback()/learn_from_traces() 让人提供纠错反馈或历史 trace 作为学习信号;Skillbook 可读(get_strategies)、可导出 markdown、可人工编辑后回载;CLAUDE.md 规定核心模块改动需人工批准。无运行时审批拦截
ace/runners/litellm.py:424 raise RuntimeError("Learning is disabled. Call enable_learning() first.")
return self._get_analyser().run(traces, epochs=epochs, wait=wait)
def learn_from_feedback(
self,
feedback: str,
ground_truth: str | None = None,
) -> bool:
"""Learn from the last :meth:`ask` interaction.
Runs the standard ``learning_tail`` pipeline (ReflectStep,
UpdateStep) on the most recent ``ask()`` call with the provided
feedback. The agentic SkillManager mutates the skillbook directly AgentScope python 规则化 PermissionEngine:5 种 mode(DEFAULT/ACCEPT_EDITS/EXPLORE/BYPASS/DONT_ASK) × 4 种 behavior(ALLOW/DENY/ASK/PASSTHROUGH),工具自报 is_read_only 与 check_permissions,危险路径/命令拦截;ASK→agent 产出 RequireUserConfirmEvent 暂停等外部确认,reply() 可喂回 UserConfirmResultEvent 续跑;外部执行经 RequireExternalExecutionEvent
规则化 PermissionEngine:5 种 mode(DEFAULT/ACCEPT_EDITS/EXPLORE/BYPASS/DONT_ASK) × 4 种 behavior(ALLOW/DENY/ASK/PASSTHROUGH),工具自报 is_read_only 与 check_permissions,危险路径/命令拦截;ASK→agent 产出 RequireUserConfirmEvent 暂停等外部确认,reply() 可喂回 UserConfirmResultEvent 续跑;外部执行经 RequireExternalExecutionEvent
permission/_engine.py:16permission/_types.py:18agent/_agent.py:882event/_event.py:46 ToolBase = "ToolBase"
class PermissionEngine:
"""Engine for checking and enforcing permission rules.
Evaluates tool execution requests against configured permission rules.
Matching strategy is delegated to each tool's :meth:`ToolBase.match_rule`:
- Bash tools: substring / prefix wildcard matching against the command
- Write/Read/Edit tools: glob matching against file paths
- Other tools: generic pattern matching (or tool-name-level only) Agentset typescript 治理=多租户 + API key + 配额:withNamespaceApiHandler 校验 org/namespace 归属(handler/namespace.ts:50),x-tenant-id 头解析租户(tenant.ts:6),Stripe 计费/isFreePlan 限额、Webhook 通知;无 LLM 动作审批/打断式 HITL
治理=多租户 + API key + 配额:withNamespaceApiHandler 校验 org/namespace 归属(handler/namespace.ts:50),x-tenant-id 头解析租户(tenant.ts:6),Stripe 计费/isFreePlan 限额、Webhook 通知;无 LLM 动作审批/打断式 HITL
apps/web/src/lib/api/handler/namespace.ts:50api/tenant.ts:6process-document.ts:394 )();
};
export const withNamespaceApiHandler = (
handler: NamespaceHandler,
{ logging }: { logging: false | { routeName: string } },
) => {
return withApiHandler(
async (params) => {
const namespaceId = normalizeId(params.params.namespaceId ?? "", "ns_");
if (!namespaceId) {
throw new AgentsetApiError({
code: "bad_request", AgentVerse python 大部分自动化运行无内建审批;human-in-the-loop 主要见于:① Pokemon demo 玩家可作为一个 agent 实时介入对话(README);② task-solving Evaluator 有被注释掉的 human_eval 交互式打分分支(tasksolving_env/rules/base.py:148,默认走 LLM 评估)。无系统化治理/权限框架
大部分自动化运行无内建审批;human-in-the-loop 主要见于:① Pokemon demo 玩家可作为一个 agent 实时介入对话(README);② task-solving Evaluator 有被注释掉的 human_eval 交互式打分分支(tasksolving_env/rules/base.py:148,默认走 LLM 评估)。无系统化治理/权限框架
environments/tasksolving_env/rules/base.py:148 result: List[ExecutorMessage],
) -> Tuple[List[int], str]:
"""evaluation stage."""
# if self.human_eval:
# print("This round, LLM gave the following result:")
# print(result)
# comprehensiveness = input("Please evaluate the comprehensiveness>> ")
# detailedness = input("Please evaluate the detailedness>> ")
# feasibility = input("Please evaluate the feasibility>> ")
# novelty = input("Please evaluate the novelty>> ")
# advice = input("Please give some advice>>")
# try:
# comprehensiveness = int(comprehensiveness) Astron Agent python workflow 的 question_answer 节点做人在环:中断工作流等待用户,靠 EventRegistry 注册中断事件,支持 resume / ignore / abort 三种恢复事件;治理侧有 core/common/audit_system(审计)+ tenant 服务(多租户/空间隔离/配额)+ Casdoor 鉴权
workflow 的 question_answer 节点做人在环:中断工作流等待用户,靠 EventRegistry 注册中断事件,支持 resume / ignore / abort 三种恢复事件;治理侧有 core/common/audit_system(审计)+ tenant 服务(多租户/空间隔离/配额)+ Casdoor 鉴权
core/workflow/engine/nodes/question_answer/question_answer_node.py:174 incomplete_data: dict
class QuestionAnswerNode(BaseLLMNode):
"""
Question-Answer node implementation for interactive workflows
This node handles both option-based and direct answer types,
supporting user interaction through interrupts and resume mechanisms.
"""
_private_config: PrivateConfig = PrivateAttr(
default_factory=lambda: PrivateConfig(timeout=None) AutoGen python UserProxyAgent 把人类作为 agent 接入:on_messages 时调用可注入的 input_func(同步/异步均可)向人取输入,并发 UserInputRequestedEvent;group chat 中作为普通 participant 参与轮转;Handoff/HandoffTermination 可把控制权交回人
UserProxyAgent 把人类作为 agent 接入:on_messages 时调用可注入的 input_func(同步/异步均可)向人取输入,并发 UserInputRequestedEvent;group chat 中作为普通 participant 参与轮转;Handoff/HandoffTermination 可把控制权交回人
autogen-agentchat/src/autogen_agentchat/agents/_user_proxy_agent.py:37conditions/_terminations.py:313 input_func: str | None = None
class UserProxyAgent(BaseChatAgent, Component[UserProxyAgentConfig]):
"""An agent that can represent a human user through an input function.
This agent can be used to represent a human user in a chat system by providing a custom input function.
.. note::
Using :class:`UserProxyAgent` puts a running team in a temporary blocked
state until the user responds. So it is important to time out the user input
function and cancel using the :class:`~autogen_core.CancellationToken` if the user does not respond. Botpress typescript 多重钩子做 guardrail:onExit 校验/拦截退出(如转账超额 throw)、onBeforeExecution 审查/改写生成代码(封禁危险操作)、onBeforeTool/onAfterTool 改 IO;Chat 模式 ListenExit 让位用户;平台侧有 HITL 插件(plugins/hitl)
多重钩子做 guardrail:onExit 校验/拦截退出(如转账超额 throw)、onBeforeExecution 审查/改写生成代码(封禁危险操作)、onBeforeTool/onAfterTool 改 IO;Chat 模式 ListenExit 让位用户;平台侧有 HITL 插件(plugins/hitl)
packages/llmz/README.md:358packages/llmz/src/llmz.ts:280
### Hooks: Custom Logic Injection
```typescript
const result = await execute({
client,
tools,
hooks: {
onTrace: (trace) => {
// Non-blocking: log tool calls, errors, outputs
logger.info(trace)
},
onExit: (exit) => { ConnectOnion python tool_approval/shell_approval 插件在 before_each_tool 拦截危险操作请求审批(bashlex 解析命令);ask_user 工具+agent.io 与前端交互;plan_mode 工具
tool_approval/shell_approval 插件在 before_each_tool 拦截危险操作请求审批(bashlex 解析命令);ask_user 工具+agent.io 与前端交互;plan_mode 工具
查看 ConnectOnion 完整笔记 →Cordum go 核心重点。① Safety Kernel 返回 5 类裁决 ALLOW/DENY/REQUIRE_HUMAN/THROTTLE/ALLOW_WITH_CONSTRAINTS(safety_client.go:235),Scheduler 在 dispatch 前据此分流:REQUIRE_APPROVAL→置 JobStateApproval 阻塞等待人审(engine.go:1596)、DENY→入 DLQ(engine.go:1608)、THROTTLE→延迟重排(engine.go:1549);② DENY-uncrossable 优先级(Global 不可被 Workflow 放宽)safetykernel/global_policy_tiers.go:92;③ 服务端 risk-tag 派生防客户端伪造低危标签(kernel.go:741);④ Edge 审批生命周期 pending/approved/rejected/expired/invalidated(core/edge/approval.go);⑤ ProvenanceGate:销毁性动作/requires_provenance 标签必须有已解析的审批记录+匹配审计事件,"approved by CFO" 之类纯文本声明一律 DENY(core/policy/actiongates/provenance_gate.go:68);⑥ Velocity/速率治理 safetykernel/velocity.go;⑦ fail-open 旁路会发专门审计事件 engine.go:1580
核心重点。① Safety Kernel 返回 5 类裁决 ALLOW/DENY/REQUIRE_HUMAN/THROTTLE/ALLOW_WITH_CONSTRAINTS(safety_client.go:235),Scheduler 在 dispatch 前据此分流:REQUIRE_APPROVAL→置 JobStateApproval 阻塞等待人审(engine.go:1596)、DENY→入 DLQ(engine.go:1608)、THROTTLE→延迟重排(engine.go:1549);② DENY-uncrossable 优先级(Global 不可被 Workflow 放宽)safetykernel/global_policy_tiers.go:92;③ 服务端 risk-tag 派生防客户端伪造低危标签(kernel.go:741);④ Edge 审批生命周期 pending/approved/rejected/expired/invalidated(core/edge/approval.go);⑤ ProvenanceGate:销毁性动作/requires_provenance 标签必须有已解析的审批记录+匹配审计事件,"approved by CFO" 之类纯文本声明一律 DENY(core/policy/actiongates/provenance_gate.go:68);⑥ Velocity/速率治理 safetykernel/velocity.go;⑦ fail-open 旁路会发专门审计事件 engine.go:1580
engine.go:1596safetykernel/kernel.go:706actiongates/provenance_gate.go:68
// Fail-closed: when no policy is loaded, deny all requests.
// This prevents a misconfigured deployment from silently allowing everything.
if policy == nil {
return &pb.PolicyCheckResponse{
Decision: pb.DecisionType_DECISION_TYPE_DENY,
Reason: "no policy loaded — fail-closed",
PolicySnapshot: snapshot,
}, nil
}
if tenant == "" {
tenant = defaultTenant Cortex Memory rust 多租户隔离(--tenant/X-Tenant-ID,tenant 后缀 collection)做数据边界;本地优先、零云依赖(MemClaw 主打隐私)。无审批/权限审查流
多租户隔离(--tenant/X-Tenant-ID,tenant 后缀 collection)做数据边界;本地优先、零云依赖(MemClaw 主打隐私)。无审批/权限审查流
查看 Cortex Memory 完整笔记 →CrewAI python Task human_input=True:agent 出终答后请求人工反馈并据此再迭代;Flow 侧 human_feedback DSL 做流程级审批;before/after_kickoff 钩子
Task human_input=True:agent 出终答后请求人工反馈并据此再迭代;Flow 侧 human_feedback DSL 做流程级审批;before/after_kickoff 钩子
crewai/task.py:227crewai/agents/crew_agent_executor.py:1596crewai/project/annotations.py:42 frozen=True,
description="Unique identifier for the object, not set by user.",
)
human_input: bool | None = Field(
description="Whether the task should have a human review the final answer of the agent",
default=False,
)
markdown: bool | None = Field(
description="Whether the task should instruct the agent to return the final answer formatted in Markdown",
default=False,
)
converter_cls: Annotated[
type[Converter] | None, Dust typescript 工具按 stake 等级(never_ask/low/high,front/lib/actions/constants.ts:40)决定是否需审批;需审批时 step 循环中断等待 validateAction 用户批准后恢复(launchAgentLoopWorkflow);外加 RBAC、space/group 权限、publishing 限制、WorkOS 审计日志
工具按 stake 等级(never_ask/low/high,front/lib/actions/constants.ts:40)决定是否需审批;需审批时 step 循环中断等待 validateAction 用户批准后恢复(launchAgentLoopWorkflow);外加 RBAC、space/group 权限、publishing 限制、WorkOS 审计日志
front/lib/api/assistant/conversation/validate_actions.ts:27front/temporal/agent_loop/workflows.ts:430front/lib/actions/constants.ts:45import type { Result } from "@app/types/shared/result";
import { Err, Ok } from "@app/types/shared/result";
export async function validateAction(
auth: Authenticator,
conversation: ConversationResource,
{
actionId,
approvalState,
messageId,
resumeAncestorConversations = false,
}: {
actionId: string; Haystack python Agent 支持 confirmation_strategies:按 tool 名映射 ConfirmationStrategy,工具执行前可拦截要求用户确认(BlockingConfirmationStrategy 等),含 ConfirmationPolicy/ConfirmationUI 协议,支持 web 场景注入 request-scoped 上下文(WebSocket 等);ToolExecutionDecision 记录决策
Agent 支持 confirmation_strategies:按 tool 名映射 ConfirmationStrategy,工具执行前可拦截要求用户确认(BlockingConfirmationStrategy 等),含 ConfirmationPolicy/ConfirmationUI 协议,支持 web 场景注入 request-scoped 上下文(WebSocket 等);ToolExecutionDecision 记录决策
human_in_the_loop/strategies.py:28human_in_the_loop/types/protocol.py:30agent.py:225USER_FEEDBACK_TEMPLATE = "With user feedback: {feedback}"
class BlockingConfirmationStrategy:
"""
Confirmation strategy that blocks execution to gather user feedback.
"""
def __init__(
self,
*,
confirmation_policy: ConfirmationPolicy,
confirmation_ui: ConfirmationUI, hcom rust 人始终在环:每个 agent 跑在可见、可滚动、可打断的真实终端。安全命令白名单免审批、危险命令(stop/kill/run/reset)需显式批准(hooks/common.rs:51)。relay 跨设备为"全有或全无"信任域:enroll 即等于给该设备 shell 权限,无分级角色/只读 peer(README:147,165)
人始终在环:每个 agent 跑在可见、可滚动、可打断的真实终端。安全命令白名单免审批、危险命令(stop/kill/run/reset)需显式批准(hooks/common.rs:51)。relay 跨设备为"全有或全无"信任域:enroll 即等于给该设备 shell 权限,无分级角色/只读 peer(README:147,165)
hooks/common.rs:51/// agents need to run without user approval prompts.
/// Excluded: `stop`, `kill`, `run`, `reset` — these are destructive or
/// admin-level and require explicit user approval.
pub(crate) const SAFE_HCOM_COMMANDS: &[&str] = &[
"send",
"start",
"help",
"--help",
"-h",
"list",
"events",
"listen",
"relay", Hermes Agent python 危险命令审批:DANGEROUS_PATTERNS 检测→CLI 交互/gateway 异步提示→可选辅助 LLM 智能自动批低风险→永久 allowlist 落 config.yaml;HERMES_YOLO_MODE 导入期冻结防 prompt-injection 提权;clarify 工具向用户提问;gateway DM 配对/容器隔离
危险命令审批:DANGEROUS_PATTERNS 检测→CLI 交互/gateway 异步提示→可选辅助 LLM 智能自动批低风险→永久 allowlist 落 config.yaml;HERMES_YOLO_MODE 导入期冻结防 prompt-injection 提权;clarify 工具向用户提问;gateway DM 配对/容器隔离
tools/approval.py:1"""Dangerous command approval -- detection, prompting, and per-session state.
This module is the single source of truth for the dangerous command system:
- Pattern detection (DANGEROUS_PATTERNS, detect_dangerous_command)
- Per-session approval state (thread-safe, keyed by session_key)
- Approval prompting (CLI interactive + gateway async)
- Smart approval via auxiliary LLM (auto-approve low-risk commands)
- Permanent allowlist persistence (config.yaml)
""" Hive python HITL=节点 client_facing=True 暂停问人(开放问答/多选/是非/表单),状态存盘可挂起数天后恢复(新版收敛为仅 Queen 直面用户,见 edge.py:542 弃用告警);hard/soft constraint 治理(违反 hard→escalate);budget/cost 限额由 runtime 强制
HITL=节点 client_facing=True 暂停问人(开放问答/多选/是非/表单),状态存盘可挂起数天后恢复(新版收敛为仅 Queen 直面用户,见 edge.py:542 弃用告警);hard/soft constraint 治理(违反 hard→escalate);budget/cost 限额由 runtime 强制
schemas/goal.py:37 model_config = {"extra": "allow"}
class Constraint(BaseModel):
id: str
description: str
constraint_type: str = Field(description="Type: 'hard' (must not violate) or 'soft' (prefer not to violate)")
category: str = Field(default="general", description="Category: 'time', 'cost', 'safety', 'scope', 'quality'")
check: str = Field(default="", description="How to check: expression, function name, or 'llm_judge'")
model_config = {"extra": "allow"}
LangChain python HumanInTheLoopMiddleware 用 langgraph interrupt() 在工具执行前暂停征求批准/编辑/拒绝(InterruptOnConfig);create_agent(interrupt_before/after=...) 节点级中断;ShellToolMiddleware 带 Docker/Codex 沙箱执行策略;PII 中间件
HumanInTheLoopMiddleware 用 langgraph interrupt() 在工具执行前暂停征求批准/编辑/拒绝(InterruptOnConfig);create_agent(interrupt_before/after=...) 节点级中断;ShellToolMiddleware 带 Docker/Codex 沙箱执行策略;PII 中间件
agents/middleware/human_in_the_loop.py:1factory.py:708middleware/shell_tool.py:1"""Human in the loop middleware."""
from __future__ import annotations
from typing import TYPE_CHECKING, Any, Literal, Protocol
from langchain_core.messages import AIMessage, ToolCall, ToolMessage
from langgraph.config import get_config
from langgraph.prebuilt.tool_node import ToolRuntime
from langgraph.types import interrupt Llama Agentic System (llama-stack-apps) python 治理核心=Llama Guard Shields:input_shields/output_shields 在推理前后做安全过滤(ShieldCallStep),含 code/cybersec shield 拦截工具调用代码;client.shields.list() 发现。无审批/打断式 HITL,但有人工反馈("Ingest into Memory Bank"点赞写回)
治理核心=Llama Guard Shields:input_shields/output_shields 在推理前后做安全过滤(ShieldCallStep),含 code/cybersec shield 拦截工具调用代码;client.shields.list() 发现。无审批/打断式 HITL,但有人工反馈("Ingest into Memory Bank"点赞写回)
examples/agents/simple_chat.py:30docs/sequence-diagram.md:14examples/agent_store/app.py:66 provider_data={"tavily_search_api_key": os.getenv("TAVILY_SEARCH_API_KEY")},
)
available_shields = [shield.identifier for shield in client.shields.list()]
if not available_shields:
print(colored("No available shields. Disabling safety.", "yellow"))
else:
print(f"Available shields found: {available_shields}")
if model_id is None:
model_id = get_any_available_model(client)
if model_id is None:
return LlamaIndex python 经 Workflow 的 InputRequiredEvent/HumanResponseEvent:工具内 ctx.write_event_to_stream(InputRequiredEvent) 暂停并 wait_for_event(HumanResponseEvent) 等人工输入再继续;无内置审批/权限沙箱,工具默认本进程执行
经 Workflow 的 InputRequiredEvent/HumanResponseEvent:工具内 ctx.write_event_to_stream(InputRequiredEvent) 暂停并 wait_for_event(HumanResponseEvent) 等人工输入再继续;无内置审批/权限沙箱,工具默认本进程执行
workflow/events.py:1agent/workflow/base_agent.py:817from workflows.events import (
Event, # noqa
EventType, # noqa
HumanResponseEvent, # noqa
InputRequiredEvent, # noqa
StartEvent, # noqa
StopEvent, # noqa
) llm-agents python 唯一"人在环"是 run_agent.py 启动时 input() 收集一次问题;无审批/拦截/危险操作治理——PythonREPL 直接 exec() 任意代码,无沙箱(安全风险)
唯一"人在环"是 run_agent.py 启动时 input() 收集一次问题;无审批/拦截/危险操作治理——PythonREPL 直接 exec() 任意代码,无沙箱(安全风险)
run_agent.py:4tools/python_repl.py:21from llm_agents import Agent, ChatLLM, PythonREPLTool, HackerNewsSearchTool, SerpAPITool
if __name__ == '__main__':
prompt = input("Enter a question / task for the agent: ")
agent = Agent(llm=ChatLLM(), tools=[PythonREPLTool(), SerpAPITool(), HackerNewsSearchTool()])
result = agent.run(prompt)
print(f"Final answer is {result}") LoongFlow python 主要是 中断治理 而非审批:AgentBase.interrupt() 取消 asyncio task,PESAgent 经 _stop_event 优雅停机并终止全部评测子进程(SIGTERM→SIGKILL,evaluator.py:427);ReAct 可注册自定义 interrupt 处理器(react_agent.py:184);ClaudeCodeAgent 有 permission_mode(prompt/acceptEdits/acceptAll)但默认自动接受;无内置工具审批/危险命令拦截层
主要是 中断治理 而非审批:AgentBase.interrupt() 取消 asyncio task,PESAgent 经 _stop_event 优雅停机并终止全部评测子进程(SIGTERM→SIGKILL,evaluator.py:427);ReAct 可注册自定义 interrupt 处理器(react_agent.py:184);ClaudeCodeAgent 有 permission_mode(prompt/acceptEdits/acceptAll)但默认自动接受;无内置工具审批/危险命令拦截层
framework/base/agent_base.py:90framework/pes/pes_agent.py:589framework/react/react_agent.py:184 """Main agent logic (must be implemented by subclasses)."""
pass
async def interrupt(self):
"""Trigger agent interruption."""
if self._interrupted:
return
self._interrupted = True
if self._task and not self._task.done():
self._task.cancel()
try: Maestro python 仅启动时 CLI 交互(目标/是否加文件/是否搜索);运行中全自动,无审批/中断/护栏;写文件无确认
仅启动时 CLI 交互(目标/是否加文件/是否搜索);运行中全自动,无审批/中断/护栏;写文件无确认
maestro.py:203 console.print(Panel(f"Code content not found for file: [bold]{key}[/bold]", title="[bold yellow]Missing Code Content[/bold yellow]", title_align="left", border_style="yellow"))
# Get the objective from user input
objective = input("Please enter your objective: ")
# Ask if the user wants to add a file
add_file = input("Do you want to add a text file? (y/n): ").lower() == 'y'
file_content = None
if add_file:
file_path = input("Please enter the file path: ")
try:
with open(file_path, 'r') as file: Mastra typescript suspend/resume:workflow step 与 tool 均可声明 suspendSchema/resumeSchema,执行中 suspend() 暂停并把状态落 storage,之后 resume() 携用户输入恢复(可无限期暂停);requireToolApproval 工具审批;DurableAgent 把整次 agent 运行包成可持久/可恢复的 workflow
suspend/resume:workflow step 与 tool 均可声明 suspendSchema/resumeSchema,执行中 suspend() 暂停并把状态落 storage,之后 resume() 携用户输入恢复(可无限期暂停);requireToolApproval 工具审批;DurableAgent 把整次 agent 运行包成可持久/可恢复的 workflow
workflows/workflow.ts:389loop/loop.ts:24agent/durable/durable-agent.ts:158 inputSchema: params.inputSchema ? toStandardSchema(params.inputSchema) : params.inputSchema,
stateSchema: params.stateSchema ? toStandardSchema(params.stateSchema) : undefined,
outputSchema: params.outputSchema ? toStandardSchema(params.outputSchema) : params.outputSchema,
resumeSchema: params.resumeSchema ? toStandardSchema(params.resumeSchema) : undefined,
suspendSchema: params.suspendSchema ? toStandardSchema(params.suspendSchema) : undefined,
requestContextSchema: params.requestContextSchema ? toStandardSchema(params.requestContextSchema) : undefined,
scorers: params.scorers,
retries: params.retries,
metadata: params.metadata,
execute: params.execute.bind(params) as Step<
TStepId,
TStateSchema extends PublicSchema<any> ? InferPublicSchema<TStateSchema> : unknown,
InferPublicSchema<TInputSchema>, MetaGPT python HumanProvider 把 is_human=True 的角色 LLM 调用替换成 input() 终端交互;Planner.ask_review(非 auto_run 时)让人审核/改计划;ActionNode.human_review 人工评审结构化产物;RoleZero.ask_human/reply_to_human 工具经 env.ask_human 向人提问
HumanProvider 把 is_human=True 的角色 LLM 调用替换成 input() 终端交互;Planner.ask_review(非 auto_run 时)让人审核/改计划;ActionNode.human_review 人工评审结构化产物;RoleZero.ask_human/reply_to_human 工具经 env.ask_human 向人提问
metagpt/provider/human_provider.py:14metagpt/strategy/planner.py:119metagpt/actions/action_node.py:665metagpt/roles/di/role_zero.py:456from metagpt.provider.base_llm import BaseLLM
class HumanProvider(BaseLLM):
"""Humans provide themselves as a 'model', which actually takes in human input as its response.
This enables replacing LLM anywhere in the framework with a human, thus introducing human interaction
"""
def __init__(self, config: LLMConfig):
self.config = config
self.model = config.model
def ask(self, msg: str, timeout=USE_CONFIG_TIMEOUT) -> str: nanobot python ask_user 工具(支持 choices)向渠道发问;DM 发送者 pairing 审批(每渠道持久配对码,pairing/store.py);渠道 allow-list / 安全默认拒绝;SSRF 硬边界(私网 URL 不可绕过,runner.py:1043);shell allow-list;/stop 中途取消 turn 并保留部分上下文
ask_user 工具(支持 choices)向渠道发问;DM 发送者 pairing 审批(每渠道持久配对码,pairing/store.py);渠道 allow-list / 安全默认拒绝;SSRF 硬边界(私网 URL 不可绕过,runner.py:1043);shell allow-list;/stop 中途取消 turn 并保留部分上下文
channels/base.py:13agent/runner.py:1043agent/loop.py:990
from nanobot.bus.events import InboundMessage, OutboundMessage
from nanobot.bus.queue import MessageBus
from nanobot.pairing import (
PAIRING_CODE_META_KEY,
format_pairing_reply,
generate_code,
is_approved,
)
class BaseChannel(ABC):
""" Open Multi-Agent typescript onPlanReady(tasks) 在任何 agent 执行前审批整份计划(返 false 中止);onApproval(completed,next) 在每轮任务之间审批;planOnly 只看不跑;AbortSignal 运行中取消;beforeRun/afterRun 钩子改写 prompt / 后处理结果;maxTokenBudget 硬性封顶花费
onPlanReady(tasks) 在任何 agent 执行前审批整份计划(返 false 中止);onApproval(completed,next) 在每轮任务之间审批;planOnly 只看不跑;AbortSignal 运行中取消;beforeRun/afterRun 钩子改写 prompt / 后处理结果;maxTokenBudget 硬性封顶花费
src/orchestrator/orchestrator.ts:1290src/orchestrator/orchestrator.ts:800src/agent/agent.ts:328src/agent/agent.ts:391 const planTasks = queue.list()
const planReadyStartMs = Date.now()
let approved = true
if (this.config.onPlanReady) {
try {
approved = await this.config.onPlanReady(planTasks)
} catch {
approved = false
}
}
if (this.config.onTrace) {
const planReadyEndMs = Date.now()
emitTrace(this.config.onTrace, { OpenClaw typescript DM pairing:未知发信人默认收到配对码、消息不被处理,openclaw pairing approve 后加入 allowlist(dmPolicy/allowFrom);沙箱:agents.defaults.sandbox.mode:"non-main" 让非 main 会话跑在 Docker/SSH/OpenShell 沙箱,默认 deny browser/canvas/nodes/cron/discord/gateway;beforeToolCall 钩子+ACP approval-classifier 对危险工具审批;openclaw doctor 体检风险配置
DM pairing:未知发信人默认收到配对码、消息不被处理,openclaw pairing approve 后加入 allowlist(dmPolicy/allowFrom);沙箱:agents.defaults.sandbox.mode:"non-main" 让非 main 会话跑在 Docker/SSH/OpenShell 沙箱,默认 deny browser/canvas/nodes/cron/discord/gateway;beforeToolCall 钩子+ACP approval-classifier 对危险工具审批;openclaw doctor 体检风险配置
README.md:145src/acp/approval-classifier.ts:27agent-loop.ts:684
Models config + CLI: [Models](https://docs.openclaw.ai/concepts/models). Auth profile rotation + fallbacks: [Model failover](https://docs.openclaw.ai/concepts/model-failover).
## Security defaults (DM access)
OpenClaw connects to real messaging surfaces. Treat inbound DMs as **untrusted input**.
Full security guide: [Security](https://docs.openclaw.ai/gateway/security).
Before remote exposure, use the [Gateway exposure runbook](https://docs.openclaw.ai/gateway/security/exposure-runbook).
Default behavior on Telegram/WhatsApp/Signal/iMessage/Microsoft Teams/Discord/Google Chat/Slack:
- **DM pairing** (`dmPolicy="pairing"` / `channels.discord.dmPolicy="pairing"` / `channels.slack.dmPolicy="pairing"`; legacy: `channels.discord.dm.policy`, `channels.slack.dm.policy`): unknown senders receive a short pairing code and the bot does not process their message. Pilot Protocol go 互信即治理:节点默认私有,必须双向 handshake 才能被解析/连接("no mutual trust"会拒绝 find);--trust-auto-approve 可自动批准(demo 用),否则人工 pilotctl trust 审批;policy 插件用 expr-lang 表达式对 connect/dial/datagram/join/leave 等事件做策略判定
互信即治理:节点默认私有,必须双向 handshake 才能被解析/连接("no mutual trust"会拒绝 find);--trust-auto-approve 可自动批准(demo 用),否则人工 pilotctl trust 审批;policy 插件用 expr-lang 表达式对 connect/dial/datagram/join/leave 等事件做策略判定
cmd/daemon/main.go:81pkg/daemon/contract.go:38README.md:121 webhookURL := flag.String("webhook", "", "HTTP(S) endpoint for event notifications (empty = disabled)")
adminToken := flag.String("admin-token", "", "admin token for network operations")
networks := flag.String("networks", "", "comma-separated network IDs to auto-join at startup")
trustAutoApprove := flag.Bool("trust-auto-approve", false, "automatically approve all incoming trust handshakes")
beaconRTTProbe := flag.Bool("beacon-rtt-probe", false, "probe beacon RTT before selection; override hash pick when >2× slower than best (ablation test, default off)")
transportMode := flag.String("transport", "udp", "tunnel transport: 'udp' (default) or 'compat' (WSS to beacon, opt-in, for UDP-blocked environments)")
compatBeacon := flag.String("compat-beacon", "wss://beacon.pilotprotocol.network/v1/compat", "beacon WSS URL for -transport=compat")
tlsTrust := flag.String("tls-trust", "system", "TLS trust store for -transport=compat: 'system' (OS trust store; current default while compat mode uses Let's Encrypt certs on beacon.pilotprotocol.network) or 'pinned' (Pilot CA root embedded in the daemon binary; will become the default in a future release once production root ships)")
showVersion := flag.Bool("version", false, "print version and exit")
logLevel := flag.String("log-level", "info", "log level (debug, info, warn, error)")
logFormat := flag.String("log-format", "text", "log format (text, json)")
flag.Parse() Pipecat python 实时交互而非审批治理:打断/barge-in——InterruptionFrame(携 asyncio.Event,到 sink 时 set)由用户轮次开始策略触发;轮次管理——UserTurnStrategies(start: VAD+转写; stop)判定用户起止说话;RTVIProcessor 作为客户端↔管道协议桥接收文本/音频/函数结果
实时交互而非审批治理:打断/barge-in——InterruptionFrame(携 asyncio.Event,到 sink 时 set)由用户轮次开始策略触发;轮次管理——UserTurnStrategies(start: VAD+转写; stop)判定用户起止说话;RTVIProcessor 作为客户端↔管道协议桥接收文本/音频/函数结果
turns/user_turn_strategies.py:55processors/frameworks/rtvi/processor.py:49
@dataclass
class UserTurnStrategies:
"""Container for user turn start and stop strategies.
If no strategies are specified, the following defaults are used:
start: [VADUserTurnStartStrategy, TranscriptionUserTurnStartStrategy]
stop: [TurnAnalyzerUserTurnStopStrategy(LocalSmartTurnAnalyzerV3)]
Parameters:
start: A list of user turn start strategies used to detect when PraisonAI python @require_approval(risk_level=...) 标记高危工具→执行前 request_approval 走审批后端(console/自定义 callback);Guardrails(LLMGuardrail 或函数式)对输入/输出做校验+重试;Policy Engine 声明式行为控制;doom-loop 检测自动恢复
@require_approval(risk_level=...) 标记高危工具→执行前 request_approval 走审批后端(console/自定义 callback);Guardrails(LLMGuardrail 或函数式)对输入/输出做校验+重试;Policy Engine 声明式行为控制;doom-loop 检测自动恢复
approval/__init__.py:166guardrails/llm_guardrail.py:15from ..output.models import TaskOutput
from .protocols import GuardrailProtocol
class LLMGuardrail:
"""
An LLM-powered guardrail that validates task outputs using natural language.
Implements GuardrailProtocol to provide input, output, and tool call validation
using LLM reasoning. Defaults to fail-closed behavior for production safety.
"""
def __init__(self, description: str, llm: Any = None):
"""Initialize the LLM guardrail. Semantic Kernel csharp ① IAutoFunctionInvocationFilter 在工具自动调用前后拦截,可设 context.Terminate=true 中止循环、把结果交还用户审批(FunctionCallsProcessor.cs:205/225/366 消费);② 编排层 OrchestrationInteractiveCallback / GroupChatManager ShouldRequestUserInput 请求人工输入;③ FunctionChoiceBehavior.None 让模型只建议不执行
① IAutoFunctionInvocationFilter 在工具自动调用前后拦截,可设 context.Terminate=true 中止循环、把结果交还用户审批(FunctionCallsProcessor.cs:205/225/366 消费);② 编排层 OrchestrationInteractiveCallback / GroupChatManager ShouldRequestUserInput 请求人工输入;③ FunctionChoiceBehavior.None 让模型只建议不执行
dotnet/src/SemanticKernel.Abstractions/Filters/AutoFunctionInvocation/AutoFunctionInvocationContext.cs:16dotnet/src/Agents/Orchestration/GroupChat/GroupChatManager.cs:77/// <summary>
/// Class with data related to automatic function invocation.
/// </summary>
public class AutoFunctionInvocationContext : Microsoft.Extensions.AI.FunctionInvocationContext
{
private ChatHistory? _chatHistory;
/// <summary>
/// Initializes a new instance of the <see cref="AutoFunctionInvocationContext"/> class from an existing <see cref="Microsoft.Extensions.AI.FunctionInvocationContext"/>.
/// </summary>
internal AutoFunctionInvocationContext(KernelChatOptions autoInvocationChatOptions, AIFunction aiFunction)
{
Verify.NotNull(autoInvocationChatOptions); Strands Agents python Interrupt/InterruptException 暂停 agent 等人类输入,经 session 持久化后 resume(agent.py:878);AfterInvocationEvent.resume 钩子可注入新输入续跑;experimental/steering 提供 LLM/ledger 引导;guardrail 触发 redactContent 自动脱敏(agent.py:1310)
Interrupt/InterruptException 暂停 agent 等人类输入,经 session 持久化后 resume(agent.py:878);AfterInvocationEvent.resume 钩子可注入新输入续跑;experimental/steering 提供 LLM/ledger 引导;guardrail 触发 redactContent 自动脱敏(agent.py:1310)
interrupt.py:11agent.py:1004agent.py:1310 from .types.interrupt import InterruptResponseContent
@dataclass
class Interrupt:
"""Represents an interrupt that can pause agent execution for human-in-the-loop workflows.
Attributes:
id: Unique identifier.
name: User defined name.
reason: User provided reason for raising the interrupt.
response: Human response provided when resuming the agent after an interrupt.
""" SwarmClaw typescript 审批门:requestApproval/submitDecision,危险工具走 durable_wait 终端边界挂起等人审,审批后 wake 续跑;E-Stop 急停(estop);learned-skill 上线需人工审查;capability/tool 策略与权限预设(OpenClaw permission-presets);mission budget 上限(USD/token/turn/wallclock)
审批门:requestApproval/submitDecision,危险工具走 durable_wait 终端边界挂起等人审,审批后 wake 续跑;E-Stop 急停(estop);learned-skill 上线需人工审查;capability/tool 策略与权限预设(OpenClaw permission-presets);mission budget 上限(USD/token/turn/wallclock)
approvals.ts:83 }
}
export function requestApproval(params: {
category: ApprovalCategory
title: string
description?: string
data: Record<string, unknown>
agentId?: string | null
sessionId?: string | null
taskId?: string | null
}): ApprovalRequest {
const id = genId(8) Swarms python interactive=True 进入 REPL,每轮经 formatter.console.input 收用户输入(agent.py:1871);AgentRearrange flow DSL 支持插入 -> H -> 人审步骤 + custom_human_in_the_loop 回调;无细粒度工具审批/沙箱
interactive=True 进入 REPL,每轮经 formatter.console.input 收用户输入(agent.py:1871);AgentRearrange flow DSL 支持插入 -> H -> 人审步骤 + custom_human_in_the_loop 回调;无细粒度工具审批/沙箱
agent.py:433structs/agent_rearrange.py:34 self.retry_interval = retry_interval
self.task = None
self.stopping_token = stopping_token
self.interactive = interactive
self.dashboard = dashboard
self.saved_state_path = saved_state_path
self.dynamic_temperature_enabled = dynamic_temperature_enabled
self.dynamic_loops = dynamic_loops
self.user_name = user_name
self.context_length = context_length
self.sop = sop
self.sop_list = sop_list
self.tools = tools Upsonic python HITL 经异常驱动暂停/恢复:ConfirmationPause/UserInputPause/ExternalExecutionPause(tools/hitl.py:92,100,108),由 ToolConfig.requires_confirmation 等触发,agent.continue_run()(agent.py:4946) 恢复;治理经 safety engine 策略(user/agent/tool_pre/tool_post policy + feedback loop) + PII 匿名化
HITL 经异常驱动暂停/恢复:ConfirmationPause/UserInputPause/ExternalExecutionPause(tools/hitl.py:92,100,108),由 ToolConfig.requires_confirmation 等触发,agent.continue_run()(agent.py:4946) 恢复;治理经 safety engine 策略(user/agent/tool_pre/tool_post policy + feedback loop) + PII 匿名化
src/upsonic/tools/hitl.py:23agent.py:4946pipeline/steps.py:292
@dataclass
class PausedToolCall:
"""Represents a tool call paused for HITL handling (external execution, confirmation, or user input)."""
tool_name: str
"""Name of the tool to execute."""
tool_args: Dict[str, Any]
"""Arguments for the tool."""
tool_call_id: str VoltAgent typescript 两条线:①Guardrails(input/output 方向,可设 severity/action 拦截校验 IO);②工具 needsApproval + Workflow suspend()/resume()(带 resumeSchema) 做审批挂起恢复(README 报销审批示例)
两条线:①Guardrails(input/output 方向,可设 severity/action 拦截校验 IO);②工具 needsApproval + Workflow suspend()/resume()(带 resumeSchema) 做审批挂起恢复(README 报销审批示例)
agent/guardrail.ts:28tool/index.ts:138 OutputGuardrailStreamHandler,
} from "./types";
export type GuardrailDirection = "input" | "output";
export interface NormalizedGuardrail<TArgs, TResult> {
id?: string;
name: string;
description?: string;
tags?: string[];
severity?: GuardrailSeverity;
metadata?: Record<string, unknown>;
handler: GuardrailFunction<TArgs, TResult>;