Codex 系统手册

上下文片段定义与提示词资产

stage-12.225 个文件

这一层像给模型准备“便签”和“说明书”的库,是幕后支撑部分,不直接干活。几个 prompts 文件把固定提示词打包进程序,随取随用。各种 context 文件把环境、用户规则、命令结果、技能说明、实时会话开头结尾、子代理状态等消息包成统一格式,标清来源和边界,还会限长,防止混成普通输入。另有旧警告识别器,用来读懂老会话,避免误处理。

本阶段的文件25

提示词资源常量

这些嵌入式提示词模板提供原始静态指令文本,之后会包装成上下文片段。

prompts/src/agents.rs源码 ↗
configcross-cutting

这个文件很小,但作用很明确:它定义了一个公开常量 HIERARCHICAL_AGENTS_MESSAGE,里面装的是 templates/agents/hierarchical.md 这个模板文件的完整内容。这里用到的 include_str! 是 Rust 的一个编译期工具,意思是“在编译程序时就把这个文本文件读进来,塞进最终程序里”。好处是运行时不需要再从磁盘读取这个 Markdown 文件,也不怕部署时漏带模板文件。可以把它理解成:原本提示词是一张纸,编译时直接把纸上的内容复印进程序手册里。这个常量大概率会被上层的提示词组装逻辑使用,用来告诉多个代理该怎样按层级分工协作。

prompts/src/apply_patch.rs源码 ↗
configstartup

这个文件很小,但作用很明确:它像是把一张工具使用说明书直接夹进程序手册里。这里的 apply_patch 是一个用来修改代码文件的工具,模型需要知道它的格式和规则,才能安全、准确地改文件。文件里定义了一个公开常量 APPLY_PATCH_TOOL_INSTRUCTIONS,内容不是手写在 Rust 代码里,而是通过 include_str! 把旁边 templates 目录下的 Markdown 说明文件原样嵌进来。好处是说明文字可以单独维护,写起来更像普通文档;同时编译后它又会成为程序的一部分,不怕运行时找不到文件。没有这个文件,其他地方如果想给模型提供 apply_patch 的使用说明,就缺少一个统一、可靠的入口。

prompts/src/compact.rs源码 ↗
configcross-cutting

这个文件很小,但作用很明确:它像一个“提示词仓库入口”。里面有两个常量,分别从两个 Markdown 文本文件里读入内容,并在编译时塞进程序。SUMMARIZATION_PROMPT 是主要的总结指令,告诉模型该怎么把内容压缩成摘要;SUMMARY_PREFIX 是摘要前缀,通常用来给生成出的总结加一个固定开头。这里用的 include_str! 可以理解成“把旁边那个文本文件的内容原封不动贴进代码里”。好处是运行时不用再去磁盘找文件,少了文件丢失、路径不对这类问题;也保证所有地方用到的总结提示词都是同一份。

prompts/src/realtime.rs源码 ↗
configcompile time / realtime session setup

这个文件很小,但作用很明确:它像一个“提示词取货窗口”。实时功能需要几份固定文本,比如给后端模型看的总提示、实时会话开始时的说明、实时会话结束时的说明。这里用 include_str! 把这些 Markdown 文件在编译时读进程序里,变成三个字符串常量。也就是说,程序运行时不用再去磁盘上找这些文件,减少了路径找错、文件漏带之类的问题。需要注意的是,这些文本是在编译时嵌进去的;如果模板文件改了,通常要重新编译程序,改动才会进入最终程序。

片段基础和适配器

这些文件定义共享的底层片段形态,并将外部技能或内部上下文数据适配到通用上下文片段接口。

context-fragments/src/additional_context.rs源码 ↗
domain_logiccross-cutting

这份文件解决的是“怎么把额外资料交给模型看”的问题。额外资料可能来自外部文件、工具结果或调用方传入的信息,需要有清楚的边界,不然模型可能分不清哪里是正文、哪里是补充材料。这里定义了两种片段:一种以 user 身份出现,用类似 <external_名字>内容</external_名字> 的外壳包起来;另一种以 developer 身份出现,用普通的 <名字>内容</名字> 包起来。它们都实现了 ContextualUserFragment,也就是一套统一接口,让别的代码不用关心具体是哪种片段,只要问它“你的角色是什么”“你的标记是什么”“正文怎么生成”。生成正文前会用 token 预算截断内容;token 可以粗略理解为模型读文本时的“字块额度”。这样做像给快递贴标签又限重:既知道包裹是什么,也不会超载。

函数细节13
AdditionalContextUserFragment::new16–18 ↗
fn new(key: String, value: String) -> Self

作用:创建一个给 user 角色使用的额外上下文片段。调用者给它一个名字和一段内容,它把这两样先存起来,等之后生成完整文本。

数据流:进去的是 key 和 value 两个字符串,key 像标签名,value 是实际补充内容。函数不加工内容,只把它们放进 AdditionalContextUserFragment 这个小容器里。出来的是一个新的片段对象,里面保存了这两份数据。

调用关系:这是 user 额外上下文的入口小工厂。后续当系统需要知道角色、标记或正文时,会继续调用这个对象上的 rolemarkersbody 等方法。

AdditionalContextUserFragment::role22–24 ↗
fn role(&self) -> &'static str

作用:告诉外部:这个片段应该以 user 身份放进对话。这样系统能把它归到用户侧上下文里。

数据流:它不读取外部输入,也不改变对象。进去的是当前片段本身,出来的是固定字符串 user

调用关系:它是 ContextualUserFragment 统一接口的一部分。组装上下文的代码会问每个片段的角色,然后按角色把内容放到合适的位置。

AdditionalContextUserFragment::markers26–28 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回 user 额外上下文外层标记的开头和结尾形式。简单说,就是告诉别人这种片段外面该怎么包。

数据流:它读取当前类型需要的标记规则,但不看具体 key 和 value。它把工作交给 AdditionalContextUserFragment::type_markers,再把得到的前缀和后缀返回。

调用关系:这是实例级的方法,方便统一接口调用。它实际复用类型级的 type_markers,避免同一套标记规则写两遍。

调用图:外部调用 1 个(type_markers)。

AdditionalContextUserFragment::type_markers30–35 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出 user 额外上下文的固定外壳规则:开头前缀是 <external_,开始标签的结尾是 >。这些标记让系统和模型都能看出这是一段外部补充信息。

数据流:它没有输入,也不改任何状态。出来的是一对固定字符串,分别代表开始标记前缀和开始标记收尾符。

调用关系AdditionalContextUserFragment::markers 会调用它。其他只知道类型、不知道具体对象的代码,也可以用它来识别或生成同类片段的边界。

AdditionalContextUserFragment::matches_text37–48 ↗
fn matches_text(text: &str) -> bool

作用:判断一段现成文本看起来是不是 user 额外上下文的格式。它像门卫一样,检查文本有没有正确的开头标签和对应的结尾标签。

数据流:进去的是一段文本。它先去掉首尾空白,再检查是否以 <external_ 开头;接着找到第一个 >,把前面的部分当成 key;最后确认文本末尾是不是 </external_key>。出来的是 true 或 false,表示格式是否匹配。

调用关系:这是识别已有片段时用的检查函数。它内部只用格式化字符串来拼出期望的结束标签,不把任务交给本文件里的其他函数。

调用图:外部调用 1 个(format!)。

AdditionalContextUserFragment::body50–52 ↗
fn body(&self) -> String

作用:把保存的 key 和 value 变成真正要放进对话里的正文部分。它负责生成 user 外部上下文的内容格式。

数据流:进去的是当前对象里保存的 key 和 value。它把这两样交给 additional_context_body,由后者负责截断过长内容并拼成标签文本。出来的是一段格式化后的字符串。

调用关系:这是统一接口中“拿正文”的方法。外部组装上下文时会调用它;它自己不做细节活,而是把具体拼接交给 additional_context_body

调用图:调用 1 个内部函数(additional_context_body)。

AdditionalContextDeveloperFragment::new62–64 ↗
fn new(key: String, value: String) -> Self

作用:创建一个给 developer 角色使用的额外上下文片段。调用者传入标签名和内容,它把它们保存起来,供后面生成 developer 消息内容。

数据流:进去的是 key 和 value 两个字符串。函数不截断、不格式化,只把它们放入 AdditionalContextDeveloperFragment。出来的是一个新的 developer 片段对象。

调用关系:这是 developer 额外上下文的构造入口。创建好后,系统会通过同一个 ContextualUserFragment 接口询问它的角色、标记和正文。

AdditionalContextDeveloperFragment::role68–70 ↗
fn role(&self) -> &'static str

作用:告诉外部:这个片段应该以 developer 身份放进对话。developer 可以理解为开发者给模型的补充说明,比普通用户内容更偏系统指导。

数据流:它不接收额外数据,也不改变对象。进去的是当前片段本身,出来的是固定字符串 developer

调用关系:它是统一片段接口的一部分。组装对话时,调用方靠这个返回值决定把该片段放到 developer 侧,而不是 user 侧。

AdditionalContextDeveloperFragment::markers72–74 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回 developer 额外上下文的外层标记规则。这里返回的是空标记,意思是外壳不由统一包装器额外添加,而是正文自己带完整标签。

数据流:它不读取 key 或 value,只调用 AdditionalContextDeveloperFragment::type_markers 取得规则。出来的是一对空字符串。

调用关系:这是实例级接口方法。它把规则集中到 type_markers,这样所有 developer 片段对外都表现一致。

调用图:外部调用 1 个(type_markers)。

AdditionalContextDeveloperFragment::type_markers76–78 ↗
fn type_markers() -> (&'static str, &'static str)

作用:说明 developer 片段没有额外的统一开始、结束标记。因为它的 body 会直接生成 <key>内容</key> 这种完整标签。

数据流:它没有输入,也不改状态。出来的是两个空字符串。

调用关系AdditionalContextDeveloperFragment::markers 会调用它。它和 user 版本形成对比:user 片段的外层标记由 markers 和 body 配合生成,developer 片段则主要靠 body 自己生成完整文本。

AdditionalContextDeveloperFragment::body80–82 ↗
fn body(&self) -> String

作用:把 developer 片段保存的 key 和 value 变成完整的标签文本。它生成的是类似 <key>内容</key> 的格式。

数据流:进去的是当前对象里的 key 和 value。它把它们交给 additional_context_developer_body,由后者先限制内容长度,再拼接成完整标签。出来的是格式化后的字符串。

调用关系:外部在真正组装 developer 上下文时会调用它。它负责把高级接口请求转交给底层的 additional_context_developer_body

调用图:调用 1 个内部函数(additional_context_developer_body)。

additional_context_body85–88 ↗
fn additional_context_body(key: &str, value: &str) -> String

作用:生成 user 外部上下文的正文部分,并确保内容不会太长。它负责把 key 和 value 拼成能和外层 <external_ 标记配合的文本。

数据流:进去的是 key 和 value 的借用文本。它先调用 truncate_middle_with_token_budget,把 value 限制在最多 1000 个 token 左右;如果太长,就从中间截掉一部分。然后用格式化拼出 key>内容</external_key>。出来的是这段字符串,不改动原始输入。

调用关系AdditionalContextUserFragment::body 会调用它。它依赖外部工具 truncate_middle_with_token_budget 做长度控制,再用格式化生成最终文本。

调用图:被 1 处调用(body);外部调用 2 个(truncate_middle_with_token_budget, format!)。

additional_context_developer_body90–93 ↗
fn additional_context_developer_body(key: &str, value: &str) -> String

作用:生成 developer 额外上下文的完整标签文本,并控制内容长度。它让开发者补充信息有清楚边界,同时不让内容过长。

数据流:进去的是 key 和 value。它先用 truncate_middle_with_token_budget 把 value 控制在 1000 个 token 左右,再拼成 <key>内容</key>。出来的是完整字符串,原来的 key 和 value 不会被修改。

调用关系AdditionalContextDeveloperFragment::body 会调用它。它和 additional_context_body 做的事很像,区别是 developer 版本自己生成完整的开始和结束标签。

调用图:被 1 处调用(body);外部调用 2 个(truncate_middle_with_token_budget, format!)。

core-skills/src/skill_instructions.rs源码 ↗
domain_logic构建模型上下文时

这个文件解决的是“怎么把技能说明安全、统一地交给模型看”的问题。项目里先有一个 SkillInjection,也就是已经找到并读出来的技能材料;这里的 SkillInstructions 会复制它的名字、路径和正文,然后实现 ContextualUserFragment。这个接口可以理解成“上下文小纸条”的统一格式:它告诉外部系统,这段内容应该以 user 身份出现,用什么开始和结束标记包起来,以及正文长什么样。正文里会写出 <name>、<path>,再放入技能内容。这样后面的拼装流程不用关心技能来自哪里,只要把它当成一段标准上下文加入即可。类比一下,它像给一份资料套上统一封面和标签,方便传阅时别人知道这是什么、从哪来、正文在哪里。

函数细节5
SkillInstructions::from13–19 ↗
fn from(skill: &SkillInjection) -> Self

作用:把一个 SkillInjection 转成 SkillInstructions。也就是把已经注入进来的技能资料,换成适合放进上下文片段系统的包装形式。

数据流:进去的是一个技能注入对象,里面有技能名、文件路径和内容;函数把这三项各复制一份,放进新的 SkillInstructions;出来的是一个新的技能说明对象,原来的技能注入对象不被改动。

调用关系:它站在“读取到技能”和“拼进模型上下文”之间,像一个换包装的步骤。后续实现 ContextualUserFragment 的那些方法,会基于这里保存下来的 name、path、contents 来生成真正要展示给模型的文本。

SkillInstructions::role23–25 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段技能说明要用什么对话身份出现。这里固定返回 user,意思是把它当作用户侧提供的上下文内容。

数据流:进去的是当前 SkillInstructions;它不读取复杂状态,也不修改任何东西;出来的是固定字符串 user,供外部拼上下文时使用。

调用关系:当上下文系统准备把这个片段放进对话时,会问它“你是什么身份”。这个函数给出答案,让技能说明按统一规则进入最终上下文。

SkillInstructions::markers27–29 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住这段技能说明的开始和结束标记。标记像文件夹的封皮,告诉模型“这里开始是一段技能”“这里结束”。

数据流:进去的是当前 SkillInstructions;函数自己不重新写标记,而是转去使用 SkillInstructions::type_markers 的统一定义;出来的是一对固定标记:开始标记和结束标记。

调用关系:它是实例层面的入口:外部系统拿着某个具体技能片段时会调用它。它把具体工作交给 SkillInstructions::type_markers,这样所有技能说明都使用同一套边界标记。

调用图:外部调用 1 个(type_markers)。

SkillInstructions::type_markers31–33 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给 SkillInstructions 这种片段定义统一的外层标签。这里规定技能说明一律用 <skill> 和 </skill> 包起来。

数据流:它不需要输入具体技能内容,也不改任何数据;直接返回两个固定字符串;结果会被用来标出技能说明的边界。

调用关系:SkillInstructions::markers 会调用它,避免每个实例各自定义一遍标记。它相当于这个类型的“统一标签标准”。

SkillInstructions::body35–40 ↗
fn body(&self) -> String

作用:生成真正放进 <skill> 标记里面的正文。正文会包含技能名字、路径和完整内容,让模型既知道内容本身,也知道它来自哪里。

数据流:进去的是当前 SkillInstructions 里保存的 name、path、contents;函数用 format! 把它们拼成一段带标签的文本;出来的是一整个字符串,不会修改原对象。

调用关系:当上下文系统准备渲染这个片段时,会调用它拿到内部正文。它依赖标准的 format! 拼字符串,然后和 role、markers 提供的信息一起,组成最终给模型看的技能说明块。

调用图:外部调用 1 个(format!)。

core/src/context/internal_model_context.rs源码 ↗
domain_logiccross-cutting

模型有时需要一些系统内部的信息来更好地回答,比如某个扩展想悄悄补充背景。这个文件就像给这类信息准备了一个专用信封:信封外面有固定的开始和结束标记,里面有来源名和正文。来源名不能随便写,只允许小写字母开头,后面跟小写字母、数字或下划线,这样不用复杂转义也不容易把包装格式弄乱。InternalContextSource 表示来源标签,InternalModelContextFragment 表示一整段隐藏上下文。它还实现了 ContextualUserFragment,也就是把这段内容伪装成一类用户上下文片段交给后续流程。文件里还保留了旧格式 goal_context 的识别能力,方便读取历史内容,不至于老数据突然失效。

函数细节12
InternalContextSource::new23–30 ↗
fn new(source: impl Into<String>) -> Result<Self, InvalidInternalContextSource>

作用:创建一个隐藏上下文的来源标签,并先检查这个标签是不是安全格式。有人要声明“这段内部提示来自谁”时会用它。

数据流:输入一个能变成字符串的来源名 → 先转成字符串,再交给 is_valid_source 检查格式 → 如果合格就产出 InternalContextSource;如果不合格,就产出 InvalidInternalContextSource 错误,里面带着原始来源名。

调用关系:它是创建来源标签的入口。InternalContextSource::from_static 会借用它来检查静态来源名;InternalModelContextFragment::matches_text 也用同一套 is_valid_source 规则来判断文本里的来源是否合法,保证写入和读取的标准一致。

调用图:调用 1 个内部函数(is_valid_source);外部调用 1 个(into)。

InternalContextSource::from_static33–36 ↗
fn from_static(source: &'static str) -> Self

作用:从程序里写死的固定字符串创建来源标签。它假设这个字符串应该永远正确,所以如果写错了就直接让程序报错,提醒开发者马上修。

数据流:输入一个静态字符串 → 调用 new 做同样的格式检查 → 成功就返回 InternalContextSource;失败就触发 panic,也就是程序级别的明确失败。

调用关系:它服务于那些代码中固定写好的来源,比如 contextual_user_fragment_is_dyn_compatible、detects_internal_model_context_fragment、goal_context_input_item 这些地方会用它快速准备测试或场景里的来源标签。

调用图:被 3 处调用(contextual_user_fragment_is_dyn_compatible, detects_internal_model_context_fragment, goal_context_input_item);外部调用 1 个(new)。

InternalContextSource::as_str38–40 ↗
fn as_str(&self) -> &str

作用:把来源标签取出来,当普通字符串查看。生成最终隐藏上下文文本时需要用到它。

数据流:输入一个 InternalContextSource 自身 → 读取里面包着的字符串 → 返回字符串引用,不复制也不修改原数据。

调用关系:InternalModelContextFragment::body 会调用它,把来源名写进隐藏上下文的包装头里。它是从安全标签到实际输出文本之间的小桥。

调用图:被 1 处调用(body)。

InvalidInternalContextSource::fmt50–56 ↗
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result

作用:把“来源标签不合法”这个错误变成人能看懂的错误消息。这样出错时不会只看到一个莫名其妙的失败。

数据流:输入错误对象和格式化器 → 读取错误对象里的 source → 写出一段说明文字,告诉读者期望格式是小写字母开头,后面只能接小写字母、数字或下划线。

调用关系:它是 Rust 错误显示机制的一部分。当 InternalContextSource::new 返回这个错误,并且外部要打印或记录错误时,就会走到这里。

调用图:外部调用 1 个(write!)。

InternalModelContextFragment::new70–75 ↗
fn new(source: InternalContextSource, body: impl Into<String>) -> Self

作用:创建一段完整的隐藏内部上下文,包括它来自哪里,以及要塞给模型看的正文。扩展或运行时准备内部提示时会用它。

数据流:输入一个已经验证过的 InternalContextSource 和一段正文 → 把正文转成字符串保存起来 → 返回 InternalModelContextFragment,不会在这里再改写正文内容。

调用关系:它依赖调用方先准备好合法来源。contextual_user_fragment_is_dyn_compatible、detects_internal_model_context_fragment、goal_context_input_item 这些地方会调用它来构造隐藏上下文片段;后续 body、role、markers 等方法再把这个片段包装成可交给模型输入流程的形式。

调用图:被 3 处调用(contextual_user_fragment_is_dyn_compatible, detects_internal_model_context_fragment, goal_context_input_item);外部调用 1 个(into)。

InternalModelContextFragment::role79–81 ↗
fn role(&self) -> &'static str

作用:告诉外部系统,这段隐藏上下文在消息里使用 user 这个角色。也就是说它按“用户上下文片段”的通道进入模型输入。

数据流:输入这个上下文片段自身 → 不读取正文,也不改任何东西 → 固定返回字符串 user。

调用关系:这是实现 ContextualUserFragment 接口的一部分。上层把不同上下文片段统一处理时,会问它的角色是什么,然后按这个角色组装模型消息。

InternalModelContextFragment::markers83–85 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回这类隐藏上下文使用的开始标记和结束标记。外部可以靠这两个标记识别一段文本是不是这种内部上下文。

数据流:输入这个上下文片段自身 → 调用 type_markers 取得固定标记 → 返回开始标记和结束标记这一对字符串。

调用关系:它是对象级别的标记查询方法,把实际工作交给 InternalModelContextFragment::type_markers。上层处理 ContextualUserFragment 时会通过它知道该用什么外壳包住正文。

调用图:外部调用 1 个(type_markers)。

InternalModelContextFragment::type_markers87–89 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出这种隐藏上下文类型固定使用的包装标记。它不依赖某个具体片段,任何同类片段的标记都一样。

数据流:没有实际输入数据 → 直接返回固定的开始标记 <codex_internal_context 和结束标记 </codex_internal_context> → 不修改任何状态。

调用关系:InternalModelContextFragment::markers 会调用它。它把“这种片段长什么样”的规则集中放在一个地方,避免多个地方各写一份。

InternalModelContextFragment::matches_text91–108 ↗
fn matches_text(text: &str) -> bool

作用:判断一段文本是不是合法的内部隐藏上下文。它既认新格式,也认旧的 goal_context 格式,避免旧数据被误判。

数据流:输入一段文本 → 先去掉首尾空白 → 先检查是否符合旧 goal_context 包装;如果不是,再检查新格式的开始标记、source 属性、正文结束位置和来源名格式 → 返回 true 或 false,不改原文本。

调用关系:这是读取或识别上下文片段时的守门员。它把旧格式判断交给 matches_legacy_goal_context,把来源名合法性判断交给 is_valid_source,确保只有包装完整、来源安全的文本才被当成内部上下文。

调用图:调用 2 个内部函数(is_valid_source, matches_legacy_goal_context)。

InternalModelContextFragment::body110–114 ↗
fn body(&self) -> String

作用:生成要放进包装标记里面的内容,其中包含来源属性和正文。简单说,它负责把内部数据变成规定格式的文本。

数据流:输入这个上下文片段自身 → 通过 as_str 取出来源名,再读取正文 → 拼出形如 source="来源">、换行、正文、换行的字符串 → 返回这段字符串,不修改片段本身。

调用关系:它是输出阶段的一环。上层通过 ContextualUserFragment 组装最终消息时,会用 role、markers 和 body 配合起来,把隐藏上下文包装成模型能接收、系统能识别的文本。

调用图:调用 1 个内部函数(as_str);外部调用 1 个(format!)。

matches_legacy_goal_context117–120 ↗
fn matches_legacy_goal_context(text: &str) -> bool

作用:识别老版本使用的 goal_context 包装格式。它的存在是为了兼容历史内容。

数据流:输入一段已经准备检查的文本 → 看它是否以 <goal_context> 开头,并以 </goal_context> 结尾 → 返回 true 或 false,不解析中间正文。

调用关系:InternalModelContextFragment::matches_text 会先调用它。这样旧格式可以优先被认出来,不需要强行符合新的 source 属性格式。

调用图:被 1 处调用(matches_text)。

is_valid_source122–129 ↗
fn is_valid_source(source: &str) -> bool

作用:检查来源标签是否符合安全命名规则。它防止来源名里混入引号、尖括号等会破坏包装格式的字符。

数据流:输入一个来源字符串 → 先确认第一个字符存在并且是小写英文字母 → 再确认后续字符只包含小写英文字母、数字或下划线 → 返回 true 或 false。

调用关系:InternalContextSource::new 用它来拦住非法来源名;InternalModelContextFragment::matches_text 也用它来验证已经写进文本里的 source 属性。这样创建和识别两边用同一把尺子。

调用图:被 2 处调用(new, matches_text)。

ext/skills/src/fragments.rs源码 ↗
domain_logic构建提示词上下文时

这份文件解决的是“怎么把技能信息安全、规整地放进提示词里”的问题。可以把它想成给资料装信封:信封上有固定的开头和结尾标签,里面放真正内容,这样后面的系统一眼就知道这段文字是什么。这里有两种信封。AvailableSkillsInstructions 用来列出当前可用的技能,并声明这段内容属于 developer 角色,也就是更像系统给模型的指导。SkillInstructions 用来放某一个技能的详细说明,包括名字、文件路径和正文,并声明它属于 user 角色。两个结构都实现了 ContextualUserFragment,这是一套“上下文片段接口”,意思是它们都能回答:我是谁说的、用什么标签包起来、正文是什么。这样别的代码不用关心细节,只要按统一接口取内容即可。

函数细节9
AvailableSkillsInstructions::from_skill_lines12–14 ↗
fn from_skill_lines(skill_lines: Vec<String>) -> Self

作用:用一组已经整理好的技能列表文字,创建一个“可用技能说明”对象。别人要把技能清单放进上下文时,会先用它打包。

数据流:输入是一串技能说明行,也就是 Vec<String>。函数不改内容,只把这些行放进 AvailableSkillsInstructions 的 skill_lines 字段里。输出是一个新的 AvailableSkillsInstructions 对象,之后可以继续生成正文或标签。

调用关系:它是这个结构的入口小工具。后续当系统需要把可用技能清单变成上下文片段时,会拿这个对象去调用 role、markers 和 body 等方法。

AvailableSkillsInstructions::role18–20 ↗
fn role(&self) -> &'static str

作用:告诉外部系统,这段“可用技能说明”应该算作 developer 角色的话。这样模型会把它当成开发者层面的指导,而不是普通用户输入。

数据流:它不需要输入,也不读取技能列表。调用后直接返回固定字符串 developer。它不修改任何数据。

调用关系:这是 ContextualUserFragment 接口的一部分。任何统一处理上下文片段的代码,都可以调用它来决定这段内容在对话里应该用什么身份出现。

AvailableSkillsInstructions::markers22–24 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住“可用技能说明”的开始和结束标签。标签就像文件夹的封皮,让后面的解析者知道这段内容从哪里开始、到哪里结束。

数据流:它不接收额外输入,而是调用 AvailableSkillsInstructions::type_markers 取得固定的一对标签。输出是开始标签和结束标签,不改动对象本身。

调用关系:它把取标签的工作交给 type_markers。这样外部通过接口调用 markers 时,拿到的就是这个类型专用的标准标签。

调用图:外部调用 1 个(type_markers)。

AvailableSkillsInstructions::type_markers26–28 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给“可用技能说明”定义固定标签。这样所有地方使用同一种开头和结尾,不会因为手写不一致而让内容边界混乱。

数据流:它不需要输入,直接返回 SKILLS_INSTRUCTIONS_OPEN_TAG 和 SKILLS_INSTRUCTIONS_CLOSE_TAG 这一对常量。它不读取对象字段,也不产生副作用。

调用关系:AvailableSkillsInstructions::markers 会调用它。它相当于这个片段类型的标签来源,保证标签集中定义、统一使用。

AvailableSkillsInstructions::body30–32 ↗
fn body(&self) -> String

作用:把保存的技能列表变成真正要放进上下文的正文。它不是随便拼字符串,而是交给专门的渲染函数生成统一格式。

数据流:它读取对象里的 skill_lines。然后调用外部的 render_available_skills_body,把空的第一组参数和技能行一起交给它。输出是一段格式化后的字符串,表示可用技能说明正文;对象本身不被修改。

调用关系:这是 ContextualUserFragment 接口里提供正文的部分。它把具体排版交给 render_available_skills_body,自己只负责提供已有的技能行。

调用图:外部调用 1 个(render_available_skills_body)。

SkillInstructions::role43–45 ↗
fn role(&self) -> &'static str

作用:告诉外部系统,某个具体技能的说明应该作为 user 角色内容放入上下文。也就是说,它会被当成用户侧提供的资料片段。

数据流:它不需要输入,也不读取 name、path 或 contents。调用后直接返回固定字符串 user,不改变任何数据。

调用关系:这是 ContextualUserFragment 接口的一部分。统一处理上下文片段的代码会通过它判断这段技能内容应该以什么角色进入对话。

SkillInstructions::markers47–49 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住单个技能说明的开始和结束标签。这样系统能清楚分辨一段技能说明的边界。

数据流:它不接收额外输入,调用 SkillInstructions::type_markers 拿到固定标签。输出是一对字符串:<skill> 和 </skill>。

调用关系:它把实际标签定义交给 type_markers。外部通过统一接口调用 markers,就能拿到单个技能说明专用的包装标签。

调用图:外部调用 1 个(type_markers)。

SkillInstructions::type_markers51–53 ↗
fn type_markers() -> (&'static str, &'static str)

作用:定义单个技能说明使用的固定标签。开头是 <skill>,结尾是 </skill>,表示中间是一份技能资料。

数据流:它不需要输入,直接返回固定的一对标签字符串。它不读取对象里的名字、路径或内容,也不改任何状态。

调用关系:SkillInstructions::markers 会调用它。它让标签定义集中在一个地方,避免不同地方写出不一样的技能边界。

SkillInstructions::body55–60 ↗
fn body(&self) -> String

作用:把一个具体技能的名字、文件路径和正文拼成上下文里要展示的内容。它会把名字和路径用明确标签标出来,方便读者或系统识别。

数据流:它读取 self.name、self.path 和 self.contents。然后用 format! 生成一段字符串,里面包含 <name>、<path> 和技能正文。输出是这段完整正文;原对象不被修改。

调用关系:这是单个技能片段提供正文的地方。外部按 ContextualUserFragment 接口要正文时会用到它;它内部只用 format! 做字符串拼接,不再把工作交给其他项目函数。

调用图:外部调用 1 个(format!)。

会话和环境上下文

这些片段注入主要运行时、指令和操作上下文,为模型的进行中对话提供框架。

core/src/context/approved_command_prefix_saved.rs源码 ↗
data_modelcross-cutting

这个文件很小,但作用很明确:它像一张便签,把已经保存的“允许执行的命令前缀”写进系统上下文里。上下文可以理解成系统交给模型或后续步骤看的背景资料。这里定义了 ApprovedCommandPrefixSaved,里面只存一段字符串 prefixes,也就是那些被批准的命令开头。它实现了 ContextualUserFragment 这个接口,意思是它能被当成一段上下文片段使用。它会声明自己的角色是 developer,也就是以“开发者提示”的身份出现;它不额外加包裹标记;正文则固定写成“Approved command prefix saved:”再接上具体前缀。这样做的好处是格式统一,别的地方只要创建这个对象,就能放心把批准记录放进上下文,不用每次手写同样的提示文本。

函数细节5
ApprovedCommandPrefixSaved::new9–13 ↗
fn new(prefixes: impl Into<String>) -> Self

作用:创建一个 ApprovedCommandPrefixSaved 对象,把外面传进来的命令前缀保存起来。有人在记录执行策略变更时会用它,把“已批准的前缀”变成一段可放入上下文的数据。

数据流:进去的是一段可以转成字符串的 prefixes → 函数把它转换成真正的 String 并存进结构体里 → 出来的是一个新的 ApprovedCommandPrefixSaved 对象,里面带着这份前缀文本。

调用关系:它会被 record_execpolicy_amendment_message 调用,用在保存或记录命令执行策略变更的时候。它自己只做装盒这一步,转换字符串时交给标准的 into 方法完成。

调用图:被 1 处调用(record_execpolicy_amendment_message);外部调用 1 个(into)。

ApprovedCommandPrefixSaved::role17–19 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段内容应该用什么身份出现。这里固定返回 developer,表示它是一条给模型看的开发者级别背景说明。

数据流:进去的是这个对象本身,但它不读取 prefixes → 直接返回固定文字 developer → 不改动任何数据。

调用关系:这是 ContextualUserFragment 接口要求提供的方法。外部在组装上下文片段时会问它“你是什么角色”,它就回答 developer。

ApprovedCommandPrefixSaved::markers21–23 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:告诉上下文系统,这段内容前后要不要加特殊标记。这里返回空标记,意思是正文直接放进去,不额外包一层标签。

数据流:进去的是这个对象本身 → 它不直接计算,而是转去调用 type_markers 取得这一类片段统一使用的标记 → 出来是一对空字符串,表示开头和结尾都没有标记。

调用关系:这是 ContextualUserFragment 接口的一部分。组装上下文时会调用它确认要不要加边界符;它把这个决定交给 ApprovedCommandPrefixSaved::type_markers,保证实例方法和类型级规则一致。

调用图:外部调用 1 个(type_markers)。

ApprovedCommandPrefixSaved::type_markers25–27 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给这一类上下文片段定义统一的前后标记。这里明确说:没有开始标记,也没有结束标记。

数据流:没有输入数据需要处理 → 直接返回两个空字符串 → 不读取也不修改任何对象。

调用关系:ApprovedCommandPrefixSaved::markers 会调用它。这样如果以后这类片段需要统一加标记,只改这里就能影响所有实例。

ApprovedCommandPrefixSaved::body29–31 ↗
fn body(&self) -> String

作用:生成真正要放进上下文里的正文。它把固定标题和保存的命令前缀拼在一起,让读的人或模型一眼知道这些前缀已经被批准过。

数据流:进去的是对象本身,里面有 prefixes → 函数用格式化模板拼出“Approved command prefix saved:”加换行再加 prefixes → 出来是一段新的字符串,不改变原对象。

调用关系:这是 ContextualUserFragment 接口要求的正文生成方法。外部组装上下文时会调用它拿到可显示、可传递的内容;它内部用标准的 format! 宏完成字符串拼接。

调用图:外部调用 1 个(format!)。

core/src/context/environment_context.rs源码 ↗
domain_logic每轮对话构建上下文、比较上下文变化、发送给模型前活跃

这个文件像是在每次对话前给模型递一张“现场说明卡”。卡上写着:现在有哪些工作环境、当前目录是什么、用的是什么 shell、今天日期和时区是什么、网络允许访问哪些域名、文件系统哪些路径能读写。它还会把这些信息包装成一种类似 XML 的文本,方便模型稳定理解。文件里有几块零件:EnvironmentContext 是总卡片;NetworkContext 写网络规则;FileSystemContext 写文件和权限规则;一些 render 函数负责把结构化数据变成文本。它还特别处理“比较两次环境变化”这件事:比如 shell 名字有时只是历史遗留信息,不该因为它不同就认为环境变了。另一个重要点是,它会给文件路径和 XML 文本做安全转义,避免路径里有特殊字符时把说明卡格式弄坏。

函数细节33
EnvironmentContextEnvironment::legacy36–42 ↗
fn legacy(cwd: AbsolutePathBuf, shell: String) -> Self

作用:用旧格式创建一个单一环境记录,只保存当前目录和 shell。它主要是为了兼容以前只有一个工作目录的上下文。

数据流:进去一个绝对路径形式的当前目录和一个 shell 名字 → 它把环境 id 留空,把目录和 shell 填进去 → 出来一个 EnvironmentContextEnvironment 记录。

调用关系:当 EnvironmentContext::diff_from_turn_context_item 发现旧上下文和新上下文的目录不一样时,会用它生成一个兼容旧格式的环境变化说明。

调用图:被 1 处调用(diff_from_turn_context_item);外部调用 1 个(new)。

EnvironmentContextEnvironment::from_turn_environments44–61 ↗
fn from_turn_environments(environments: &[TurnEnvironment], shell: &Shell) -> Vec<Self>

作用:把一轮会话里的多个运行环境转换成模型上下文能使用的环境列表。它会尽量使用每个环境自己的 shell,没有的话就用默认 shell。

数据流:进去一组 TurnEnvironment 和默认 Shell → 它逐个读取环境 id、当前目录、shell 名称,并跳过无法转成绝对路径的环境 → 出来一组 EnvironmentContextEnvironment。

调用关系:EnvironmentContext::from_turn_context 在从完整会话上下文生成环境说明时会调用它,作为环境列表的来源。

调用图:被 1 处调用(from_turn_context);外部调用 1 个(iter)。

EnvironmentContextEnvironments::from_vec72–82 ↗
fn from_vec(environments: Vec<EnvironmentContextEnvironment>) -> Self

作用:把环境列表压成三种更好表达的状态:没有、一个、多个。这样生成文本时就不用把单环境和多环境混在一起处理。

数据流:进去一个环境数组 → 它看数组长度,空数组变 None,一个元素变 Single,多个元素变 Multiple → 出来一个 EnvironmentContextEnvironments 枚举值。

调用关系:EnvironmentContext::new 和 EnvironmentContext::from_turn_context_item 都用它把原始环境列表整理成统一格式。

调用图:被 2 处调用(from_turn_context_item, new);外部调用 2 个(Multiple, Single)。

EnvironmentContextEnvironments::equals_except_shell84–97 ↗
fn equals_except_shell(&self, other: &Self) -> bool

作用:比较两份环境列表是否相同,但故意不比较 shell。这样可以避免因为 shell 字段不可配置或历史差异,误判环境真的变了。

数据流:进去两份环境列表 → 它比较是否同为空、同为单个并且目录相同、同为多个并且 id 和目录逐个相同 → 出来 true 或 false。

调用关系:EnvironmentContext::equals_except_shell 会把总上下文的环境部分交给它比较。

调用图:被 1 处调用(equals_except_shell)。

FileSystemContext::from_permission_profile123–147 ↗
fn from_permission_profile(
        permission_profile: &PermissionProfile,
        workspace_roots: &[AbsolutePathBuf],
    ) -> Self

作用:把项目的文件系统权限配置整理成模型能读懂的文件系统上下文。它会把“项目根目录”这类占位规则展开成实际工作区路径。

数据流:进去一个 PermissionProfile 和工作区根目录列表 → 它复制并展开权限规则,把根目录转成字符串,再区分 managed、disabled、external 三种权限模式 → 出来一个 FileSystemContext。

调用关系:EnvironmentContext::from_turn_context 和 EnvironmentContext::filesystem_from_turn_context_item 都会调用它;测试里也会用它检查完整文件系统权限能否正确序列化。

调用图:调用 1 个内部函数(from);被 3 处调用(filesystem_from_turn_context_item, from_turn_context, serialize_environment_context_with_full_filesystem_profile);外部调用 3 个(Managed, clone, iter)。

FileSystemContext::render149–161 ↗
fn render(&self) -> String

作用:把文件系统上下文变成一段类似 XML 的文本,放进给模型看的环境说明里。

数据流:进去一个 FileSystemContext → 它先写 filesystem 标签,再写工作区根目录,最后追加权限配置文本 → 出来完整的文件系统说明字符串。

调用关系:EnvironmentContext::body 在生成整段环境说明时会调用它;它会继续把权限部分交给 FileSystemPermissionProfileContext::render。

调用图:调用 2 个内部函数(render, push_text_element)。

ManagedFileSystemContext::from165–179 ↗
fn from(file_system: ManagedFileSystemPermissions) -> Self

作用:把底层的托管文件系统权限转换成本文件使用的简化表示。它还会去掉重复权限条目,避免给模型重复、混乱的信息。

数据流:进去 ManagedFileSystemPermissions → 如果是受限模式,它先给规则去重,再保存规则和扫描深度;如果是不受限模式,就直接标记为不受限 → 出来 ManagedFileSystemContext。

调用关系:FileSystemContext::from_permission_profile 在遇到 managed 权限配置时会调用它。

调用图:调用 1 个内部函数(dedupe_file_system_entries);被 1 处调用(from_permission_profile)。

FileSystemPermissionProfileContext::render183–201 ↗
fn render(&self, rendered: &mut String)

作用:把“权限配置类型”写成模型能读懂的文本,比如托管、禁用、外部控制。它告诉模型文件访问到底由谁管、怎么管。

数据流:进去一个权限配置上下文和正在拼接的字符串 → 它按类型追加对应标签;托管类型会继续写详细文件系统规则 → 改动传入的字符串,不单独返回新值。

调用关系:FileSystemContext::render 会调用它来补上权限配置部分;托管权限的细节会交给 ManagedFileSystemContext::render。

调用图:被 1 处调用(render)。

ManagedFileSystemContext::render205–230 ↗
fn render(&self, rendered: &mut String)

作用:把托管文件系统权限写成文本,明确告诉模型是受限还是不受限。受限时还会列出具体路径规则和 glob 扫描深度。

数据流:进去一个托管文件系统上下文和正在拼接的字符串 → 它按受限或不受限写标签;受限模式下逐条写路径规则 → 改动传入的字符串。

调用关系:FileSystemPermissionProfileContext::render 遇到 managed 权限时会调用它;具体每条规则交给 render_file_system_entry。

调用图:调用 1 个内部函数(render_file_system_entry);外部调用 1 个(format!)。

render_file_system_entry233–254 ↗
fn render_file_system_entry(rendered: &mut String, entry: &FileSystemSandboxEntry)

作用:把一条文件系统权限规则写成文本,比如某个路径允许访问、某个 glob 模式匹配的文件被拒绝。它是文件权限说明里的“单条规则打印机”。

数据流:进去正在拼接的字符串和一条 FileSystemSandboxEntry → 它写入访问方式,再根据路径类型写 path、glob 或 special → 改动传入的字符串。

调用关系:ManagedFileSystemContext::render 在渲染受限权限时会逐条调用它;特殊路径会继续交给 render_special_path 转成可读格式。

调用图:调用 2 个内部函数(push_text_element, render_special_path);被 1 处调用(render)。

render_special_path256–269 ↗
fn render_special_path(value: &FileSystemSpecialPath) -> String

作用:把特殊路径代号转成稳定的文字,比如根目录、临时目录、工作区根目录。这样模型看到的不是内部枚举,而是清楚的路径含义。

数据流:进去一个 FileSystemSpecialPath → 它按类型转成类似 :root、:tmpdir、:workspace_roots 的字符串,必要时拼上子路径 → 出来一个字符串。

调用关系:render_file_system_entry 在遇到特殊路径规则时会调用它;带子路径的情况交给 render_special_path_with_subpath。

调用图:调用 1 个内部函数(render_special_path_with_subpath);被 1 处调用(render_file_system_entry)。

render_special_path_with_subpath271–276 ↗
fn render_special_path_with_subpath(base: &str, subpath: &Option<PathBuf>) -> String

作用:把一个特殊路径基础名和可选子路径拼在一起。比如把 :workspace_roots 和 src 拼成 :workspace_roots/src。

数据流:进去基础字符串和可选 PathBuf 子路径 → 有子路径就用斜杠拼接,没有就只返回基础字符串 → 出来拼好的字符串。

调用关系:render_special_path 在处理工作区根目录或未知特殊路径时会调用它。

调用图:被 1 处调用(render_special_path);外部调用 1 个(format!)。

dedupe_file_system_entries278–281 ↗
fn dedupe_file_system_entries(entries: &mut Vec<FileSystemSandboxEntry>)

作用:去掉重复的文件系统权限规则。这样生成的上下文更短,也避免模型误以为重复规则有特殊含义。

数据流:进去一个可修改的权限规则数组 → 它用集合记住见过的规则,只保留第一次出现的 → 原数组被就地改成去重后的版本。

调用关系:ManagedFileSystemContext::from 在转换受限文件系统权限时会调用它。

调用图:被 1 处调用(from);外部调用 1 个(new)。

push_text_element283–287 ↗
fn push_text_element(rendered: &mut String, name: &str, value: &str)

作用:往字符串里追加一个简单文本标签,比如 <root>某路径</root>。它会顺手处理特殊字符,防止文本破坏标签格式。

数据流:进去正在拼接的字符串、标签名和值 → 它写开始标签,调用 push_xml_escaped_text 写安全文本,再写结束标签 → 改动传入的字符串。

调用关系:FileSystemContext::render 和 render_file_system_entry 都用它写路径、glob、特殊路径等文本内容。

调用图:调用 1 个内部函数(push_xml_escaped_text);被 2 处调用(render, render_file_system_entry);外部调用 1 个(format!)。

push_xml_escaped_text289–300 ↗
fn push_xml_escaped_text(rendered: &mut String, value: &str)

作用:把文本里的 XML 特殊字符转义。比如把 < 变成 &lt;,避免路径或域名里有特殊字符时把整段上下文弄乱。

数据流:进去正在拼接的字符串和原始文本 → 它逐个字符检查,遇到 &、<、>、引号、单引号就替换成安全写法 → 改动传入的字符串。

调用关系:push_text_element 在写任何标签文本时都会调用它。

调用图:被 1 处调用(push_text_element)。

NetworkContext::new309–314 ↗
fn new(allowed_domains: Vec<String>, denied_domains: Vec<String>) -> Self

作用:创建一个网络上下文,记录允许访问和禁止访问的域名列表。它让模型知道联网边界在哪里。

数据流:进去允许域名数组和禁止域名数组 → 它直接保存这两份列表 → 出来一个 NetworkContext。

调用关系:EnvironmentContext::network_from_turn_context、EnvironmentContext::network_from_turn_context_item 和相关测试都会用它创建网络说明。

调用图:被 3 处调用(network_from_turn_context, network_from_turn_context_item, serialize_environment_context_with_network)。

NetworkContext::render316–322 ↗
fn render(&self) -> String

作用:把网络权限写成一段文本,告诉模型网络是开启的,以及哪些域名允许或禁止。

数据流:进去一个 NetworkContext → 它生成 network 标签,并把 allowed、denied 域名列表写进去 → 出来网络说明字符串。

调用关系:EnvironmentContext::body 在生成完整环境说明时会调用它;域名列表的具体标签由 NetworkContext::push_rendered_domain_element 添加。

调用图:外部调用 1 个(push_rendered_domain_element)。

NetworkContext::push_rendered_domain_element324–332 ↗
fn push_rendered_domain_element(rendered_network: &mut String, name: &str, domains: &[String])

作用:把一组域名写进一个标签里,比如 allowed 或 denied。空列表不会写,避免输出无意义的空标签。

数据流:进去正在拼接的网络字符串、标签名、域名列表 → 如果列表非空,就用逗号连接域名并包上标签 → 改动传入的字符串。

调用关系:NetworkContext::render 会分别用它写允许域名和禁止域名。

调用图:外部调用 1 个(format!)。

EnvironmentContext::new336–351 ↗
fn new(
        environments: Vec<EnvironmentContextEnvironment>,
        current_date: Option<String>,
        timezone: Option<String>,
        network: Option<NetworkContext>,
        subagents: Op

作用:创建一份基础环境上下文,包含环境列表、日期、时区、网络和子代理信息。文件系统信息默认先不填。

数据流:进去环境数组、日期、时区、网络、子代理文本 → 它把环境数组整理成 None、Single 或 Multiple,并保存其他字段 → 出来一个 EnvironmentContext。

调用关系:EnvironmentContext::from_turn_context 会先用它建基础对象;多处测试也用它构造待比较或待序列化的环境上下文。

调用图:调用 1 个内部函数(from_vec);被 10 处调用(equals_except_shell_compares_cwd, equals_except_shell_compares_cwd_differences, equals_except_shell_ignores_shell, serialize_environment_context_prefers_environment_shell_when_present, serialize_environment_context_with_full_filesystem_profile, serialize_environment_context_with_multiple_selected_environments, serialize_environment_context_with_network, serialize_environment_context_with_subagents, serialize_read_only_environment_context, serialize_workspace_write_environment_context)。

EnvironmentContext::new_with_environments353–369 ↗
fn new_with_environments(
        environments: EnvironmentContextEnvironments,
        current_date: Option<String>,
        timezone: Option<String>,
        network: Option<NetworkContext>,

作用:用已经整理好的环境状态创建完整环境上下文。它比 new 更底层,允许直接传入文件系统信息。

数据流:进去整理好的环境状态、日期、时区、网络、文件系统、子代理文本 → 它逐项保存 → 出来一个 EnvironmentContext。

调用关系:EnvironmentContext::diff_from_turn_context_item 和 EnvironmentContext::from_turn_context_item 会用它精确重建或生成差异上下文。

调用图:被 1 处调用(diff_from_turn_context_item)。

EnvironmentContext::equals_except_shell374–381 ↗
fn equals_except_shell(&self, other: &EnvironmentContext) -> bool

作用:比较两份完整环境上下文是否一样,但忽略 shell 差异。它适合判断两轮对话之间环境是否真的需要更新。

数据流:进去两份 EnvironmentContext → 它比较环境、日期、时区、网络、文件系统、子代理,其中环境比较会忽略 shell → 出来 true 或 false。

调用关系:它把环境列表部分交给 EnvironmentContextEnvironments::equals_except_shell;相关测试会验证它确实忽略 shell、但不忽略目录变化。

调用图:调用 1 个内部函数(equals_except_shell)。

EnvironmentContext::diff_from_turn_context_item383–423 ↗
fn diff_from_turn_context_item(
        before: &TurnContextItem,
        after: &EnvironmentContext,
    ) -> Self

作用:根据旧的 TurnContextItem 和新的 EnvironmentContext 生成一份“变化说明”。这样系统不用每次都重复发送所有旧环境信息。

数据流:进去旧上下文条目和新环境上下文 → 它重建旧网络和文件系统,比较新旧差异;单环境只在目录变了时写入,多环境则保留完整列表 → 出来一份用于更新的 EnvironmentContext。

调用关系:build_environment_update_item 会调用它来生成环境更新;它会用 network_from_turn_context_item、filesystem_from_turn_context_item、legacy 和 new_with_environments 协作完成。

调用图:调用 2 个内部函数(new_with_environments, legacy);被 1 处调用(build_environment_update_item);外部调用 4 个(filesystem_from_turn_context_item, network_from_turn_context_item, Multiple, Single)。

EnvironmentContext::from_turn_context425–441 ↗
fn from_turn_context(turn_context: &TurnContext, shell: &Shell) -> Self

作用:从当前完整会话状态生成模型要看的环境上下文。这是运行时构建环境说明的主要入口之一。

数据流:进去 TurnContext 和默认 Shell → 它提取环境列表、日期、时区、网络规则,再补上文件系统权限和工作区根目录 → 出来完整 EnvironmentContext。

调用关系:build_initial_context 和 build_environment_update_item 会调用它;它会把环境转换交给 EnvironmentContextEnvironment::from_turn_environments,把文件系统转换交给 FileSystemContext::from_permission_profile。

调用图:调用 2 个内部函数(from_turn_environments, from_permission_profile);被 3 处调用(build_environment_update_item, build_initial_context, environment_context_uses_session_shell_when_environment_shell_is_absent);外部调用 2 个(network_from_turn_context, new)。

EnvironmentContext::from_turn_context_item443–461 ↗
fn from_turn_context_item(
        turn_context_item: &TurnContextItem,
        shell: String,
    ) -> Self

作用:从已经保存过的 TurnContextItem 重建环境上下文。它用于和当前环境做比较,或者兼容历史记录。

数据流:进去一个旧上下文条目和 shell 名字 → 它把 cwd 转成绝对路径,失败时用根目录兜底解析,再取日期、时区、网络、文件系统 → 出来一份 EnvironmentContext。

调用关系:build_environment_update_item 会用它重建旧状态;文件系统部分交给 EnvironmentContext::filesystem_from_turn_context_item。

调用图:调用 3 个内部函数(from_vec, resolve_path_against_base, try_from);被 2 处调用(turn_context_item_filesystem_uses_workspace_roots_instead_of_cwd, build_environment_update_item);外部调用 4 个(filesystem_from_turn_context_item, network_from_turn_context_item, new_with_environments, vec!)。

EnvironmentContext::with_subagents463–468 ↗
fn with_subagents(mut self, subagents: String) -> Self

作用:给环境上下文附加子代理说明。子代理可以理解为系统可用的辅助角色或工具说明。

数据流:进去一个 EnvironmentContext 和一段子代理文本 → 如果文本不是空的,就保存到 subagents 字段;空文本则不改 → 出来更新后的 EnvironmentContext。

调用关系:调用者可以在创建环境上下文后链式调用它,把子代理信息加入最终给模型看的 body。

EnvironmentContext::network_from_turn_context470–490 ↗
fn network_from_turn_context(turn_context: &TurnContext) -> Option<NetworkContext>

作用:从当前会话配置里提取网络权限。它把配置文件里的允许域名和禁止域名整理成 NetworkContext。

数据流:进去 TurnContext → 它查看配置层里的网络要求;如果没有网络配置就返回 None,有的话提取 allowed 和 denied 域名 → 出来可选的 NetworkContext。

调用关系:EnvironmentContext::from_turn_context 会调用它;创建 NetworkContext 的工作交给 NetworkContext::new。

调用图:调用 1 个内部函数(new)。

EnvironmentContext::network_from_turn_context_item492–503 ↗
fn network_from_turn_context_item(
        turn_context_item: &TurnContextItem,
    ) -> Option<NetworkContext>

作用:从已保存的上下文条目里恢复网络权限。它用于比较历史环境和当前环境。

数据流:进去 TurnContextItem → 如果里面没有 network 字段就返回 None;有的话复制允许和禁止域名 → 出来可选的 NetworkContext。

调用关系:EnvironmentContext::diff_from_turn_context_item 和 EnvironmentContext::from_turn_context_item 会调用它;最终对象由 NetworkContext::new 创建。

调用图:调用 1 个内部函数(new)。

EnvironmentContext::filesystem_from_turn_context_item505–512 ↗
fn filesystem_from_turn_context_item(
        turn_context_item: &TurnContextItem,
    ) -> Option<FileSystemContext>

作用:从已保存的上下文条目里恢复文件系统权限说明。它会同时考虑权限配置和工作区根目录。

数据流:进去 TurnContextItem → 它读取权限配置,调用 workspace_roots_from_turn_context_item 找到工作区根目录,再转换成 FileSystemContext → 出来可选的 FileSystemContext。

调用关系:EnvironmentContext::diff_from_turn_context_item 和 EnvironmentContext::from_turn_context_item 用它重建旧文件系统状态;具体转换交给 FileSystemContext::from_permission_profile。

调用图:调用 3 个内部函数(from_permission_profile, workspace_roots_from_turn_context_item, permission_profile)。

workspace_roots_from_turn_context_item515–528 ↗
fn workspace_roots_from_turn_context_item(
    turn_context_item: &TurnContextItem,
) -> Vec<AbsolutePathBuf>

作用:从旧上下文条目里找出工作区根目录。它还兼容老数据:如果老数据没有保存工作区根目录,就退回使用 cwd。

数据流:进去 TurnContextItem → 如果有 workspace_roots 就直接复制;否则尝试把 cwd 转成绝对路径,成功就作为唯一根目录,失败就返回空列表 → 出来根目录数组。

调用关系:EnvironmentContext::filesystem_from_turn_context_item 会调用它,为文件系统权限展开项目根目录提供基础信息。

调用图:调用 1 个内部函数(try_from);被 1 处调用(filesystem_from_turn_context_item);外部调用 2 个(new, vec!)。

EnvironmentContext::role531–533 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段环境说明应该以 user 角色发送。也就是说,它会被当成用户侧提供的背景信息。

数据流:进去 EnvironmentContext 自身 → 它不读取具体字段,直接返回固定字符串 user → 出来角色名。

调用关系:这是 ContextualUserFragment 接口的一部分;上层组装消息时会通过这个接口判断该片段用什么角色。

EnvironmentContext::markers535–537 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住环境上下文的开始和结束标记。标记就像信封边界,让模型和系统知道这段文本是哪一类上下文。

数据流:进去 EnvironmentContext 自身 → 它调用 type_markers 取得固定标记 → 出来一对开始、结束字符串。

调用关系:这是 ContextualUserFragment 接口的一部分;上层生成消息片段时会用它包裹 EnvironmentContext::body 的内容。

调用图:外部调用 1 个(type_markers)。

EnvironmentContext::type_markers539–544 ↗
fn type_markers() -> (&'static str, &'static str)

作用:提供环境上下文专用的固定开始标签和结束标签。它不依赖某个具体对象的内容。

数据流:没有业务输入 → 它返回协议里定义好的 ENVIRONMENT_CONTEXT_OPEN_TAG 和 ENVIRONMENT_CONTEXT_CLOSE_TAG → 出来一对标记字符串。

调用关系:EnvironmentContext::markers 会调用它;其他需要知道环境上下文边界的代码也可以使用这个固定定义。

EnvironmentContext::body546–595 ↗
fn body(&self) -> String

作用:生成最终放进消息里的环境说明正文。它把目录、shell、日期、网络、文件系统、子代理等信息排成清楚的文本块。

数据流:进去 EnvironmentContext → 它按字段逐项检查:有单环境就写 cwd 和 shell,有多环境就逐个写;有日期、时区、网络、文件系统、子代理就追加对应块 → 出来带前后换行的正文字符串。

调用关系:这是 ContextualUserFragment 接口最核心的输出函数;上层发送上下文给模型时会用它生成实际文本,其中网络和文件系统分别调用 NetworkContext::render 与 FileSystemContext::render。

调用图:外部调用 2 个(new, format!)。

core/src/context/guardian_followup_review_reminder.rs源码 ↗
domain_logicrequest handling

这个文件很小,但作用很明确:它像是在审核流程旁边贴的一张便签,提醒模型处理“后续复审”时该怎么想。这里定义了一个叫 GuardianFollowupReviewReminder 的上下文片段,它实现了 ContextualUserFragment,也就是“能生成一段给模型看的上下文内容”的接口。它声明自己的角色是 developer,意思是这段话会以开发者指令的身份出现,比普通用户话语更像规则提醒。它不加额外标记,正文则强调三件事:以前的审核只能当参考;必须遵守工作区政策;如果用户已经被告知具体风险后,仍明确批准之前被拒绝的操作,那么除非政策明确禁止这种用户覆盖,否则结果应改为 allow。没有这段提醒,系统可能会把旧审核当成不可推翻的判决,导致该允许的后续操作仍被拒绝。

函数细节4
GuardianFollowupReviewReminder::role7–9 ↗
fn role(&self) -> &'static str

作用:这个函数告诉外部:这段提醒文字应该用什么身份放进上下文里。这里返回 developer,表示它是开发者级别的指导,而不是普通聊天内容。

数据流:进去的是这个提醒片段本身,但它不需要读取任何内部数据;它直接给出固定字符串 developer;出来的是一个角色名称,供上下文组装器决定这段文字的地位。

调用关系:当系统准备把 GuardianFollowupReviewReminder 放进模型上下文时,会询问它的 role。这个函数不把工作交给别的函数,只负责给出固定身份,让后续拼装上下文的人知道该怎么摆放这段提醒。

GuardianFollowupReviewReminder::markers11–13 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:这个函数返回这段上下文内容前后要不要加特殊标记。这里实际上不加任何标记,因为它直接使用 type_markers 给出的空字符串。

数据流:进去的是这个提醒片段本身;它调用 type_markers 拿到一对开始和结束标记;出来的是两个空字符串,表示正文前后都不包额外标签。

调用关系:上下文组装流程需要知道每段内容是否要包一层标记时,会调用它。它把具体答案交给 GuardianFollowupReviewReminder::type_markers,这样实例方法和类型级方法保持一致。

调用图:外部调用 1 个(type_markers)。

GuardianFollowupReviewReminder::type_markers15–17 ↗
fn type_markers() -> (&'static str, &'static str)

作用:这个函数给出 GuardianFollowupReviewReminder 这种提醒片段的默认标记设置。它返回两个空字符串,意思是不用特殊开头,也不用特殊结尾。

数据流:它不接收业务数据,也不读取外部状态;只是直接返回一对固定的空字符串;结果会被 markers 使用,表示这段提醒文字裸放即可。

调用关系:GuardianFollowupReviewReminder::markers 会调用它来取得标记。它是这个文件里标记规则的唯一来源,避免以后实例方法和类型规则写出两套不同答案。

GuardianFollowupReviewReminder::body19–28 ↗
fn body(&self) -> String

作用:这个函数生成真正要给模型看的提醒正文。它说明旧审核只能当参考、必须跟随工作区政策,并说明用户明确承担风险后什么时候可以允许原本被拒绝的动作。

数据流:进去的是这个提醒片段本身,但函数不需要读取可变状态;它用 concat! 把几段固定英文拼成一整段文字,再转成 String;出来的是完整提醒正文,没有改动其他东西。

调用关系:当上下文组装器需要拿到这段提醒的实际内容时,会调用 body。它使用 concat! 这个编译期拼接工具把固定句子合成正文,然后把这段规则交给后续的模型提示流程使用。

调用图:外部调用 1 个(concat!)。

core/src/context/hook_additional_context.rs源码 ↗
data_modelcross-cutting

这个文件定义了一个叫 HookAdditionalContext 的小结构,里面只装一段文字。可以把它理解成给主提示词夹进去的一张便签:钩子程序,也就是在某些流程点自动运行的小扩展,可以把额外说明写到这张便签上。这个结构实现了 ContextualUserFragment 这个接口;接口就是一套约定,说明“我能提供角色、标记和正文”。这里它把角色固定成 developer,意思是这段内容会被当作开发者层面的补充说明。它不加任何前后包裹标记,正文就是原始文字本身。这样做的好处是,其他代码不需要关心这段文字从哪里来,只要按统一接口读取 role、markers 和 body,就能把它放进最终上下文里。

函数细节5
HookAdditionalContext::new9–11 ↗
fn new(text: impl Into<String>) -> Self

作用:创建一个新的 HookAdditionalContext,把外部传进来的一段文字收进结构里。别人想把钩子程序给出的补充说明变成统一上下文片段时,会用它。

数据流:进去的是一段可以转换成字符串的内容,比如 String 或字符串字面量 → 它调用 into 把内容变成真正的 String,并存到 text 字段里 → 出来的是一个新的 HookAdditionalContext,里面带着这段补充文字。

调用关系:这是这个小结构的入口式构造函数。上游代码先用它把零散文本包装起来,之后统一通过 ContextualUserFragment 接口里的 role、markers 和 body 读取这段内容。

调用图:外部调用 1 个(into)。

HookAdditionalContext::role15–17 ↗
fn role(&self) -> &'static str

作用:告诉外部:这段补充内容应该用什么身份出现。这里固定返回 developer,也就是开发者说明这一层的内容。

数据流:进去的是这个 HookAdditionalContext 本身,但它不需要读取 text → 它直接给出固定角色名 → 出来的是字符串 developer,不会改动任何数据。

调用关系:当统一上下文组装流程需要知道每段内容的身份时,会调用它。它和 body 配合使用:role 说明“这是谁说的”,body 提供“说了什么”。

HookAdditionalContext::markers19–21 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:告诉外部这段内容前后要不要加特殊标记。这里返回空标记,意思是原样放进去,不额外包一层标签。

数据流:进去的是这个 HookAdditionalContext 本身 → 它把工作交给 type_markers,拿到这一类上下文统一使用的前后标记 → 出来的是两个空字符串,表示没有开始标记、也没有结束标记。

调用关系:上下文组装代码在拼接内容时会问每个片段要标记。这个函数不自己决定细节,而是转去调用 HookAdditionalContext::type_markers,保证实例方法和类型级规则保持一致。

调用图:外部调用 1 个(type_markers)。

HookAdditionalContext::type_markers23–25 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给 HookAdditionalContext 这一类内容定义统一的前后标记。这里明确规定:不使用任何标记。

数据流:没有输入数据需要读取 → 它直接返回一对固定值 → 出来的是两个空字符串,表示这类内容不会被额外的开始符号或结束符号包住。

调用关系:HookAdditionalContext::markers 会调用它。这样以后如果这类上下文需要统一加标签,只要改这里,所有实例拿到的标记都会一致。

HookAdditionalContext::body27–29 ↗
fn body(&self) -> String

作用:取出真正要放进上下文里的正文,也就是钩子程序提供的那段额外文字。

数据流:进去的是这个 HookAdditionalContext,它读取内部的 text 字段 → 它复制一份字符串,避免把原来的内容拿走 → 出来的是正文 String,原对象仍然保留原来的文字。

调用关系:上下文组装流程在最终拼接内容时会调用它。它通常和 role、markers 一起工作:role 定身份,markers 定包装方式,body 给实际文字。

core/src/context/model_switch_instructions.rs源码 ↗
domain_logicrequest handling

当用户从一个模型换到另一个模型时,新模型可能不知道之前模型有哪些特殊要求或延续方式。这个文件就是为了解决这个断档问题。它定义了一个小结构体 ModelSwitchInstructions,用来保存一段“切换模型后的说明”。然后它实现了 ContextualUserFragment,也就是一种“上下文片段”的统一约定:这个片段应该用什么身份说、用什么标记包起来、正文长什么样。这里它把角色设成 developer,可以理解为比普通用户消息更像系统给模型的工作说明;同时用 <model_switch> 和 </model_switch> 把内容圈起来,像给文件贴标签,方便后续识别。这段内容最终会告诉模型:用户之前用的是别的模型,请按下面的说明继续对话。

函数细节5
ModelSwitchInstructions::new9–13 ↗
fn new(model_instructions: impl Into<String>) -> Self

作用:创建一份模型切换说明,把外面传进来的文字保存起来。有人准备把“换模型后的继续规则”放进上下文时,会先用它做出这个对象。

数据流:输入是一段可以变成字符串的说明文字 → 函数把它转成 String 并存进 model_instructions 字段 → 输出一个新的 ModelSwitchInstructions 对象,不改动其他东西。

调用关系:它会被 build_model_instructions_update_item 调用,通常是在系统需要生成一条“模型切换说明更新项”时使用。它自己只做包装,文字转换这一步交给标准的 into 方法完成。

调用图:被 1 处调用(build_model_instructions_update_item);外部调用 1 个(into)。

ModelSwitchInstructions::role17–19 ↗
fn role(&self) -> &'static str

作用:告诉外层系统,这段提示应该以什么身份放进上下文。这里固定返回 developer,意思是把它当成开发者给模型的指导,而不是普通用户闲聊。

数据流:不需要额外输入,只读取当前对象但其实不看里面的说明内容 → 直接返回固定字符串 developer → 不修改任何数据。

调用关系:当外层系统把这个上下文片段真正拼进对话时,会询问它的 role。这个函数就是回答“这段话用哪个身份发给模型”。

ModelSwitchInstructions::markers21–23 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:告诉外层系统,这段内容前后应该用什么标记包住。这样别的代码或模型一眼就能看出:这是一段模型切换说明。

数据流:输入是当前对象 → 函数不读取对象里的具体说明,而是调用 type_markers 取得固定的一对开始和结束标签 → 输出这两个标签,不修改对象。

调用关系:它是 ContextualUserFragment 约定的一部分。外层拼接上下文时会问“这段内容用什么边界标记”,它把这个问题转交给 type_markers,避免同一组标签写两遍。

调用图:外部调用 1 个(type_markers)。

ModelSwitchInstructions::type_markers25–27 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出 ModelSwitchInstructions 这种片段专用的开始和结束标签。这里固定是 <model_switch> 和 </model_switch>。

数据流:没有输入,也不读取对象状态 → 直接返回一对固定字符串 → 不产生副作用,也不改动任何东西。

调用关系:它被 markers 使用,作为统一的标签来源。这样如果以后要改模型切换片段的标签,只需要改这里,调用 markers 的地方不用跟着改。

ModelSwitchInstructions::body29–34 ↗
fn body(&self) -> String

作用:生成真正要给模型看的正文。它会把保存的说明文字放进一段更完整的提示里,明确告诉模型“用户之前用的是另一个模型”。

数据流:输入是当前对象,主要读取里面的 model_instructions → 用固定提示语加上这段说明文字,拼成一整段文本 → 返回拼好的 String,不修改原对象。

调用关系:当外层系统需要把这个片段写进对话上下文时,会调用它拿正文。它用 format! 宏完成字符串拼接,和 role、markers 一起组成完整的上下文片段。

调用图:外部调用 1 个(format!)。

core/src/context/network_rule_saved.rs源码 ↗
data_modelcross-cutting

这个文件很小,但作用很明确:当网络访问策略被修改后,它负责生成一条“记录已保存”的上下文消息。可以把它想成一张小便签,写着“这个主机已经被允许”或“这个主机已经被拒绝”,然后塞回系统的上下文里,方便后面的执行逻辑参考。核心类型是 NetworkRuleSaved,里面只记两件事:动作,也就是允许还是拒绝;以及目标主机名。它实现了 ContextualUserFragment,这个接口可以理解为“能被放进上下文的一小段用户相关信息”。它声明自己的角色是 developer,不额外加开始/结束标记,并在 body 里把规则翻译成人能读懂的一句话,例如保存到了 allowlist(允许名单)或 denylist(拒绝名单)。

函数细节5
NetworkRuleSaved::new12–17 ↗
fn new(amendment: &NetworkPolicyAmendment) -> Self

作用:用一次网络策略修改记录,创建一个 NetworkRuleSaved 小对象。别人不需要手动拆字段,只要把修改记录交给它,它就会记住“允许/拒绝”和“哪个主机”。

数据流:输入是一条 NetworkPolicyAmendment,也就是网络策略的一次改动说明;函数从里面读取 action 和 host,把 action 原样保存,把 host 复制一份;输出是一个新的 NetworkRuleSaved,不改动原来的修改记录。

调用关系:当 record_network_policy_amendment_message 需要把网络策略改动写进上下文时,会调用这个函数先做成标准的小便签对象。后面的显示文字则由这个对象自己的 body 来生成。

调用图:被 1 处调用(record_network_policy_amendment_message)。

NetworkRuleSaved::role21–23 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段信息应该用什么身份出现。这里固定说它属于 developer,也就是系统内部给模型看的开发者级提示。

数据流:它不读取外部输入,也不改变对象;进去的是当前 NetworkRuleSaved;出来的是固定字符串 developer。

调用关系:这是 ContextualUserFragment 接口的一部分。上下文系统在整理这段信息时会问它“你是什么角色”,它就返回固定答案,方便上层按角色组织消息。

NetworkRuleSaved::markers25–27 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:告诉上下文系统,这段内容前后要不要包一层特殊标记。这里返回空标记,意思是直接放正文,不额外加标签。

数据流:输入是当前对象;它把工作交给 type_markers;输出是一对字符串,表示开始标记和结束标记,这里两者都是空字符串。

调用关系:这是 ContextualUserFragment 接口要求提供的函数。它不自己决定具体标记,而是转去调用 type_markers,这样同一种类型的标记规则集中在一个地方。

调用图:外部调用 1 个(type_markers)。

NetworkRuleSaved::type_markers29–31 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给 NetworkRuleSaved 这种上下文片段定义统一的前后标记。这里明确表示不需要任何标记。

数据流:它没有输入,也不读取对象状态;直接输出一对空字符串,表示正文前后都不加额外文字。

调用关系:markers 会调用它来取得标记规则。因为这是类型级别的规则,所以所有 NetworkRuleSaved 片段都会使用同样的空标记。

NetworkRuleSaved::body33–42 ↗
fn body(&self) -> String

作用:把保存下来的网络规则变成一句人能看懂的文字。它会根据动作写成 Allowed 或 Denied,并说明规则进了允许名单还是拒绝名单。

数据流:输入是当前 NetworkRuleSaved,里面有 action 和 host;函数先判断 action 是允许还是拒绝,再选出对应的英文动作和名单名;最后用格式化文本拼出一句完整说明并返回,不修改对象本身。

调用关系:这是上下文系统真正拿来展示或传递内容的地方。role 和 markers 说明这段话怎么放,body 则生成这段话本身;它内部只调用格式化工具来拼字符串。

调用图:外部调用 1 个(format!)。

core/src/context/token_budget_context.rs源码 ↗
data_modelcontext assembly

大模型每次对话能看的文字量是有限的,这个上限常叫“上下文窗口”。token 可以粗略理解成模型眼里的文字小颗粒,不完全等于字数。这个文件做的事很直接:把剩余 token 数、线程编号、窗口编号这些信息,整理成一段带标签的开发者提示。它定义了两种版本:TokenBudgetContext 是完整版本,会告诉模型线程 id、当前窗口 id、还剩多少 token;TokenBudgetRemainingContext 是简化版本,只说还剩多少,或者说“不知道还剩多少”。两者都实现了 ContextualUserFragment,也就是“能被放进上下文的一段用户/系统补充信息”的统一接口。它们都会用同一对 <token_budget> 标签包起来,像给文件夹贴标签,方便后续识别这段内容是 token 预算说明。

函数细节11
TokenBudgetContext::new12–18 ↗
fn new(thread_id: ThreadId, window_id: u64, tokens_left: i64) -> Self

作用:创建一份完整的 token 预算说明材料。调用者把线程编号、窗口编号和剩余 token 数交进来,它就打包成一个 TokenBudgetContext。

数据流:进去的是 thread_id、window_id 和 tokens_left → 函数不做计算,只把这三样原样放进结构体字段里 → 出来的是一个可用于生成上下文提示的 TokenBudgetContext。

调用关系:它会在 build_initial_context 里被使用,也就是一开始拼装对话上下文时,先把当前 token 预算做成一个标准片段,后面再由这个片段生成给模型看的文字。

调用图:被 1 处调用(build_initial_context)。

TokenBudgetContext::role22–24 ↗
fn role(&self) -> &'static str

作用:告诉外层系统,这段 token 预算提示应该以什么身份放进对话里。这里固定返回 developer,意思是作为“开发者提示”给模型看。

数据流:进去的是这个 TokenBudgetContext 自身 → 函数不读取具体 token 数,也不修改任何东西 → 出来的是固定字符串 developer。

调用关系:它是 ContextualUserFragment 接口的一部分。外层在收集上下文片段时,会问每个片段该用什么角色发送,这个函数就负责回答。

TokenBudgetContext::markers26–28 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住这段内容的开始和结束标签。这样外层系统和模型都能看出:中间这段是在说 token 预算。

数据流:进去的是这个 TokenBudgetContext 自身 → 它把工作转交给 type_markers → 出来的是一对固定标记:开头的 <token_budget> 和结尾的 </token_budget>。

调用关系:它是 ContextualUserFragment 接口要求的函数。需要真正包裹正文时,外层会调用它;它内部复用 type_markers,避免同样的标签写两遍。

调用图:外部调用 1 个(type_markers)。

TokenBudgetContext::type_markers30–32 ↗
fn type_markers() -> (&'static str, &'static str)

作用:提供 TokenBudgetContext 这种片段统一使用的标签。这个函数不依赖某个具体对象,只说明这种类型的内容该怎么标记。

数据流:没有业务输入 → 直接返回固定的开始标签和结束标签 → 不改动任何状态。

调用关系:TokenBudgetContext::markers 会调用它。它相当于这类 token 预算片段的“标签模板”,保证所有同类片段用同一套包装。

TokenBudgetContext::body34–41 ↗
fn body(&self) -> String

作用:把完整的 token 预算信息写成模型能读懂的几行英文说明。它会说明线程 id、当前上下文窗口 id,以及还剩多少 token。

数据流:进去的是 TokenBudgetContext 里保存的 thread_id、window_id、tokens_left → 函数用格式化字符串把这些值拼成一段文本 → 出来的是这段正文字符串,结构体本身不变。

调用关系:它是 ContextualUserFragment 接口的一部分。外层拼上下文时,会先用 markers 拿标签,再用 body 拿正文,最后形成一段完整的 token_budget 提示。

调用图:外部调用 1 个(format!)。

TokenBudgetRemainingContext::new50–54 ↗
fn new(tokens_left: i64) -> Self

作用:创建一份简化版的 token 剩余量说明。它只关心还剩多少 token,不带线程编号和窗口编号。

数据流:进去的是 tokens_left → 函数把这个数字放进 Some,也就是“有明确数值”的状态 → 出来的是一个 TokenBudgetRemainingContext。

调用关系:它会被 maybe_record_token_budget_remaining_context 和 fragment 调用。也就是说,当系统确实知道剩余 token 数时,会用它做出一个简短提示片段。

调用图:被 2 处调用(maybe_record_token_budget_remaining_context, fragment)。

TokenBudgetRemainingContext::unknown56–58 ↗
fn unknown() -> Self

作用:创建一份“剩余 token 数未知”的提示。这个函数用于系统拿不到准确预算时,仍然能明确告诉模型:现在不知道还剩多少。

数据流:没有输入 → 函数把 tokens_left 设成 None,也就是“没有明确数值” → 出来的是一个表示未知状态的 TokenBudgetRemainingContext。

调用关系:它会被 fragment 调用。通常是在需要生成 token 预算片段、但没有具体数字可用时,用这个函数走备用路线。

调用图:被 1 处调用(fragment)。

TokenBudgetRemainingContext::role62–64 ↗
fn role(&self) -> &'static str

作用:告诉外层系统,简化版 token 预算提示也应该作为 developer 角色发送给模型。

数据流:进去的是这个 TokenBudgetRemainingContext 自身 → 不看里面有没有具体数字 → 出来的是固定字符串 developer。

调用关系:它是 ContextualUserFragment 接口的一部分。外层统一处理各种上下文片段时,会调用它来决定这段提示放在哪个角色下。

TokenBudgetRemainingContext::markers66–68 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回简化版 token 预算提示使用的包裹标签。它和完整版本使用同一套 <token_budget> 标签。

数据流:进去的是这个 TokenBudgetRemainingContext 自身 → 它调用 type_markers 取得固定标签 → 出来的是开始标签和结束标签。

调用关系:它是 ContextualUserFragment 接口要求的函数。外层在组装最终上下文时,用它给 body 生成的正文加上边界。

调用图:外部调用 1 个(type_markers)。

TokenBudgetRemainingContext::type_markers70–72 ↗
fn type_markers() -> (&'static str, &'static str)

作用:提供 TokenBudgetRemainingContext 这种片段统一使用的标签。标签固定表示:这里面是 token 预算相关信息。

数据流:没有业务输入 → 直接返回固定的一对字符串标记 → 不修改任何东西。

调用关系:TokenBudgetRemainingContext::markers 会调用它。它保证简化版片段和完整版本一样,都被识别为 token_budget 内容。

TokenBudgetRemainingContext::body74–81 ↗
fn body(&self) -> String

作用:生成简化版 token 预算正文。如果知道剩余数量,就写出具体数字;如果不知道,就写明 unknown。

数据流:进去的是 tokens_left 这个可有可无的值 → 如果是 Some,就把数字填进句子;如果是 None,就生成“剩余 token 未知”的句子 → 出来的是给模型看的正文字符串,原对象不变。

调用关系:它是 ContextualUserFragment 接口的一部分。外层生成最终提示时会调用它;它内部只用格式化字符串来组织文字,不再把工作交给别的项目函数。

调用图:外部调用 1 个(format!)。

core/src/context/turn_aborted.rs源码 ↗
data_modelcross-cutting

这个文件定义了一个小结构叫 TurnAborted,意思是“一轮对话中途停止了”。可以把它想成贴在病历上的醒目便签:提醒后面接手的人,前面有操作没正常收尾,可能已经做了一半。它保存一段 guidance,也就是给模型看的说明文字。文件里还准备了两段默认提示,一段偏给用户上下文用,一段偏给开发者上下文用。TurnAborted 实现了 ContextualUserFragment,这可以理解成“能被放进对话上下文的一小块用户信息”的统一格式。它会告诉系统:这块信息的角色是 user,用什么开始和结束标记包起来,以及正文应该长什么样。最终效果是,系统可以用固定标签 <turn_aborted>...</turn_aborted> 包住这段提醒,让后续处理流程清楚识别:这里不是普通聊天内容,而是一次中断事件的记录。

函数细节5
TurnAborted::new12–16 ↗
fn new(guidance: impl Into<String>) -> Self

作用:创建一个 TurnAborted 记录,把传进来的提示文字保存起来。有人在发现上一轮被打断时,会用它做出一张“中断提醒卡片”。

数据流:进去的是一段可以转成字符串的 guidance 提示文字 → 函数调用 into 把它变成真正的 String → 出来的是一个新的 TurnAborted,里面装着这段提示;它不改动别的地方。

调用关系:在调用图里,它由 interrupted_turn_history_marker 使用。也就是说,当系统要给历史上下文加上“上一轮被打断”的标记时,会先调用这个函数造出对应的 TurnAborted 对象。

调用图:被 1 处调用(interrupted_turn_history_marker);外部调用 1 个(into)。

TurnAborted::role20–22 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段中断提示应该按 user,也就是“用户侧内容”的身份放进去。这样后续模型能按正确角色理解它。

数据流:进去的是这个 TurnAborted 本身,但函数不需要读取 guidance 内容 → 它直接给出固定文本 user → 出来的是角色名,不修改任何数据。

调用关系:它是 ContextualUserFragment 这套统一接口的一部分。系统在把这类片段拼进对话上下文时,会通过这个接口询问“这段话算谁说的”,这里的回答固定是用户。

TurnAborted::markers24–26 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:给这段中断提示提供外层标签,也就是告诉系统用什么文字标出开始和结束。这样后面的人或模型一眼能看出:这是中断事件,不是普通聊天。

数据流:进去的是这个 TurnAborted 本身 → 它不看具体 guidance,而是转去取这一类内容固定使用的标签 → 出来是一对字符串:开始标签和结束标签。

调用关系:它在实现 ContextualUserFragment 接口时使用,并把具体标签的选择交给 type_markers。这样实例方法 markers 和类型级方法 type_markers 保持一致,不会一个地方改了另一个地方忘记改。

调用图:外部调用 1 个(type_markers)。

TurnAborted::type_markers28–30 ↗
fn type_markers() -> (&'static str, &'static str)

作用:返回 TurnAborted 这种内容固定使用的包裹标签:<turn_aborted> 和 </turn_aborted>。这些标签像文件夹封面,说明里面装的是“上一轮被打断”的提示。

数据流:它不需要输入任何对象或额外信息 → 直接返回一对固定字符串 → 不产生副作用,也不修改数据。

调用关系:markers 会依赖它来拿标签。这样无论系统是从某个具体 TurnAborted 对象询问标签,还是只想知道 TurnAborted 这种类型的标签,得到的都是同一套标记。

TurnAborted::body32–34 ↗
fn body(&self) -> String

作用:把保存的 guidance 提示文字整理成正文格式,供系统放进上下文里。它会在前后加换行,让内容被标签包住时更清楚。

数据流:进去的是这个 TurnAborted,主要读取里面的 guidance 字符串 → 它用 format! 拼出一个前后带换行的新字符串 → 出来的是可直接放进上下文的正文;原来的 guidance 不会被改动。

调用关系:它是 ContextualUserFragment 接口的一部分。上下文组装流程需要真正写出这段中断说明时,会通过这个方法拿到正文;外层的角色和标签则分别由 role、markers 提供。

调用图:外部调用 1 个(format!)。

core/src/context/user_instructions.rs源码 ↗
data_modelrequest handling

这个文件定义了一个小盒子:UserInstructions。它装两样东西:这些说明属于哪个目录,以及说明正文是什么。系统在和模型对话时,不能只把用户当前的问题发过去,还要把项目里的长期规则一起带上,比如代码风格、测试要求、注意事项。这个文件就负责把这些规则包装成统一格式。它会声明这段内容在对话里扮演“user”角色,也就是像用户补充说明一样出现;还会给内容加上固定的开头和结尾标记,方便后续系统识别“这里是一段 AGENTS.md 指令”。如果有目录名,它会写明“这是给哪个目录用的”;如果没有,就只放指令正文。可以把它理解成给一张便签贴上标题、来源和边框,避免模型把项目规则和普通聊天内容混在一起。

函数细节4
UserInstructions::role10–12 ↗
fn role(&self) -> &'static str

作用:这个函数说明这段指令在对话里应该用什么身份出现。这里固定返回“user”,意思是把它当成用户给出的补充要求。

数据流:进去的是当前这份 UserInstructions,但它不需要读取里面的目录或正文 → 它直接给出固定身份 → 出来的是字符串“user”,不会改动任何数据。

调用关系:当系统把不同上下文片段拼成一段完整对话时,会询问每个片段的角色。这个函数就是告诉上层拼装流程:这段 AGENTS.md 指令要按用户消息来放。

UserInstructions::markers14–16 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:这个函数给这段指令提供一对边界标记,也就是“从哪里开始、到哪里结束”。这样系统之后能认出这块内容是项目说明,不是普通文字。

数据流:进去的是当前 UserInstructions → 它不自己写标记,而是转去调用 UserInstructions::type_markers 取统一标记 → 出来的是一对固定字符串:开头标记和结尾标记,数据本身不被修改。

调用关系:上层在包装上下文片段时会调用它来拿边界。它把实际工作交给 UserInstructions::type_markers,这样实例方法和类型级别方法使用的是同一套标记,不会各写各的。

调用图:外部调用 1 个(type_markers)。

UserInstructions::type_markers18–20 ↗
fn type_markers() -> (&'static str, &'static str)

作用:这个函数定义 UserInstructions 这种内容专用的固定边界标记。它相当于给这类便签规定统一的标题和结束线。

数据流:没有输入数据需要处理 → 它直接返回固定的一对文本标记 → 结果是“# AGENTS.md instructions”和“</INSTRUCTIONS>”,不会读取或修改任何 UserInstructions 实例。

调用关系:UserInstructions::markers 会调用它来取得标记。这样无论系统从实例上问标记,还是从类型本身问标记,得到的都是同一套格式。

UserInstructions::body22–29 ↗
fn body(&self) -> String

作用:这个函数把目录信息和指令正文排版成最终要放进上下文的正文内容。它保证模型看到的是清楚分段、带标签的说明。

数据流:进去的是 UserInstructions 里的 directory 和 text → 如果 directory 有值,就生成类似“ for 某目录”的提示;如果没有,就留空 → 然后用固定格式拼出正文,包含目录提示、空行、<INSTRUCTIONS> 标签和指令文字 → 出来的是一整段字符串,不会改动原对象。

调用关系:当上层要真正发送或组装上下文时,会调用它拿到可展示的内容。它内部使用格式化操作把零散字段拼成一段完整文本,并和 UserInstructions::role、UserInstructions::markers 一起构成这类上下文片段的完整包装方式。

调用图:外部调用 1 个(format!)。

core/src/context/user_shell_command.rs源码 ↗
data_modelcross-cutting

这个文件定义了一个小盒子:UserShellCommand。它专门装一条用户在命令行里跑过的命令,以及这条命令跑完后的结果。里面包括命令文本、退出码、耗时秒数和输出内容。退出码可以理解成程序的“考试分数”:通常 0 表示成功,非 0 表示出了问题。这个盒子还实现了 ContextualUserFragment,也就是“可以放进用户上下文的一段内容”的约定。它会告诉外部:这段内容属于 user 角色,并且用 <user_shell_command> 这类标记包起来。真正生成正文时,它会把命令放进 <command>,把退出码、耗时和输出放进 <result>。这样后续读这段上下文的组件,就像看一张格式固定的报告单,不用猜每一行是什么意思。

函数细节5
UserShellCommand::new14–26 ↗
fn new(
        command: impl Into<String>,
        exit_code: i32,
        duration: Duration,
        output: impl Into<String>,
    ) -> Self

作用:创建一条新的“用户 shell 命令记录”。调用者把命令、退出码、耗时和输出交进来,它把这些内容统一存成 UserShellCommand。

数据流:进去的是命令文字、退出码、Duration(表示一段时间的标准类型)和输出文字。函数会把命令和输出转成 String,把 Duration 转成小数形式的秒数。出来的是一个填好字段的新 UserShellCommand,不会改动别的东西。

调用关系:它通常由 user_shell_command_fragment 在需要把命令执行结果变成上下文片段时调用。它只负责把原始材料装盒,后面真正怎么展示这盒内容,会交给 role、markers、body 这些方法。

调用图:被 1 处调用(user_shell_command_fragment);外部调用 2 个(as_secs_f64, into)。

UserShellCommand::role30–32 ↗
fn role(&self) -> &'static str

作用:说明这段内容在对话上下文里属于谁。这里固定返回 user,意思是这是一段来自用户侧的信息。

数据流:进去的是这个 UserShellCommand 本身,但它不需要读取具体命令或输出。它直接给出字符串 user。它不产生副作用,也不改任何字段。

调用关系:当外部把不同上下文片段组装进最终消息时,会问每个片段的角色是什么。这个方法的作用就是让组装者知道:这段 shell 命令记录应放在用户角色下面。

UserShellCommand::markers34–36 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:给这段内容提供一对外层标记,告诉别人“这里开始和结束的是一条用户 shell 命令记录”。

数据流:进去的是当前对象。它不看对象里的具体数据,而是调用 type_markers 拿到固定的开始标记和结束标记。出来的是两个字符串:开始标签和结束标签。

调用关系:它是 ContextualUserFragment 约定里的一部分。外部包装上下文片段时会用它拿标记;它自己把具体标记来源交给 UserShellCommand::type_markers,避免同一对标记写两遍。

调用图:外部调用 1 个(type_markers)。

UserShellCommand::type_markers38–40 ↗
fn type_markers() -> (&'static str, &'static str)

作用:返回 UserShellCommand 这种内容专用的固定标签。它定义了这类片段在文本里如何被清楚圈出来。

数据流:不需要输入对象状态,也不读取命令结果。它直接返回 <user_shell_command> 和 </user_shell_command> 这一对固定字符串。

调用关系:UserShellCommand::markers 会调用它来取得标签。它相当于这类上下文片段的“标签说明书”,让所有地方使用同一套边界标记。

UserShellCommand::body42–47 ↗
fn body(&self) -> String

作用:把命令执行记录排版成一段可读、结构清楚的正文。里面会明确分出命令本身和运行结果。

数据流:进去的是当前 UserShellCommand,函数读取 command、exit_code、duration_seconds 和 output。它把这些值填进固定模板里,耗时保留 4 位小数。出来的是一段 String 文本;原对象不被修改。

调用关系:当外部需要把这个片段真正写进上下文消息时,会调用它生成正文。它只负责正文内容;外层角色和边界标签分别由 role 和 markers 提供,三者合起来组成完整的上下文片段。

调用图:外部调用 1 个(format!)。

Realtime 生命周期片段

这些文件定义用于开启、自定义和关闭 realtime 交互模式的标准化提示词片段。

core/src/context/realtime_start_instructions.rs源码 ↗
domain_logicconversation startup

可以把这个文件理解成一张固定的“开场提示卡”。实时对话开始时,系统需要告诉模型一些基本要求,但这段话不能随便混在用户消息里,否则模型可能分不清哪些是规则、哪些是真正的聊天内容。这里定义了 RealtimeStartInstructions,并让它符合 ContextualUserFragment 这个接口。这个接口可以理解为“上下文碎片”的统一格式:要说明自己是什么角色、用什么开始和结束标记包起来、正文是什么。它把角色固定成 developer,也就是给模型看的开发者级说明;把边界标记固定成实时对话的打开和关闭标签;正文则来自 codex_prompts 里的 START_INSTRUCTIONS,并去掉首尾多余空白后再加换行。这样别的代码只要拿到这个碎片,就能稳定地把实时会话启动说明拼进上下文。

函数细节4
RealtimeStartInstructions::role10–12 ↗
fn role(&self) -> &'static str

作用:告诉外部系统,这段开场说明应该以什么身份放进对话里。这里固定返回 developer,意思是它不是普通用户聊天,而是给模型遵守的开发者说明。

数据流:进去的是这个 RealtimeStartInstructions 对象本身,但它不需要读取对象里的任何数据 → 它直接选择固定的角色名 developer → 出来的是一个不会变的文本角色名,不修改任何东西。

调用关系:当上下文组装器需要把这段说明放进对话时,会先问它“你是什么角色”。这个函数只负责回答身份,不再把活儿交给其他函数。

RealtimeStartInstructions::markers14–16 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:告诉外部系统,这段说明前后应该用哪两个标签包起来。标签就像文件夹的封面和封底,用来让模型和系统知道实时对话内容从哪里开始、到哪里结束。

数据流:进去的是这个对象本身 → 它不自己硬写标签,而是转去调用 type_markers 取得这类说明统一使用的开始标签和结束标签 → 出来的是一对固定文本标签,不修改对象状态。

调用关系:上下文组装器在拼接内容时会调用它来拿边界标记。它把具体标签来源交给 RealtimeStartInstructions::type_markers,这样实例方法和类型级方法使用的是同一套标记,不容易写岔。

调用图:外部调用 1 个(type_markers)。

RealtimeStartInstructions::type_markers18–23 ↗
fn type_markers() -> (&'static str, &'static str)

作用:提供 RealtimeStartInstructions 这种碎片固定使用的开始标签和结束标签。有人即使还没有具体对象,也能通过这个函数知道它应该被什么标签包住。

数据流:没有实际输入数据 → 它读取协议里定义好的 REALTIME_CONVERSATION_OPEN_TAG 和 REALTIME_CONVERSATION_CLOSE_TAG → 输出这两个标签组成的一对值,不产生副作用。

调用关系:RealtimeStartInstructions::markers 会调用它来拿标签。它本身站在更底层,负责把协议里约定好的实时会话边界暴露给这个上下文碎片使用。

RealtimeStartInstructions::body25–27 ↗
fn body(&self) -> String

作用:生成真正要放进上下文里的开场说明正文。它把预先写好的 START_INSTRUCTIONS 整理成适合拼接进对话的文本。

数据流:进去的是这个对象本身,但对象里没有可变内容 → 它读取 START_INSTRUCTIONS,去掉首尾多余空白,再用格式化把正文前后各补一个换行 → 出来的是一段新的字符串,不改动原始常量。

调用关系:当上下文组装器需要这块碎片的具体内容时会调用它。它把文字来源交给 codex_prompts 里的 START_INSTRUCTIONS,把字符串包装交给 format! 这个格式化工具完成。

调用图:外部调用 1 个(format!)。

core/src/context/realtime_start_with_instructions.rs源码 ↗
data_modelrequest handling

实时对话开始时,系统往往要先告诉模型一些规则或背景,比如“接下来按这种方式回答”。这个文件就像给这段说明装了一个信封:信封上写明它来自 developer(开发者一侧的指令,不是普通用户发言),信封开头和结尾还贴上固定标签,方便后面的协议识别“这一段是实时会话说明”。核心结构是 RealtimeStartWithInstructions,它只保存一段 instructions 文本。创建时把外部传来的文字收进来;真正输出时,它会返回角色、起止标记,以及带前后换行的正文。这样做的好处是格式集中在一个地方,别的代码不用每次手写标签,减少漏写、写错、混淆消息类型的风险。

函数细节5
RealtimeStartWithInstructions::new11–15 ↗
fn new(instructions: impl Into<String>) -> Self

作用:创建一个新的实时会话说明片段,把传进来的说明文字保存起来。别人要把启动说明放进上下文时,会先用它做出这个小对象。

数据流:进去的是一段可以变成字符串的说明文字 → 函数把它转换成真正的 String 并存到 instructions 字段里 → 出来的是一个 RealtimeStartWithInstructions 对象,之后可以被当作上下文片段使用。

调用关系:它由 build_realtime_update_item 在组装实时更新内容时调用。它自己只做文字收纳,转换字符串这件事交给标准的 into 来完成,后续格式化输出则由这个对象实现的其他方法负责。

调用图:被 1 处调用(build_realtime_update_item);外部调用 1 个(into)。

RealtimeStartWithInstructions::role19–21 ↗
fn role(&self) -> &'static str

作用:告诉系统这段内容应该用什么身份发送。这里固定返回 developer,意思是这是开发者侧的指令,而不是用户随口说的话。

数据流:进去的是这个说明片段本身 → 函数不读取复杂数据,只返回固定的角色名 → 出来的是字符串 developer,供上下文组装时标记消息身份。

调用关系:它是 ContextualUserFragment 这套接口的一部分。凡是外部代码把这个对象当作上下文片段处理时,都可以通过这个方法知道该用什么角色包装这段说明。

RealtimeStartWithInstructions::markers23–25 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住这段实时会话说明的开头标签和结尾标签。标签像文件夹的封面和封底,用来告诉接收方这段内容的范围。

数据流:进去的是这个说明片段本身 → 函数不单独计算标签,而是调用 type_markers 取得这个类型统一使用的标签 → 出来的是一对固定字符串:开始标记和结束标记。

调用关系:它也是 ContextualUserFragment 接口的一部分。外部在渲染上下文片段时会问它“该用什么标签包起来”,它再把这件事转给 type_markers,保证实例方法和类型级别的标签保持一致。

调用图:外部调用 1 个(type_markers)。

RealtimeStartWithInstructions::type_markers27–32 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出 RealtimeStartWithInstructions 这个类型专用的固定边界标签。这样不用创建对象,也能知道这种片段应该用哪对标签包住。

数据流:没有实际输入数据 → 函数直接取协议里定义好的实时会话开始标签和结束标签 → 出来的是这两个标签组成的一对值。

调用关系:markers 会调用它来拿标签。它依赖 codex_protocol 里统一定义的协议常量,这样整个项目对实时会话边界的写法都一致。

RealtimeStartWithInstructions::body34–36 ↗
fn body(&self) -> String

作用:生成这段说明真正要显示或发送的正文。它会把保存的 instructions 前后各加一个换行,让内容在拼接到大上下文里时更清楚。

数据流:进去的是当前对象里保存的 instructions 文本 → 函数用格式化工具把它变成“换行 + 说明 + 换行”的样子 → 出来的是一段新的字符串,不会改动原来的 instructions。

调用关系:它是 ContextualUserFragment 接口要求提供的正文部分。外部在把上下文片段拼成完整消息时,会用 role 决定身份、用 markers 决定边界、再用 body 取得中间真正的说明内容。

调用图:外部调用 1 个(format!)。

core/src/context/realtime_end_instructions.rs源码 ↗
domain_logicrequest handling / realtime conversation ending

在实时对话里,系统有时需要明确告诉模型:“这轮实时会话要结束,请按指定方式处理。”这个文件就像一张固定格式的通知单。通知单里有两部分:一部分是通用的结束说明,来自 END_INSTRUCTIONS;另一部分是本次结束的具体原因 reason。RealtimeEndInstructions 保存这个原因,并实现 ContextualUserFragment,也就是“可以放进上下文的一小段内容”。它会声明自己的身份是 developer,意思是这段话像开发者指令一样给模型看;还会用固定的开始和结束标记把内容包起来,避免模型或系统把它和普通用户话语混在一起。最后 body 会把结束说明和原因拼成真正要发送的文字。没有这个文件,实时会话结束时的提示就可能不统一,模型也更容易误解当前阶段。

函数细节5
RealtimeEndInstructions::new12–16 ↗
fn new(reason: impl Into<String>) -> Self

作用:创建一条新的实时会话结束说明,并把“为什么结束”的原因存进去。别人需要把结束原因放进上下文时,就会先用它做出这个小对象。

数据流:进去的是一个可以变成字符串的 reason,例如“用户停止了语音输入”。函数把它转换成真正的 String,放到 RealtimeEndInstructions 里面。出来的是一个带着结束原因的新对象,本身不发送消息,只是准备好后面格式化使用。

调用关系:它会被 build_realtime_update_item 调用,通常是在构造实时更新内容的时候用。它只负责把原因装好,后面真正把内容写成提示文本的活儿交给 body 等方法完成。

调用图:被 1 处调用(build_realtime_update_item);外部调用 1 个(into)。

RealtimeEndInstructions::role20–22 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段结束说明应该以什么身份出现。这里固定返回 developer,表示它是系统开发者给模型的指导,不是普通用户随口说的话。

数据流:它不需要输入,也不读取外部数据。调用后直接返回字符串 developer。它不改动对象,只提供一个标签,让后续组装上下文的人知道该把这段内容放在哪种角色下。

调用关系:当上下文系统处理 ContextualUserFragment 这类片段时,会询问它的 role。这个方法就是给外层流程一个明确身份,避免结束指令被当成普通聊天内容。

RealtimeEndInstructions::markers24–26 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回包住这段实时会话结束说明的起止标记。标记的作用像信封上的边框,告诉读者:从这里开始到那里结束,是一块特殊内容。

数据流:它不接收额外输入,而是调用 type_markers 取得固定的一对标记。输出是一组开始标记和结束标记。它不会改变对象里的 reason。

调用关系:上下文系统需要把片段放进最终提示时,会用这个方法拿到边界标记。它自己不决定标记内容,而是转交给 type_markers,保证实例方法和类型级方法使用同一套标记。

调用图:外部调用 1 个(type_markers)。

RealtimeEndInstructions::type_markers28–33 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出这种片段专用的固定边界标记:实时对话开始标记和实时对话结束标记。这样系统一看标记,就知道这块内容属于实时对话结束相关信息。

数据流:它不需要输入,也不看对象状态。它直接返回两个常量:REALTIME_CONVERSATION_OPEN_TAG 和 REALTIME_CONVERSATION_CLOSE_TAG。没有副作用,只是提供标准答案。

调用关系:markers 会调用它来拿标记。把标记集中放在这里,可以让所有 RealtimeEndInstructions 片段都使用同一套边界,减少格式不一致的问题。

RealtimeEndInstructions::body35–37 ↗
fn body(&self) -> String

作用:生成真正要给模型看的正文。正文包含通用结束指令,以及本次结束的具体原因。

数据流:它读取对象里保存的 reason,并读取固定的 END_INSTRUCTIONS。然后去掉 END_INSTRUCTIONS 前后的多余空白,把它和 Reason: 加原因拼成一段文本。输出是一个新的字符串;对象本身不被修改。

调用关系:当外层上下文系统准备把这个片段写入最终消息时,会调用 body 拿正文。它依赖 new 之前存好的 reason,也配合 role 和 markers,让这段结束说明既有内容,也有身份和边界。

调用图:外部调用 1 个(format!)。

通知和旧版兼容

这些片段涵盖实时 subagent 状态信号,以及为存储会话兼容性保留的旧警告消息识别器。

core/src/context/subagent_notification.rs源码 ↗
domain_logiccross-cutting

可以把它想成一张递给主代理的小纸条:纸条上写着“哪个子代理”和“它现在的状态”。这里的 SubagentNotification 保存两样东西:子代理的引用路径 agent_reference,以及状态 status。它还实现了 ContextualUserFragment,也就是“能放进用户上下文里的片段”这个约定。这个约定要求它说明自己看起来像谁说的话、用什么开始和结束标记包起来、正文长什么样。正文会被写成 JSON(一种常见的结构化文本格式,方便机器稳定读取),里面包含 agent_path 和 status。这样后续拼接上下文时,系统能清楚地区分:这不是普通用户闲聊,而是一条子代理状态通知。

函数细节5
SubagentNotification::new12–17 ↗
fn new(agent_reference: impl Into<String>, status: AgentStatus) -> Self

作用:创建一条新的子代理通知。调用者只要给出子代理的名字或路径,以及它当前的状态,就能得到一个完整的通知对象。

数据流:进去的是 agent_reference 和 status;函数把 agent_reference 转成真正保存用的字符串,把 status 原样放进去;出来的是一个 SubagentNotification,里面已经装好“哪个子代理”和“什么状态”。

调用关系:当外部需要格式化一条子代理通知消息时,format_subagent_notification_message 会先用它造出通知对象。它自己只做装配,不负责把通知写成最终文本。

调用图:被 1 处调用(format_subagent_notification_message);外部调用 1 个(into)。

SubagentNotification::role21–23 ↗
fn role(&self) -> &'static str

作用:告诉上下文系统,这段通知要伪装成“user”角色的内容。这样它能被放进用户侧上下文里,让模型按用户可见信息来读取。

数据流:进去的是这条通知本身,但函数不读取具体字段;它固定返回字符串 user;不会改动任何数据。

调用关系:上下文拼接器在处理 ContextualUserFragment 这类片段时会问它“你是什么角色”。这个函数给出固定答案,后面的正文和标记再由其他函数提供。

SubagentNotification::markers25–27 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:给这段通知提供开始和结束标签。标签像信封的边框,告诉读者和机器:中间这一块是子代理通知。

数据流:进去的是通知对象本身;函数不看具体字段,而是转去拿这个类型统一规定的标签;出来的是一对字符串:开始标记和结束标记。

调用关系:上下文系统要包裹这段内容时会调用它。它把具体标签的决定交给 SubagentNotification::type_markers,保证实例方法和类型级方法使用同一套标记。

调用图:外部调用 1 个(type_markers)。

SubagentNotification::type_markers29–31 ↗
fn type_markers() -> (&'static str, &'static str)

作用:定义子代理通知专用的固定标签。所有这种通知都会用同一对标签,避免和普通文本混在一起。

数据流:没有输入数据需要处理;它直接返回 <subagent_notification> 和 </subagent_notification> 这两个固定字符串;不修改任何东西。

调用关系:SubagentNotification::markers 会调用它来取得标签。它相当于这个通知类型的“统一信封格式”,让不同地方生成的通知长得一致。

SubagentNotification::body33–41 ↗
fn body(&self) -> String

作用:把通知的核心内容写成机器容易读懂的正文。正文里包含子代理路径和状态,方便模型或后续程序准确理解。

数据流:进去的是通知对象;函数读取 agent_reference 和 status,把它们放进一个 JSON 对象里,再在前后加换行;出来的是一段字符串,不会改动原通知。

调用关系:上下文系统在真正生成要放进对话里的内容时会调用它。它只负责写正文,外层的角色和标签分别由 role、markers 或 type_markers 提供。

调用图:外部调用 1 个(format!)。

core/src/context/legacy_apply_patch_exec_command_warning.rs源码 ↗
domain_logic读取或整理旧会话上下文时

这个文件像一个“旧票据识别章”。以前如果有人用 exec_command 去请求 apply_patch,系统会发一段警告,提醒应该用 apply_patch 工具。后来这个警告不再产生了,但老的对话记录里可能还留着它。为了兼容这些旧记录,这里保留了 LegacyApplyPatchExecCommandWarning 这个片段定义。它实现了 ContextualUserFragment,也就是“上下文里的用户片段”接口:告诉系统这段内容属于 user 角色、有没有包裹标记、如何判断一段文字是不是这个旧警告、以及如果要重新生成正文应该是什么。特别的是,它的标记是空的,正文也是空的;真正有用的是 matches_text,它通过检查文字开头和结尾,认出那条老警告。

函数细节5
LegacyApplyPatchExecCommandWarning::role8–10 ↗
fn role(&self) -> &'static str

作用:告诉系统,这种旧警告在上下文里应该被看作 user,也就是用户侧的消息片段。

数据流:进去的是这个旧警告对象本身 → 它不读取额外数据,只返回固定字符串 → 出来的是 "user",让外层上下文知道该把它归到用户角色下。

调用关系:当通用的上下文片段处理流程需要询问“这段算谁说的”时,会调用它。它不把工作交给别的函数,只提供一个固定答案。

LegacyApplyPatchExecCommandWarning::markers12–14 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回这种片段在文本中使用的起止标记。这里的旧警告没有专门包裹标记,所以实际返回的是两个空字符串。

数据流:进去的是这个旧警告对象本身 → 它调用 type_markers 取得该类型统一的标记 → 出来的是一对空字符串,表示没有明显的开始和结束标签。

调用关系:上下文系统在处理片段边界时会问它要标记。它把这件事交给 LegacyApplyPatchExecCommandWarning::type_markers,这样实例方法和类型方法保持同一个答案。

调用图:外部调用 1 个(type_markers)。

LegacyApplyPatchExecCommandWarning::type_markers16–18 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出这种旧警告类型的固定标记定义。因为这种历史消息没有独立标记,所以返回空的开始和结束标记。

数据流:没有输入对象,也不读取外部状态 → 直接给出这个类型约定好的标记 → 出来的是 ("", ""),也就是开始标记和结束标记都为空。

调用关系:LegacyApplyPatchExecCommandWarning::markers 会调用它来拿统一结果。它本身是一个小型规则来源,避免同样的标记信息写在多个地方。

LegacyApplyPatchExecCommandWarning::matches_text20–24 ↗
fn matches_text(text: &str) -> bool

作用:判断一段文字是不是那条旧版 apply_patch 警告。它的作用是从老会话里认出这种过时提示,方便系统过滤或特殊处理。

数据流:进去的是一段文本 → 它先去掉前后的空白,再检查文本是否以指定的警告开头、并以“请改用 apply_patch 工具”这句话结尾 → 出来的是 true 或 false,表示有没有匹配上这类旧警告。

调用关系:读取历史上下文、判断某段文字属于哪种片段时,外层流程会用它做识别。它不调用项目里的其他函数,只依靠字符串的开头和结尾来做判断。

LegacyApplyPatchExecCommandWarning::body26–28 ↗
fn body(&self) -> String

作用:返回这个片段如果要写回上下文时的正文。这里返回空字符串,因为系统现在不再主动生成这条旧警告。

数据流:进去的是这个旧警告对象本身 → 它创建一个新的空字符串 → 出来的是空正文,不会向上下文添加实际内容。

调用关系:当统一的片段接口要求“给我正文”时会调用它。它只调用标准的字符串创建函数来生成空字符串,表达这个旧片段只用于识别,不用于输出。

调用图:外部调用 1 个(new)。

core/src/context/legacy_model_mismatch_warning.rs源码 ↗
domain_logiccross-cutting

这个文件像一个“旧标签识别器”。以前系统可能会在对话里放入一段风险提示文字,开头是“Warning: Your account was flagged for potentially high-risk cyber activity”。现在这类提示不再新生成了,但用户打开旧会话时,历史记录里可能还残留它。如果系统完全忘了它,就可能把这段旧警告当成用户真的说过的话,影响后续上下文。这里定义的 LegacyModelMismatchWarning 实现了 ContextualUserFragment,也就是一种“上下文片段”的统一接口。它声明自己看起来像 user 角色的内容,但没有包裹标记,也没有正文可输出。最关键的是 matches_text:它只负责判断一段文本是不是这种旧警告。这样系统在清理或过滤旧会话消息时,仍然能认出并跳过它。

函数细节5
LegacyModelMismatchWarning::role8–10 ↗
fn role(&self) -> &'static str

作用:说明这个旧警告在历史上下文里被当成哪种说话角色。这里固定返回 user,意思是它过去看起来像是一条用户侧内容。

数据流:进去的是这个 LegacyModelMismatchWarning 对象本身,但它不读取任何内部数据,因为这个结构本来没有字段。它直接给出固定结果:字符串 "user"。它不会改动任何东西。

调用关系:这是 ContextualUserFragment 这套统一接口的一部分。外层处理上下文片段时会问每个片段“你属于哪个角色”,这个函数就给出答案,方便系统按角色组织或过滤对话内容。

LegacyModelMismatchWarning::markers12–14 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回这个片段正文前后的标记。这里实际没有标记,所以返回空标记,用来表示旧警告不是靠特殊包裹符号识别的。

数据流:进去的是这个对象本身。函数不自己写死结果,而是转交给 type_markers。type_markers 返回一对空字符串,markers 再把这对结果交出去。对象状态不变。

调用关系:它是实例层面的接口方法,外层代码拿到一个具体片段对象时会调用它。它把具体工作交给 LegacyModelMismatchWarning::type_markers,这样“对象方法”和“类型方法”保持同一个答案。

调用图:外部调用 1 个(type_markers)。

LegacyModelMismatchWarning::type_markers16–18 ↗
fn type_markers() -> (&'static str, &'static str)

作用:说明这种旧警告类型使用什么开始标记和结束标记。它返回两个空字符串,意思是这种旧内容没有专门的边界符号。

数据流:没有输入数据需要处理。它直接产出一对字符串:开始标记是空,结束标记也是空。它不读取外部信息,也不改变任何状态。

调用关系:LegacyModelMismatchWarning::markers 会调用它。它也可以被需要按“类型”了解标记规则的代码使用,用来统一说明:识别这个旧警告不能靠标记,只能靠文字内容本身。

LegacyModelMismatchWarning::matches_text20–24 ↗
fn matches_text(text: &str) -> bool

作用:判断一段文本是不是那种已经废弃的旧风险警告。有人会用它来过滤老会话,防止这段系统遗留提示混进正常对话。

数据流:进去的是一段文本。函数先去掉文本前后的空白,再检查它是不是以固定句子 "Warning: Your account was flagged for potentially high-risk cyber activity" 开头。出来的是 true 或 false:true 表示匹配这类旧警告,false 表示不是。它不改文本本身。

调用关系:这是这个文件最关键的判断点。外层上下文清理或加载历史消息时,会把消息文本交给这类 matches_text 方法检查;如果匹配,就能把它识别为旧系统警告,而不是普通用户输入。

LegacyModelMismatchWarning::body26–28 ↗
fn body(&self) -> String

作用:给出这个片段真正要输出的正文。这里返回空字符串,因为这种旧警告现在不应该再被重新生成或插回上下文。

数据流:进去的是这个对象本身,但函数不读取任何字段。它创建一个新的空字符串并返回。结果是没有正文内容,同时不修改任何状态。

调用关系:这是 ContextualUserFragment 接口要求的输出方法。外层如果尝试把片段变回文本,会调用它;它返回空内容,配合这个文件的用途:只识别旧消息,不再生产旧警告。

调用图:外部调用 1 个(new)。

core/src/context/legacy_unified_exec_process_limit_warning.rs源码 ↗
domain_logiccontext load / old session filtering

这个文件像一个“旧标签识别器”。以前的会话里可能保存过一段警告文字,开头是“Warning: The maximum number of unified exec processes...”。新版本虽然不再发这种警告,但如果读旧记录时不认识它,就可能把这段过时提示当成正常用户内容,影响后续上下文。这里定义了 LegacyUnifiedExecProcessLimitWarning,并让它实现 ContextualUserFragment,也就是“上下文里的用户片段”这套接口。它告诉系统:这类片段看起来属于 user 角色;没有特殊包围标记;判断时只看文字开头是否符合旧警告;真正输出正文时返回空字符串。简单说,它的价值不在于生成内容,而在于让系统能认出并忽略一类历史遗留噪音。

函数细节5
LegacyUnifiedExecProcessLimitWarning::role8–10 ↗
fn role(&self) -> &'static str

作用:告诉系统这个片段在对话里算作“user”,也就是用户一侧的内容。这样旧警告在格式上能被放进原来的上下文分类体系里。

数据流:进去的是这个警告片段本身 → 函数不读取额外数据,也不做复杂判断 → 出来固定字符串 "user",表示它的角色。

调用关系:当系统按 ContextualUserFragment 这套接口处理上下文片段时,会询问每个片段的角色。这个函数只负责回答角色,不把工作交给别的函数。

LegacyUnifiedExecProcessLimitWarning::markers12–14 ↗
fn markers(&self) -> (&'static str, &'static str)

作用:返回这个片段的起止标记。这里标记是空的,意思是旧警告文本本身没有专门的包装符号。

数据流:进去的是这个警告片段本身 → 它转而调用 type_markers 取得该类型统一使用的标记 → 出来是一对空字符串,表示没有开始标记和结束标记。

调用关系:系统需要知道片段是否有固定外壳时会调用它。它把具体答案交给 LegacyUnifiedExecProcessLimitWarning::type_markers,避免同一份标记规则写两遍。

调用图:外部调用 1 个(type_markers)。

LegacyUnifiedExecProcessLimitWarning::type_markers16–18 ↗
fn type_markers() -> (&'static str, &'static str)

作用:给出这种旧警告片段的类型级标记规则。结果是没有标记,因为识别它靠文字开头,而不是靠特殊标签。

数据流:进去不需要具体片段,也不读取外部信息 → 直接给出固定规则 → 出来是一对空字符串,分别代表开始和结束标记都不存在。

调用关系:LegacyUnifiedExecProcessLimitWarning::markers 会调用它来拿标记。它是这个文件里标记规则的唯一来源,保证实例方法和类型规则保持一致。

LegacyUnifiedExecProcessLimitWarning::matches_text20–24 ↗
fn matches_text(text: &str) -> bool

作用:判断一段文字是不是那种旧的“统一执行进程数量上限”警告。有人读旧会话内容时,会靠它把这类历史噪音识别出来。

数据流:进去是一段文本 → 先去掉首尾空白,再检查它是不是以固定警告句子开头 → 出来是 true 或 false;true 表示这段文字匹配旧警告,false 表示不是。

调用关系:这是过滤旧会话时最关键的判断点。系统拿到历史文本后会用这类 matches_text 函数试着匹配不同片段;这个函数只做本警告的识别,不调用本文件里的其他逻辑。

LegacyUnifiedExecProcessLimitWarning::body26–28 ↗
fn body(&self) -> String

作用:返回这个片段真正要放进上下文的正文。这里故意返回空字符串,因为这种旧警告不应该再作为有用内容保留下来。

数据流:进去的是这个警告片段本身 → 函数创建一个新的空字符串 → 出来是空内容;它不改动片段,也不产生可见文本。

调用关系:当系统需要把片段还原成正文时会调用它。它调用标准的字符串创建能力生成空字符串,配合 matches_text 达到“认得旧消息,但不再输出它”的效果。

调用图:外部调用 1 个(new)。