改成只读以后,谁还在说可以运行?
还是检查 main.py 与 expected.txt。这次宿主切到只读配置,移除了 run_main,可 system 里还留着一句:“必要时运行程序,报告实际结果。”模型到底该相信哪一份?
前面几课已经有了工具、技能、计划和记忆。第七课按目录生成技能说明,第九课又追加记忆目录;我们已经在组装提示。现在的问题是,说明来源散在几个入口里,修改能力时容易漏掉另一处文字。
第十课把这件事收成一条明确链路:实际状态生成快照,快照组装有序段落,完整结果交给本次模型请求。 System Prompt(系统提示)里的固定行为原则仍要写清,随项目和能力变化的部分则从实际状态生成。
“只读”两个字,为什么不等于工具已经关闭?
先反过来试一次:注册表里还留着运行工具,只把说明改成只读,会怎样?下面没有实际程序,只观察文字与工具集合是否一起变化:
registered = {"read_file", "run_main"}
system = "本次只做只读检查。"
print("只改文字,运行工具仍注册:", "run_main" in registered)
registered.remove("run_main")
system = "本次工具:" + "、".join(sorted(registered))
print(system)
print("运行工具仍注册:", "run_main" in registered)点击行号设置断点,再点“调试”;也可以直接运行。
第一次输出 True,说明改文字没有改注册;移除以后才输出 False。本册的实际分发入口还会检查注册、参数和许可,不能把一句提示当作这些检查已经完成。
因此需要同时关心两件事:模型看到的能力说明是否准确,执行器是否真的限制了能力。模型可能误用工具,过时文字也可能仍然出现;实际未注册的调用仍应被拒绝。反过来,工具已经注册,也不代表用户批准了这次执行。
同一份事实,进入三个不同位置
模型请求不是把所有东西粘成一段文字。几个字段承担不同职责:
| 字段 | 本册放入的内容 |
|---|---|
| system | 固定行为原则、工作区、实际工具摘要和必要目录 |
| tools | 同一注册生成的正式工具定义与参数 schema |
| messages | 当前问题、完整工具交接,以及本次附带的计划和相关记忆正文 |
system 里写有 read_file,不会凭空创建一个读取函数;正式定义让模型知道怎样提出请求,实际注册才让宿主找到处理入口。三者应该沿同一来源生成,而不是各自维护一个“应该可用”的工具清单。
用户要求检查main.py并核对输出。宿主实际注册了读取、子任务与运行等工具;运行工具存在,仍须取得本次许可。
当前配置:run_main可申请,尚未批准。- 先由宿主确定实际能力:用户要求检查main.py并核对输出。宿主实际注册了读取、子任务与运行等工具;运行工具存在,仍须取得本次许可。
- 从注册与目录收集快照:工具定义沿真实注册生成;工作目录由宿主提供。本例已有技能和记忆目录,只取元数据,处理函数、正文与旧执行结果不放进这个快照。
- 按顺序加入固定与可选段:身份、行为原则、工具摘要和工作区固定在前;有技能/记忆目录,才加入对应段。没有运行工具时,说明也如实改变。
- 比较本地组装输入:同一进程已有相同上下文与模板,才可复用组装结果;工具或固定规则变化就重新组装。这一步没有替代模型请求,也没有缓存许可。
- 把三个字段一起交给模型:system带组装后的说明,tools带同一注册生成的正式定义,messages带当前问题和本次请求附件。相关记忆正文仍留在messages里。
- 工具说明不能替宿主批准:模型提出run_main以后,执行入口仍要检查注册、参数和宿主许可。只有取得本次结果,才有依据报告实际输出。
这张图推演可申请运行和只读两种配置。说明变化发生在模型请求之前,真正运行仍经过原来的执行入口;它没有增加一个绕过第三课许可的新通道。
分段之后,哪些始终有,哪些按需有?
先写清稳定原则:围绕当前用户问题工作,不把源码推导当成运行结果,不用技能或记忆文字代替宿主许可。这些原则不会因为目录为空而消失。
随后加入当前工具摘要与工作区。技能目录非空且加载工具确实存在,才加入技能段;项目记忆有目录,才加入记忆段。决定这些段是否出现的是注册和目录状态,没有在用户消息里搜到“技能”就打开加载工具。
这里有两个容易混淆的“按需”。第九课用当前问题选择哪条记忆正文与任务相关;这一课判断是否有必要说明记忆目录存在。前者决定本次附件,后者决定 system 的组成。目录在 system,相关正文仍进请求副本,两者没有交换职责。
顺序也应稳定。固定原则在前,能力和工作区随后,可选目录按约定位置加入。同一份输入应得到同样顺序,便于比较两次请求到底改了哪一段。分段的价值首先是来源清楚、变化可见,不是段落越多越好。
连续问模型时,每次还要重新拼一遍吗?
一次检查可能发出多次模型请求。期间工具和目录都没变,宿主可以复用已经组装好的 system。为此,本课把规范化上下文和实际模板文本一起序列化,作为本地缓存键。
输入相同就返回旧组装结果的副本;工具、目录、工作区或固定规则变化,就重新组装。候选还要通过最终大小检查,才更新缓存。否则一次失败可能把先前可用的结果也覆盖掉。
这个复用只发生在本进程里。它没有缓存模型回答,也没有因为上次允许运行就批准下一次。当前问题、计划和工具结果照常放进 messages,模型请求仍然发出;重启进程以后,本地缓存也重新开始。
对于很短的提示,先直接组装通常就够了。比较键、复制数据同样有开销,本课没有测量或承诺性能提升。缓存只是把“什么输入可以复用,什么变化必须重建”变成一个可检查的设计。
API 的提示缓存,又是在缓存什么?
API Prompt Caching(提示缓存)由模型服务处理可复用的输入前缀,和本地 Python 是否少拼一次字符串是两个位置。它有自己的启用方式、缓存条件与命中信息,不能从本地 reused=True 推断。
本课没有启用 API 缓存,也没有在线命中证据。SDK 模拟验证会故意核对:本地已经复用,下一次 HTTP 请求仍然产生。需要评估 API 缓存时,应另看服务端实际返回的使用信息,不能把这里的布尔值当作节省 token 的证明。
回到只读检查:真正需要验证的,是提示说明、正式工具定义和实际分发有没有同时反映宿主配置。实践篇逐个展开状态收集、分段组装和本地复用;可以关掉运行工具,也可以改一段固定规则,看旧结果为什么不能直接沿用。
资料核对于2026-10-19:课程 System Prompt提供分段与运行时组装的学习路线;Claude Platform 提示缓存文档用于区分服务端输入前缀缓存;Claude Code 机制说明用于核对系统说明、上下文与工具的职责。本册独立实现,不把课程中不可核验的内部源码描述当作官方实现事实。