我开始编写提示词,向模型解释贪吃蛇实验。
提示词描述神经网络、可调参数、参数范围、当前配置,以及看起来相关的实验历史。Fr3d 然后选择一个参数值,通过 Snake Lab API 提交新实验。
整个过程形成一个循环:
提示词 → LLM → 配置 → Snake Lab → 结果 → 提示词
令人惊讶的是,它很快就跑通了。
虽然算不上优雅。
但真的能工作。
我已经有一个可用的贪吃蛇配置,最高分大约是 38;而且经过几个月对 RNN 的手动试验,我知道还存在更好的配置。所以 Fr3d 面对的是一个真实的问题。
这不只是让 LLM 生成看起来像样的 JSON。
它必须根据真实实验结果作出决策。
差不多在这个时候,我试用了 Qwen 2.5 VL,也就是模型的视觉语言版本。
这个想法很诱人。
也许我不必用数字描述所有内容,而可以把贪吃蛇实验的图表甚至图像交给模型,让它通过视觉分析发生了什么。技术上确实可行,但我很快遇到更重要的问题。
视觉模型的推理能力不如 Qwen 3.5。对于这个应用来说,推理能力远比视觉重要。
Qwen 3.5 还能展示推理过程,这对提示词开发极其有帮助。我能看到模型认为我在要求它做什么。有时候,它几乎是在明确告诉我:
“这提示词根本不合理。”
当然,它未必会真的这么说,但意思就是这样。
有时我要求它选择一个未使用的参数值,却从推理过程中发现,所有合法值其实都已经用过了。有时我遗漏了模型显然需要的某段实验历史。还有时我用自己觉得非常清楚的数据结构组织信息,却让模型难以判断哪些数值可以直接比较。
阅读推理过程使这些缺陷显露出来。
这大概是我对提示词工程的第一堂真正的课。一个糟糕的回答,不一定是模型的问题。有时只是你提出了一个糟糕的问题。
开发 Fr3d 之前,我主要把提示词看成指令。到了项目后期,我越来越觉得它们更像接口。
模型只能根据你提供的信息进行推理。信息结构很重要,术语很重要,约束很重要,所包含的历史记录也很重要。
如果你让模型从搜索空间里选择一个值,它就必须知道搜索空间是什么。如果必须避开之前测试过的值,它就需要可靠的已测试值记录。如果不同随机种子的结果不能直接比较,提示词就必须明确这一点。如果提示词里有互相矛盾的要求,LLM 遇到的问题,就和程序员面对矛盾规格说明时一样。
提示词已经成了系统架构的一部分。
大约一周后,Fr3d 跑起来了。
但代码糟透了。
并不是因为某个模块特别复杂,而是因为我一边弄清系统究竟应该做什么、一边研究底层代码,程序就这样自然生长起来了。
这就是熟悉的原型阶段:软件从技术上说能工作,但理解它为什么能工作却越来越困难。这时,我又意识到自己做错了另一件事。
我一直把日志和报告视为附加在实验周围的功能。这个顺序完全反了。对于自主实验系统来说,可观测性就是实验的一部分。我需要轻松回答这些基础问题:
一堆日志文件在技术上也许能回答这些问题。但“技术上能找到答案”和“系统真正容易理解”,完全是两码事。下一代系统必须从一开始就围绕报告能力设计。
Fr3d 还教会了我如何识别自然的模块边界。
有些部分是通用的:
另一些则是这个实验特有的:
在 Fr3d 中,这两类职责纠缠不清。当我看清这个区别,下一版架构就变得明显多了。
面对一个能运行的原型,人总想继续修修补补:重构一部分,移动一个函数,抽取一个类,整理提示词,删除重复代码。
但我决定不这样做。
Fr3d 已经完成了自己的使命。它证明了想法可行。更重要的是,它让我了解真正的难点在哪里。这些信息极有价值,而且很难通过事先设计完美架构得到。所以,我保留教训,抛弃原有架构。
我从 Fr3d 得到的结论不是动手之前先把一切设计正确,反而几乎正好相反。
Fr3d 很凌乱,但它确实能用。正因为它能用,我终于知道下一步该建造什么。