跑通 Google Flights:7 秒完成一次真实搜索
上一课装好了环境,这一课让 agent 干真活:复现官方的 Jev Ultrafast Google Flights 实录——在真实的 Google Flights 上,用一句自然语言目标完成 Zürich → London 的单程搜索。官方视频里这一趟用时 7.073 秒(1× 速度原速播放),含模型调用、城市名生成、浏览器操作和加载等待。
这一课用同一个目标跑两条路:先在 inspector 里逐决策看懂过程,再用 examples/flights.py 复现实录,让代码独立验证结果。过程中你会反复撞见一句官方提醒——DONE 不算数,独立验证才算——它就是本课的主角。
学完你能做什么
- 在 inspector 的
Google Flights · real web场景里跑完一次真实搜索,用 Choose next 逐步看清操作和目标是怎么选出来的 - 用
uv run --env-file .env python examples/flights.py --keep-open复现官方实录:搜索、独立验证、保存 trace,全程不选不订 - 说出 verify() 的 7 项独立检查各查什么(比如 origin 字段值必须是
Zürich),以及为什么 DONE 不能当成功证据 - 说清 7.073 秒的计时口径:从首次预测开始、到接受 DONE 结束,哪些在表内、哪些不在
- 知道示例日期过期时怎么同步修改目标与验证字符串,把示例变成自己的浏览器自动搜索实验
你现在的困境
- 官方视频里 7 秒搜完航班,自己一跑 inspector,页面咔咔几下就结束了——每一步 Jev 选了什么、为什么这么选,完全没看清
- 想跟别人说「这 agent 真的能跑」,但除了「它说 DONE 了」拿不出证据——万一它其实搜错了呢?
- 自己写网页自动填表脚本太痛苦:选择器全靠扒 DOM,Google 一改版就成片挂掉,还处理不了日期选择器和自动补全这种「要等页面反应」的控件
什么时候用这一招
当你需要:
- 亲眼验证 Jev Ultrafast 在真实网站上(不是本地测试页)能完成一次完整搜索
- 一个可复现、可验证的 ultrafast flights 基准跑法,之后再改目标、改网站做对比实验
- 给团队或朋友演示浏览器 agent,并且能指着 verify() 的 7 项
true说「结果核对过了」
而且不想:
- 手写 Google Flights 的选择器脚本——页面一改版就作废
- 只凭模型自己说 DONE 就宣布任务成功
🎒 开始前的准备
- 完成了 Ultrafast 安装运行:
uv sync装好依赖、.env填好TYPESAFE_API_KEY和TEXT_MODEL_API_KEY、inspector 能打开127.0.0.1:8766 - Chrome 连接过一次(首次 Start demo 时允许过远程调试;不行就回上一课跑
uv run browser-harness --doctor) - 网络能访问
www.google.com和api.typesafe.ai——这次是真的要出网 - 看过 Jev Ultrafast 原理的 8 种操作和「一次往返双决策」,看 inspector 面板不懵
- 一杯水——7 秒很快,但你今天要看很多遍
核心思路
官方拿 Google Flights 当「毕业考」不是随便选的:表单复杂(单程切换、两个带自动补全的城市框、日期选择器、搜索按钮)、加载真实(结果页要等)、而且结果可机器验证——URL、字段值、航班列表全在结果页上。
这一课的三件事:
1. 一句话目标就是全部输入。 README 原话:策略里没有站点专属的动作脚本、没有预制的字段串(There are no site-specific action scripts or prepared field strings in the policy)。你要提供的就是一个起始 URL 加一句目标,其余全靠每轮观察动态生成的编号元素表——所以同一套策略换个 URL 和目标就能去开 Wikipedia(第 5 课的例子)。
2. 7 秒是「决策循环」的秒数,有明确口径。 官方视频 7,073 ms:从首次预测开始(初始页面观察之后),到接受 DONE 结束;含模型调用、文本生成、浏览器操作、stale 决策和加载等待;不含浏览器启动、初始导航、事后独立验证。你跑出来的秒数和它对不上很正常——口径细节下一课性能解读拆。
3. DONE 不算数,verify() 才算。 README 写得直白:A DONE choice still requires independent outcome verification(选了 DONE 仍需独立的结果验证)。examples/flights.py 里就带着这么一个 verify()——它读的是结果页面本身,不是模型的回答。
同一个目标,两条跑法,各有分工:
inspector(uv run jev) | 脚本(examples/flights.py) | |
|---|---|---|
| 用途 | 用眼睛看决策 | 用秒表量 + 自动验证 |
| 截图 | 开(demo 显式传 screenshots=True) | 不开(Agent 默认 screenshots=False) |
| 结果验证 | 你自己看 | verify() 7 项自动检查 |
| trace | 面板 Export trace ↓ 手动导 | 自动存进 artifacts/flights/latest/ |
| 适合 | 第一次跑、逐步慢放 | 复现实录、改目标做实验 |
💡 为什么 inspector 不适合计时
README 明说默认 agent 循环不截图,inspector 是「主动开了截图」的那个(The inspector opts into screenshots)。截图要渲染、要传输,走的还是本地 HTTP 服务——想复现官方秒数,用本课第 4 步的脚本跑法。
跟我做
第 1 步:读懂目标——这次 Google Flights 搜索要满足什么
为什么:你要给 Ultrafast 的输入就是一句自然语言目标(外加一个起始 URL),没有站点脚本、没有字段清单。目标里每个短语都是 agent 要满足(并且大多被验证)的要求——先把它拆开,后面每一步才知道自己在看什么。
示例目标(examples/flights.py 的 GOALS,也是 inspector 任务框预填的那段):
Find one-way flights from Zurich to London on September 20, 2026,
for one adult in economy. Stop when matching flight options are visible.
Do not select or book a flight.中文对照:查找 2026 年 9 月 20 日从 Zurich 到 London 的单程航班,一名成人、经济舱;看到匹配的航班选项就停;不要选择或预订航班。
把目标逐句拆开,每条要求对应一个任务,其中大半还被 verify() 盯着:
| 目标里的要求 | agent 要做什么 | verify() 对应检查 |
|---|---|---|
| one-way(单程) | 把 ticket type 切到 One way | one_way:字段值 == One way |
| from Zurich | 在 Where from? 框输入并选中自动补全建议 | origin:字段值 == Zürich |
| to London | 在 Where to? 框输入并选中建议 | destination:字段值 == London |
| September 20, 2026 | 点开 Departure,点日期再确认 | date:字段值 == Sun, Sep 20;year:URL 或页面文本含 2026-09-20 |
| one adult in economy | 按指令「设置每个要求的控件」核对乘客与舱位 | 无对应检查(7 项里不含这项) |
| Stop when … visible | 结果可见且全部要求满足后才 DONE | results:至少一个 Select flight,且都含 Sunday, September 20 |
| Do not select or book | 到结果页为止,不点任何航班 | 示例文档字符串:never selects or books a flight |
乘客与舱位没有对应的 verify 检查——想核这一项只能自己看结果页。
两个细节值得注意:
- 起始 URL 是
https://www.google.com/travel/flights?hl=en——?hl=en把界面钉在英文。verify() 断言的字段标签(Where from?、Select flight)都是英文,换语言跑,验证就对不上了。 - 目标里写
Zurich(无变音符),verify 期望字段值是Zürich(有变音符)——中间隔着「输入 → 选中自动补全建议」这一步,建议选中后字段里留下的是 Google 自己的写法。模型指令也要求:A typed query still needs its matching autocomplete suggestion selected.
你应该看到:目标拆解后,要求大多能落到一个机器检查上——单程、两个城市、日期、结果列表都有;「一名成人经济舱」没有对应检查,「不选不订」则由示例自身的设计保证(文档字符串原话:never selects or books a flight)。
- 能说出目标里至少 3 项要求对应的 verify 检查
第 2 步:在 inspector 里发起 Jev Ultrafast Google Flights 搜索
为什么:第一次跑真实网站,建议先用 inspector——它能逐步暂停、能看每个候选的概率,出了问题知道卡在哪一步。
在仓库根目录启动(上一课装好的环境):
cd jev-ultrafast
uv run jev你应该看到:终端打印 Jev Ultrafast: http://127.0.0.1:8766。浏览器打开这个地址,任务框里已预填第 1 步那段 Zurich → London 目标。
然后:
- 场景下拉框确认是
Google Flights · real web(默认即是;另两个· fixture场景是本地页面,不出网) - 点 Start demo,按提示允许 Chrome 远程调试(界面状态会显示 Opening a fresh browser…)
- 点 Run automatically 一口气跑完,先感受一次完整搜索
你应该看到(README 原话列的四样):编号元素(numbered elements)、操作概率(operation probabilities)、目标概率(target probabilities)、已执行动作(executed actions)。右侧指标区还会给出每次决策的 Decision time、Target confidence 和选中的 Operation;浏览器面板里,Google Flights 页面开始被自动操作。跑完后状态栏会显示 Jev reports complete · inspect the page——界面都在提醒你:去检查页面,别只看它说完成了。
- inspector 出现编号元素和两列概率,Chrome 里的 Google Flights 页面开始变化
第 3 步:Choose next 慢放,看清 7 秒里的决策链
为什么:7 秒太快,自动跑完什么也看不清。Choose next 在执行前暂停,把「候选怎么排」摆出来——这是理解 agent 的最佳视角。
再点一次 Start demo 重开一轮,这次逐步走:点一次 Choose next 只预测、不执行——Indexed elements 区会按概率排序(界面提示从 Choose next to rank 变成 Ranked by Jev),NEXT ACTION 区显示选中的操作和目标;看明白了点 Execute choice 才真正执行。想看得更慢,勾上 Slow motion 再 Run automatically——每步会在「预测」和「执行」之间停 450 ms。
对照目标,典型的一轮决策链长这样(动作类型出自模型指令与元素表;具体顺序每轮可能不同):
| 阶段 | 你会看到什么 | 背后在发生什么 |
|---|---|---|
| 切单程 | CLICK 类元素(如 Change ticket type,README 元素表示例第一条) | 操作和目标在一次 TypeSafe 请求里同时定 |
| 输城市 | TYPE_TEXT 出现,短暂停顿后框里出现文字 | 文本 helper 生成字符串(官方实录:Zurich 用了 581 ms、London 346 ms);输入后等自动补全建议(≤200 ms)再选中 |
| 选日期 | CLICK Departure → 点某天 → 点确认 | 指令原文:For date pickers, CLICK the field, date, then confirmation |
| 提交 | CLICK Search | 指令原文:If Search/Submit is visible and the required fields are ready, CLICK it immediately |
| 等结果 | WAIT | 指令原文:WAIT only when … submitted results are still loading |
| 收工 | DONE | 指令原文:DONE requires visible evidence that ALL requirements are satisfied |
官方实录的账单(performance.md):17 次 Jev 请求、10 次交互加 1 次显式 WAIT、2 次文本生成;Jev 单次决策中位延迟 178 ms;搜索在第 5.217 秒执行,7.073 秒完成。
你应该看到:跑完后别急着关页面,三个地方值得看——
Decision trail:每个已执行动作一行,带延迟、概率和 Page changed / No change observed——这就是本次运行的 trace,Export trace ↓ 可导出成
typesafe-browser-trace.jsonDecision trail 标题旁的计数器:
N actions · X.XX s——这次运行的动作数和累计秒数页面底部折叠块 What the model sees:发给 Jev 的结构化 state 原文(不是截图);面板里还有一句界面原话值得记住:Operation and target are separate choices in one request. Text is generated only for TYPE_TEXT.
用 Choose next 走完至少 3 步,说出每步选中的操作和目标
打开过一次 What the model sees,看到的是结构化 state 而不是截图
第 4 步:用 examples/flights.py 复现官方实录
为什么:inspector 是给你看的(开了截图),官方秒数出自不带截图的脚本跑法;flights.py 还多做两件事——跑完自动验证,并把 trace 存盘。
在仓库根目录:
uv run --env-file .env python examples/flights.py --keep-open两个参数都不能省:
--env-file .env:flights.py 不会自己读.env(demo.py 才有这段逻辑),Key 全靠 uv 注入——漏了它,第一个决策就抛KeyError: 'TYPESAFE_API_KEY'(model.py 用os.environ["TYPESAFE_API_KEY"]直接取值)--keep-open:跑完不调用agent.close(),Chrome 里的结果页留着,方便你亲眼核对
你应该看到:终端先滚动打印进度,每行三个字段——累计毫秒、状态、刚执行的动作(动作标签来自页面元素,每轮不同):
<毫秒> ready <刚执行的动作标签>
<毫秒> ready <刚执行的动作标签>
……
<毫秒> done <最后一个执行的动作标签>(示意;毫秒数与动作以你本次运行为准,最后一行状态是 done)
然后打印验证结果 JSON(结构出自 flights.py,visible_flights 的内容取决于当天的可选航班):
{
"passed": true,
"checks": {
"search_page": true,
"one_way": true,
"origin": true,
"destination": true,
"date": true,
"year": true,
"results": true
},
"visible_flights": [
"…当天可选航班的 Select flight 按钮,每条都含 Sunday, September 20…"
]
}如果 7 项里有任何一项 false,脚本以非零码退出,最后是这行:
Final page did not satisfy the route/date checks同时,artifacts/flights/latest/ 下多了两个文件(想换目录就加 --output <目录>):
| 文件 | 内容 |
|---|---|
state.json | 完整快照 + verification 结果(7 项检查与可见航班都在里面) |
session.json | 浏览器 target 与 session 信息 |
artifacts/ 在仓库 .gitignore 里,trace 不会被误提交。
- 终端最后打印出
"passed": true的 verification JSON artifacts/flights/latest/下有state.json和session.json- Chrome 结果页还开着,出发地/到达地/日期分别是 Zürich / London / Sep 20
第 5 步:读懂 verify() 的 7 项独立检查——DONE 不算数
为什么:这是本课最重要的一步。flights.py 的 verify() 文档字符串写得明明白白:Independent checks on the resulting page, not the model's DONE answer——它查的是结果页面,不是模型的自我报告。
7 项检查逐个看(键名与期望值都出自 flights.py 源码):
| 检查项 | 断言 | 通过意味着 |
|---|---|---|
search_page | hostname 是 www.google.com 且路径是 /travel/flights/search | 真的到了搜索结果页 |
one_way | Change ticket type. One way 字段值 == One way | 单程设置生效了 |
origin | Where from? 字段值 == Zürich | 出发城市选对了建议 |
destination | Where to? 字段值 == London | 到达城市选对了建议 |
date | Departure 字段值 == Sun, Sep 20 | 日期选的是 9 月 20 日 |
year | URL 的 tfs 参数 base64 解码后含 2026-09-20,或页面文本含 departing 2026-09-20 | 年份也是 2026(两条判定路径,任一命中即可) |
results | 至少一个动作标签含 Select flight,且每条都含 Sunday, September 20 | 结果列表真的渲染出来了,而且都是那天的航班 |
三个设计细节,值得抄进你自己的任务:
- 验证读页面,不读模型。 DONE 只是 Jev 的一个选项;verify() 用 urlparse、字段值、页面文本把结果重新核对一遍。
- 年份检查有两条路。 结果页 URL 带一个
tfs查询参数,verify() 把它 base64 解码后找2026-09-20;解码失败就回落到查页面文本departing 2026-09-20。字段值Sun, Sep 20不带年份,所以年份要单独查。 - 检查是硬编码的。 7 项全部锚定示例的航线和日期——改目标就必须同步改 verify()(见踩坑提醒第 2 条)。
任一项不过,passed 为 false,脚本 SystemExit 退出。以它为准,不以 DONE 为准。
你应该看到:打开 artifacts/flights/latest/state.json,里面的 verification.checks 与上表 7 个键名一一对应——每个 true 背后都是一条对结果页面的断言,而不是模型的自我报告。
- 能说出 7 项检查中至少 3 项的断言内容
检查点 ✅
- inspector 的
Google Flights · real web场景跑完,Decision trail 里列出已执行动作,状态栏显示 Jev reports complete · inspect the page examples/flights.py --keep-open跑完,终端打印"passed": true的 verification JSONartifacts/flights/latest/下有state.json和session.json- 全程没有选航班、没有订票——示例只搜不订(flights.py 文档字符串原话:never selects or books a flight)
- 能说清为什么 DONE 不能当成功证据,以及 verify() 查的是结果页面还是模型回答
踩坑提醒
❌ 把 DONE 当成功的证据
现象:agent 状态到 done,你以为任务成功了。
原因:README 在 Evidence and limits 里写得很直白——A DONE choice still requires independent outcome verification。DONE 是模型的选项,不是页面的状态;官方还补了一句:有效操作也可能是错的(a valid operation can still be wrong)。inspector 的状态文案自己也写着 inspect the page。
正确做法:看 verify() 的 passed,或亲手核对结果页(航线、日期、结果列表)。以后写自己的浏览器任务(第 5 课)时,同样要配一个独立验证。
❌ 示例日期已成过去时,results 检查过不了
现象:跑 flights.py,results(或 date/year)是 false,脚本以 Final page did not satisfy the route/date checks 退出。
原因:示例的 GOALS 和 verify() 都锚定 2026-09-20——verify() 里有四条日期断言:Sun, Sep 20、2026-09-20(tfs 解码)、departing 2026-09-20、Sunday, September 20。本页更新于 2026-11-04,这个日期已经过去;Google Flights 对过去日期的行为,信息源里没有说明。
正确做法:只想复现官方跑法,原样执行即可(脚本流程与验证逻辑都会照常执行,能否通过取决于 Google 对过去日期的行为);想看到真实的可选航班,把 GOALS 里的日期换成未来日期,并同步改 verify() 里的四条日期字符串——注意 Sun, Sep 20 带星期缩写,新日期的星期要真实对应(2026-09-20 恰好是周日,所以原文是 Sun)。
❌ 不带 --env-file .env 就跑脚本
现象:uv run python examples/flights.py 一启动就抛 KeyError: 'TYPESAFE_API_KEY'。
原因:demo.py 启动时会自己读当前目录的 .env,flights.py 没有这段逻辑——环境变量全靠 uv run --env-file .env 注入。model.py 用 os.environ["TYPESAFE_API_KEY"] 直接取值,缺了就是 KeyError。
正确做法:命令逐字用 README 的写法——uv run --env-file .env python examples/flights.py --keep-open,并且在仓库根目录执行。
❌ 拿自己跑出的秒数质疑「7 秒」
现象:你跑出 8 秒、9 秒,inspector 里甚至十几秒,觉得官方数字有水分。
原因:三个口径差异——① 官方计时从首次预测开始(初始页面观察之后)、到接受 DONE 结束,浏览器启动、初始导航、事后独立验证都不在表内;② Google、网络、缓存都是活的,官方对比实测的 3 次优化版运行就分布在 6.964–7.913 秒;③ inspector 开了截图,本来就不是计时跑法。
正确做法:比较前先对齐口径(脚本跑法、同 URL 同目标);计时边界的完整拆解见下一课 性能解读。
进阶技巧(可选)
换 --output 保留多轮运行。 默认输出目录是 artifacts/flights/latest,反复跑会覆盖;加 --output artifacts/flights/run2 之类就能把每轮 trace 分开存,方便对比。
录制你自己的「官方视频」。 仓库带 scripts/record_flights.py <新目录>,按 README 说明可捕获带原始浏览器时间戳的运行。注意 README 的提醒:live 示例和录制脚本都会产生付费 API 调用。
对照官方视频看。 仓库里的 docs/demo.mp4 就是 7,073 ms 那次实录(1× 速度);inspector 页面顶部的 Watch the real Google Flights run 链接直接指向它(demo 服务把它路由在 /demo.mp4)。视频里的元素标签是渲染时事后加的,不驱动浏览器。
改目标试试别的搜索。 inspector 任务框支持 1–2000 字符的自定义目标(demo 的校验规则)——比如换个日期或航线。改乱了想恢复默认?把场景下拉切走再切回来,目标框会自动重填默认文本。注意:inspector 不做验证,改了目标要自己核对结果;脚本要验证就得连 verify() 一起改。
本课小结
✅ Jev Ultrafast Google Flights 实录有两条路复现:inspector 开截图、用眼睛看(Choose next 逐步慢放、看概率排序);flights.py 不开截图、用秒表量 + 自动验证。
✅ 一句自然语言目标就是全部输入——没有站点脚本、没有预制字段串;?hl=en 把界面钉在英文,verify() 断言的标签才对得上。
✅ 7.073 秒有口径:首次预测开始、接受 DONE 结束;含模型调用、文本生成、浏览器操作、stale 决策、加载等待;不含浏览器启动、初始导航、事后验证。
✅ DONE 不算数:verify() 对结果页面做 7 项独立检查(search_page / one_way / origin / destination / date / year / results),任一不过脚本直接 SystemExit。
✅ 示例锚定 2026-09-20:日期已过时要么接受 results 可能空,要么目标与 verify() 的四条日期字符串一起改。
关键命令与参数回顾:
| 命令/参数 | 推荐值 | 说明 |
|---|---|---|
uv run jev | 仓库根目录执行 | inspector,场景选 Google Flights · real web |
uv run --env-file .env python examples/flights.py --keep-open | 逐字用 | 复现实录 + 自动验证 + 存 trace,浏览器留着 |
--env-file .env | 必带 | flights.py 不自己读 .env,漏了抛 KeyError |
--keep-open | 建议带上 | 不加则跑完自动关浏览器 |
--output | artifacts/flights/latest(默认) | 换目录可保留多轮运行 |
下一课预告
跑通之后,下一课 性能解读:7.073 秒是怎么测出来的把秒表拆开看。你会学到:
- 计时边界的完整定义:从首次预测到接受 DONE,哪些环节在表内、哪些不在
- 一次实录的完整账单:17 次 Jev 请求、中位延迟 178 ms、2 次文本生成、搜索在第 5.217 秒执行
- 官方对比实测:任务时间 9.450 → 7.092 秒、协议调用 1,092 → 101,以及为什么官方自己说这只是小样本
- 成本口径:90,558 输入 token、文本 helper 两次调用 $0.00006272(不含 TypeSafe 计费)
附录:源码参考
点击展开查看源码位置
更新时间:2026-11-04(仓库 github.com/browser-use/jev-ultrafast,本地信息源 sources/jev/repo-ultrafast/)
| 功能 | 文件路径 | 关键位置 |
|---|---|---|
| 示例目标与起始 URL | examples/flights.py | L11–15 URL(google.com/travel/flights?hl=en)、GOALS(与 static/index.html 预填目标一致) |
| 独立验证 7 项检查 | examples/flights.py | L18–38 verify():search_page / one_way / origin / destination / date / year(tfs 解码或页面文本两条路)/ results;L63–64 验证不过 SystemExit("Final page did not satisfy the route/date checks") |
| 命令行参数与 trace 保存 | examples/flights.py | L43 --output(默认 artifacts/flights/latest);L44 --keep-open;L54–59 保存 state.json / session.json |
| 运行命令与「不选不订」 | README.md | 「Use the library」:uv run --env-file .env python examples/flights.py --keep-open … does not select or book a flight |
| 7,073 ms 计时口径与 DONE 警告 | README.md、docs/performance.md | README「Evidence and limits」:Timing starts after initial page observation…;A DONE choice still requires independent outcome verification;performance.md「Where the time went」:计时到 accepted DONE choice 结束,browser setup / initial navigation / post-run verification 在表外 |
| 实录统计与对比数据 | docs/performance.md | 「Where the time went」:17 请求 / 10 交互 + 1 WAIT / 2 次 helper / 中位 178 ms / 5.217 s / 7.073 s;「Matched runtime comparison」:6.964 / 7.913 / 7.092 |
| inspector 场景与预填目标 | jev_ultrafast/demo.py | L44–62 command():scenario flights → Google Flights URL;goal 1–2000 字符;screenshots=True;L88–91 /demo.mp4 路由 |
| 面板控件与状态文案 | jev_ultrafast/static/index.html + app.js | index.html L33–37 场景下拉、L40–44 预填目标与 Start demo、L52–59 Slow motion / Choose next / Execute choice / Run automatically、L123–126 Decision trail 与 Export trace、L132–136 What the model sees;app.js L82–88 状态文案(Jev reports complete · inspect the page)、L160–168 Choose→predict / Execute→act、L173–186 Slow motion 每步停 450 ms、L217–235 导出 typesafe-browser-trace.json |
| 决策循环与状态 | jev_ultrafast/agent.py | L163–165 run();status ∈ ready/predicted/done/blocked;L102–104 MAX_STEPS=60 动作预算;L153–157 连续 3 次无效动作 blocked |
| 模型指令原文 | jev_ultrafast/questions.py | NEXT_ACTION(日期选择器 CLICK 字段/日期/确认、自动补全建议、DONE 需可见证据、WAIT 条件、Search 立即点);MAX_STEPS = 60 |
| Key 读取方式 | jev_ultrafast/model.py | L119 os.environ["TYPESAFE_API_KEY"](缺失抛 KeyError);L160–168 field_text() 的 TEXT_MODEL_API_KEY 检查 |
| 文本 helper 输出约束 | jev_ultrafast/model.py | L187–193 输出必须是单键 {"text": …}、非空、≤2000 字符,否则不键入 |
| trace 不进 git | .gitignore | .env、.venv/、artifacts/ 均被忽略 |
| 录制脚本 | scripts/record_flights.py | README「Development」:captures original browser timestamps;live 示例与录制脚本产生付费 API 调用 |