资金确认机制
每个涉及资金移动的工具都经过「预览再执行」门控。本文介绍其设计原因、覆盖范围,以及唯一的例外情形。
你告诉 Agent:"把 100 USDC 兑换成 ETH。"Agent 不会直接执行,而是先弹出一张确认卡片,展示交易细节:
Sell: 100 USDC (Ethereum)
Buy: ~0.0286 ETH (estimated, 0.5% slippage)
Route: Uniswap V3
Gas estimate: ~$1.20确认内容符合你的意图后,点接受。此时 Agent 才会广播交易。
为什么 Agent 不直接执行? 因为 LLM 会误读输入。"100 USDC"可能变成"1000 USDC",你输入的地址可能偏移一个字符,链也可能悄悄从 Ethereum 切换到 Base。一旦广播,这些错误都无法撤回。因此,每个涉及资金的工具在结构上都分为两步:先预览,再执行。
合约约定
每个涉及资金移动的工具都在 ToolEntry 上声明 controlPolicy.confirm。控制策略门在工具调用否决点运行预览、收集同意,然后才把调用交给只负责执行的 handler。这由运行时强制执行,不靠提示词,也不在 handler 内部。
为什么用硬约束,而不是提示词层面的软性要求? 因为在提示词注入、恶意技能的障眼法,或 LLM 将恶意工具结果视为指令等情况下,LLM 可能"遗忘"提示词级别的规则。模型只需带着操作参数调用一次工具。没有确认证据时,门返回预览信封,绝不会发起交易。客户端渲染确认卡片;模型看不到预览,也没有第二次工具调用。
策略定义在
tool-control-policy.ts。
解释它的钩子在
tier-gate.ts。
每个新增的涉及资金的工具都必须声明 controlPolicy.confirm;未声明的 PR 会被 code review 拒绝。带 isFundMoving 却没有确认路径的条目会 fail-closed。
覆盖范围
工具在工具注册表中标记为 isFundMoving: true。以下按用途分组列出:
代币交易
swap_tokens:跨链 DEX 闪兑buy_token/sell_token:显式买入 / 卖出transfer_token:钱包到地址的转账
永续合约
open_perps_position/close_perps_positionminara_perps_wallet_sweep:子账户间余额划转minara_perps_wallet_transfer:永续合约余额转账minara_wallet_fund_perp:从现货向永续合约账户充值(存入 USDC)
Autopilot:开启或关闭自动交易
minara_autopilot_enable/minara_autopilot_disable
Autopilot 开关属于资金类操作,因为开启它即代表操作员授权 Agent 自主交易。一旦启用,后续每笔 Autopilot 交易均继承该授权,不会对每笔交易单独提示确认。
工作流
workflow_activate/workflow_deactivate
逻辑相同:激活工作流的那一刻,即代表你批准该自动化流程独立运行。
Strategy Studio:自有策略
- strategy-codegen 技能的
deploy.sh
部署是一次性授权,详见下文策略部署是一次性授权。
运行他人创建的策略
minara_top_strategies_apply_start:运行精选 Top Strategies 策略
这些操作会使用用户资金启动或停止自主交易,且不存在作者授权时机,因此与 Autopilot 一样走确认门(apply_start / sub_start 为 MANUAL_ONLY;sub_stop 为 ALWAYS_CONFIRM)。
直接下单到交易所
- 所有
hyperliquid_*下单 / 撤单 / 提现接口
如需新增涉及资金的工具,请标记 isFundMoving: true 并声明 controlPolicy.confirm。未声明的 PR 会被 code review 拒绝。
实际流程
从 Agent 调用到你点接受:
LLM: swap_tokens({
sell_token: "USDC",
sell_amount: 100,
buy_token: "ETH",
chain: "ethereum"
})
→ 门:没有确认证据
→ 运行声明的 preview(模拟),以 minara-confirm 信封拦住调用
→ 客户端渲染预览卡片;模型看不到它
你在卡片上点接受
→ 同一次调用进入只负责执行的 handler
→ handler 签名并广播
→ 返回 {tx_hash: "0x...", executed: true}调用方传入的 confirm: true 仍可作为交互式同意证据。检查是宽松的(true、"true"、1、"yes"、"on"),因为部分工具调用协议会把布尔值序列化成字符串。界面路径并不依赖模型传入该标志。
策略部署是一次性授权
策略部署的工作方式有所不同。strategy-codegen 技能的 deploy.sh 在部署时执行一次两步确认:不带 --confirm 运行时只打印完整的部署预览(标的、K 线周期、杠杆、子账户、最大回撤上限),你查看后做出一次性决策,再用同一条命令加 --confirm 重新执行。
之后,策略的生命周期脚本(start.sh、stop.sh、reconfig.sh)各自走同样的「预览 → --confirm」两步流程,其中 stop 默认会市价平掉未平仓位。这些是对已授权策略的调度操作,而非新的资金移动。
运行他人创建的策略则不同:不存在作者授权时机,因此 Top Strategies 的 minara_top_strategies_apply_start 确实会走确认门,与启用 Autopilot 的逻辑相同。
为什么有这个例外? 如果每笔算法交易都需要人工确认,策略就只是手动交易脚本。Strategy Studio 的意义在于在你预先设定的约束范围内委托执行。部署是你授权策略运行空间的时机;此后,策略受回撤上限、资产配置上限和部署配置中的标的白名单约束,而非逐笔交易的提示词约束。
这个例外仅适用于 Strategy Studio。不要在其他地方模仿该模式。工具层面的其他部分都走统一确认门。
MINARA_SKIP_FUND_CONFIRM:操作员级旁路
在非交互式场景下,有一个逃生口:环境变量 MINARA_SKIP_FUND_CONFIRM=1(或偏好 safety.skipFundConfirm)会让确认门把该调用视为已有证据。以下说明何时可用、何时绝对不可用:
合理用途(少数合法场景):
- 回测。 调用方是 Python 测试框架,没有人工读取预览,且运行环境已针对历史数据进行沙盒隔离。
- 工作流引擎执行。 工作流已在激活时获得批准,执行循环是在已授权工作流上调度,而非要求用户逐步确认。
- CI 冒烟测试。 目的就是驱动涉及资金的路径并验证接口调用。
绝对不可用的场景:
- 交互式 REPL 会话。 这会绕过保护你的最后一道防线。
- "每次确认太烦了"的生产环境。 这等于把安全机制关掉。
- Shell rc 文件。 不要把
export MINARA_SKIP_FUND_CONFIRM=1写进.zshrc后忘掉。下次启动 Agent 时,它将在无防护状态下运行。
完整的环境变量参考文档见 环境变量 → MINARA_SKIP_FUND_CONFIRM。
第 3 层与第 4 层各自拦截什么
最常见的疑惑:拦截行为属于第 3 层(本页)还是第 4 层(脚本风险闸门)?快速参考如下:
| 你的请求 | 拦截层 |
|---|---|
| "把 100 USDC 兑换成 ETH"(直接交易请求) | 第 3 层:控制策略门先预览,再确认 |
运行包含 subprocess.run(["minara","swap",…]) 的 Python 脚本 | 第 4 层:脚本风险闸门检测到涉及资金的 CLI 调用,触发 YELLOW 提示 |
在 terminal 中运行 cast send 0x… 1ether | 第 4 层:闸门检查 shell 命令,触发 YELLOW 提示 |
修改文件,导致其内容包含 minara swap | 第 4 层:闸门扫描应用后的内容,触发 YELLOW 提示 |
通过 codegen 技能的 deploy.sh 部署策略 | 脚本级两步确认:不带 --confirm 只出预览,带上才执行;后端风控层兜底 |
简而言之:第 3 层拦截 Agent 直接发起的涉及资金的工具调用;第 4 层拦截 Agent 运行的脚本和命令中包含的涉及资金的代码。 两者覆盖同一威胁的不同维度,并行运作。
操作员检查清单
- ✅ 看到预览时,务必仔细阅读。核对目标地址、链、金额和滑点是否与你的意图一致。两步确认的存在,也是为了让你发现自己的失误。
- ✅ 部署策略时,仔细审查部署配置。这是唯一一次被要求确认的机会。
- ✅ 激活工作流时,在点击确认前从头到尾阅读工作流定义。后续执行不会再次询问。
- ❌ 不要在交互式 REPL 会话中设置
MINARA_SKIP_FUND_CONFIRM=1,也不要写入.zshrc或.envrc。 - ❌ 不要接受你没读过的预览。确认依赖的是你的判断,而非 Agent。