这是什么 · What this is

中文:对重新设计后的游戏(排名列表 + 调查员)逐组件的注解。7 个「站」共享同一条由「因素」参数化的生成流水线,所以这份文档拆解的是一个站的解剖结构 + 它周边的各个部件:每一部分是什么、怎么生成、用哪个模型、确切的提示词。代码:gaokao/

EN: A component-by-component annotation of the redesigned game (ranked-list + investigator). The 7 “stops” share one generation pipeline parameterized by a factor, so this documents the anatomy of a stop + the pieces around it: what each part is, how it’s generated, which model, and the exact prompt. Code: gaokao/.

✏️ 这些提示词可以直接编辑 · Edit these prompts live

中文:开发者可在 提示词编辑器里直接改下面这些生成提示词(investigate 调查员 / translate_major 看懂这个专业 / frame_test 框架测试),保存后游戏下一次调用即生效,无需重新部署。编辑器路径 = 高考服务的 /prompts(当前开发隧道:easy-justify-…/prompts,隧道为临时,以最新分享的链接为准)。保存有护栏:删掉任一必需占位符(如 {top}/{g})会被拒绝,线上游戏不会被改坏。底层:gaokao/prompts.json(可编辑)+ prompts.defaults.json(出厂默认,供 Reset)。

EN: Developers can edit the generative prompts below (investigate / translate_major / frame_test) directly in the prompt editor; saves take effect on the game’s next call, no redeploy. Editor path = /prompts on the gaokao service (current dev tunnel: easy-justify-…/prompts — tunnel is ephemeral, use the latest shared link). Guarded: dropping a required placeholder (e.g. {top}/{g}) is rejected, so the live game can’t be broken from the editor. Under the hood: gaokao/prompts.json (editable) + prompts.defaults.json (factory default for Reset).

模型与调用 · Model & call

中文

  • 模型deepseek/deepseek-chat(DeepSeek-V3,非推理)经 OpenRouter——快速档(~20 秒/次,对比 V4-Pro 的 ~50 秒),偏创意而非推理的活。覆盖项:GAOKAO_TRAJECTORY_MODEL
  • 调用gaokao/ranked.py::_llm_json()——POST 到 openrouter.ai/.../chat/completionsresponse_format=json_object,带退避的 4 次重试。下面每个部件都是这样一次调用。
  • 网络检索调查那一次调用跑网络检索 grounded(DeepSeek-V3 + OpenRouter 网络插件,plugins:[{id:web}];开关 GAOKAO_WEBSEARCH)。它有来源纪律:提示词限定权威来源,一个过滤器(_auth_sources)从展示引用里剔除新闻/UGC,若检索不到权威信息就退回 grounding 知识库。来源显示在每一站下方。

EN:

  • Model: deepseek/deepseek-chat (DeepSeek-V3, non-reasoning) via OpenRouter — the fast tier (~20s/call vs ~50s for V4-Pro), creative-not-reasoning work. Override: GAOKAO_TRAJECTORY_MODEL.
  • Call: gaokao/ranked.py::_llm_json() — POST to openrouter.ai/.../chat/completions, response_format=json_object, 4 retries w/ backoff. Every piece below is one such call.
  • Web search: the investigation call runs web-search-grounded (DeepSeek-V3 + OpenRouter web plugin, plugins:[{id:web}]; toggle GAOKAO_WEBSEARCH). It’s source-disciplined: the prompt restricts to authoritative sources, a filter (_auth_sources) drops news/UGC from the displayed citations, and it falls back to the grounding KB if nothing authoritative is found. Sources show under each stage.

延迟:推测式预取 · Latency: speculative prefetch

中文:每次调查都是一次 ~20 秒的网络检索调用。与其让学生干等,我们把它藏到他的阅读时间背后——他读一站 + 决定要不要重排所花的时间,远多于生成下一站所需的时间。

怎么运作的gaokao/ranked.py + web.py):

  1. 一站刚被送出(/rank_start/rank_next)的那一刻,一个后台线程_spawn_prefetch)就调用 RankedSession.prefetch_next()——它为当前第 1 名生成下一站,存进以 (stage_index, top_label) 为键的内存缓存里。
  2. 趁它在炖着,学生正在阅读。当他点下一站next_stage() 查缓存:命中则瞬时送出(~0 秒);未命中则当场生成。
  3. 如果他重排了第 1 名,缓存的预取是给的榜首准备的,所以它被清除——下一次点击会为新的第 1 名重新生成(轨迹仍正确地转向)。一次重排只花一次普通生成的成本。

结果:已验证——预取过的一站在 0.00 秒内送出,对比冷启动 ~23 秒。大多数点击(学生没动第 1 名)是瞬时的;只有重排才付等待的代价。

范围 / 扩展:v1 只预取当前第 1 名的下一站(主流情形)。一个自然的扩展是同时预取当前第 2 名的下一站,这样重排到第 2 名也瞬时——代价是每站多一次后台调用。默认不开(网络检索 × 2 = 更多 $/延迟);若重排被证明常见,很容易启用。

EN: Each investigation is a ~20s web-search call. Rather than make the student wait, we hide it behind their read-time — they spend far longer reading a stage + deciding whether to re-rank than the next stage takes to generate.

How it works (gaokao/ranked.py + web.py):

  1. The moment a stage is served (/rank_start, /rank_next), a background thread (_spawn_prefetch) calls RankedSession.prefetch_next() — it generates the next stage for the current #1 and stores it in an in-memory cache keyed by (stage_index, top_label).
  2. While that cooks, the student is reading. When they click 下一站, next_stage() checks the cache: a hit serves instantly (~0s); a miss generates on the spot.
  3. If they re-rank #1, the cached prefetch is for the old top, so it’s cleared — the next click regenerates for the new #1 (the trajectory still pivots correctly). A re-rank just costs one normal generation.

Result: verified — a prefetched stage serves in 0.00s vs ~23s cold. Most clicks (the student didn’t move #1) are instant; only a re-rank pays the wait.

Scope / extensions: v1 prefetches only the current #1’s next stage (the dominant case). A natural extension is to also prefetch the current #2’s next stage, so a re-rank to #2 is instant too — at the cost of a second background call per stage. Not on by default (web search × 2 = more $/latency); easy to enable if re-ranks prove common.

7 个站(因素序列)· The 7 stops (factor sequence)

中文:轨迹跟随学生的当前第 1 名;每一站是一个因素,隐式地织进去(不标注「第 1 年」):

EN: The trajectory follows the student’s current #1; each stop is one factor, woven in implicitly (not labelled “year 1”):

#key维度what it probes
1course课程与学业大一大二真正学的课、强度、扛不扛得住
2daily日常与社群校园日常、城市生活、社群归属(最常被忽视)
3interest兴趣与擅长做这些事是享受还是煎熬
4finance经济与回报学费/家庭负担、起薪与长期收入
5city城市与机会城市能级、实习就业机会、离家远近
6career职业前景对口岗位、天花板、行业周期、35岁
7cost代价与放弃选它放弃了什么(机会成本)

一站 = 一次调用 → 三个部件 · One stop = ONE call → three pieces

③ Tip「你可能没考虑过」(did_you_know) 已于 2026-06-17 退役(易产生无依据对比);④「看懂」与 ⑤ 框架测试也已退役。详见末尾 已退役 / Legacy

中文:一站由 ranked.py::investigate(factor, options, profile) 产出。一次 LLM 调用返回一个 JSON,包含全部四个页面部件。输入:因素、第 1 名的 grounded 事实(grounding.grounding_block(top))、其余 4 个候选的 grounded 事实、以及玩家画像。

EN: A stop is produced by ranked.py::investigate(factor, options, profile). One LLM call returns a JSON with all four on-page pieces. Inputs: the factor, the grounded facts of the #1 (grounding.grounding_block(top)), the grounded facts of the other 4 candidates, and the player profile.

On-page pieceJSON fieldwhat it is
① Storylineprosea ≤110-字 first-person vignette of living the #1 and hitting this factor, using real courses/jobs from the grounding
② Investigationtop_take + contender_takehow #1 fares on this factor vs one contender the model picks — now web-search-grounded against authoritative sources, so it carries real figures
③ Tip “你可能没考虑过” (已退役 RETIRED 2026-06-17)did_you_know已下线 — 曾提示本维度被忽视的信息,因易生成无依据对比而退役;见末尾 Legacy
(selection)contenderwhich of the other 4 it chose to contrast
来源sources2-3 authoritative citations (就业质量报告 / 麦可思 / .edu.cn…), news/UGC filtered out

中文所有部件的确切提示词见下方 All prompts (verbatim) 附录。 EN: The exact prompts for all pieces are in the All prompts (verbatim) appendix below.

grounding 块 · The grounding block

中文:grounding 块是带事实/观点标注的(课程 · 职业 · 路径 · 薪资 · 日常现实 · 失败模式 · 转向),取自 859-专业知识库——见 Grounding Layer。那份具体性契约禁止编造数字/雇主。

EN: The grounding block is fact/opinion-labeled (courses · careers · paths · salary · daily reality · failure modes · pivots) from the 859-major KB — see Grounding Layer. The concreteness contract forbids inventing numbers/employers.

④「看懂这个专业」按钮 (已退役 RETIRED 2026-06-17,见 Legacy) · ④ “看懂这个专业” button — RETIRED (on-demand, separate call)

中文:每站可选。ranked.py::translate_major(top) → 端点 /rank_translate。从第 1 名的 grounding 出发一次 V3 调用 → 三部分:

EN: Per stop, optional. ranked.py::translate_major(top) → endpoint /rank_translate. One V3 call from the #1’s grounding → three parts:

fieldshown as
official📋 招生简章怎么说(官腔原文模仿)
plain🗣️ 说人话(一句:到底学什么/干什么)
hidden⚠️ 没告诉你的(3-5 条真实坑:数学要求/新设专业/对口率/35岁…)

⑤ 框架稳定性测试 (已退役 RETIRED 2026-06-17,见 Legacy) · ⑤ Framing-stability test — RETIRED (formerly after stop 7)

中文ranked.py::frame_test(top, profile) → 端点 /rank_frametest。一次 V3 调用,把第 1 名放进三种说服框架重述,每一种都标注它所利用的认知偏差:

EN: ranked.py::frame_test(top, profile) → endpoint /rank_frametest. One V3 call reframes the #1 under three persuasion frames, each tagged with the cognitive bias it exploits:

framebias
恐惧框架损失厌恶
机会框架乐观偏差
权威框架权威服从

中文:学生标记每一种是否动摇了自己;框架不变 = 一个反思均衡信号;动摇则定位出一处偏差。(BE 2.0 ——ideal-preference checklist。)

EN: The student marks whether each sways them; frame-invariance = a 反思均衡 signal; wavering localizes a bias. (BE 2.0 — ideal-preference checklist.)

首尾两端(无 LLM)· Bookends (no LLM)

中文

  • 开头gaokao/quiz.py):5 项排名列表 + 一个信心评分 + 一份画像问卷——6 道精选单选题(非生成,所以瞬时),每个选项标注 8 极 dimension_vector,折叠进 PersonalityFile
  • 结尾ranked.py::ending()):完全确定性——新排名、初始→最终的位移、n_reranks、框架稳定计数 + 动摇的偏差、叶晓阳的 6 项反思均衡检查清单(gaokao/checklist.py)、以及最终对比初始的信心。

EN:

  • Starter (gaokao/quiz.py): the ranked list of 5 + a confidence rating + a profiling quiz — 6 curated MCQs (not generated, so it’s instant), each option tagged with 8-pole dimension_vectors that fold into the PersonalityFile.
  • End (ranked.py::ending()): fully deterministic — the new ranking, initial→final shift, n_reranks, the frames-stable count + biases wavered on, 叶晓阳’s 6-item 反思均衡检查清单 (gaokao/checklist.py), and final-vs-initial confidence.

重排 = 那个转向 · Re-ranking = the pivot

中文:重排侧边列表(/rank_reorder)会改变选项顺序;下一次 investigate()options[0],所以移动第 1 名会把整条轨迹转向到新的榜首,从下一站起生效。

EN: Reordering the side list (/rank_reorder) mutates the option order; the next investigate() reads options[0], so moving #1 pivots the whole trajectory onto the new top choice from the next stop on.

去哪里读它 · Where to read it

全部提示词(逐字)· All prompts (verbatim)

中文:游戏发送的每一条提示词,与构建时一模一样({...} = 插值字段)。模型:deepseek/deepseek-chat(V3);调查那次带网络插件运行。

EN: Every prompt the game sends, exactly as built ({...} = interpolated fields). Model: deepseek/deepseek-chat (V3); the investigation runs with the web plugin.

② 调查 — ranked.py::investigate()(已移至 investigator)· ② Investigation — moved to investigator

中文:调查员的当前提示词、机制、与输出处理已整理到独立页 → Investigator(原 websearch 页重命名)。 EN: The investigator’s current prompt, mechanism, and output handling now live on their own page → investigator (the former websearch page, renamed).

④ 看懂这个专业 — ranked.py::translate_major()

把高考专业「{label}」翻译给一个高中生看。基于真实背景:
{grounding_block(label)}
只输出一个 JSON 对象(简体中文),不要多余文字:
{
 "official": "一段官腔的专业介绍(模仿高校招生简章口吻,2-3句)",
 "plain": "一句说人话:这个专业到底在干什么、学完干什么",
 "hidden": ["3-5 条'招生简章没告诉你的'真实情况,如数学要求高/是不是新设专业/技术更新快/热门=竞争激烈/对口率/天坑点(取自上面的真实背景,不要编造)"]
}

⑤ 框架稳定性测试 — ranked.py::frame_test() · ⑤ Framing-stability test — ranked.py::frame_test()

学生当前第 1 选择是「{top}」。用三种"框架"重新描述这个选择,测试他的决定稳不稳——真正想清楚的选择,不会因为换个说法就动摇。三种框架各对应一个认知偏差:
- 恐惧框架(利用 损失厌恶):强调选它/不选它的可怕后果
- 机会框架(利用 乐观偏差):把它包装成不可错过的绝佳机会
- 权威框架(利用 权威服从):假托父母/老师/专家的口吻施压
真实背景(可引用,不要编造):{grounding_block(top)}
只输出一个 JSON 对象(简体中文):
{"frames":[
  {"frame":"恐惧框架","bias":"损失厌恶","pitch":"一段 45 字内、用这个框架施压的话"},
  {"frame":"机会框架","bias":"乐观偏差","pitch":"..."},
  {"frame":"权威框架","bias":"权威服从","pitch":"..."}
]}
这些是"诱导话术",用来让学生看清自己会不会被带跑;不要替他下结论。

问卷与结尾 · The quiz and the end

中文:画像问卷(gaokao/quiz.py)是精选的、非生成的——无提示词;结尾(ranked.py::ending())是确定性的——无提示词。

EN: The profiling quiz (gaokao/quiz.py) is curated, not generated — no prompt; the end (ranked.py::ending()) is deterministic — no prompt.

已退役 · Legacy (retired 2026-06-17)

中文。 以下部件在 ZH 实测后退役。机制说明保留在本页(上方,已标注 已退役)以备日后参考/复活;它们不在当前线上流程里:

  • ③ Tip「你可能没考虑过」(did_you_know) — 会编造无依据的跨校/跨专业对比(典型:声称”在 X 校比在北大更易拓展人脉”,无来源、未检索)。已从场景 UI 和 investigate 提示词中移除。现在的调查由 WTE 选定维度 → 因人而异的具体问题 → grounded 的第1名 vs 对比项 承担。
  • ④「看懂这个专业」按钮 (translate_major / /rank_translate) — 暂退;端点保留待命,日后做更有据可依的版本再启用。
  • ⑤ 框架稳定性测试 (frame_test / /rank_frametest) — 暂退;第 7 站后直接进入结尾。端点保留待命。

English. These pieces were retired after ZH’s live testing. Their mechanics are kept above (tagged RETIRED) for reference/revival; they are not in the current live flow:

  • ③ “你可能没考虑过” (did_you_know) — fabricated unsourced cross-school/major comparisons; removed from the UI and the investigate prompt. The WTE-chosen dimension → person-specific question → grounded 1-vs-contender now carries the investigation.
  • ④ “看懂这个专业” (translate_major / /rank_translate) — retired; endpoint kept dormant for a future more grounded version.
  • ⑤ Framing-stability test (frame_test / /rank_frametest) — retired; the last stop goes straight to the ending. Endpoint kept dormant.