问题
你在做一个浏览器 agent(自己在网页上操作的程序):给它一个目标,它一路点过去。第一版你让模型直接写一个 CSS 选择器 —— 那是一小段用来在页面上指认某个元素的代码 —— 告诉你该点哪里。
它回了一句「#login > button.primary」。这一页根本没有 login 这个 id;换一页,它写的 class 同时匹配三个按钮;再换一页,它给的选择器干脆有语法错。三次错法全不一样,而且没有一种能在点下去之前验出来。
你想加个校验,却发现没东西可校验:模型能写出来的字符串有无穷多种,你没法提前列出「哪些是对的」。错误空间没有边。
解决方案
菜单模式说:让代码把这一页上能选的东西列出来、编上号,模型只回答「第几个」。
40 个可见元素就是 40 个选项,每个配一句短标签 —— 按钮上写的那几个字。答案空间从无穷变成 40,而「12」这个答案一行代码就能验:它在不在 0 到 39 之间。
菜单上永远多留一项「都不是」。没有合适的候选时,让模型能明说,而不是在 40 个不对的里面挑个最不难看的。列表如果截过 —— 页面上有 300 个元素你只放了 240 个 —— 要在发过去的材料里写明,因为模型看不见的东西等于不存在。
问 · 答 · 拍
- 问把页面文字和 40 个编了号的元素放进去,问「下一步该点第几个」。
- 答12 号 0.71,7 号 0.12,「都不是」0.03。
- 拍12 号最高,而且它确实在这 40 个里面,代码就去点它。
名字从哪来
菜单。在餐馆你只能点菜单上印着的,厨房不会因为你说「来个店里没有的」就现做。菜单还替你把话说清楚了:服务员不用猜你说的「那个辣的」到底是哪一道。
什么时候用
候选能由代码列出来,数量在几百以内,每个都能用一句短标签说清楚。
什么时候别用
正确答案得有人写出来才存在 —— 搜索框里要填的词、提交信息的正文。或者候选超过 255 个,又没法切成几块分别问。
做这件事的项目
- jev-ultrafast — 浏览器代理:Jev 决定每个动作和元素,仅在输入文本时调用语言模型。
- jev-browser — 由 TypeSafe 的 Jev 模型驱动的快速、低成本浏览器自动化。
- jev-review — 由 Jev 把关的分阶段代码审查工作流与本地仪表盘。
看谁的代码
- jev-ultrafast
jev_ultrafast/model.py:48-78代码给页面上每个元素编号、按操作分组,模型见到的只有号码,碰不到选择器,也碰不到坐标。
- jev-browser
lib.ts:89-99页面上抓到的可点元素被写成 click_e3、type_e1 这样的名字,模型只能在这些名字里选一个。
- jev-review
src/review/judgments.ts:180-191一次改动的每一块是菜单上的一个选项,另外多留一个「都不是」。
想看代码
const els = visibleElements(page).slice(0, 40); // 代码来列,有上限
const menu = Object.fromEntries(els.map((e, i) => [String(i), label(e)]));
menu.none = "None of these"; // 永远留这条后路
const { answers } = await jev(
{ goal, page: pageText, listWasCut: els.length === 40 },
{ target: choice("下一步该点哪个元素?", menu) },
);
const pick = answers.target.choice;
if (pick === "none" || !(pick in menu)) return escalate();
await els[Number(pick)].click();jevbooks 怎么认出它
我们怎么知道一个项目用没用这一招?拿它的 README 问 Jev 一句话,Jev 给个百分比,数字越高说明越像。你要抄这道题,记住一点:别问「这个项目好不好」,要问「有没有讲到这一件具体的事」。
{
"candidate-enumeration": {
"type": "noul",
"instructions": {
"what": "Does the text describe code listing the candidates such as DOM nodes, diff hunks, or files, and Jev choosing an index among them?",
"not_for": "Asking the model to produce a selector, coordinate, or free-text answer."
},
"criteria": {
"true": "The text explicitly describes this. One sentence is enough; other content does not cancel it.",
"false": "The text does not describe this."
}
}
}