Coding Agent痛点
- 自作主张。即使使用了plan mode/openspec一类的计划工具,agent仍然会在实际执行过程中偏离人类意图。原因包括上下文窗口大小、计划本身没有涵盖全部细节等。理想情况下agent应该能够在遇到关键决策时咨询人类,但是实际技术水平还未达到。现在human in the loop实际上要求human on the watch,这样开发效率很低。
- 作弊。这实际上是上一点的子问题。由于各模型厂商的商业需求,模型被训练得越来越倾向于长时间运行且一次性解决整个问题而不是先问人类,这对宣传和卖token都大有裨益。但这也就使得模型倾向于走捷径。在不太严重的情况下,它会留下TODO然后绕过一些case;在严重的情况下,他会通过硬编码等方式作弊。
- 遗忘/长视野问题。老生常谈的问题了。
尝试
- 由前面三点,一个自然的推论是我们应该切分子任务,降低它的Context压力以及给出更明确的指导。
- 另一个可能的方向是:更细粒度地操控agent的行为。现有的LangGraph一类的工作流系统,虽然可以在一定程度上操控agent的行为,但本质上是从构建一个multi-agent系统然后从整个系统外部去操控/观察agent的行为。
- 结合这两点,我们作如下尝试:做一个API允许写prompt,并且在prompt里允许引用程序里的表达式。
Semantic Sorting
这个例子展示了如何在程序中调用agent。这里写了一个插入排序对一个作家列表按照伟大程度排序。其中“比较两个作家谁更伟大”这个comparator是一个agent完成的。
Code
Output
Click Run.
Programming by Examples
这个例子是program synthesis领域的经典例子。给出一个DSL,以及一堆输入输出对,要求用这个DSL写一个变换器,把输入变换为输出,并且对所有例子都成立。
传统的做法是用搜索算法在DSL空间中搜索出一个正确的DSL程序。这里我们尝试用agent来做这个搜索。agent的prompt里包含了DSL定义和一个DSL的解释器。然后我们逐个给agent输入例子,要求它输出一个DSL程序对这些例子都成立。
Code
Output
Click Run.
小结
- 这两个例子展示了agent/prompt如何调用program,program如何调用agent/prompt。可以想象prompt与program互相嵌套可以形成一个无限嵌套的结构。
- 这两个例子模糊地展示了我们如何接近前面提到的两个方向:切分子任务和更细粒度地操控agent的行为。下一步我们希望把这个设计推广到更复杂的场景中,如SWE-bench之类的实际任务里,考察这些设计在实际任务中的表现。
- 在这两个例子的实现过程中,发现了一些Rust语言的特性与这个设计的摩擦:
- 当我们想把程序本身(代码原文)暴露给agent时,Rust的宏系统不够灵活。
- Rust的Generics参数个数限制了我们在prompt里Rust表达式的灵活性。Rust的泛型参数个数是有限制的,我们不能在prompt里引用任意个数的program表达式。
- 这里我们仍然是把agent当作一个黑盒子来使用的。但是根据Agent=Model+Context的观点,如果我们把Model看作常量,那么agent的行为就完全由Context决定了。我们可以把Context看作一个continuation,然后去操控这个continuation本身。