图形工程需要编译器
人工智能可以生成组件的速度快于人类理解它们的组合执行的速度。图形使应用程序结构可见。编译器可以将这种结构转换为确定性的调度者。AI 编码创造了一种奇怪的颠倒:编写代码变得便宜,而理解所有这些代码将一起做什么的成本却在增加。一个大语言模型(LLM)可以在几分钟内添加处理程序、连接 API、引入队列、实现重试、更新某些状态并调用其他服务。每个更改在单独阅读时看起来都是合理的。问题出现在这些合理的部分互动时。应用程序的真实行为很少包含在一个方法中。它是从回调、监听器、定时器、队列、重试、生命周期钩子和状态变化组合的顺序中产生的。一个 LLM 现在能生成这种调度比人类重建它正在创建的执行模型的速度还要快。这是图形工程引起关注的一个原因。LangChain 最近使用这个术语来描述构建作为包含确定性代码、模型调用、工具和完整代理的图形的代理系统。图形约束了系统可能遵循的路径,而不是将每个决定留给 LLM。这是一个重要的改进——但使图形可见只是解决方案的一半。另一半是决定图形确切如何执行。AI 在本地编写。系统在全局执行。LLM 通常在实现本地行为方面表现得很好。假设一个应用程序接收一个交易并且必须:更新头寸 ↓ 重新计算风险 ↓ 发布结果 一个 LLM 可能生成:updatePosition(trade); publishPosition(); recalculateRisk(); 每个方法调用都是有效的,代码是可读的,且实现可以编译并通过许多测试。但是全局顺序是不正确的。在这些方法签名中没有任何东西告诉编译器风险必须在头寸发布之前重新计算。这个要求存在于代码之外的某个地方:在架构文档中、在测试中、在注释中,或者在经验丰富的开发人员的头脑中。危险的 AI 生成代码通常不是明显的胡言乱语。它是局部合理的代码,微妙地违反了全局不变性。这个问题随着时间的推移而加剧。一个提示引入了重试。另一个将工作移到执行器上。第三个添加了指标。第四个支持另一个事件类型。每个更改在孤立时可能是合理的,同时改变:执行顺序;状态可见性;可重入性;失败处理;完成语义;重放行为。应用程序逐渐发展出一个执行模型,没有任何人——也没有任何单独的提示——明确设计出来。循环不是坏人 小循环可以是完全确定的:while (running) { Event event = queue.take(); updateState(event); calculateRisk(); publishResult(); } 在相同的初始状态和相同的有序输入下,这个循环可以每次产生相同的结果。问题不是循环本质上是不可预测的。问题是现实应用程序很少只保持一个循环。随着时间的推移,updateState 会发布另一个事件。一个监听器接收它。一个定时器刷新引用数据。一个重试安排更多工作。一个框架调用生命周期方法。在另一个回调完成更新之前,一个回调观察某些状态。原始循环并没有消失。它已经分布在整个应用程序中:事件循环 ├── 监听器 │ └── 回调 │ └── 队列 ├── 计划任务 ├── 重试处理器 └── 异步发布者 仍然需要有人理解完整的序列。在传统项目中,这个人通常是一个经验丰富的开发人员,他们在几年内积累了系统的非书面模型。在 AI 生成的项目中,我们可能冒险要求一个 LLM 成为那个全球协调工程师。这是责任划分不当。图形工程使应用程序模型可见 图形用显式关系取代隐式控制流:交易 ↓ 头寸 ↓ 风险 ↓ 政策 ↓ 路由 图形表明风险依赖于更新的头寸,政策依赖于风险,路由依赖于政策决策。这样比在队列、回调和监听器中查找等效行为要容易检查得多。这也是一种自然模型,适用于将普通代码与概率组件结合的系统:请求 ↓ 分类代理 ↓ 政策验证 ↓ 人工批准 ↓ 批准的行动 分类代理可以保持概率性。周围的图形限制了模型可以操作的地方以及在允许外部行为之前必须发生的事情。当前的代理图形框架通常使这个图形明确:节点执行工作,而边或路由定义决定接下来发生什么。例如,微软的代理框架将工作流描述为执行者和边的有向图;LangGraph 类似地使用节点、状态和转移来定义代理工作流。这比隐藏工作流要好得多。
本站免费、广告极少。如果觉得有帮助,可以请我们喝杯咖啡 —— 任何金额都对持续运营有实际帮助。
☕请我喝杯咖啡