Catalyst:让AI Agent对编译代码提问并获得可验证答案

Catalyst 为 AI Agent 提供基于 LLVM IR 自动微分的确定性工程测量工具,使其无需盲目信任工具返回值。
Catalyst 是一个专为 LangChain/LangGraph 等 AI Agent 框架设计的确定性工程测量工具,核心能力是读取 Rust/C/C++ 编译器生成的 LLVM IR,通过自动微分计算程序输出相对于全部输入变量的精确导数,并在返回结果前完成独立验证。以一个拥有 52 个可调参数的数据中心冷却仿真系统为例,Catalyst 在 1.37 秒内完成 105 个验证点的全部验证,且相同源码在热浪与温和条件下的关键控制变量完全不同——证明 LLM 读源码只能判断逻辑结构,而无法知晓当前状态下各变量的实际影响强度。Catalyst 的工具接口结构化且机器可读,Agent 可请求测量但无法修改验证标准,验证产物带溯源信息可跨 Agent 流转,并可导出为已校验的 Go 与 R 实现。其定位是 LangGraph 工作流中的确定性验证节点,与 LangSmith 的可观测性能力互补。
AI Agent 的信任难题
当下的 LangChain/LangGraph 智能体在"工具调用"这件事上已经相当成熟——搜索、数据库、代码执行、API、浏览器,几乎无所不包。但一个被长期忽视的问题是:当工具返回一个数字后,Agent 通常只能选择相信它。
如果 Agent 要修改真实的工程软件,问题更棘手:它怎么知道自己"该改什么"?它可以读源码、可以推理、可以做出看起来合理的猜测。但这与真正测量程序本身的行为是两回事。一个 Rust 开发者在 Reddit 上分享的 Catalyst 项目,正是针对这个断层而设计。
Catalyst 的定位不是又一个 Agent 框架,而是给现有 Agent 提供一个确定性的工程测量工具。当 Agent 需要知道"当前到底是什么在控制这个结果、影响有多强、方向如何、这个答案能否在使用前被独立验证"时,它可以调用 Catalyst。这与 LangChain 已有的工具思路天然契合:带结构化输入输出、由模型按需选择的可调用操作。
一个数据中心冷却系统的实验
作者用了一个普通的 Rust 数据中心冷却与供电模型来演示。这个系统有 52 个可调"旋钮",包括 IT 负载、室外温度、电价、风扇设置、冷却水设置、回流、冷机效率、UPS/PDU 效率、限流阈值、碳成本、热风险限制等。
模型会模拟连续 72 小时的运行,每小时的状态都会影响下一小时——风扇随温度反应、室温向前传递、滤网逐渐堵塞、限流改变未来算力负载、冷机效率随条件变化。最终整个过程产出一个数字:总运营成本。
设想一个 LangGraph Agent 负责优化这个系统,它面对 52 个可能改动的变量。朴素的 Agent 循环通常是:读源码 → 判断哪些变量看起来重要 → 改动 → 重跑。而 Catalyst 提供的思路是:先测量程序的真实行为。
它的做法是读取 rustc -O 编译器已经生成的 LLVM IR,计算最终 72 小时成本相对于全部 52 个输入、贯穿整个耦合仿真的变化率(导数),并在返回结果前独立验证每一个导数。
在现场演示中:
- 52 个导数
- 105 个独立验证点
- 105/105 全部通过
- 总耗时 1.37 秒(含导数计算、验证与可复用产物生成)
同样的代码,不同的状态,不同的答案
最有意思的结果出现在"工作点"分析上。
在 38°C 热浪、1900 kW 负载条件下,成本的头号驱动因素是 fan_target_c(风扇目标温度),其次是 IT 负载、限流阈值、风扇下限、安全温度限制、气流和回流。52 个输入中只有 17 个承担了 90% 的实测影响。
当作者把同一个编译程序放在温和条件下(18°C、1200 kW),重要变量完全变了:UPS 效率、PDU 效率、IT 基础负载、电价、峰值电价乘数成了主角。这次只有 9 个输入承担了 90% 的影响。
关键在于:风扇目标在热浪中排第一,而在温和日子里它的实测导数是 0.0,限流控制也归零了。同一份源码,不同状态,答案截然不同。
一个读源码的 LLM 只能看到风扇控制器"看起来是重要代码"。而 Catalyst 能告诉 Agent:"在当前条件下,这是系统中最强的控制项",或者反过来:"在当前条件下,动这个控制项毫无意义。"对于智能体系统而言,这个区分极具价值。
LangGraph 循环长什么样
作者设想的图结构是:检查代码 → 调用 Catalyst → 对可控输入排序 → Agent 提出改动 → 必要时人工审批 → 应用改动 → 重跑已验证产物 → 演练回归 → 报告证据。
LangGraph 本就为混合确定性步骤与智能体决策、持久化、人在回路控制的工作流而设计。Catalyst 在其中充当确定性的测量/验证环节,而不是试图替代编排层。
演示实际执行了这个决策循环的简化版:取最强的可控输入,保持在其声明范围内,按实测导数指示的降本方向移动。建模成本从 40,033.55 → 34,355.69 → 32,769.23,降幅 18.1%。
但作者强调,比降幅更重要的是变量为何被选中——它们被选中是因为编译程序测量出它们是主导控制项,而不是因为 LLM 觉得它们的名字听起来重要。
首个验证产物一旦生成,就能在另一个点上快速评估。基准测试显示产物重跑约 12 毫秒。于是 Agent 可以做:谨慎的验证测量 → 探索 → 移动 → 重新评估 → 判断是否需要再做一次完整测量,而不必盲目地全部重算。
结构化工具接口与验证边界
Catalyst 的工具接口是结构化的而非基于提示词,暴露出 discover、validate_problem、evaluate、export_go、differentiate_llvm 等经过校验的操作。discover 调用会描述其他工具、它们的 schema、拒绝码和补救方案,因此工具型 Agent 能自行发现 Catalyst 的能力,并接收机器可读的成功或失败,而不是去抓取终端里的文字。
这意味着一次 Catalyst 调用可以返回 verified,或者返回 refused because the derivative disagrees,而不是逼着 LLM 去判断一个可疑数字"看起来对不对"。
作者还刻意加了一条边界:**Agent 无法修改 Catalyst 的验证容差,也不能重新定义什么算成功。**模型可以请求测量,但不能批准自己的测量结果。这让 Catalyst 能被放在 Agent 背后,而不至于让 Agent 既当运动员又当裁判。
产物可在多 Agent 间流转
导数结果包含源码、编译器、LLVM IR、原始计算、导数和验证的溯源信息(provenance)。演示中作者修改了存储导数中的一个字节,Catalyst 拒绝运行并返回 catalyst.artifact_tampered。
这带来一个有趣的多 Agent 模式:Agent A 测量某事,Agent B 稍后收到产物,B 不必接受"A 说这已验证"——产物可以再次被检验。
结果也不必停留在 Python/Rust 里。演示还把一个已校验的冷机计算导出为独立的 Go 和 base R 实现,两者都来自同一份优化计算,并针对 26 个 fixture 用例进行校验。于是 LangChain Agent 理论上可以编排:现有 Rust/C/C++ 代码 → Catalyst 分析 → 供服务用的已校验 Go 实现 → 供分析师用的已校验 R 实现,而 LangGraph 仍负责编排、状态与审批。
第二部分:证明一个修复真的有效
演示还包含一个本地工单服务,它平时运行正常,但在持续突发压力下开始返回 POST /orders -> 503。
Catalyst 从一次 96 请求的运行中复现故障,将其缩减为 16 请求的复现器,生成留出(held-out)场景,并对比基线与候选修复:
- 基线:16 个中 9 个错误,p99 约 245 ms
- 候选:16 个中 0 个错误,p99 约 16 ms,3/3 留出场景通过
这对编码 Agent 尤其相关。LangGraph Agent 可以提出修复,Catalyst 则给图一个确定性的门:**原始故障是否仍会复现?**而不是让"测试看起来是绿的"成为工作流的终点。
与 LangSmith 的定位差异
社区里关于调试多步 Agent、工具失败、以及"最终答案看起来没问题但早期某个工具调用其实错了"的讨论很多。Catalyst 瞄准的是这个问题稍微不同的层面:
- LangSmith 能告诉你 Agent 做了什么;
- Catalyst 能帮你确认 Agent 所依据的数值结果或工程改动,是否真的有独立验证过的证据支持。
最简版本的定位是:LangChain/LangGraph 决定何时行动,Catalyst 给 Agent 提供它无需凭空发明或盲目信任的工程测量。
作者列出的潜在用途包括:修改数值型 Rust/C/C++ 系统的编码 Agent、需要已校验导数而非 LLM 估计关系的科学/数据 Agent、判断哪些配置变量真正驱动指标的基础设施 Agent、多 Agent 系统间传递已验证计算、以及需要确定性审批门的 LangGraph 工作流等。
项目已开源(仓库地址见原文),作者也向社区抛出一个开放问题:这样一个确定性工具,作为普通 LangChain 工具、LangGraph 验证节点、还是作为 Agent 被允许应用改动前的一道门,哪种形态最有用?


