跳转至

2026-05-24 周报

自然周:2026-05-18 至 2026-05-24

本周主线

这一周的主线集中在“框架边界如何暴露给使用者”。Vite 的 import.meta.env、Claude Code 的子 Agent 模型分配、Pi 的扩展系统和 skill 加载机制,看似分属前端构建、Agent 配置和 CLI 框架,实际都在回答同一个问题:哪些能力是框架约定,哪些是底层标准,哪些必须通过封装层隔离。

Pi 相关内容构成本周最完整的认知块。它的扩展机制、Slot UI、Context 分层、Skill 读取时机、Agent/ReAct 引擎分层和 TypeBox schema 兼容性,共同说明一个成熟 Agent 框架应该如何把核心循环、会话能力、UI 扩展、技能注入和 Provider 适配拆开,而不是堆在一个全能对象里。

另一条线是“不要把实现便利误认为通用标准”。import.meta.env 是 Vite 构建时静态替换,不是浏览器 API;Claude Code 子 Agent 的模型选择有固定优先级链,不能假设 settings.json 里存在任意配置入口;TypeBox 的 Type.Union 在某些 Provider 上不可用,框架需要用更保守的 schema 表达换取跨 Provider 兼容。

主题一:构建工具环境变量与可移植边界

核心脉络

  • 问题起点:在使用 Vite 时,容易把 import.meta.env 当成浏览器原生能力,或者把 MODEDEVPROD 混成同一套环境判断。
  • 推进关系:进一步拆开后可以看到,Vite 的环境变量是构建时静态替换;MODE 来自 --mode 并影响 .env.{mode} 加载,DEV/PROD 只由命令类型决定。再横向比较 Webpack、Turbopack、esbuild、Rollup,就能看出不同构建工具的注入机制并不互通。
  • 最终判断:业务代码不要直接扩散构建工具专有 API。只要项目存在迁移构建工具、复用业务模块或跨框架运行的可能,就应该把环境读取收口到一层配置模块,避免把 Vite 专有语义污染到业务层。

沉淀认知

  • Vite 专有import.meta.env.MODE/DEV/PROD/BASE_URL 是 Vite 在构建时做的静态替换,不是浏览器标准 API;VITE_ 前缀变量会被暴露并替换成字符串值。
  • 口径分离MODE 表示构建模式,可通过 --mode 自定义并影响 env 文件加载;DEV/PROD 是命令类型布尔值,vite dev 才是 DEV=truevite build 才是 PROD=true,不会被 --mode staging 改写。
  • 封装隔离:Webpack/Turbopack 偏 process.env.NODE_ENV 和 DefinePlugin,Vite 偏 import.meta.env,其他工具也有各自机制。需要可移植性时,应在 src/config/env.ts 一类模块里统一导出业务需要的环境值。

适用边界

这个判断适用于业务代码会跨构建工具、跨框架或长期维护的项目。如果项目明确只使用 Vite,且环境变量访问集中、迁移概率低,直接使用 import.meta.env 是可以接受的;但仍要避免把 MODE 当成 DEV/PROD,也不要把 VITE_ 暴露变量放入敏感信息。

来源

  • 2026-05-18:Vite import.meta.env 机制与可移植性

主题二:Agent 框架的扩展分层与能力边界

核心脉络

  • 问题起点:围绕 Pi 和 Claude Code 的问题,本质是在确认 Agent 框架中“谁负责声明能力、谁负责执行动作、谁负责选择模型、谁负责注入上下文”。
  • 推进关系:Pi 的扩展入口是工厂函数,加载阶段只允许注册,动作要等 core 绑定后通过更具体的上下文执行;UI 扩展采用固定 Slot,而不是让插件任意改布局;Skill 加载只读 frontmatter,body 到 /skill:name 执行时才读;Agent 包只保留 ReAct 循环,coding-agent 外层再组合会话、持久化、扩展和 TUI。
  • 最终判断:Agent 框架的稳定性来自分层约束。核心循环越纯,外围系统越能独立演进;扩展 API 越明确,插件越不容易绕过生命周期;模型和 schema 适配越集中,业务代码越少暴露在 Provider 差异里。

沉淀认知

  • 注册优先:Pi 扩展通过 export default function(pi, ctx) 声明式注册事件、工具和命令。工厂函数执行期间不能执行 sendMessage 等动作,因为 core 尚未绑定,动作方法仍是桩函数。
  • Slot 边界:Pi TUI 只在 editor 上下方 Widget、底部状态栏、editor 组件、全屏 overlay、消息/工具渲染器等固定位置开放扩展点。扩展可以在 Slot 中注入组件,但不能随意新增侧边栏这类布局区域;深度定制要走全屏接管。
  • Context 分层:ExtensionContext、ExtensionCommandContext、ReplacedSessionContext 逐层增加能力。事件处理器不能拿到 newSession/fork 等命令级能力,是为了避免从事件回调触发会话操作造成死锁。
  • Skill 延迟读取:Pi 加载 skill 时只保留 frontmatter 元数据和 filePath,body 不进入常驻 Skill 对象;用户执行 /skill:name 时才重新读取文件并注入 system prompt。因此改 SKILL.md 正文通常不需要热重载。
  • 无模板变量:Pi 遵循 Agent Skills 标准,SKILL.md body 不做 {currentDate}{projectName} 这类变量替换。动态信息应由 system prompt 固定注入,或由 skill 指导模型读文件、执行命令获取。
  • 目录命名空间~/.pi/agent 多出的 agent 层用于把 agent 数据从 ~/.pi 共享命名空间中隔离出来;项目级 cwd/.pi 已天然处于仓库上下文,不需要再加 agent 子目录。
  • 核心循环纯化packages/agent 的 Agent 类只做 prompt、LLM stream、tool calls、continue 的 ReAct 循环。文件系统、工具实现、会话持久化、压缩、扩展系统和 TUI 都由 coding-agent 外层通过组合叠加。
  • 消息网关before_agent_start 是每轮用户消息进入 agent 前的网关,适合注入选中文本或调整 system prompt;/new 则是创建新会话,会触发完整的 session shutdown/start 生命周期。
  • 模型优先级:Claude Code 子 Agent 模型选择按 CLAUDE_CODE_SUBAGENT_MODEL、工具调用传参 model、代理定义 frontmatter、继承父会话排序。自定义子 Agent 应在 frontmatter 或 --agents JSON 中写 model,不是去 settings.json 里找按代理类型配置入口。
  • Provider 兼容:TypeBox 的 Type.Union([Type.Literal(...)]) 会生成 anyOf,Google Gemini tool use 不识别;Pi 的 StringEnum(['a', 'b'] as const) 输出普通 { type: 'string', enum: [...] },用更保守的 schema 形态换取多 Provider 兼容。

适用边界

这些结论适用于设计或理解 Agent CLI、插件系统、skill 加载、子 Agent 模型分配和跨 Provider 工具 schema。不要把 Pi 的延迟读取机制泛化到所有 Agent Skills 实现,也不要假设所有框架都允许从 skill 正文动态替换变量。Claude Code 的模型优先级也只适用于其当前子 Agent 机制;如果接入代理层或后端转写,实际模型可能被外层再次改写。

来源

  • 2026-05-22:子Agent 模型分配机制
  • 2026-05-23:Pi 扩展机制与 Slot UI 模式
  • 2026-05-23:Pi Skill 与数据目录设计
  • 2026-05-23:Pi Agent 架构分层
  • 2026-05-23:TypeBox StringEnum Google 兼容

其他杂项

  • 无。本周材料虽然分散在 Vite、Claude Code 和 Pi 上,但都能归入“框架边界与扩展分层”这个更高层问题,没有需要单独降级到杂项的 insight。

修正报告

  • 无。