Guardian review 和协调式审批会话
这一段像系统动手前的“安全检查岗”,属于主流程里的把关环节。mod 是总开关,判断哪些自动批准要先送审,并在连续被拒时拉保险丝。approval_request 把命令、改文件、联网、提权等动作写成统一审批单。prompt 把上下文整理给 Guardian 模型看,再读回结论。review 和 review_session 负责拉起或复用只读审查线程,按保守规则给出批准、拒绝、超时或中止。session/review 则复制必要环境并收紧权限,让这次复查不越界。
守护器基础
定义守护器子系统的共享类型,以及所有后续审查流程都会使用的批准请求模型。
core/src/guardian/mod.rs源码 ↗
这个文件像 guardian 这个子系统的门牌和总开关。guardian 的作用是:当助手想执行一个需要批准的动作时,不一定马上打扰用户,而是先把用户意图、最近对话、工具上下文和计划动作整理成一份简短材料,交给专门的审核会话判断“能不能自动放行”。如果审核超时、失败,或者返回的内容格式不对,系统会保守处理,也就是默认不放行。文件里还定义了审核结果的数据样子,比如风险等级、用户是否授权、最终同意还是拒绝,以及原因。另一个重要部件是 GuardianRejectionCircuitBreaker,也就是“拒绝保险丝”:如果同一轮对话里 guardian 拒绝次数太多,它会触发中断,防止助手在被多次否决后还继续绕着试。这个文件还把子模块里的请求构造、提示词、审核流程、审核会话等能力统一导出,方便系统其他地方调用。
GuardianRejectionCircuitBreaker::clear_turn99–101 ↗
fn clear_turn(&mut self, turn_id: &str)
作用:清掉某一轮对话的 guardian 拒绝记录。通常在这一轮结束或不再需要统计时使用,避免旧账影响后面的判断。
数据流:进去的是一个 turn_id,也就是某一轮对话的编号。函数用这个编号去内部记录表里找到对应条目,然后直接删除。出来没有返回值,但内部状态变了:这一轮之前累计的连续拒绝次数、最近拒绝窗口和是否已触发中断都会消失。
调用关系:它是这个“保险丝”的清理动作。审核流程在开始新阶段或结束某轮时可以调用它,把某个 turn_id 对应的统计归零;它不把工作交给别的函数,只是直接移除记录。
GuardianRejectionCircuitBreaker::record_denial103–120 ↗
fn record_denial(&mut self, turn_id: &str) -> GuardianRejectionCircuitBreakerAction
作用:记录一次 guardian 的拒绝,并判断是否该中断当前这轮对话。它的意义是防止系统在同一轮里被多次拒绝后还继续反复尝试。
数据流:进去的是 turn_id。函数先找到这轮的统计记录;如果没有,就新建一份。然后把“连续拒绝次数”加一,并通过 record_recent_review 把这次“被拒绝”塞进最近审核记录里。接着它数一数最近记录里有多少次拒绝。如果连续拒绝达到上限,或者最近一段审核里的拒绝总数达到上限,而且之前还没触发过中断,它就返回 InterruptTurn,并带上连续拒绝数和最近拒绝数;否则返回 Continue,表示还能继续。
调用关系:它是在一次自动审核结果为“拒绝”之后被调用的核心检查点。它会把维护最近记录的小活交给 GuardianRejectionCircuitBreaker::record_recent_review,然后自己负责判断是否达到“保险丝熔断”的条件。调用方拿到 Continue 就继续流程,拿到 InterruptTurn 就应该停止这一轮或提醒上层处理。
调用图:外部调用 1 个(record_recent_review)。
GuardianRejectionCircuitBreaker::record_non_denial122–126 ↗
fn record_non_denial(&mut self, turn_id: &str)
作用:记录一次 guardian 没有拒绝的审核结果,比如通过或不算拒绝的情况。它会把“连续拒绝”的计数清零,因为拒绝链条已经被打断了。
数据流:进去的是 turn_id。函数找到或创建这一轮的统计记录,把 consecutive_denials 设为 0,然后通过 record_recent_review 把这次“不是拒绝”的结果加入最近审核记录。出来没有返回值,但内部统计被更新:连续拒绝归零,最近审核窗口多了一条非拒绝记录。
调用关系:它是在一次审核没有被拒绝后调用的配套记录动作。它和 record_denial 是一对:一个记录坏消息并可能触发中断,一个记录非坏消息并打断连续拒绝。它同样把维护最近窗口的细节交给 GuardianRejectionCircuitBreaker::record_recent_review。
调用图:外部调用 1 个(record_recent_review)。
GuardianRejectionCircuitBreaker::record_recent_review128–133 ↗
fn record_recent_review(turn: &mut GuardianRejectionCircuitBreakerTurn, denied: bool)
作用:维护某一轮最近一批审核结果的小队列。它只记“这次是不是拒绝”,并保证队列不会无限变长。
数据流:进去的是某一轮的统计记录 turn,以及一个 denied 布尔值;true 表示这次被拒绝,false 表示没有拒绝。函数把这个结果放到队列末尾。如果队列长度超过固定窗口大小,就从最前面删掉最老的一条。出来没有返回值,但 turn 里的 recent_denials 被更新成“只保留最近若干次”的状态。
调用关系:它是 record_denial 和 record_non_denial 共同使用的内部小工具。外部流程不直接靠它判断是否中断;它只负责把最近审核历史整理好,让 record_denial 后续能数出最近拒绝次数。
core/src/guardian/approval_request.rs源码 ↗
这个文件像一张“审批申请单”的模板库。系统里有很多可能有风险的事:执行 shell 命令、用 exec 运行程序、打补丁改文件、访问网络、调用 MCP 工具,或者请求更高权限。它先用 GuardianApprovalRequest 这个枚举把这些事分门别类,每一类都带上必要信息,比如命令内容、工作目录、权限、理由、目标主机等。然后它提供几种“翻译”:一种翻译成 JSON,方便放进提示词或传给别的模块;一种翻译成 GuardianAssessmentAction,让守护者知道要评估什么;一种翻译成 GuardianReviewedAction,用于分析和记录“刚刚评审过什么”。它还会把太长的字符串截短,避免一个巨大命令或补丁把提示词撑爆。一个重要细节是,格式化 JSON 前会递归处理里面的每个字符串,并按键名排序对象,让展示内容更稳定、更容易比较。
serialize_guardian_action172–174 ↗
fn serialize_guardian_action(value: impl Serialize) -> serde_json::Result<Value>
作用:把一个可以序列化的审批动作变成 JSON 值。简单说,就是把 Rust 里的结构体装进一个通用的 JSON 盒子里,后面才能展示、发送或进一步处理。
数据流:进去的是任意一个带有 Serialize 能力的动作对象 → 它调用 serde_json::to_value,把字段和值转换成 serde_json::Value → 出来的是转换成功的 JSON,或者转换失败的错误;它不改动原对象。
调用关系:它是最底层的小转换器。serialize_command_guardian_action 会先把命令包装好再交给它;guardian_approval_request_to_json 在处理网络、MCP、权限申请等类型时也直接用它。
调用图:被 2 处调用(guardian_approval_request_to_json, serialize_command_guardian_action);外部调用 1 个(to_value)。
serialize_command_guardian_action176–194 ↗
fn serialize_command_guardian_action(
tool: &'static str,
command: &[String],
cwd: &Path,
sandbox_permissions: crate::sandboxing::SandboxPermissions,
additional_permissions: Option
作用:专门把“运行命令”这一类审批动作整理成 JSON。因为 shell 和 exec_command 长得很像,所以用这个函数避免到处重复拼同样的字段。
数据流:进去的是工具名、命令参数、当前目录、沙箱权限、额外权限、理由,以及是否使用 tty 这类信息 → 它先把这些信息放进 CommandApprovalAction 这个临时包装里 → 再交给 serialize_guardian_action 转成 JSON 结果。
调用关系:guardian_approval_request_to_json 在遇到 Shell 或 ExecCommand 请求时会调用它。它自己不做复杂判断,只负责把命令类请求打包好,再把序列化工作交给 serialize_guardian_action。
调用图:调用 1 个内部函数(serialize_guardian_action);被 1 处调用(guardian_approval_request_to_json)。
command_assessment_action196–206 ↗
fn command_assessment_action(
source: GuardianCommandSource,
command: &[String],
cwd: &AbsolutePathBuf,
) -> GuardianAssessmentAction
作用:把命令类请求变成守护者评估时使用的简洁动作描述。它会把一串命令参数合成一行人能看懂的命令文本。
数据流:进去的是命令来源、命令参数数组、当前目录 → 它用 shlex_join 把参数安全地拼成一条命令字符串,并复制当前目录 → 出来的是 GuardianAssessmentAction::Command,表示“请评估这个命令”。
调用关系:guardian_assessment_action 在处理 Shell 和 ExecCommand 时会调用它。它相当于命令类审批到“评估动作”之间的专用翻译器。
调用图:调用 1 个内部函数(shlex_join);被 1 处调用(guardian_assessment_action);外部调用 1 个(clone)。
guardian_command_source_tool_name209–214 ↗
fn guardian_command_source_tool_name(source: GuardianCommandSource) -> &'static str
作用:在 Unix 系统上,把命令来源转换成展示用的工具名字。比如来源是普通 shell,就显示成 "shell";来源是统一执行接口,就显示成 "exec_command"。
数据流:进去的是 GuardianCommandSource 这个来源枚举 → 它根据来源做一次匹配 → 出来的是固定的字符串工具名;没有额外副作用。
调用关系:guardian_approval_request_to_json 在处理 Unix 下的 Execve 请求时会用它。它解决的是“内部来源名字”和“JSON 里展示给守护者看的工具名”之间的对应关系。
调用图:被 1 处调用(guardian_approval_request_to_json)。
truncate_guardian_action_value216–251 ↗
fn truncate_guardian_action_value(value: Value) -> (Value, bool)
作用:把审批动作 JSON 里过长的文字截短,防止提示词或展示内容太大。可以把它理解成给申请单做“摘要裁剪”,但保留整体结构。
数据流:进去的是一个 JSON 值 → 如果是字符串,它用 guardian_truncate_text 按最大 token 数裁剪;如果是数组或对象,它会递归处理里面的每一项;对象还会按键名排序 → 出来的是处理后的 JSON 值,以及一个布尔值表示有没有发生截断。
调用关系:format_guardian_action_pretty 会在生成漂亮 JSON 文本前调用它。它不决定审批结果,只保证拿去展示或放进提示词的内容不会无限膨胀。
调用图:调用 1 个内部函数(guardian_truncate_text);被 1 处调用(format_guardian_action_pretty);外部调用 3 个(Array, Object, String)。
guardian_approval_request_to_json259–373 ↗
fn guardian_approval_request_to_json(
action: &GuardianApprovalRequest,
) -> serde_json::Result<Value>
作用:把各种类型的 GuardianApprovalRequest 统一转换成 JSON。这样无论原来是命令、补丁、联网还是工具调用,后面都能用同一种格式展示和处理。
数据流:进去的是一个审批请求引用 → 它按请求类型分别取出相关字段,给 JSON 里加上 tool 名称和必要参数;命令类会交给 serialize_command_guardian_action,其他结构化类型多半交给 serialize_guardian_action,补丁类直接用 json! 生成 → 出来的是 JSON 值或序列化错误。
调用关系:format_guardian_action_pretty 会先调用它拿到原始 JSON。它是本文件里最核心的“申请单转 JSON”入口,内部会按需要调用 guardian_command_source_tool_name、serialize_command_guardian_action 和 serialize_guardian_action。
调用图:调用 3 个内部函数(guardian_command_source_tool_name, serialize_command_guardian_action, serialize_guardian_action);被 1 处调用(format_guardian_action_pretty);外部调用 1 个(json!)。
guardian_assessment_action375–441 ↗
fn guardian_assessment_action(
action: &GuardianApprovalRequest,
) -> GuardianAssessmentAction
作用:把审批请求转换成守护者真正要评估的动作。它会去掉一些审批流程用的外壳信息,只留下判断风险最需要看的内容。
数据流:进去的是一个 GuardianApprovalRequest → 它按类型提取关键字段,比如命令、目录、文件列表、网络目标、MCP 工具名或权限申请内容 → 出来的是 GuardianAssessmentAction;过程中会复制需要保留下来的字符串、路径和权限信息。
调用关系:run_guardian_review 在启动守护者评审时会调用它。命令类请求会进一步交给 command_assessment_action 来生成评估动作,其他类型则在这里直接拼出对应的评估描述。
调用图:调用 1 个内部函数(command_assessment_action);被 1 处调用(run_guardian_review)。
guardian_reviewed_action443–501 ↗
fn guardian_reviewed_action(
request: &GuardianApprovalRequest,
) -> GuardianReviewedAction
作用:把审批请求转换成“已经被评审的动作”记录,主要用于分析、统计或日志。它保留适合记录的摘要信息,而不是完整的大块内容。
数据流:进去的是一个审批请求 → 它按请求类型挑出记录时有价值的字段,比如权限、协议、端口、工具名、连接器信息等 → 出来的是 GuardianReviewedAction;它不会修改请求本身。
调用关系:run_guardian_review 在评审流程中会调用它,用来记录这次守护者看过的是什么。它不负责判断是否批准,只负责把“被评审对象”整理成分析系统认识的形状。
调用图:被 1 处调用(run_guardian_review)。
guardian_request_target_item_id503–514 ↗
fn guardian_request_target_item_id(request: &GuardianApprovalRequest) -> Option<&str>
作用:从审批请求里取出它对应的目标项目 id。这个 id 像申请单编号,能把审批结果和原来的命令、补丁或工具调用对上号。
数据流:进去的是一个审批请求 → 如果这种请求本身带有 id,就返回这个 id 的引用;如果是 NetworkAccess 这种没有目标项目 id 的请求,就返回 None → 它只读取信息,不复制也不修改。
调用关系:run_guardian_review 会用它来判断审批结果应该挂回哪个具体项目。它处理了一个现实差异:大多数请求有 id,但网络访问请求在这里没有对应的目标项目 id。
调用图:被 1 处调用(run_guardian_review)。
guardian_request_turn_id516–530 ↗
fn guardian_request_turn_id(
request: &'a GuardianApprovalRequest,
default_turn_id: &'a str,
) -> &'a str
作用:找出审批请求属于哪一轮对话。turn_id 可以理解成“这次对话回合的编号”,方便把审批和用户当前那轮操作关联起来。
数据流:进去的是审批请求和一个默认 turn_id → 如果请求自己带 turn_id,比如 NetworkAccess 或 RequestPermissions,就返回它自己的;否则返回传进来的默认值 → 出来的是一个字符串引用,不改动任何数据。
调用关系:run_guardian_review 会在评审时调用它,用来确定这次审批归属于哪轮对话。它让不同类型请求即使携带信息不一样,也能统一落到某个对话回合上。
调用图:被 1 处调用(run_guardian_review)。
format_guardian_action_pretty532–541 ↗
fn format_guardian_action_pretty(
action: &GuardianApprovalRequest,
) -> serde_json::Result<FormattedGuardianAction>
作用:生成一段适合展示给守护者或放进提示词的漂亮 JSON 文本。它还会标记内容是否被截短,避免读者误以为看到的是完整原文。
数据流:进去的是一个审批请求 → 它先用 guardian_approval_request_to_json 转成 JSON,再用 truncate_guardian_action_value 裁剪过长字符串,最后用 serde_json::to_string_pretty 变成缩进清楚的文本 → 出来的是 FormattedGuardianAction,里面有文本和 truncated 标记。
调用关系:build_guardian_prompt_items_with_parent_turn 在构建守护者提示内容时会调用它。它把前面的 JSON 转换、长度控制、漂亮排版串成一条流水线,是审批动作进入提示词前的最后整理步骤。
调用图:调用 2 个内部函数(guardian_approval_request_to_json, truncate_guardian_action_value);被 1 处调用(build_guardian_prompt_items_with_parent_turn);外部调用 1 个(to_string_pretty)。
审查提示构建
构建面向审查者的提示,并解析用于守护器批准评估的结构化审查结果。
core/src/guardian/prompt.rs源码 ↗
当 Codex 想做某些需要批准的事,比如联网或执行敏感动作时,系统会请一个“Guardian”(守护者审核会话)来判断该不该允许。这个文件就是给它打包材料的地方:先从会话历史里挑出真正有用的用户话、助手话、工具调用和工具结果;再按长度预算裁剪,防止提示词太长;然后把待审批的动作 JSON 原样放进去,让审核模型判断。它还支持“增量审核”,也就是如果上次已经看过一部分记录,这次只补新内容,像法官续看案卷而不是每次从第一页重读。最后,它规定 Guardian 必须输出什么 JSON,并负责把输出解析成系统能用的审核结果。
GuardianTranscriptEntryKind::role46–53 ↗
fn role(&self) -> &str
作用:把一条记录的内部类型转换成提示词里能看懂的角色名,比如 user、assistant、developer,或者某个工具名。这样 Guardian 看到整理后的记录时,能知道每句话或每段证据是谁产生的。
数据流:进去的是一条记录的种类 → 它判断这是开发者、用户、助手,还是某个工具 → 出来的是一个短字符串,用来写在 transcript(整理后的会话记录)每一行前面。
调用关系:它是渲染会话记录时的小翻译器。构建 Guardian 提示词时,记录会被排成类似“[1] user: ...”的格式,这个函数提供其中的角色标签。
GuardianTranscriptEntryKind::is_user55–57 ↗
fn is_user(&self) -> bool
作用:判断一条记录是不是用户说的话。这个判断很重要,因为用户的真实意图是审核是否授权的核心证据。
数据流:进去的是一条记录类型 → 它用匹配判断是否等于 User → 出来是 true 或 false,不改动任何数据。
调用关系:整理 transcript 时会优先保留用户发言,尤其是最早和最新的用户请求。这个函数就是给筛选规则提供“这是不是用户发言”的答案。
调用图:外部调用 1 个(matches!)。
GuardianTranscriptEntryKind::is_tool59–61 ↗
fn is_tool(&self) -> bool
作用:判断一条记录是不是工具调用或工具结果。工具内容会单独占一套长度预算,避免大量工具输出把用户对话挤掉。
数据流:进去的是一条记录类型 → 它检查是否属于 Tool → 出来是 true 或 false,不产生副作用。
调用关系:渲染 Guardian 会话记录时,会用它决定一条内容该走“工具预算”还是“普通消息预算”。这让审核材料既有证据,又不会被工具日志淹没。
调用图:外部调用 1 个(matches!)。
build_guardian_prompt_items92–106 ↗
async fn build_guardian_prompt_items(
session: &Session,
retry_reason: Option<String>,
request: GuardianApprovalRequest,
mode: GuardianPromptMode,
) -> serde_json::Result<GuardianPromp
作用:这是测试环境下使用的简化入口,用来构造 Guardian 要看的提示内容。它默认没有父级 turn(一次交互上下文),然后把真正的工作交给更完整的构建函数。
数据流:进去的是当前会话、可选的重试原因、待审批请求和构建模式 → 它补上 parent_turn 为空这个默认值 → 出来是 GuardianPromptItems,也就是要发给 Guardian 的用户输入片段、记录游标和动作是否被裁剪的信息。
调用关系:它直接调用 build_guardian_prompt_items_with_parent_turn。平时完整流程会用后者;这个函数主要让测试不用准备额外上下文也能生成同样形状的提示词。
调用图:调用 1 个内部函数(build_guardian_prompt_items_with_parent_turn)。
build_guardian_prompt_items_with_parent_turn108–242 ↗
async fn build_guardian_prompt_items_with_parent_turn(
session: &Session,
parent_turn: Option<&TurnContext>,
retry_reason: Option<String>,
request: GuardianApprovalRequest,
mode: G
作用:这是本文件最核心的函数,负责把会话历史、权限上下文、重试说明和待审批动作拼成 Guardian 能审的完整材料。没有它,审核模型就可能看不到用户到底授权了什么,也看不到当前要批准的动作细节。
数据流:进去的是 Session(当前会话)、可选父级 TurnContext(一次父交互的权限环境)、可选重试原因、审批请求和 Full/Delta 模式 → 它复制历史,抽取可用于审核的记录,决定是全量发送还是只发新增部分,裁剪并渲染 transcript,再把待审批动作格式化成 JSON → 出来是 GuardianPromptItems;里面包含多段 UserInput、一个新的 transcript_cursor,以及待审动作 JSON 是否被截断的标记。
调用关系:它会调用 collect_guardian_transcript_entries 收集证据,调用 render_guardian_transcript_entries 或 render_guardian_transcript_entries_with_offset 压缩成可读记录,并调用 format_guardian_action_pretty 格式化审批动作。它被 build_guardian_prompt_items 包装调用,也会在 run_review_on_session 需要发起 Guardian 审核时使用。
调用图:调用 4 个内部函数(format_guardian_action_pretty, collect_guardian_transcript_entries, render_guardian_transcript_entries, render_guardian_transcript_entries_with_offset);被 2 处调用(build_guardian_prompt_items, run_review_on_session);外部调用 3 个(new, clone_history, format!)。
parent_turn_denied_reads_context244–267 ↗
fn parent_turn_denied_reads_context(turn: &TurnContext) -> Option<String>
作用:把父级交互里“禁止读取”的路径或文件匹配规则整理成一段提醒文字。这样 Guardian 知道有些地方本来就不该被读,不能批准一个绕过限制去读它们的升级请求。
数据流:进去的是 TurnContext,里面有当前目录和文件系统权限策略 → 它找出在当前目录下不可读的根路径和 glob(文件匹配规则)→ 如果没有限制就返回 None;如果有,就返回一段列出这些限制的说明文字。
调用关系:build_guardian_prompt_items_with_parent_turn 在有父级 turn 时会用它补充权限背景。它不直接做审批,只是把重要的安全边界写进给 Guardian 的材料里。
调用图:外部调用 1 个(format!)。
render_guardian_transcript_entries297–305 ↗
fn render_guardian_transcript_entries(
entries: &[GuardianTranscriptEntry],
) -> (Vec<String>, Option<String>)
作用:把收集好的审核记录渲染成短而清楚的文字列表。它是全量 transcript 渲染的公开入口,负责给没有偏移量的普通情况套上默认参数。
数据流:进去的是 GuardianTranscriptEntry 列表 → 它把起始编号设为 0,把空记录占位文字设为“没有保留记录” → 出来是一组可放进提示词的字符串,以及可选的“有记录被省略”提示。
调用关系:它把实际工作交给 render_guardian_transcript_entries_with_offset。build_guardian_prompt_items_with_parent_turn 在 Full 模式下会用它生成完整会话摘录。
调用图:调用 1 个内部函数(render_guardian_transcript_entries_with_offset);被 1 处调用(build_guardian_prompt_items_with_parent_turn)。
render_guardian_transcript_entries_with_offset307–409 ↗
fn render_guardian_transcript_entries_with_offset(
entries: &[GuardianTranscriptEntry],
entry_number_offset: usize,
empty_placeholder: &str,
) -> (Vec<String>, Option<String>)
作用:按固定规则挑选和裁剪会话记录,生成 Guardian 能读的 transcript。它解决的问题是:历史可能很长,但审核模型的输入空间有限,所以必须保留最关键的用户意图和最近证据。
数据流:进去的是记录列表、编号偏移量和空内容占位文字 → 它先逐条截短,再给每条编号和标角色;然后优先保留第一个用户请求、最后一个用户请求、能放下的其他用户请求;最后在预算内加入最近的助手和工具记录 → 出来是渲染后的记录列表,以及如果有内容被跳过就返回一条省略说明。
调用关系:Full 模式会通过 render_guardian_transcript_entries 间接使用它;Delta 模式会由 build_guardian_prompt_items_with_parent_turn 直接调用,并传入“已经看过多少条”的偏移量。它是提示词瘦身的关键零件。
调用图:被 2 处调用(build_guardian_prompt_items_with_parent_turn, render_guardian_transcript_entries);外部调用 4 个(is_empty, iter, len, vec!)。
collect_guardian_transcript_entries419–521 ↗
fn collect_guardian_transcript_entries(
items: &[ResponseItem],
) -> Vec<GuardianTranscriptEntry>
作用:从原始会话历史里挑出 Guardian 审核真正需要看的内容。它会丢掉一些系统自动塞进去、对审批判断没帮助的背景噪声,只保留用户、助手、少量开发者标记、工具调用和工具结果。
数据流:进去的是 ResponseItem 列表,也就是会话历史里的各种原始事件 → 它逐项判断类型:用户消息会跳过纯上下文脚手架,助手消息转成文本,工具调用保存参数,工具结果按 call_id 找回工具名 → 出来是一串 GuardianTranscriptEntry,每条都有种类和可读文字。
调用关系:build_guardian_prompt_items_with_parent_turn 会先调用它,把杂乱历史变成统一的审核记录。它还会调用 content_items_to_text、is_contextual_user_message_content、plaintext_agent_message_content 和 JSON 序列化工具,把不同来源的内容变成普通文本。
调用图:调用 3 个内部函数(content_items_to_text, is_contextual_user_message_content, plaintext_agent_message_content);被 1 处调用(build_guardian_prompt_items_with_parent_turn);外部调用 4 个(new, new, Tool, to_string)。
guardian_truncate_text523–545 ↗
fn guardian_truncate_text(content: &str, token_cap: usize) -> (String, bool)
作用:把过长文本截短到大致的 token(模型读文本时使用的粗略长度单位)预算内,同时明确标出中间省略了多少内容。这样既不撑爆模型输入,又不会假装内容完整。
数据流:进去的是原文本和允许的 token 上限 → 它把 token 上限估算成字节数;如果文本够短就原样返回;如果太长,就保留开头和结尾,中间插入一个带 omitted_approx_tokens 的截断标记 → 出来是新文本和一个“是否截断”的布尔值。
调用关系:它会调用 split_guardian_truncation_bounds 来安全地找开头和结尾的切分点,也会用近似 token/字节换算函数估算长度。调用图里它被 truncate_guardian_action_value 使用,用来裁剪待审动作里的过长字段;本文件渲染 transcript 时也依赖同类截断思想来控制输入长度。
调用图:调用 1 个内部函数(split_guardian_truncation_bounds);被 1 处调用(truncate_guardian_action_value);外部调用 4 个(new, approx_bytes_for_tokens, approx_tokens_from_byte_count, format!)。
split_guardian_truncation_bounds547–583 ↗
fn split_guardian_truncation_bounds(
content: &str,
prefix_bytes: usize,
suffix_bytes: usize,
) -> (&str, &str)
作用:为截断文本找安全的前半段和后半段边界。它避免从一个 UTF-8 字符中间切开,防止中文、emoji 等多字节字符被切坏。
数据流:进去的是原文本、想保留的前缀字节数和后缀字节数 → 它按字符边界扫描,找到不破坏字符的前缀结束位置和后缀开始位置 → 出来是两个字符串切片:可安全保留的开头和结尾。
调用关系:guardian_truncate_text 在需要把长文本变成“开头 + 省略标记 + 结尾”时调用它。它只负责切边界,不负责决定是否截断或写省略标记。
调用图:被 1 处调用(guardian_truncate_text)。
parse_guardian_assessment589–630 ↗
fn parse_guardian_assessment(text: Option<&str>) -> anyhow::Result<GuardianAssessment>
作用:把 Guardian 最终回复的文字解析成系统内部的审核结论。它要求核心内容必须是 JSON,但也容忍 JSON 外面包了一点废话,减少模型偶尔格式漂移造成的硬失败。
数据流:进去的是可选文本 → 如果没有文本就报错;如果整段是合法 JSON 就直接解析;如果不是,就尝试截取第一个“{”到最后一个“}”之间的 JSON;然后补齐缺省的风险等级、用户授权程度和理由 → 出来是 GuardianAssessment,包含风险、授权、允许/拒绝和解释。
调用关系:run_guardian_review_session_before_deadline 在 Guardian 审核完成后会调用它。它是从“模型说的话”到“程序能执行的审批结果”之间的翻译关口,解析失败就意味着本次审核不能被当作有效批准。
调用图:被 1 处调用(run_guardian_review_session_before_deadline);外部调用 1 个(bail!)。
guardian_output_schema645–668 ↗
fn guardian_output_schema() -> Value
作用:生成 Guardian 最终答案应该遵守的 JSON 结构说明。这个 schema(结构规则)告诉模型只能输出哪些字段、哪些值合法,避免返回一段自由发挥的文字。
数据流:进去不需要业务输入 → 它构造一个 JSON schema:允许 risk_level、user_authorization、outcome、rationale,并要求至少有 outcome → 出来是 serde_json::Value,可直接交给模型请求作为结构化输出约束。
调用关系:run_guardian_review 会用它约束 Guardian 的最终回答,test_review_params 也会用它检查测试参数。它和 guardian_output_contract_prompt 保持同一套输出约定:一个给机器检查,一个给模型阅读。
调用图:被 2 处调用(run_guardian_review, test_review_params);外部调用 1 个(json!)。
guardian_output_contract_prompt672–684 ↗
fn guardian_output_contract_prompt() -> &'static str
作用:返回一段给 Guardian 看的文字,明确告诉它最终必须用严格 JSON 回答。它还说明低风险允许时可以只返回最短形式:{"outcome":"allow"}。
数据流:进去没有参数 → 它返回一段固定字符串,里面写明可使用只读工具检查,以及最终 JSON 字段该怎么填 → 出来是静态文本片段。
调用关系:guardian_policy_prompt_with_config 会把这段输出契约追加到完整策略提示词后面。它和 guardian_output_schema 是一对:前者让模型读懂规则,后者让程序约束格式。
guardian_policy_prompt695–697 ↗
fn guardian_policy_prompt() -> String
作用:生成默认的 Guardian 策略提示词。它把内置的 policy.md 作为租户策略配置传进去,得到最终给审核模型看的完整安全规则。
数据流:进去没有参数 → 它用 include_str! 在编译时读入 policy.md 的内容 → 把这段默认策略交给 guardian_policy_prompt_with_config → 出来是一整段 Guardian policy prompt。
调用关系:它是默认策略入口。真正拼模板、插配置、追加输出格式说明的工作由 guardian_policy_prompt_with_config 完成。
调用图:调用 1 个内部函数(guardian_policy_prompt_with_config);外部调用 1 个(include_str!)。
guardian_policy_prompt_with_config699–703 ↗
fn guardian_policy_prompt_with_config(tenant_policy_config: &str) -> String
作用:把可配置的租户策略内容塞进 Guardian 策略模板,并追加最终输出格式要求。这样项目可以把大框架固定住,只替换中间那段可配置规则。
数据流:进去的是 tenant_policy_config,也就是某个环境或租户自己的策略文字 → 它读取 policy_template.md,替换其中的 {tenant_policy_config} 占位符,再追加 guardian_output_contract_prompt 返回的 JSON 输出说明 → 出来是一份完整的 Guardian 策略提示词字符串。
调用关系:guardian_policy_prompt 会用默认 policy.md 调它;如果别的地方想传入自定义策略,也可以直接用它。它把“审核规则”和“必须怎么回答”合成一份最终提示词。
调用图:被 1 处调用(guardian_policy_prompt);外部调用 2 个(format!, include_str!)。
审查会话执行
设置审查模式轮次,并运行可复用的嵌套守护器审查会话来执行评估。
core/src/session/review.rs源码 ↗
当用户触发审查时,系统不能直接把它当成普通聊天来跑。审查需要一个单独的小线程:有自己的模型、自己的任务编号、自己的提示词,但又要继承主会话里的目录、权限、网络状态、工具环境等上下文。这个文件做的事就像给一位“临时审稿人”准备工位:先选模型;再关掉审查时不允许用的功能,比如网页搜索和目标功能;然后准备工具模式、可用模型列表、遥测信息、元数据和扩展数据;最后把审查提示词包装成一条用户输入,交给会话去真正执行。执行开始后,它还会发一个“已进入审查模式”的事件,让界面知道可以切换到审查状态。重要的是,这里不是审查内容本身的算法,而是把一次审查任务安全、完整地搭起来。
spawn_review_thread7–185 ↗
async fn spawn_review_thread(
sess: Arc<Session>,
config: Arc<Config>,
parent_turn_context: Arc<TurnContext>,
sub_id: String,
resolved: crate::review_prompts::ResolvedReviewRequest
作用:这个函数启动一个新的审查子任务。有人请求审查时,它负责准备模型、权限、工具、提示词和上下文,然后把任务交给会话系统去运行。
数据流:输入是一份当前会话、配置、父任务上下文、子任务编号,以及已经整理好的审查请求。函数先决定审查要用哪个模型,并查询模型信息;接着复制配置,关闭审查中不该用的功能,比如网页搜索;然后把父任务里的目录、权限、网络、时间、工具、遥测等信息整理成新的审查上下文。之后,它把审查提示词变成一条用户消息,调用会话启动任务。最后,它发出“进入审查模式”的事件,告诉外部界面这次子任务已经开始。
调用关系:它通常在主会话需要派生审查任务时被调用,是主对话和审查执行之间的桥梁。它自己不直接完成审查,而是先调用多个构造函数创建上下文、元数据和扩展数据,也会借助 tool_user_shell_type 和 for_session 之类的工具判断 shell 执行环境;准备好以后,再把真正执行交给 sess.spawn_task,并用 sess.send_event 通知界面。
调用图:调用 7 个内部函数(new, new, new, tool_user_shell_type, new, new, for_session);外部调用 8 个(new, new, new, unified_exec_feature_mode_for_features, default, EnteredReviewMode, warn!, vec!)。
core/src/guardian/review_session.rs源码 ↗
可以把这里看成一个“安全检查岗”。主会话遇到需要 Guardian 审查的操作时,不直接让同一个助手继续判断,而是开一个受限制的审查会话:只能读、不能批准网络或执行有风险动作。文件里有一个会话管理器,保存一个长期复用的主审查会话(trunk),还会在主会话忙或配置不匹配时开临时会话(ephemeral)。它会比较配置生成“复用钥匙”,确保模型、权限、工作目录等关键设置变了就不误用旧会话。审查时,它会构造提示词,提交给 Guardian,等待指定轮次的结果;如果超时或用户取消,就发送中断并把事件流清干净,避免旧结果串到下次审查里。它还记录一些分析数据,比如用了多少 token、是否复用了上下文、首次输出耗时等。文件末尾的测试覆盖了配置失效、超时取消、事件过滤等容易出错的地方。
had_prior_review_context117–119 ↗
fn had_prior_review_context(prompt_mode: &GuardianPromptMode) -> bool
作用:判断这次审查提示词是不是带了上一次审查之后的增量上下文。简单说,就是看 Guardian 是“从头看”还是“接着上回看”。
数据流:输入一个提示词模式 → 检查它是不是 Delta(增量)模式 → 输出 true 或 false,不改任何状态。
调用关系:run_review_on_session 在准备分析数据时调用它,用来标记本次审查是否利用了之前的审查上下文。
调用图:被 1 处调用(run_review_on_session);外部调用 1 个(matches!)。
token_usage_delta121–130 ↗
fn token_usage_delta(start: &TokenUsage, end: &TokenUsage) -> TokenUsage
作用:计算一次审查期间新增消耗了多少 token。token 可以理解成模型读写文字时的计费单位。
数据流:输入审查开始前和结束后的 token 统计 → 两者相减,并把负数修正为 0 → 输出本次新增的 token 用量。
调用关系:run_review_on_session 在 Guardian 成功完成后调用它,把这次审查的实际消耗写进分析结果。
调用图:被 1 处调用(run_review_on_session)。
GuardianReviewSessionReuseKey::from_spawn_config172–198 ↗
fn from_spawn_config(
spawn_config: &Config,
user_instructions: Option<UserInstructions>,
) -> Self
作用:从审查会话的配置里提取“是否还能复用旧会话”的关键项。它像一把钥匙:钥匙不同,就说明旧会话不该再用。
数据流:输入 spawn_config 和用户指令 → 抽出模型、权限、工作目录、工具开关等会影响会话行为的字段 → 生成一个可比较的复用 key。
调用关系:run_review 用它决定缓存的 trunk 会话是否过期;测试辅助函数和多项测试也用它确认配置变化会正确让缓存失效。
调用图:被 7 处调用(cache_for_test, register_ephemeral_for_test, run_review, guardian_review_session_compact_scope_change_invalidates_cached_session, guardian_review_session_config_change_invalidates_cached_session, run_review_removes_trunk_when_event_stream_is_broken, test_review_session)。
prompt_cache_key_override_for_review_session201–213 ↗
fn prompt_cache_key_override_for_review_session(
session_source: &SessionSource,
parent_thread_id: Option<ThreadId>,
) -> Option<String>
作用:给 Guardian 子会话生成专用的提示缓存 key,让同一个父线程下的审查可以共享缓存,但不会串到别的线程。
数据流:输入会话来源和父线程 ID → 只有来源是 Guardian 子代理且有父线程 ID 时才拼出 guardian:父线程ID → 否则返回 None。
调用关系:测试 guardian_prompt_cache_key_is_scoped_to_parent_thread 调用它,确认缓存 key 既稳定又按父线程隔离。
调用图:被 1 处调用(guardian_prompt_cache_key_is_scoped_to_parent_thread);外部调用 1 个(format!)。
GuardianReviewSession::shutdown216–219 ↗
async fn shutdown(&self)
作用:彻底关闭一个 Guardian 审查会话。它先发取消信号,再等待底层 Codex 线程停干净。
数据流:读取会话里的取消令牌和 Codex 对象 → 触发取消 → 等待 shutdown_and_wait 完成,结果不向外返回。
调用关系:会话管理器关闭 trunk 或临时会话时会走到这里;后台关闭函数也会把最终清理交给它。
调用图:调用 1 个内部函数(shutdown_and_wait);外部调用 1 个(cancel)。
GuardianReviewSession::shutdown_in_background221–226 ↗
GuardianReviewSession::fork_snapshot228–230 ↗
async fn fork_snapshot(&self) -> Option<GuardianReviewForkSnapshot>
作用:取出当前审查会话最近保存的“可复制历史快照”。这个快照能让临时会话从 trunk 的上下文继续审查。
数据流:读取会话内部状态锁 → 克隆 last_committed_fork_snapshot → 返回快照或 None,不改状态。
调用关系:run_review 发现 trunk 正忙时会调用它,把快照传给 run_ephemeral_review,让临时会话尽量少从头开始。
GuardianReviewSession::refresh_last_committed_fork_snapshot232–250 ↗
async fn refresh_last_committed_fork_snapshot(&self)
作用:在一次 trunk 审查成功后,刷新可供临时会话 fork 的历史快照。这样下次临时审查可以接着已有审查上下文走。
数据流:从当前 Codex session 加载 rollout 历史 → 如果有历史项,就连同审查次数和游标一起存成快照 → 失败时只打警告,不让主流程崩掉。
调用关系:run_review_on_session 成功后,run_review 会调用它;它把加载历史的细节交给 load_rollout_items_for_fork。
调用图:调用 1 个内部函数(load_rollout_items_for_fork);外部调用 2 个(Forked, warn!)。
EphemeralReviewCleanup::new254–262 ↗
fn new(
state: Arc<Mutex<GuardianReviewSessionState>>,
review_session: Arc<GuardianReviewSession>,
) -> Self
作用:创建一个临时审查会话的清理保险。万一后续流程提前退出,它负责把临时会话从列表里拿掉并关闭。
数据流:输入共享状态和临时审查会话 → 存到 cleanup 对象里 → 返回这个清理对象。
调用关系:run_ephemeral_review 注册临时会话后马上创建它,用来保证异常路径也不会留下孤儿会话。
调用图:被 1 处调用(run_ephemeral_review)。
EphemeralReviewCleanup::disarm264–266 ↗
fn disarm(&mut self)
作用:解除清理保险,表示临时会话已经被正常处理了,drop 时不要再重复清理。
数据流:读取 cleanup 里的 review_session → 把它设为 None → 后续析构时不会再关这个会话。
调用关系:run_ephemeral_review 在自己成功取出并安排关闭临时会话后调用它,避免清理逻辑重复执行。
EphemeralReviewCleanup::drop270–288 ↗
GuardianReviewSessionManager::trunk_rollout_path292–302 ↗
async fn trunk_rollout_path(&self) -> Option<PathBuf>
作用:拿到长期复用的 Guardian trunk 会话对应的 rollout 文件路径。rollout 可以理解成会话历史的落盘记录。
数据流:读取当前 trunk → 确保历史已经写到磁盘 → 查询当前 rollout 路径 → 成功返回路径,失败记录警告并返回 None。
调用关系:外部需要定位 Guardian trunk 历史文件时会调用它;它只看 trunk,不处理临时审查会话。
调用图:外部调用 1 个(warn!)。
GuardianReviewSessionManager::shutdown304–318 ↗
async fn shutdown(&self)
作用:关闭整个 Guardian 审查管理器持有的所有会话。包括一个 trunk 和所有临时会话。
数据流:锁住状态 → 取走 trunk 和 ephemeral_reviews 列表 → 逐个调用 shutdown → 状态里不再保存这些会话。
调用关系:系统收尾或父会话结束时会用它,确保审查线程不会继续运行。
调用图:外部调用 1 个(take)。
GuardianReviewSessionManager::run_review324–449 ↗
async fn run_review(
&self,
params: GuardianReviewSessionParams,
) -> (GuardianReviewSessionOutcome, GuardianReviewAnalyticsResult)
作用:这是发起一次 Guardian 审查的总调度入口。它决定复用 trunk、重建 trunk,还是开临时会话。
数据流:输入一次审查的所有参数 → 生成复用 key,检查缓存会话是否匹配和空闲 → 必要时创建新会话或临时会话 → 在选定会话上运行审查 → 返回审查结果和分析数据,并按情况保留或关闭会话。
调用关系:上层 Guardian 流程会调用它;它串起 from_spawn_config、run_before_review_deadline、spawn_guardian_review_session、run_review_on_session、run_ephemeral_review 和 remove_trunk_if_current。
调用图:调用 8 个内部函数(without_session, remove_trunk_if_current, run_ephemeral_review, from_spawn_config, run_before_review_deadline, run_before_review_deadline_with_cancel, run_review_on_session, spawn_guardian_review_session);外部调用 8 个(clone, new, pin, new, anyhow!, Completed, PromptBuildFailed, matches!)。
GuardianReviewSessionManager::cache_for_test452–468 ↗
async fn cache_for_test(&self, codex: Codex)
作用:测试用:手动塞一个 trunk 审查会话进管理器,方便测试复用和清理行为。
数据流:输入一个 Codex 测试会话 → 根据它的配置生成 reuse key → 包装成 GuardianReviewSession 并放进 state.trunk。
调用关系:只在测试编译时存在;测试可以用它跳过真实创建流程,直接搭好缓存状态。
调用图:调用 1 个内部函数(from_spawn_config);外部调用 4 个(new, new, new, new)。
GuardianReviewSessionManager::register_ephemeral_for_test471–491 ↗
async fn register_ephemeral_for_test(&self, codex: Codex)
作用:测试用:手动登记一个临时审查会话,方便验证临时会话列表和关闭逻辑。
数据流:输入 Codex 测试会话 → 生成 reuse key → 包成 GuardianReviewSession → 推入 ephemeral_reviews 列表。
调用关系:只给测试使用,用来模拟正在运行的临时 Guardian 会话。
调用图:调用 1 个内部函数(from_spawn_config);外部调用 4 个(new, new, new, new)。
GuardianReviewSessionManager::committed_fork_rollout_items_for_test494–502 ↗
async fn committed_fork_rollout_items_for_test(&self) -> Option<Vec<RolloutItem>>
作用:测试用:读取 trunk 最近保存的 fork 快照里的历史项。
数据流:读取 trunk 和内部状态 → 找到 last_committed_fork_snapshot → 如果它是 Forked 历史,就返回其中的 RolloutItem 列表,否则返回 None。
调用关系:测试用它确认 refresh_last_committed_fork_snapshot 是否真的保存了可 fork 的历史。
GuardianReviewSessionManager::send_trunk_event_raw_for_test505–514 ↗
async fn send_trunk_event_raw_for_test(&self, event: Event)
作用:测试用:直接往 trunk 会话里塞一个原始事件,模拟底层 Codex 发来的消息。
数据流:输入 Event → 找到当前 trunk → 调用 trunk session 的 send_event_raw 把事件送进去。
调用关系:只用于测试事件流相关场景,让测试不用启动真实模型也能驱动会话。
GuardianReviewSessionManager::remove_trunk_if_current516–530 ↗
async fn remove_trunk_if_current(
&self,
trunk: &Arc<GuardianReviewSession>,
) -> Option<Arc<GuardianReviewSession>>
作用:只有当传入的会话仍然是当前 trunk 时,才把它从管理器里移除。这样可以避免误删后来新建的 trunk。
数据流:输入一个 trunk 引用 → 锁住状态并用指针身份比较 → 如果正是当前 trunk,就 take 出来返回;否则返回 None。
调用关系:run_review 在发现 trunk 不该继续保留时调用它,随后把返回的会话交给后台关闭。
调用图:被 1 处调用(run_review)。
GuardianReviewSessionManager::register_active_ephemeral532–538 ↗
async fn register_active_ephemeral(&self, review_session: Arc<GuardianReviewSession>)
作用:把一个正在运行的临时审查会话登记到管理器里。这样 shutdown 或清理保险能找到它。
数据流:输入临时会话引用 → 锁住状态 → push 到 ephemeral_reviews 列表。
调用关系:run_ephemeral_review 创建临时会话后马上调用它,随后 EphemeralReviewCleanup 会负责兜底清理。
调用图:被 1 处调用(run_ephemeral_review)。
GuardianReviewSessionManager::take_active_ephemeral540–550 ↗
async fn take_active_ephemeral(
&self,
review_session: &Arc<GuardianReviewSession>,
) -> Option<Arc<GuardianReviewSession>>
作用:从正在运行的临时会话列表中取走指定会话。取走后调用者通常会安排关闭它。
数据流:输入临时会话引用 → 在 ephemeral_reviews 里按指针身份查找 → 找到就 swap_remove 并返回,找不到返回 None。
调用关系:run_ephemeral_review 在审查结束后调用它,配合 cleanup.disarm 避免重复清理。
调用图:被 1 处调用(run_ephemeral_review)。
GuardianReviewSessionManager::run_ephemeral_review552–604 ↗
async fn run_ephemeral_review(
&self,
params: GuardianReviewSessionParams,
reuse_key: GuardianReviewSessionReuseKey,
deadline: tokio::time::Instant,
fork_snapsh
作用:运行一次临时 Guardian 审查。它用于 trunk 忙、配置不匹配,或需要从 trunk 快照 fork 出来的情况。
数据流:输入审查参数、复用 key、截止时间和可选快照 → 标记配置为 ephemeral,创建临时会话 → 登记并设置清理保险 → 运行审查 → 审查结束后移除登记并后台关闭 → 返回结果和分析数据。
调用关系:run_review 在不能安全使用 trunk 时调用它;它依赖 spawn_guardian_review_session 创建会话,依赖 run_review_on_session 执行审查。
调用图:调用 7 个内部函数(without_session, new, register_active_ephemeral, take_active_ephemeral, run_before_review_deadline_with_cancel, run_review_on_session, spawn_guardian_review_session);被 1 处调用(run_review);外部调用 5 个(clone, new, pin, new, PromptBuildFailed)。
spawn_guardian_review_session607–645 ↗
async fn spawn_guardian_review_session(
params: &GuardianReviewSessionParams,
spawn_config: Config,
reuse_key: GuardianReviewSessionReuseKey,
cancel_token: CancellationToken,
fork_
作用:真正创建一个 Guardian 审查会话。它会启动一个新的 Codex 子线程,并把可选历史快照接进去。
数据流:输入审查参数、配置、reuse key、取消令牌和 fork 快照 → 解析初始历史和已有审查状态 → 调用 run_codex_thread_interactive 启动 Guardian 子代理 → 返回 GuardianReviewSession。
调用关系:run_review 创建 trunk 时调用它;run_ephemeral_review 创建临时会话时也调用它。
调用图:调用 1 个内部函数(run_codex_thread_interactive);被 2 处调用(run_ephemeral_review, run_review);外部调用 6 个(clone, pin, clone, new, new, Other)。
run_review_on_session647–831 ↗
async fn run_review_on_session(
review_session: &GuardianReviewSession,
params: &GuardianReviewSessionParams,
guardian_session_kind: GuardianReviewSessionKind,
deadline: tokio::time::I
作用:在已经选好的 Guardian 会话上执行一次完整审查。它负责准备提示词、提交请求、等待结果、更新状态和统计数据。
数据流:输入会话、审查参数、会话类型和截止时间 → 根据历史决定全量或增量提示 → 同步网络批准信息,构造 Guardian 提示 → 以只读权限提交给 Codex → 等待指定轮次完成 → 成功后更新游标、审查次数和 token 统计 → 返回结果、是否保留会话、分析数据。
调用关系:run_review 和 run_ephemeral_review 都把真正的审查执行交给它;它内部调用 append_guardian_followup_reminder、build_guardian_prompt_items_with_parent_turn、wait_for_guardian_review 等。
调用图:调用 9 个内部函数(from_session, build_guardian_prompt_items_with_parent_turn, append_guardian_followup_reminder, had_prior_review_context, run_before_review_deadline, token_usage_delta, wait_for_guardian_review, read_only, new);被 3 处调用(run_ephemeral_review, run_review, run_review_on_reused_session_waits_for_submitted_turn);外部调用 4 个(pin, default, PromptBuildFailed, matches!)。
append_guardian_followup_reminder833–840 ↗
async fn append_guardian_followup_reminder(review_session: &GuardianReviewSession)
作用:给 Guardian 会话补一条后续审查提醒。它用于第二次审查前,提醒模型这不是第一次看。
数据流:把 GuardianFollowupReviewReminder 转成 ResponseItem → 注入到当前会话历史里,但不新开一个 turn → 不返回业务结果。
调用关系:run_review_on_session 在 prior_review_count 等于 1 时调用它,让复用会话带上提示。
调用图:调用 1 个内部函数(into);被 1 处调用(run_review_on_session);外部调用 1 个(vec!)。
load_rollout_items_for_fork842–850 ↗
async fn load_rollout_items_for_fork(
session: &Session,
) -> anyhow::Result<Option<Vec<RolloutItem>>>
作用:从一个会话里加载可用于 fork 的历史记录。fork 可以理解成“复制一份当前对话历史给临时会话继续用”。
数据流:输入 Session → 确保 rollout 已落盘并刷新 → 打开持久化线程历史 → 加载包含归档内容的历史项 → 返回 RolloutItem 列表。
调用关系:GuardianReviewSession::refresh_last_committed_fork_snapshot 调用它,用这些历史项生成临时会话可用的快照。
调用图:被 1 处调用(refresh_last_committed_fork_snapshot);外部调用 3 个(flush_rollout, live_thread_for_persistence, try_ensure_rollout_materialized)。
wait_for_guardian_review852–934 ↗
async fn wait_for_guardian_review(
review_session: &GuardianReviewSession,
expected_turn_id: &str,
deadline: tokio::time::Instant,
external_cancel: Option<&CancellationToken>,
anal
作用:等待 Guardian 对指定 turn 给出结果。它会忽略旧 turn 的消息,避免把上一次审查结果误当成这次的。
数据流:输入审查会话、期望 turn id、截止时间、取消令牌和分析结果对象 → 循环等事件、超时或取消 → 遇到当前 turn 完成就返回答案,遇到错误会保留错误信息,超时或取消会先中断并排空当前 turn → 同时更新首次输出耗时等统计。
调用关系:run_review_on_session 提交审查后调用它;多项测试直接调用它验证旧事件、错误、超时和取消的处理。
调用图:被 7 处调用(run_review_on_session, wait_for_guardian_review_cancel_drains_expected_turn_after_stale_terminal_event, wait_for_guardian_review_ignores_prior_turn_aborts, wait_for_guardian_review_ignores_prior_turn_completion, wait_for_guardian_review_ignores_prior_turn_errors, wait_for_guardian_review_preserves_structured_session_error, wait_for_guardian_review_timeout_drains_expected_turn_after_stale_terminal_event);外部调用 3 个(pin!, select!, sleep_until)。
event_matches_turn936–948 ↗
fn event_matches_turn(event: &Event, expected_turn_id: &str) -> bool
作用:判断一个事件是不是属于正在等待的那个 turn。它是防止事件串线的小过滤器。
数据流:输入 Event 和期望 turn id → 先比事件 id,再对完成和中断事件额外核对内部 turn_id → 输出 true 或 false。
调用关系:interrupt_and_drain_turn 用它判断是否已经等到目标 turn 的结束事件;它也体现了事件过滤规则。
调用图:被 1 处调用(interrupt_and_drain_turn)。
build_guardian_review_session_config950–1026 ↗
fn build_guardian_review_session_config(
parent_config: &Config,
live_network_config: Option<codex_network_proxy::NetworkProxyConfig>,
active_model: &str,
reasoning_effort: Option<code
作用:从父会话配置生成一份更安全、更收敛的 Guardian 审查配置。重点是只读、少重试、少插件、少副作用。
数据流:输入父配置、实时网络代理配置、模型名和推理强度 → 克隆父配置后改成 Guardian 专用:设模型、设政策提示词、禁用记忆/通知/开发者指令/MCP/部分功能,权限改成只读且永不请求批准 → 如有网络代理则按约束重建 → 返回新配置或错误。
调用关系:上层 run_guardian_review_session_before_deadline 会调用它来准备 spawn_config;多项测试验证它会正确禁用 hooks、技能指令,并让关键配置变化影响 reuse key。
调用图:调用 3 个内部函数(allow_only, from_config_and_constraints, read_only);被 6 处调用(run_guardian_review_session_before_deadline, guardian_review_session_compact_scope_change_invalidates_cached_session, guardian_review_session_config_change_invalidates_cached_session, guardian_review_session_config_disables_hooks, guardian_review_session_config_disables_skill_instructions, test_review_params);外部调用 3 个(new, clone, warn!)。
run_before_review_deadline1028–1044 ↗
async fn run_before_review_deadline(
deadline: tokio::time::Instant,
external_cancel: Option<&CancellationToken>,
future: impl Future<Output = T>,
) -> Result<T, GuardianReviewSessionOutco
作用:给任意异步工作加上 Guardian 审查的截止时间和外部取消控制。它像一个带闹钟和急停按钮的包装器。
数据流:输入截止时间、可选取消令牌和一个 future → 同时等待 future 完成、时间到、或取消发生 → 成功返回工作结果,超时返回 TimedOut,取消返回 Aborted。
调用关系:run_review、run_review_on_session 和 run_before_review_deadline_with_cancel 都用它;测试覆盖了正常超时和取消行为。
调用图:被 5 处调用(run_review, run_before_review_deadline_with_cancel, run_review_on_session, run_before_review_deadline_aborts_when_cancelled, run_before_review_deadline_times_out_before_future_completes);外部调用 1 个(select!)。
run_before_review_deadline_with_cancel1046–1057 ↗
async fn run_before_review_deadline_with_cancel(
deadline: tokio::time::Instant,
external_cancel: Option<&CancellationToken>,
cancel_token: &CancellationToken,
future: impl Future<Outp
作用:在截止时间包装的基础上,如果任务没成功,还会顺手取消内部任务。它用于创建会话这类需要及时停掉的工作。
数据流:输入截止时间、外部取消令牌、内部取消令牌和 future → 先调用 run_before_review_deadline → 如果返回错误,就 cancel 内部令牌 → 返回原来的结果。
调用关系:run_review 创建 trunk、run_ephemeral_review 创建临时会话时调用它;测试确认超时和外部取消会触发内部取消,成功时不会误取消。
调用图:调用 1 个内部函数(run_before_review_deadline);被 5 处调用(run_ephemeral_review, run_review, run_before_review_deadline_with_cancel_cancels_token_on_abort, run_before_review_deadline_with_cancel_cancels_token_on_timeout, run_before_review_deadline_with_cancel_preserves_token_on_success);外部调用 1 个(cancel)。
interrupt_and_drain_turn1059–1079 ↗
async fn interrupt_and_drain_turn(codex: &Codex, expected_turn_id: &str) -> anyhow::Result<()>
作用:中断 Guardian 当前 turn,并把这个 turn 的结束事件读完。这样下次审查不会读到残留事件。
数据流:输入 Codex 和期望 turn id → 提交 Interrupt → 在短时间内持续读取事件 → 直到看到目标 turn 的完成或中断事件才返回成功;等不到就返回超时错误。
调用关系:wait_for_guardian_review 在超时或外部取消时会需要这种清场动作;测试 interrupt_and_drain_turn_ignores_prior_turn_completion 验证它会忽略旧 turn。
调用图:调用 3 个内部函数(event_matches_turn, next_event, submit);被 1 处调用(interrupt_and_drain_turn_ignores_prior_turn_completion);外部调用 2 个(matches!, timeout)。
tests::test_review_session1091–1127 ↗
async fn test_review_session() -> (
GuardianReviewSession,
async_channel::Sender<Event>,
async_channel::Receiver<Submission>,
)
作用:测试辅助函数:搭一个假的 GuardianReviewSession,并暴露事件发送端和提交接收端。这样测试不用真的连模型。
数据流:创建测试 Session、提交通道、事件通道和状态 watch → 根据配置生成 reuse key → 组装 GuardianReviewSession → 返回会话、事件发送器、提交接收器。
调用关系:大量等待和事件流测试调用它,配合 turn_complete_event、turn_aborted_event 手动模拟 Codex 行为。
调用图:调用 3 个内部函数(from_spawn_config, completed_session_loop_termination, make_session_and_context_with_rx);外部调用 6 个(new, new, new, bounded, unbounded, channel)。
tests::turn_complete_event1129–1144 ↗
fn turn_complete_event(
turn_id: &str,
last_agent_message: Option<&str>,
time_to_first_token_ms: Option<i64>,
) -> Event
作用:测试辅助函数:快速造一个 turn 完成事件。
数据流:输入 turn id、可选最后消息、可选首次输出耗时 → 填进 TurnCompleteEvent → 包成 Event 返回。
调用关系:事件流测试用它模拟 Guardian 正常完成或旧 turn 完成。
调用图:外部调用 1 个(TurnComplete)。
tests::turn_aborted_event1146–1156 ↗
fn turn_aborted_event(turn_id: &str) -> Event
作用:测试辅助函数:快速造一个 turn 被中断的事件。
数据流:输入 turn id → 填入 Interrupted 中断原因 → 包成 Event 返回。
调用关系:超时、取消和清场相关测试用它模拟 Guardian 收到中断后的结束消息。
调用图:外部调用 1 个(TurnAborted)。
tests::test_review_params1158–1199 ↗
async fn test_review_params() -> GuardianReviewSessionParams
作用:测试辅助函数:生成一套可用的 GuardianReviewSessionParams。它让测试可以专注审查流程,不必重复搭配置。
数据流:创建测试父 session 和 turn → 从 turn 里取模型、推理设置、工作目录等 → 调用 build_guardian_review_session_config 生成 Guardian 配置 → 组装一个 shell 审查请求和 schema → 返回完整参数。
调用关系:run_review_on_session 和 run_review 相关测试调用它,作为标准审查输入。
调用图:调用 3 个内部函数(guardian_output_schema, build_guardian_review_session_config, make_session_and_context);外部调用 4 个(new, from_secs, now, vec!)。
tests::guardian_review_session_config_change_invalidates_cached_session1202–1239 ↗
async fn guardian_review_session_config_change_invalidates_cached_session()
作用:测试:确认会影响 Guardian 行为的配置变化会让复用 key 变化,从而不会误用旧会话。
数据流:先用原配置生成 Guardian 配置和 key → 修改模型 provider 的 base_url → 再生成新 key → 断言新旧 key 不同,同时相同配置生成的 key 稳定相等。
调用关系:它调用 build_guardian_review_session_config 和 from_spawn_config,验证 run_review 的复用判断基础是可靠的。
调用图:调用 3 个内部函数(test_config, from_spawn_config, build_guardian_review_session_config);外部调用 2 个(assert_eq!, assert_ne!)。
tests::guardian_prompt_cache_key_is_scoped_to_parent_thread1242–1279 ↗
async fn guardian_prompt_cache_key_is_scoped_to_parent_thread()
作用:测试:确认 Guardian 提示缓存 key 按父线程隔离,不会跨对话混用。
数据流:构造 Guardian 子代理来源和父线程 ID → 调用 prompt_cache_key_override_for_review_session → 检查格式、长度、稳定性、不同父线程不同、非 Guardian 或无父线程返回 None。
调用关系:直接覆盖 prompt_cache_key_override_for_review_session 的边界行为。
调用图:调用 2 个内部函数(prompt_cache_key_override_for_review_session, new);外部调用 5 个(SubAgent, assert!, assert_eq!, assert_ne!, Other)。
tests::guardian_review_session_compact_scope_change_invalidates_cached_session1282–1312 ↗
async fn guardian_review_session_compact_scope_change_invalidates_cached_session()
作用:测试:确认自动压缩 token 范围配置变化会让 Guardian 缓存失效。
数据流:用原父配置生成 key → 修改 model_auto_compact_token_limit_scope → 再生成 key → 断言两者不同。
调用关系:它验证 from_spawn_config 把压缩范围纳入 reuse key,避免复用不匹配的审查会话。
调用图:调用 3 个内部函数(test_config, from_spawn_config, build_guardian_review_session_config);外部调用 1 个(assert_ne!)。
tests::guardian_review_session_config_disables_hooks1315–1331 ↗
async fn guardian_review_session_config_disables_hooks()
作用:测试:确认 Guardian 配置会禁用 hooks。hooks 是可在流程中插入额外动作的机制,审查会话不应带这种副作用。
数据流:先在父配置里启用 CodexHooks → 构建 Guardian 配置 → 断言 Guardian 配置里该功能已关闭。
调用关系:覆盖 build_guardian_review_session_config 对高风险功能的收敛处理。
调用图:调用 2 个内部函数(test_config, build_guardian_review_session_config);外部调用 1 个(assert!)。
tests::guardian_review_session_config_disables_skill_instructions1334–1347 ↗
async fn guardian_review_session_config_disables_skill_instructions()
作用:测试:确认 Guardian 审查不会继承技能指令。这样审查模型更专注于安全策略,而不是被额外技能提示干扰。
数据流:把父配置 include_skill_instructions 设为 true → 构建 Guardian 配置 → 断言 Guardian 配置里它变成 false。
调用关系:覆盖 build_guardian_review_session_config 对提示来源的裁剪行为。
调用图:调用 2 个内部函数(test_config, build_guardian_review_session_config);外部调用 1 个(assert!)。
tests::run_before_review_deadline_times_out_before_future_completes1350–1364 ↗
async fn run_before_review_deadline_times_out_before_future_completes()
作用:测试:确认工作太慢时,deadline 包装器会返回超时。
数据流:设置很短的截止时间和一个更慢的 sleep future → 调用 run_before_review_deadline → 断言结果是 TimedOut。
调用关系:直接验证 run_before_review_deadline 的超时分支。
调用图:调用 1 个内部函数(run_before_review_deadline);外部调用 4 个(from_millis, assert!, now, sleep)。
tests::run_before_review_deadline_aborts_when_cancelled1367–1386 ↗
async fn run_before_review_deadline_aborts_when_cancelled()
作用:测试:确认外部取消令牌触发时,deadline 包装器会返回 Aborted。
数据流:创建取消令牌并安排短暂延迟后 cancel → 用一个永不完成的 future 调用 run_before_review_deadline → 断言结果是 Aborted。
调用关系:直接验证 run_before_review_deadline 的取消分支。
调用图:调用 1 个内部函数(run_before_review_deadline);外部调用 7 个(new, from_millis, from_secs, assert!, spawn, now, sleep)。
tests::run_before_review_deadline_with_cancel_cancels_token_on_timeout1389–1407 ↗
async fn run_before_review_deadline_with_cancel_cancels_token_on_timeout()
作用:测试:确认带内部取消的包装器在超时时会取消内部令牌。
数据流:创建内部取消令牌 → 设置短截止时间和慢 future → 调用 run_before_review_deadline_with_cancel → 断言返回 TimedOut,且内部令牌已取消。
调用关系:覆盖 run_before_review_deadline_with_cancel 对超时后的清理动作。
调用图:调用 1 个内部函数(run_before_review_deadline_with_cancel);外部调用 5 个(new, from_millis, assert!, now, sleep)。
tests::run_before_review_deadline_with_cancel_cancels_token_on_abort1410–1432 ↗
async fn run_before_review_deadline_with_cancel_cancels_token_on_abort()
作用:测试:确认外部取消时,也会取消内部任务令牌。
数据流:创建外部取消令牌和内部取消令牌 → 安排外部令牌稍后 cancel → 调用 run_before_review_deadline_with_cancel 等待 pending future → 断言 Aborted 且内部令牌已取消。
调用关系:覆盖 run_before_review_deadline_with_cancel 对用户取消后的清理动作。
调用图:调用 1 个内部函数(run_before_review_deadline_with_cancel);外部调用 7 个(new, from_millis, from_secs, assert!, spawn, now, sleep)。
tests::run_before_review_deadline_with_cancel_preserves_token_on_success1435–1448 ↗
async fn run_before_review_deadline_with_cancel_preserves_token_on_success()
作用:测试:确认任务正常成功时,不会误取消内部令牌。
数据流:创建内部取消令牌 → 用会立即返回 42 的 future 调用 run_before_review_deadline_with_cancel → 断言结果是 42,且令牌没有被取消。
调用关系:覆盖 run_before_review_deadline_with_cancel 的成功路径。
调用图:调用 1 个内部函数(run_before_review_deadline_with_cancel);外部调用 5 个(new, from_secs, assert!, assert_eq!, now)。
tests::had_prior_review_context_tracks_prompt_mode1451–1459 ↗
fn had_prior_review_context_tracks_prompt_mode()
作用:测试:确认 Full 模式不算已有上下文,Delta 模式算已有上下文。
数据流:分别构造 Full 和 Delta 提示模式 → 调用 had_prior_review_context 的判断逻辑 → 用断言确认结果符合预期。
调用关系:保证 run_review_on_session 写入分析字段 had_prior_review_context 时不会标错。
调用图:外部调用 1 个(assert!)。
tests::token_usage_delta_never_reports_negative_usage1462–1488 ↗
fn token_usage_delta_never_reports_negative_usage()
作用:测试:确认 token 差值不会出现负数。即使某些统计项回落,也要按 0 处理。
数据流:构造开始和结束 token 统计,其中部分结束值更小 → 调用 token_usage_delta → 断言负差被钳成 0,正差正常保留。
调用关系:保护 run_review_on_session 的 token 分析结果不出现不合理负值。
调用图:外部调用 1 个(assert_eq!)。
tests::run_review_on_reused_session_waits_for_submitted_turn1491–1534 ↗
async fn run_review_on_reused_session_waits_for_submitted_turn()
作用:测试:确认复用会话时,只等本次提交的 turn,不会被旧 turn 的完成事件骗到。
数据流:创建测试审查会话并设置已有审查状态 → 启动 run_review_on_session → 捕获提交生成的 turn id → 先发送旧 turn 完成,再发送当前 turn 完成 → 断言返回当前 turn 的 fresh 消息和正确耗时。
调用关系:覆盖 run_review_on_session 与 wait_for_guardian_review 的配合,尤其是复用会话下的事件过滤。
调用图:调用 1 个内部函数(run_review_on_session);外部调用 9 个(from_secs, assert!, assert_eq!, test_review_params, test_review_session, turn_complete_event, panic!, spawn, now)。
tests::run_review_removes_trunk_when_event_stream_is_broken1537–1559 ↗
async fn run_review_removes_trunk_when_event_stream_is_broken()
作用:测试:确认 trunk 的事件流坏掉时,管理器会把这个 trunk 移除,避免下次继续复用坏会话。
数据流:创建测试 trunk 并让它的事件发送端关闭 → 调用 manager.run_review → 断言结果是错误完成,且 state.trunk 变成 None。
调用关系:覆盖 run_review 在 run_review_on_session 返回不可保留会话时调用 remove_trunk_if_current 的行为。
调用图:调用 1 个内部函数(from_spawn_config);外部调用 6 个(new, new, new, assert!, test_review_params, test_review_session)。
tests::wait_for_guardian_review_ignores_prior_turn_completion1562–1590 ↗
async fn wait_for_guardian_review_ignores_prior_turn_completion()
作用:测试:确认等待 Guardian 结果时会忽略旧 turn 的完成事件。
数据流:创建测试会话 → 先发送 prior-turn 完成,再发送 current-turn 完成 → 调用 wait_for_guardian_review 等 current-turn → 断言拿到 fresh 消息并保留会话。
调用关系:直接覆盖 wait_for_guardian_review 的事件过滤逻辑。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 7 个(from_secs, assert!, assert_eq!, test_review_session, turn_complete_event, panic!, now)。
tests::wait_for_guardian_review_ignores_prior_turn_errors1593–1631 ↗
async fn wait_for_guardian_review_ignores_prior_turn_errors()
作用:测试:确认旧 turn 的错误不会污染当前 turn 的结果。
数据流:先发送 prior-turn 的错误事件 → 再发送 current-turn 完成但没有最后消息 → 调用 wait_for_guardian_review → 断言当前 turn 正常完成且不把旧错误当成失败。
调用关系:覆盖 wait_for_guardian_review 对 last_error 的作用范围控制。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 8 个(from_secs, assert!, assert_eq!, test_review_session, turn_complete_event, panic!, Error, now)。
tests::wait_for_guardian_review_preserves_structured_session_error1634–1672 ↗
async fn wait_for_guardian_review_preserves_structured_session_error()
作用:测试:确认当前 turn 的结构化错误信息不会丢。结构化错误就是机器可识别的错误类型,比如服务器过载。
数据流:发送 current-turn 的 ErrorEvent,里面带 CodexErrorInfo → 再发送 current-turn 完成且无最后消息 → 调用 wait_for_guardian_review → 断言返回 SessionFailed,并保留错误类型。
调用关系:覆盖 wait_for_guardian_review 在当前 turn 无答案但有错误时的失败路径。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 8 个(from_secs, assert!, assert_eq!, test_review_session, turn_complete_event, panic!, Error, now)。
tests::wait_for_guardian_review_ignores_prior_turn_aborts1675–1703 ↗
async fn wait_for_guardian_review_ignores_prior_turn_aborts()
作用:测试:确认旧 turn 的中断事件不会让当前等待提前结束。
数据流:先发送 prior-turn 中断 → 再发送 current-turn 完成 → 调用 wait_for_guardian_review → 断言返回 current-turn 的 fresh 消息。
调用关系:覆盖 wait_for_guardian_review 对旧中断事件的过滤。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 8 个(from_secs, assert!, assert_eq!, test_review_session, turn_aborted_event, turn_complete_event, panic!, now)。
tests::wait_for_guardian_review_timeout_drains_expected_turn_after_stale_terminal_event1706–1738 ↗
async fn wait_for_guardian_review_timeout_drains_expected_turn_after_stale_terminal_event()
作用:测试:确认超时时即使前面有旧完成事件,也会中断并排空当前 turn。
数据流:先发送旧 turn 完成 → 启动一个任务等待 Interrupt 提交并回发 current-turn 中断 → 调用 wait_for_guardian_review,设置很短超时 → 断言结果是 TimedOut,且会话可保留但不统计 token。
调用关系:覆盖 wait_for_guardian_review 超时后调用清场逻辑的行为。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 7 个(from_millis, assert!, test_review_session, turn_aborted_event, turn_complete_event, spawn, now)。
tests::wait_for_guardian_review_cancel_drains_expected_turn_after_stale_terminal_event1741–1775 ↗
async fn wait_for_guardian_review_cancel_drains_expected_turn_after_stale_terminal_event()
作用:测试:确认外部取消时,也会中断并排空当前 turn,而不是被旧事件干扰。
数据流:先发送旧 turn 完成 → 准备收到 Interrupt 后回发 current-turn 中断 → 立即取消外部令牌 → 调用 wait_for_guardian_review → 断言结果是 Aborted,且清场完成。
调用关系:覆盖 wait_for_guardian_review 的外部取消路径。
调用图:调用 2 个内部函数(without_session, wait_for_guardian_review);外部调用 8 个(new, from_secs, assert!, test_review_session, turn_aborted_event, turn_complete_event, spawn, now)。
tests::interrupt_and_drain_turn_ignores_prior_turn_completion1778–1794 ↗
async fn interrupt_and_drain_turn_ignores_prior_turn_completion()
作用:测试:确认 interrupt_and_drain_turn 清场时会忽略旧 turn 的完成事件,只等目标 turn 结束。
数据流:创建测试会话 → 队列里放入 prior-turn 完成和 current-turn 中断 → 调用 interrupt_and_drain_turn → 断言事件队列被正确消费干净。
调用关系:直接覆盖 interrupt_and_drain_turn 与 event_matches_turn 的配合。
调用图:调用 1 个内部函数(interrupt_and_drain_turn);外部调用 4 个(assert!, test_review_session, turn_aborted_event, turn_complete_event)。
批准审查编排
协调完整的守护器批准审查工作流,将请求建模、提示生成、会话执行和结果处理串联起来。
core/src/guardian/review.rs源码 ↗
可以把这个文件想成机场安检的调度台。普通流程里,某些操作需要用户点头;如果配置允许,系统会先把请求交给一个叫 Guardian 的自动审查员。这个文件会生成审查编号,发出“正在审查”的事件,启动一个受限制的审查会话,让 Guardian 只读地判断风险,然后解析它的回答。结果如果是允许,就放行;如果是拒绝,就保存拒绝理由,提醒后续不要绕路硬干;如果超时或出错,也会偏保守,不会默默放行。它还会记录统计数据,用重试处理短暂网络或服务问题,并有一个“熔断器”:如果同一轮里连续被拒太多次,就直接打断这一轮,防止系统反复尝试危险动作。
new_guardian_review_id67–69 ↗
fn new_guardian_review_id() -> String
作用:生成一个新的 Guardian 审查编号。别人可以用这个编号把一次审查的开始、结果、拒绝理由和统计记录串起来。
数据流:进去不需要任何业务输入 → 它调用 UUID 生成器造出一个几乎不会重复的随机标识 → 出来是一段字符串形式的审查 ID,不改动其他状态。
调用关系:它通常在发起审批审查前被调用。后面的审查流程会拿这个 ID 发事件、存拒绝原因、给界面或日志做追踪。
调用图:外部调用 1 个(new_v4)。
guardian_rejection_message71–90 ↗
async fn guardian_rejection_message(session: &Session, review_id: &str) -> String
作用:把某次 Guardian 拒绝的原因整理成给代理看的提示。它不仅说“被拒了”,还明确提醒不能用变通办法绕过风险。
数据流:进去是当前会话和审查 ID → 它从会话里保存的拒绝理由表取出对应理由;如果没有理由,就用默认说法 → 出来是一段完整文字,并且会把这条拒绝记录从表里移走。
调用关系:当某个审批已经被 Guardian 拒绝、后续需要告诉代理为什么不能继续时会用它。它依赖 run_guardian_review 之前保存的拒绝理由。
调用图:外部调用 1 个(format!)。
guardian_timeout_message92–94 ↗
fn guardian_timeout_message() -> String
作用:生成 Guardian 审查超时时要给代理看的说明。它强调超时不等于一定危险,但不能擅自当作通过。
数据流:进去没有输入 → 它返回固定的超时说明文字 → 不读取也不修改会话状态。
调用关系:当审查在限定时间内没完成时,外层流程可以调用它,把统一口径展示给代理或用户。
GuardianReviewError::prompt_build119–123 ↗
fn prompt_build(err: anyhow::Error) -> Self
作用:把“审查提示或配置构造失败”包装成 GuardianReviewError。这样后面的流程能统一处理不同种类的失败。
数据流:进去是一个底层错误 → 它把错误转成文字,放进 PromptBuild 这个错误分类 → 出来是结构化的审查错误,不额外改状态。
调用关系:run_guardian_review_session_before_deadline 在构造审查会话配置失败时会用它;测试也用它确认错误分类不会混淆。
调用图:被 3 处调用(guardian_review_error_reason_distinguishes_error_kinds, guardian_review_retry_only_retries_transient_session_and_parse_errors, run_guardian_review_session_before_deadline);外部调用 1 个(to_string)。
GuardianReviewError::session125–130 ↗
fn session(err: anyhow::Error) -> Self
作用:把 Guardian 审查会话运行失败包装成统一错误。这里的会话失败指审查员运行过程中出问题,而不是它正常给出拒绝。
数据流:进去是一个底层错误 → 它把错误消息保存到 Session 错误里,且不带额外错误码 → 出来是 GuardianReviewError。
调用关系:run_guardian_review_session_before_deadline 在审查会话没能正常完成时会调用它;重试判断会根据是否带有可识别错误信息来决定是否再试。
调用图:被 3 处调用(guardian_review_error_reason_distinguishes_error_kinds, guardian_review_retry_only_retries_transient_session_and_parse_errors, run_guardian_review_session_before_deadline);外部调用 1 个(to_string)。
GuardianReviewError::session_with_error_info132–137 ↗
fn session_with_error_info(err: anyhow::Error, error_info: CodexErrorInfo) -> Self
作用:把带有明确错误信息的会话失败包装起来。这个额外信息可能说明是服务器过载、连接断开这类临时问题。
数据流:进去是底层错误和 CodexErrorInfo(结构化错误信息)→ 它保存错误文字和错误信息 → 出来是一个可供后续判断是否重试的 Session 错误。
调用关系:run_guardian_review_session_before_deadline 遇到带错误码的会话失败时用它;should_retry_guardian_review 会读取这个错误信息来决定要不要重试。
调用图:被 3 处调用(guardian_review_error_reason_distinguishes_error_kinds, guardian_review_retry_only_retries_transient_session_and_parse_errors, run_guardian_review_session_before_deadline);外部调用 1 个(to_string)。
GuardianReviewError::parse139–143 ↗
fn parse(err: anyhow::Error) -> Self
作用:把“看不懂 Guardian 输出”的问题包装成统一错误。也就是审查员说了话,但格式不是系统能解析的结果。
数据流:进去是解析时得到的底层错误 → 它转成文字并放进 Parse 错误分类 → 出来是 GuardianReviewError。
调用关系:run_guardian_review_session_before_deadline 解析 Guardian 最后一条回答失败时会调用它;重试逻辑会把解析失败当成可以再试的情况。
调用图:被 3 处调用(guardian_review_error_reason_distinguishes_error_kinds, guardian_review_retry_only_retries_transient_session_and_parse_errors, run_guardian_review_session_before_deadline);外部调用 1 个(to_string)。
GuardianReviewError::failure_reason145–153 ↗
fn failure_reason(&self) -> GuardianReviewFailureReason
作用:把内部错误类型翻译成统计系统能识别的失败原因。这样报表里能区分是超时、取消、解析失败,还是会话失败。
数据流:进去是一个 GuardianReviewError → 它按错误种类做对应映射 → 出来是 GuardianReviewFailureReason,不改变原错误。
调用关系:run_guardian_review 在记录审查结果和统计数据时会用它,把技术错误转成可分析的失败标签。
guardian_risk_level_str156–163 ↗
fn guardian_risk_level_str(level: GuardianRiskLevel) -> &'static str
作用:把风险等级变成普通英文单词,比如 low、medium、high、critical。主要用于拼出人能读懂的提示消息。
数据流:进去是 GuardianRiskLevel 枚举值,也就是固定几档风险之一 → 它选择对应字符串 → 出来是静态文字。
调用关系:run_guardian_review 在生成“自动审查批准/拒绝”的警告消息时会调用它,让事件内容更容易阅读。
routes_approval_to_guardian168–170 ↗
fn routes_approval_to_guardian(turn: &TurnContext) -> bool
作用:判断当前这一轮的审批请求是不是应该交给 Guardian,而不是直接问用户。它使用当前轮次配置里的默认审查员设置。
数据流:进去是 TurnContext(当前轮次的上下文,包含审批策略和配置)→ 它取出配置里的 approvals_reviewer → 交给更通用的判断函数 → 出来是真或假。
调用关系:这是外部流程最常用的入口判断函数。它把具体判断委托给 routes_approval_to_guardian_with_reviewer,避免重复写规则。
调用图:调用 1 个内部函数(routes_approval_to_guardian_with_reviewer)。
routes_approval_to_guardian_with_reviewer173–181 ↗
fn routes_approval_to_guardian_with_reviewer(
turn: &TurnContext,
approvals_reviewer: ApprovalsReviewer,
) -> bool
作用:判断一个带指定审查员设置的审批请求是否要走 Guardian。只有审批策略允许请求审批,并且审查员被设为自动审查时,才会走这里。
数据流:进去是当前轮次上下文和一个审查员选择 → 它检查审批策略是否是“按请求审批”或“细粒度审批”,再检查审查员是否是 AutoReview → 出来是布尔值。
调用关系:routes_approval_to_guardian 会调用它。它是路由规则的核心,决定后续是否进入本文件的完整自动审查流程。
调用图:被 1 处调用(routes_approval_to_guardian);外部调用 1 个(matches!)。
is_guardian_reviewer_source183–191 ↗
fn is_guardian_reviewer_source(
session_source: &codex_protocol::protocol::SessionSource,
) -> bool
作用:判断某个会话来源是不是 Guardian 审查员自己。这样系统可以识别“这是审查子代理”,不要把它当成普通用户会话。
数据流:进去是 SessionSource(会话来源)→ 它检查来源是否是带 Guardian 名字的子代理 → 出来是真或假。
调用关系:这个判断用于区分主会话和 Guardian 子会话,避免审查员的内部运行被误认为普通代理行为。
调用图:外部调用 1 个(matches!)。
track_guardian_review193–212 ↗
fn track_guardian_review(
session: &Session,
tracking: &GuardianReviewTrackContext,
approval_request_source: GuardianApprovalRequestSource,
reviewed_action: &GuardianReviewedAction,
作用:统一记录一次 Guardian 审查的统计数据。它既写本地指标,也把事件发给分析系统。
数据流:进去是会话、追踪上下文、请求来源、被审查动作、结果和完成时间 → 它计算耗时,发送指标,再发送分析事件 → 出来没有返回值,但外部统计系统多了一条审查记录。
调用关系:run_guardian_review 在审查完成、失败、超时或取消时都会调用它。它把业务结果交给 emit_guardian_review_metrics 和 analytics_events_client。
调用图:调用 1 个内部函数(emit_guardian_review_metrics);被 1 处调用(run_guardian_review)。
record_guardian_non_denial214–221 ↗
async fn record_guardian_non_denial(session: &Arc<Session>, turn_id: &str)
作用:告诉“拒绝熔断器”:这次不算一次明确拒绝。熔断器是一种保护机制,用来防止连续危险请求太多时还继续跑。
数据流:进去是会话和轮次 ID → 它拿到会话里的熔断器并记录一次非拒绝 → 出来无返回值,但熔断器内部计数会更新。
调用关系:run_guardian_review 在批准、超时、取消或某些失败场景下调用它,用来重置或缓和连续拒绝统计。
调用图:被 1 处调用(run_guardian_review)。
record_guardian_denial223–261 ↗
async fn record_guardian_denial(session: &Arc<Session>, turn: &Arc<TurnContext>, turn_id: &str)
作用:记录一次 Guardian 明确拒绝,并在拒绝太多时打断当前轮次。它是防止系统反复撞安全红线的保护闸。
数据流:进去是会话、当前轮次上下文和轮次 ID → 它把拒绝记进熔断器;如果熔断器认为还不用中断,就结束;如果需要中断,就发出警告事件并异步请求终止当前轮次 → 出来没有直接结果,但可能改变轮次运行状态。
调用关系:run_guardian_review 在 Guardian 明确 Deny 时调用它。测试辅助函数 record_guardian_denial_for_test 也会转调它。
调用图:被 2 处调用(record_guardian_denial_for_test, run_guardian_review);外部调用 3 个(clone, format!, GuardianWarning)。
record_guardian_denial_for_test264–270 ↗
async fn record_guardian_denial_for_test(
session: &Arc<Session>,
turn: &Arc<TurnContext>,
turn_id: &str,
)
作用:给测试代码用的入口,用来模拟记录一次 Guardian 拒绝。它让测试能验证熔断器行为,而不用走完整审查流程。
数据流:进去是会话、轮次上下文和轮次 ID → 它直接调用 record_guardian_denial → 出来无返回值,效果和真实拒绝记录一样。
调用关系:它只在测试编译时存在,是 record_guardian_denial 的薄包装,方便测试访问内部逻辑。
调用图:调用 1 个内部函数(record_guardian_denial)。
run_guardian_review275–591 ↗
async fn run_guardian_review(
session: Arc<Session>,
turn: Arc<TurnContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
approval_request
作用:执行一次完整的 Guardian 自动审批审查。它从发“开始审查”事件,到运行审查、处理结果、记录指标、保存拒绝理由,最后给出放行或拦截决定。
数据流:进去是会话、当前轮次、审查 ID、审批请求、重试原因、请求来源和可选取消信号 → 它提取被审查动作信息,发送进行中事件,运行带重试的 Guardian 会话;然后按结果分支处理:批准就放行,拒绝就保存原因并可能触发熔断,超时就返回超时,取消就返回中止,其他失败按保守策略拒绝 → 出来是 ReviewDecision,同时会发送事件、写统计、更新拒绝记录和熔断器状态。
调用关系:review_approval_request 和 review_approval_request_with_cancel 都把实际工作交给它。它再调用 run_guardian_review_session_with_retry、track_guardian_review、record_guardian_denial、record_guardian_non_denial 等函数,把整条审查流水线串起来。
调用图:调用 12 个内部函数(without_session, new, guardian_assessment_action, guardian_request_target_item_id, guardian_request_turn_id, guardian_reviewed_action, guardian_output_schema, record_guardian_denial, record_guardian_non_denial, run_guardian_review_session_with_retry (+2 more));被 2 处调用(review_approval_request, review_approval_request_with_cancel);外部调用 5 个(pin, format!, matches!, GuardianAssessment, GuardianWarning)。
review_approval_request594–613 ↗
async fn review_approval_request(
session: &Arc<Session>,
turn: &Arc<TurnContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
) -> ReviewDec
作用:这是普通主流程调用的 Guardian 审批入口。调用者只要把审批请求交进来,就能得到批准、拒绝、超时或中止的决定。
数据流:进去是会话、轮次、审查 ID、请求和可选重试原因 → 它克隆共享引用,并把请求来源固定为 MainTurn,且不传外部取消信号 → 出来是 run_guardian_review 给出的 ReviewDecision。
调用关系:它是公开给本 crate 内其他审批流程用的入口。真正复杂的审查逻辑全部交给 run_guardian_review。
调用图:调用 1 个内部函数(run_guardian_review);外部调用 2 个(clone, pin)。
review_approval_request_with_cancel615–634 ↗
async fn review_approval_request_with_cancel(
session: &Arc<Session>,
turn: &Arc<TurnContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
作用:这是带取消能力的 Guardian 审批入口。适合外部流程可能提前取消审查的场景。
数据流:进去是会话、轮次、审查 ID、请求、重试原因、请求来源和取消令牌 → 它把这些原样传给 run_guardian_review → 出来是最终 ReviewDecision;如果取消令牌被触发,结果可能是 Abort。
调用关系:spawn_approval_request_review 会在线程里调用它。它和 review_approval_request 的区别是允许指定来源并接收取消信号。
调用图:调用 1 个内部函数(run_guardian_review);外部调用 1 个(clone)。
spawn_approval_request_review636–666 ↗
fn spawn_approval_request_review(
session: Arc<Session>,
turn: Arc<TurnContext>,
review_id: String,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
approval_req
作用:把 Guardian 审查放到一个新线程里跑,并立刻返回一个接收结果的通道。这样调用方不用卡在当前执行栈里等完整审查。
数据流:进去是会话、轮次、审查 ID、请求、重试原因、来源和取消令牌 → 它创建一次性通道,开新线程,在新线程里建 Tokio 运行时并执行 review_approval_request_with_cancel → 出来是接收端,之后会收到 ReviewDecision;如果运行时创建失败,会发送 Denied。
调用关系:它服务于需要异步隔离审查的调用方。它把实际审查交给 review_approval_request_with_cancel,只负责开线程和传回结果。
run_guardian_review_session_before_deadline682–842 ↗
async fn run_guardian_review_session_before_deadline(
session: Arc<Session>,
turn: Arc<TurnContext>,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
schema: serde_j
作用:在截止时间前实际启动 Guardian 审查会话,并把审查员最后的回答解析成系统能用的判断。这里是“让审查员开工”的核心步骤。
数据流:进去是会话、轮次、审批请求、重试原因、输出格式要求、取消信号和截止时间 → 它读取当前网络配置,挑选审查模型,构造一个受限制的 Guardian 会话配置,运行审查会话;如果得到回答,就解析成 GuardianAssessment;如果构造、运行、解析、超时或取消出问题,就包装成对应错误 → 出来是审查结果或错误,以及本次会话的统计信息。
调用关系:run_guardian_review_session_with_retry 每次尝试都会调用它。它把配置构造交给 build_guardian_review_session_config,把回答解析交给 parse_guardian_assessment,把各种失败包装成 GuardianReviewError。
调用图:调用 7 个内部函数(without_session, parse_guardian_assessment, parse, prompt_build, session, session_with_error_info, build_guardian_review_session_config);被 1 处调用(run_guardian_review_session_with_retry);外部调用 5 个(clone, pin, anyhow!, Completed, Error)。
run_guardian_review_session_with_retry844–878 ↗
async fn run_guardian_review_session_with_retry(
session: Arc<Session>,
turn: Arc<TurnContext>,
request: GuardianApprovalRequest,
retry_reason: Option<String>,
schema: serde_json::
作用:给 Guardian 审查会话加上有限重试。临时网络错误、服务器忙、或者输出格式解析失败时,它会等一小会儿再试,直到成功、不可重试、取消、超时或达到次数上限。
数据流:进去是会话、轮次、请求、重试原因、输出格式、取消信号和最大尝试次数 → 它设置总截止时间,从第 1 次开始调用 run_guardian_review_session_before_deadline;每次失败后用 should_retry_guardian_review 判断是否值得再试,再用 wait_before_guardian_retry 等待 → 出来是最终结果和带尝试次数的统计信息。
调用关系:run_guardian_review 调用它来获得 Guardian 的最终审查结果。它负责重试策略,具体单次运行交给 run_guardian_review_session_before_deadline。
调用图:调用 3 个内部函数(run_guardian_review_session_before_deadline, should_retry_guardian_review, wait_before_guardian_retry);被 1 处调用(run_guardian_review);外部调用 6 个(clone, now, clone, assert!, clone, Error)。
wait_before_guardian_retry880–899 ↗
async fn wait_before_guardian_retry(
attempt_count: i64,
deadline: Instant,
external_cancel: Option<&CancellationToken>,
) -> Option<GuardianReviewError>
作用:在两次 Guardian 重试之间等一段时间,同时监听超时和取消。这样不会一失败就立刻狂重试。
数据流:进去是当前尝试次数、总截止时间和可选取消令牌 → 它根据 backoff(退避等待,也就是失败越多等得越久)算出下一次尝试时间,但不会超过截止时间;等待期间如果取消令牌触发就返回 Cancelled,到点后如果已经过截止时间就返回 Timeout,否则返回 None → 出来是可选错误。
调用关系:run_guardian_review_session_with_retry 在决定要重试后调用它。测试会单独验证它能正确响应取消和截止时间。
调用图:调用 1 个内部函数(backoff);被 3 处调用(guardian_review_retry_wait_honors_cancellation, guardian_review_retry_wait_honors_deadline, run_guardian_review_session_with_retry);外部调用 2 个(now, select!)。
should_retry_guardian_review901–917 ↗
fn should_retry_guardian_review(outcome: &GuardianReviewOutcome) -> bool
作用:判断某个 Guardian 审查失败值是否值得重试。它只重试看起来可能是临时的问题,比如服务器过载、连接断开,或者输出解析失败。
数据流:进去是一次审查结果 → 它检查结果是不是带特定错误信息的会话错误,或者解析错误 → 出来是真或假,不修改任何状态。
调用关系:run_guardian_review_session_with_retry 每次失败后都会问它要不要继续。相关测试覆盖了哪些错误该重试、哪些不该重试。
调用图:被 1 处调用(run_guardian_review_session_with_retry);外部调用 1 个(matches!)。
review_tests::guardian_review_error_reason_distinguishes_error_kinds925–951 ↗
fn guardian_review_error_reason_distinguishes_error_kinds()
作用:测试不同 GuardianReviewError 会不会被正确翻译成不同失败原因。它防止统计数据把解析错、提示构造错、会话错混在一起。
数据流:进去没有外部输入 → 它构造几种错误,调用 failure_reason 检查映射 → 出来是测试通过或失败,不影响运行时状态。
调用关系:它调用 GuardianReviewError::parse、prompt_build、session 和 session_with_error_info。它保护 track_guardian_review 依赖的失败分类准确性。
调用图:调用 4 个内部函数(parse, prompt_build, session, session_with_error_info);外部调用 2 个(anyhow!, assert!)。
review_tests::guardian_review_retry_only_retries_transient_session_and_parse_errors954–1024 ↗
fn guardian_review_retry_only_retries_transient_session_and_parse_errors()
作用:测试重试规则是否足够谨慎:只重试临时会话问题和解析失败,不重试明确批准、明确拒绝、超时、取消或明显不可恢复的错误。
数据流:进去没有外部输入 → 它准备一组不同结果和期望值,逐个调用 should_retry_guardian_review → 出来是断言全部符合预期,否则测试失败。
调用关系:它直接验证 should_retry_guardian_review,也会用各种 GuardianReviewError 构造函数准备测试样本。
调用图:调用 4 个内部函数(parse, prompt_build, session, session_with_error_info);外部调用 4 个(anyhow!, assert_eq!, Completed, Error)。
review_tests::guardian_review_retry_wait_honors_cancellation1027–1039 ↗
async fn guardian_review_retry_wait_honors_cancellation()
作用:测试重试等待时如果外部已经取消,会不会马上返回取消错误。这样能保证审查不会在用户或上层已经取消后继续拖着跑。
数据流:进去没有外部输入 → 它创建一个取消令牌并立刻取消,然后调用 wait_before_guardian_retry → 出来应当是 Cancelled,否则测试失败。
调用关系:它专门覆盖 wait_before_guardian_retry 的取消分支,确保 run_guardian_review_session_with_retry 能及时停下。
调用图:调用 1 个内部函数(wait_before_guardian_retry);外部调用 4 个(new, from_secs, now, assert!)。
review_tests::guardian_review_retry_wait_honors_deadline1042–1051 ↗
async fn guardian_review_retry_wait_honors_deadline()
作用:测试重试等待遇到已经到期的截止时间时,会不会返回超时错误。这样能保证总审查时间不会被重试无限拉长。
数据流:进去没有外部输入 → 它把截止时间设为当前时刻,调用 wait_before_guardian_retry → 出来应当是 Timeout,否则测试失败。
调用关系:它覆盖 wait_before_guardian_retry 的截止时间分支,间接保护 run_guardian_review_session_with_retry 的总超时限制。
调用图:调用 1 个内部函数(wait_before_guardian_retry);外部调用 2 个(now, assert!)。