参数没错,就能直接运行吗?
第二课已经能读文件,再运行它。现在用户多加了一句:
先读
main.py;运行之前问我,得到允许后再运行并报告结果。
模型发来 run_main,参数也是正确的空对象 {}。分发器找得到函数,参数检查也全部通过。此时直接启动 Python,算不算完成了用户要求?
还没有。用户要求的不只是运行文件,还包含运行前的一次确认。参数检查回答“这份请求能不能被实现接收”,权限检查回答“宿主现在是否允许这么做”。两件事需要分别成立。
Learn Claude Code 第三课把权限判断放在工具执行之前。本章沿这个位置增加一道决定:继续读取第二课的同一份文件,但先决定是否允许运行。
三个结果,只有一个能直接进入实现
Harness 收到请求后,先验证名称和参数,再按本地策略返回三个结果之一:
| 决策 | 现在做什么 | 工具已经执行了吗? |
|---|---|---|
allow | 可以进入工具实现 | 还没有;只是可以往下走 |
ask | 把具体请求交给宿主,等待用户回答 | 没有 |
deny | 返回未执行的原因 | 没有 |
在本章的教学策略中,工作目录内读取工具可以直接放行,运行 main.py 需要询问;宿主还可以把运行工具设为禁止。没有配置策略的新工具默认拒绝。
这些是本册的设计选择。真实产品的规则需要结合操作内容、用户授权和环境约束制定,不能把“文件读取永远安全”写成通用结论。例如,读取函数仍然要检查路径,权限放行不会取消第二课的目录和大小限制。
沿同一份请求,看看决定在哪里改变路径
下面是预设的流程推演,不执行文件。依次切换允许、拒绝和策略禁止,观察调用有没有到达工具实现;“直接读取”用来对照不需要询问的路径。
模型提供工具名和参数;宿主先检查,再匹配本地规则。模型没有替自己签发许可。
- 请求先过参数与宿主规则检查。
- allow 直接执行;deny 不询问、不执行。
- ask 等待宿主;只有明确允许才执行,拒绝则形成未执行反馈。
- 每份反馈保留原调用 ID,写入记录后模型才能据此回答。
模型决定提出 run_main 请求,宿主决定是否允许这次请求。模型在文本里写“用户已经同意”,也不能代替宿主实际收到的决定。Claude Code 官方权限文档同样明确区分模型指令与运行框架强制执行的权限规则;本册不据此声称复现了它的内部源码。
另一个值得停住看的地方是策略禁止。禁止项命中后,不能再弹出一个“允许吗”让用户覆盖它;本章直接返回拒绝。否则,所谓禁止只是多问了一次,并没有约束执行。
ask 还不是允许
ask 表示缺少一个决定,不是“已经问过,所以继续”。在本章同步入口里,调用停在宿主确认函数处,等待它返回布尔值。只有 True 才转成 allow;空回答、拒绝和无法取得有效回答,都不能继续执行。
为了把这个区别看清,先用一个短例子。它只打印是否到达执行入口,不读取文件,也不调用模型。Python 可以直接运行或设置断点:
host_answer = False # 改成 True,再运行一次。
def permission_for(name):
return "ask" if name == "run_main" else "deny"
def ask_user():
# 教学固定回答,替代宿主界面。
return host_answer
decision = permission_for("run_main")
if decision == "ask":
answer = ask_user()
decision = "allow" if answer is True else "deny"
if decision == "allow":
print("到达执行入口")
else:
print("未执行")点击行号设置断点,再点“调试”;也可以直接运行。
在 answer = ask_user() 处设置断点。此时 decision 还是 ask,还没有执行任何工具;下一步取得宿主回答后,才能确定最终路径。默认输出 未执行,把第一行改成 True 后输出 到达执行入口。真实的终端询问和三个权限函数在实践篇展开。
“没获准运行”和“运行报错”不能混在一起
拒绝后要不要把结果交回模型?要,否则模型只看见自己提出过运行请求,却不知道宿主把它拦住了。
本章仍使用第二课的结果信封,保留原调用 ID,但内容写清“未执行”:
{
"type": "tool_result",
"tool_use_id": "run_1",
"content": "未执行:本次调用未获用户允许。",
"is_error": true
}这里没有退出码、标准输出或 Python 异常,因为程序根本没有启动。模型应报告“本次没有运行”,不能编造 42,也不能把权限拒绝写成程序运行失败。
循环保存这份反馈后再调用模型。模型可能据此如实回答,也可能继续提出请求。权限决定只约束当前工具操作,不保证模型下一轮一定停止,因此第一课的调用次数上限仍然需要。
获得允许以后,允许的到底是哪一份操作?
假设确认界面显示了 run_main({}),等待期间却有人把原始请求改成另一个工具或另一组参数。若执行器直接读取被修改的对象,刚才的确认就失去了对应关系。
本章先保存请求副本,再用同一份副本完成权限判断和执行;宿主界面得到的也是显示副本。确认对象与执行对象必须对应,这是把权限落实到代码时需要保留的关系。
请求副本不等于文件内容锁定。用户确认后,磁盘上的 main.py 仍可能变化;进程启动以后,也仍有本机权限。权限层决定是否调用工具,操作系统沙箱负责限制已启动进程的访问范围,两者不能互相替代。
回到最初的任务,得到允许就运行并报告真实结果;用户拒绝就报告本次未运行。两条路径都应有可追踪的证据,而执行决定要落在 Harness 的调用入口上。