资讯动态

rig-agent 0.42.0 变更深度解析:截断可观测性、Hook 元数据与模型擦除架构

发布时间:2026/10/2 1:57:49 来源:尧图企业网站定制
AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载本文以 crates/rig-agent/CHANGELOG.md 为骨架结合仓库源码与示例系统梳理 rig-agent 自 0.41.0 拆分以来、0.42.0 中的全部关键变更响应身份元数据与 provider 原始响应的可观测性、截断回合truncated turn的终止语义修复、可移植的模型回合重试 Hook、ToolSet 与完成响应的规范化重构以及模型类型擦除model erasure带来的 API 演进。读完你将掌握这些破坏性变更的来龙去脉、迁移要点以及基于AgentHook实现跨 Provider 通用截断重试的完整实战方案。一、版本脉络总览rig-agent 是 Rig 工作区中承载 Agent 运行时builder、runner、hook、tool、streaming的核心 crate。近两个版本呈现出清晰的演进主线0.41.02026-07-28完成 rig-core / rig-agent 的 facade 拆分见rig根 crate 的 src/lib.rs确立 WASM 支持矩阵收敛为单一 canonicalCompletionClientAgentClientExt并移除过早的 runtime-conformance crate。0.42.02026-08-17聚焦可观测性与正确性——把 provider 的原始响应、响应身份identity和终止原因finish reason全部暴露到 Hook 与运行记录中修复截断回合被误判为成功空回答的缺陷将OneOrManyT彻底替换为VecT引入ModelHandle与ProviderCapabilities在构造时擦除具体模型类型。下文逐条展开每条变更均标注对应源码位置方便在仓库中对照验证。二、可观测性增强raw 响应与响应身份元数据2.1 provider 原始响应抵达每个 observer#2366/#2367在 0.42.0 之前Agent 在构造后擦除了具体模型类型规范化后的CompletionResponse只携带所有 provider 共有的字段而 OpenAI 的system_fingerprint/service_tier、Anthropic 的stop_sequence、Ollama 的 timings 等 provider 特有字段无处落地。0.42.0 的修复是provider 自身的响应以序列化 JSON 形式raw到达所有 observer逐次尝试per attempt同时覆盖阻塞与流式两条表面Hook 事件CompletionResponse、StreamResponseFinish、ModelTurnFinished均携带raw运行记录CompletionCall::raw、ModelTurn::raw、流式终态记录StreamedAssistantContent::Final。examples/raw_response_hook.rs 是这一能力的最小可运行演示Hook 通过OutcomeEvent::completion()拿到response.raw再借助 provider 自己的 wire 类型openai::wire::StreamingCompletionResponse/openai::CompletionResponse反序列化出规范化之外的字段。示例明确区分两条表面——HookContext::is_streaming()为 true 时raw是流式终态记录顶层 chunk 中未被规范化的字段落在其additional_params否则是 Chat Completions 响应本身。对应的CompletionResponse定义位于 crates/rig-core/src/completion/request.rsraw: serde_json::Value字段的文档说明它不覆盖规范化字段调用方构造响应时必须自行提供。2.2 响应身份元数据#2265/#2313伴随raw的另一项新增是response identity 元数据统一封装在 rig-core 的ResponseIdentity载体中crates/rig-core/src/completion/request.rsmessage_idprovider 签发的 assistant 消息 ID可用于回放response_id响应级 ID不作为消息 ID 回放provider_request_id来自 HTTP 头或 SDK 元数据的传输层请求 ID。它被挂载到每个已完成的模型调用的 observer 上CompletionResponse、StreamResponseFinish、ModelTurnFinished三个 Hook 事件新增identity字段而ModelTurnFinished对两条表面上每个被接受的回合都会触发文本回合、纯工具回合、纯推理回合、多轮回合因此仅观察这一事件即可为每个已完成调用记录身份重试时每个事件携带的是被重试尝试自身的 ID绝不会是前一次尝试的。PromptResponse.completion_calls条目同步新增message_id、response_id、provider_request_idserde 带默认值旧运行 JSON 仍可加载。阻塞侧CompletionCall因持有自有 identity 字符串而不再是CopyModelTurn::with_identity构建器可显式附加身份。2.3 completion 错误携带传输层请求 ID#2314/#2315除成功路径外失败路径也获得可观测性completion 错误现在携带 provider 传输层的 request id便于把 agent 侧失败与网关/Provider 侧的请求日志对上号。这与 0.42.0 中core 在非成功 HTTP 错误上保留响应头#2333的修复同属一个主题——调试时不再丢失 HTTP 层的诊断信息。三、截断语义修复终止原因抵达调用方3.1 缺陷背景空截断回合曾是成功空回答#23220.42.0 修复了一个隐蔽且影响面大的行为缺陷流式组装器streamed assembler只从 provider 终态记录上读取usage和是否见到文本丢弃了其余信息导致FinishReason::Lengthprovider 层在StreamFinal上正确携带永远到不了 agent 表面。后果是一个在输出 token 上限处被截断的回合通过prompt/stream_prompt完全无法察觉最终被当作成功的交付给用户再叠加 rig-core 对 Gemini 硬编码的maxOutputTokens: 4096截断响应就以无解释的空白回答呈现且无任何可检查的线索。3.2 修复方案finish_reason 全面下沉修复后终止原因在两条表面上完整存活CompletionCall新增finish_reason按调用记录而非按 run 记录——多轮 run 有 N 个 reason被截断的那一个才是诊断关键ModelTurn新增finish_reason与with_finish_reason构建器ModelTurn::new(..)签名不变StreamedTurn本身无固有 impl由StreamedTurnAssembler::finish填充该字段serde 带默认值持久化运行 JSON 仍可加载StreamedTurnEvent::Completed新增finish_reason字段AgentRun::record_streamed_completion_call的签名增加第三个参数finish reason——这是签名破坏点。关键语义一个未交付任何答案无工具调用、无图片、无非空文本且 reason 为Length或ContentFilter的回合现在会以命名该 reason 的ResponseError失败——这与阻塞侧 Gemini 路径对无内容候选的处理对齐。注意两个精心设计的边界推理不算答案。Gemini 把 thinking token 计入maxOutputTokens因此被截断的思考回合典型形态是只有推理、没有文本纯推理形态恰是这种失败最常见的面貌范围刻意收窄部分输出后截断仍是有效答案reason 保留在调用记录上reportStop/ToolCalls的回合行为不变FinishReason::Other不视为截断因为它携带 provider 自身的 wire 拼写、无规范化含义。回合在报错前仍会写入历史因此部分推理内容保留下来可供调试。这同时收窄了OutputMode::Tool的恢复路径无答案且报告截断性 reason 的回合立即失败而不是消耗一次 output-retry 去重新提示——因为截断它的是预算或过滤器不是措辞重试只会再次截断并报出更不具体的错误其他 reason 的无答案回合仍按原逻辑重提示。FinishReason的规范化定义在 crates/rig-core/src/completion/request.rsStop、Length、ToolCalls、ContentFilter与携带 provider 原拼写的Other(String)。配套方法truncated_output()即Length | ContentFilterL144-L149reconcile_with_output()会在输出含工具调用时将Stop校正为ToolCallsL132-L142。四、可移植截断重试ModelTurnFinished 的 finish_reason max_tokens4.1 为何需要可移植finish_reason过去只存在于M::Response/M::StreamingResponse这类具体模型关联类型上意味着一个想检测长度截断、无工具调用回合的 Hook 必须点名某个 provider、触碰原始响应类型——这违背了AgentHook的模型无关性。0.42.0 的 #2184 解决了这一矛盾ModelTurnFinished新增finish_reason: OptionFinishReason与max_tokens: Optionu64两者配合即可让一个 provider 中立的 Hook 检测截断并返回ModelTurnAction::Retry。ModelTurnFinished的完整结构定义见 crates/rig-agent/src/agent/hook.rs字段含义turn1 起始的模型调用序号content已规范化、待 Hook 验收的 assistant 内容usage本回合用量identity本次尝试的ResponseIdentity重试时是本次尝试自己的finish_reason规范化终止原因None表示 provider 未上报max_tokens本次尝试的输出去 token 上限含 agent 配置、请求覆盖与 RequestPatchraw本次尝试的 provider 原始响应JSON4.2 语义细节读对本次尝试的值finish_reason是本次尝试在Stop→ToolCalls校正之后记录的值因此把工具回合误标为 barestop的 provider 读起来仍是ToolCallsNone意味着 provider 压根没上报 reason——这是多家 OpenAI 兼容网关的真实行为且刻意不平滑为Stop因为正常结束与没有说明是两种应分别决策的事实max_tokens是本次尝试实际准备的输出上限解析顺序为agent 配置值 → runner/请求覆盖 → 合并后的RequestPatch。因此一个有状态的 Hook 若在准备重试时提高上限下一回合读回的是它自己设置的新值而非 agent 的基线——这让截断在我选择的上限处与截断在我未选择的上限处可区分两条表面从同一个按尝试携带的载体PreparedCompletionRequest在 builder 被消费前绑定读取这两个值阻塞与流式对同一回合不会报告不同元数据没有新增任何重试计数器到 agent/builder/runner/run state更窄的策略上限仍是 Hook 自己的职责存放在 run 作用域的Scratchpad中与响应重试原本的做法一致。4.3 实战示例retry_on_truncationcrates/rig-agent/examples/retry_on_truncation.rs 是一个免凭据、可运行的完整示例脚本化模型Budgeted的答案恰好需要 40 个输出 tokenanswer_under(cap)在 cap 不足时按比例截断文本并报告FinishReason::Length——因此升级是因果驱动的循环结束是因为上限终于够大而非脚本设定。核心策略GrowCapOnTruncation通过两个 Hook 方法协作// 每次尝试都重新准备请求当前上限在此应用并回报到该尝试的 // ModelTurnFinished 上on_completion_call 中使用 CompletionCallAction::patch async fn on_completion_call(self, _ctx: HookContext, _event: CompletionCallEvent_) - CompletionCallAction { CompletionCallAction::patch(RequestPatch::new().max_tokens(self.cap.load(Ordering::Relaxed))) } async fn on_model_turn_finished(self, _ctx: HookContext, event: ModelTurnFinished_) - ModelTurnAction { let truncated event.finish_reason.is_some_and(FinishReason::truncated_output); let has_tool_call event.content.iter().any(|c| matches!(c, AssistantContent::ToolCall(_))); let room event.max_tokens.is_none_or(|cap| cap self.ceiling); if truncated !has_tool_call room { let grown event.max_tokens.map_or(self.ceiling, |cap| cap.saturating_mul(2).min(self.ceiling)); self.cap.store(grown, Ordering::Relaxed); return ModelTurnAction::repeat(); } ModelTurnAction::continue_run() }示例同时展示了要点FinishReason::truncated_output()正是 rig-agent 内部使用的判定谓词两者不会漂移has_tool_call检查是因为重试携带工具调用的回合会被拒绝见 hook.rs 中ModelTurnAction的文档重试会消费 run 现有的总模型调用预算rig 不设独立的响应重试上限需要上限的 Hook 应把 run 级状态放在Scratchpadmax_turns(8)必须覆盖所有尝试。同一 Hook 无需改动即可在流式表面复用——示例后半段以完全相同的GrowCapOnTruncation::new(8, 256)驱动.stream()验证两条表面读取同一按尝试载体。运行方式cargo run -p rig-agent --example retry_on_truncation五、模型选择与类型擦除ModelHandle、ProviderCapabilities、on_model_select0.42.0 对 agent 的类型系统做了一次方向性收敛#2257/#2277 等构造时擦除具体模型类型Agent、AgentBuilder::new()之后、AgentRunner、prompt/stream 请求、Extractor不再携带具体模型参数typed 模型在构造时被擦除一次。直接使用 provider 模型的 completion/streaming API 仍保持类型化ModelHandle不透明、可克隆的模型句柄附带按值快照的ProviderCapabilities。支持默认替换、按 run 覆盖using_model以及通过AgentHook::on_model_select的逐调用选择ProviderCapabilities公开类型取代CompletionModel::composes_native_output_with_toolsHook 解析时机前移completion-call hooks 现在先于模型选择解析——合并后的RequestPatch暴露在ModelSelection::request_patch上请求准备针对被选模型的捕获能力执行ModelSelection::previous_model只反映已发出issued的尝试。这使选择模型与修补请求两个 Hook 阶段可以正确协同。模型选择的完整事件/动作定义在 crates/rig-agent/src/agent/hook.rsModelSelection携带default_modelrunner 默认候选与selected_model经过前序模型选择 Hook 后的候选配合on_completion_call中合并的request_patch。另一个可对照的示例是 examples/runtime_model_routing.rs展示运行时按调用路由模型。同一方向的完成响应规范化落在 rig-coreCompletionResponse/StreamingCompletionResponse成为具体类型携带规范化的finish_reason/provider/model/message_id每个 provider 模型暴露 typed 的raw_completion/raw_stream逃生舱。由此产生一组移除项破坏性CompletionModel::{Response, StreamingResponse, Client, make}删除模型构造迁移到必需的CompletionClient::completion_modelGetTokenUsagetrait 删除改为读取StreamFinal::usageCompletionResponse::raw_response删除改用 provider 模型的raw_completion/raw_stream。六、ToolSet 重构原地填充与检索工具6.1 移除 ToolSetBuilder破坏性0.42.0 删除了ToolSetBuilder与ToolSet::builder()工具集改为原地填充ToolSet::default()或from_tools/from_dynamic_tools配合add_tool、add_dynamic_tool、add_portable_dynamic_tool及新增的add_retrieved_tool。迁移示例原文档给出// 旧写法已移除 // ToolSet::builder().retrieved_tool(t).build() // 新写法 let mut set ToolSet::default(); set.add_retrieved_tool(t);对应实现见 crates/rig-agent/src/tool/registry.rsfrom_tools、add_tool、add_dynamic_tool与add_retrieved_tool均在同一ToolSet上原地操作。6.2 新增 add_retrieved_toolToolSet::add_retrieved_toolT: ToolEmbedding注册一个在提示时从 embedding 索引检索的工具保留其 embedding 上下文与文档使ToolSet::schemas能把它们交给向量存储——它是被移除的ToolSetBuilder::retrieved_tool的原位替代。这属于 0.42.0 检索工具能力的显式化工具不再只是静态注册的描述还可以是提示时动态从索引取回的条目。相关测试见 crates/rig-agent/src/tool/migrated_tests.rs。6.3 工具调用身份的类型化#2277 方向延续ToolCallDeltaHook 载荷移除tool_call_idprovider 的 id 出现在已完成调用上internal_call_id才是关联键流式组装器以internal_call_id为 delta 状态键并只从 provider 签发的provider_id取组装后推理块的持久 id——rig 的关联器不再进入历史工具调用身份遵循 rig-core 的类型化模型Hook 或消费者观察到的每个调用都携带唯一、非空的ToolCallIdprovider 签发则用之否则铸造provider 缺省记录在ToolCall::provider上HookContext的ToolCallEvent/ToolResultEvent/InvalidToolCallContext暴露该持久 id不再出现Some()无效调用的重试记录按各自 id 关联无效调用与每个被验证的同伴无 id 的 wire 不再把同伴折叠到共享反馈或回放空tool_call_id。七、消息模型与回合语义的规范化这一批变更把消息/回合的表示收敛到与 rig-core 一致并消除了一组持久化与分类的边角缺陷OneOrManyT全部替换为VecT#2273fake 容器删除强制校验前移agent 构造、检查或交给 Hook 的每个内容列表都是Vec本 crate 不再 re-exportOneOrManyassistant 内容带 tag#2277持久化历史与AgentRun/PromptResponseJSON 携带 rig-core 的 tagged 形式{type: text, ...}旧的无 tag 形状不再加载详见根仓库的 MIGRATING.md 与 rig-core 的 changelog 条目flatten Some({})往返伪影消失is_empty_assistant_turn在持久化/恢复前后分类一致、无需特判真空 assistant 回合不再取消 run、也不再填充虚构的空文本部件——空列表即可诚实表示is_empty_assistant_turn将其中和而非 agent 编造内容或使原本成功的 run 失败PromptResponse::content返回[AssistantContent]而非VecAssistantContent破坏性与其 slice 返回的同类方法一致且content成为必填字段——content字段出现之前序列化的 JSON 不再反序列化serde shadow 表示与缺失重构一并删除但 JSON wire 形状未变请求内容本地校验两条 turn driver 的每个模型调用都经由CompletionRequestBuilder::send/stream发出内部执行CompletionRequest::validate_message_content空历史或空内容消息以带索引的本地错误失败而不是远程 400。八、内部工程与依赖策略8.1 AgentConfig 共享#2326/#2327AgentBuilder、Agent、AgentRunner现在共享同一个私有AgentConfigAgentRunner::from_agent以单一单元克隆它而非逐字段复制 15 个设置——新 agent 设置不可能再编译通过却悄悄不传导到执行。按 run 的覆盖只修改 runner 克隆的 config绝不改动源 agent。私有配置内部改名default_max_turns变为已解析的max_turns: usize默认1保持一次调用的预算default_conversation_id变为conversation_id。无公开 API 变化。同时builder 的 typestate 转换与三个build()通过一个共享核心线程传递字段消除了五份拷贝中漏字段的可能擦除后的 Hook 分发与首个非Continue观察循环由一份事件清单生成阻塞与流式表面共享 agent-span/内存解析序言显式历史仍完全绕过内存流式内存加载失败仍在 agent span 下浮现。8.2 依赖下限dependency floors#2195/#2369依赖要求改为下限floors——rig 自身代码所需的最低版本裸 major或引入 rig 依赖 API 的版本而非发布时的最新 patch。Dependabot 只对区间内版本移动Cargo.lockderanged 0.5.8精确 pin 删除。CI 通过 scripts/check-dependency-floors.pydependency-floors任务按声明的下限构建整个工作区。下游用户不再需要为升级 rig 而cargo update无关 crate。8.3 LOC 整合与杂项0.42.0 的 Other 区段记录了多轮工作区级精简pass 7/8 及多次 provider/agent plumbing 合并累计净删数千行生产代码以及若干正确性修复openai 六个由 live cassette 录制发现的 wire 层缺陷#2332参见 rig-cassette 的录制回放体系 crates/rig-cassette、gemini/agent 关闭整个输出预算截断链而非仅 4096 上限#2324、移除工作区级#[non_exhaustive]#2335这也是ModelTurnFinished手工构造成为破坏点的原因。九、破坏性变更速查与迁移要点按升级到 0.42.0 需要动哪里整理Hook 构造手工构造ModelTurnFinished的代码主要是测试桩需补finish_reason/max_tokens/identity/raw字段读取该事件的行为不受影响签名变化AgentRun::record_streamed_completion_call增加 identity第二参数与 finish reason第三参数CompletionCall不再是Copy工具集ToolSetBuilder/ToolSet::builder()删除改为ToolSet::default()add_tool/add_dynamic_tool/add_portable_dynamic_tool/add_retrieved_tool消息表示持久化 JSON 采用 tagged assistant content旧形状不加载PromptResponse::content返回 slicecontent必填内容列表均为VecT完成 APICompletionModel::{Response, StreamingResponse, Client, make}、GetTokenUsage、CompletionResponse::raw_response移除分别迁移到CompletionClient::completion_model、StreamFinal::usage、raw_completion/raw_streamToolCallDelta移除tool_call_id跨 run 关联请始终使用internal_call_id行为无答案且Length/ContentFilter的回合在两条表面都报错OutputMode::Tool下这类回合立即失败而非消耗重试Nonefinish reason 不再平滑为Stop。十、结语0.42.0 的 rig-agent 用一组相互咬合的变更回答了同一个问题当模型在输出预算处截断、provider 上报怪异原因、或响应携带规范化之外的字段时agent 层如何做到既不失真、又可观测、还能让 Hook 用 provider 无关的方式采取行动。finish_reason/max_tokens的下沉、raw与 identity 的全链路携带、以及ModelHandle的类型擦除共同把截断重试从需要点名 provider 的特判变成了一个在任何模型上都可运行的普通AgentHook策略——这正是retry_on_truncation示例想传达的核心策略可移植语义不漂移。升级时请对照 MIGRATING.md 逐一核对上述破坏点。赞分享AI AgentAgent 框架RAG后端【免费下载链接】rig⚙️ Build modular and scalable LLM Applications in Rust项目地址https://gitcode.com/GitHub_Trending/rig2/rig点击查看免费下载相关推荐Coroot 架构深度解析Node Agent、Cluster Agent 与统一遥测存储的全链路可观测性设计Coroot 架构深度解析Node Agent、Cluster Agent 与统一遥测存储的全链路可观测性设计 Coroot 是一个集指标Metrics、可观测性指标监控链路追踪APMTypeScript 擦除的结构类型Erased Structural Types深度解析结构兼容、多余属性检查与编译期类型擦除TypeScript 擦除的结构类型Erased Structural Types深度解析结构兼容、多余属性检查与编译期类型擦除 本指南基于《The Co文档教程Gopeed 下载管理器完整新手指南从安装到跑满带宽的实操清单Gopeed 下载管理器完整新手指南从安装到跑满带宽的实操清单 Gopeed 是一款用 Golang 和 Flutter 开发的免费开源下载管理器同时支持网络CLI后端上一篇CyberStrikeAI如何用AI智能体重新定义安全测试的未来下一篇告别昂贵授权3款开源牙科诊所管理系统实测推荐创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑