renderer 直接读取已参与运行的对象,按固定顺序输出五个 section。没有工具时 tools 明确写 (none)——工具是核心能力,「没有」是模型需要知道的事实;skills/memory 是补充信息,「没有」就等于不存在,整段省略。判断标准:这个 section 为空时模型行为该不该变。可选 section 只 push 尾部,用测试断言相对位置而非包含。
const sections = [
`## identity\n${identity}\ncontext: ${contextJson}`,
`## tools\n${toolCatalog}`,
`## workspace\n${workspace}`,
];
if (skillCatalog.length > 0) sections.push(`## skills\n${skillCatalog}`);
if (memoryBody.length > 0) sections.push(`## memory\n${memoryBody}`);
只接受 JSON object,值递归限制为 scalar / array / object;拒绝 NaN、无穷、循环引用、symbol key、Date/Map/类实例;undefined 也拒(与 stringify 静默丢键不同)。JSON.parse 产生的 __proto__ 是普通自有键。循环检测用 active Set 且 finally 删除——同一对象在兄弟位置出现(合法 DAG)不误判。合法值按键排序、紧凑序列化,语义相同必然文本相同。
type JsonValue = boolean | number | string | null
| readonly JsonValue[] | JsonObject;
缓存放在 renderer 实例字段,每次 buildAgent 新建,天然会话隔离。键 = identity + context + tools + workspace + skills + memory 六项,少一项的后果不是缓存效率低,而是给模型发过期指令(如新注册的工具模型不知道)。校验全部在写缓存之前完成,非法输入不会污染缓存;cacheHits 是可测试性指标。它与供应商 API prompt cache 不是一回事:一个省进程内拼装 CPU,一个省服务端重复前缀费用。
buildAgent(P10, ...) → 新 DynamicPromptRenderer
cacheKey = identity + context + tools
+ workspace + skills + selected memory
AgentRunner 只依赖一个零参数 provider,每次模型请求前调用 render()。两道校验:构造时验证 render 是函数;每轮验证返回非空字符串。失败不回退到构建期固定 systemPrompt——那段字符串不含 workspace/工具/Skill/记忆,用它等于换了个 Agent。Loop 也不捕获 renderer 内部异常:数据源坏了让它炸比静默降级安全。
render(): string {
return this.#renderer.render({
identity, tools, workspace, context,
...(skills ? { skills } : {}),
...(memory ? { memory } : {}),
});
}
P09 由 MemorySession.beforeModel() 追加系统 context 消息;P10 改为 selected memory 进入动态 Prompt 的 memory section,并关闭原消息注入(emitContextMessages: !dynamicPrompt)。注意只是关注入不关功能:选择、提取、整理照常。注入位置从「用户问题之后 index=2」挪到「系统提示内部 index=0」,请求少一条消息;测试断言出现次数 =1 而非包含。
new MemorySession({
...,
emitContextMessages: false, // P10 起关闭独立消息注入
});
createMemorySession(deps, !dynamicPrompt) // P09 用 true
「下一轮生效」要按真实实现理解,不是什么都会自动更新。工具与选中记忆每轮重读;workspace 是构建期绑定;SkillRegistry 是构建期扫描的快照,磁盘新增/删除不生效,要重建 Agent。不监视目录是刻意的:每轮扫磁盘会给每次请求加 I/O,而 Skill 目录在一次会话里几乎不变。
ToolRegistry 注册/移除 → 下一轮生效
本轮选中记忆变化 → 下一轮生效
workspace 路径 → 构建期固定,不变
磁盘新增/删除 Skill → 不生效,需重建 Agent
renderer 读取工具/选中记忆的当前值;Skill 目录是构建期快照,变更需重建。
identity → tools → workspace → (可选)skills → (可选)memory。
覆盖全部可见输入的规范快照;相等则复用。
Loop 在每次模型请求前读取零参数 provider。