第 19 章

第 19 章:游戏——Gaming

第 19 章:游戏——Gaming

游戏是决策层最自然的试验场:规则写在代码里、状态能被读出来、可选动作是有限集合,而且每一步都要在肉眼可见的时间里给出判断。本章看 Jev 如何在不生成一行文本的前提下驱动角色、裁决对局,并支撑成千上万次模拟。

游戏里最难写死的判断

游戏开发者对"判定"这件事并不陌生,难的地方在于,很多判定没法用规则优雅地写出来。一个 AI 队友该不该开团?这句话在数值上可以拆成血量、位置、技能冷却,但真正决定它的是"当前这个局面像不像一个值得打的团"——这是一句语义判断,代码判不了,而传统做法是堆一堆启发式权重,调起来痛苦,上线后还常常"看着就不对"。

常见的补救办法是把判断丢给大模型:"根据以下状态,输出一个动作。"问题是游戏对输出有硬约束。大模型可能返回一段解释、一个不存在的动作,或者一个已经冷却中的技能。你必须在外面写解析器和校验器,把一个开放文本硬塞回封闭的动作空间。这和我们在第 1 章看到的错配是同一个。

Jev 的切法很直接:动作空间本来就是有限的,那就把它声明成一道 Choice 题的选项。模型不生成动作名,它只能从你给出的选项里选一个。于是"选出一个不存在的动作"这件事从"不太可能"变成"不可能"——这是一个类型系统的性质,不是调参的结果。

看一个 NPC 该做决策的最简形态:

def npc_turn(state_text: str, legal_actions: list[str]) -> str:
    d = client.systemone(
        state=state_text,                 # 血量、位置、敌人距离、队友状态……
        questions={
            "action": {
                "type": "choice",
                "question": "这一回合,这个角色最该做什么?",
                "choices": legal_actions,  # 只可能是代码认为合法的动作
            }
        },
        instructions="你是这个角色的战术决策器,优先保证存活,其次推进目标。",
    )
    return d["action"].choice

控制流仍然在代码里:legal_actions 由引擎按规则算出来,Jev 只负责在合法集合里选出当下最合理的一个。角色的"性格""战术偏好"写在 instructions 里,"能不能做"由代码保证,"该不该做"由决策层判断。这三件事第一次被干净地分开了。

详解一:jev-torneo-animales——一场 0.01 美元的锦标赛

jev-torneo-animales(hectorlcastro09)把"守擂制锦标赛"做成了一个近乎纯粹的决策循环。参赛者是最多 2,569 种动物,每一场对决都是 Jev 的一道 Choice:在两个名字之间选一个赢家,而胜负规则(陆地、水域、空中三套)就写在 state 里。

它有一个值得学的工程技巧:不逐场调用,而是一次性把冠军和接下来 K 个挑战者全问出来。一旦冠军在某一轮落败,后面那些"推测性"的回答直接丢弃。

def run_fight(champion: str, challengers: list[str], arena: str) -> str:
    # 一次请求把冠军与接下来 K 个挑战者全部问完
    questions = {
        f"vs_{i}": {
            "type": "choice",
            "question": f"按{arena}规则,{champion} 对 {challenger},谁赢?",
            "choices": [champion, challenger],
        }
        for i, challenger in enumerate(challengers)
    }
    d = client.systemone(
        state=f"守擂制锦标赛。规则场地:{arena}。当前冠军:{champion}。",
        questions=questions,
        instructions="只依据两种动物在给定场地下的实际对抗能力判断胜负。",
    )
    for i, challenger in enumerate(challengers):
        if d[f"vs_{i}"].choice == challenger:   # 冠军落败
            return challenger                    # 丢弃后面所有推测回答
    return champion                              # 冠军守擂到底

实测结果是1,999 场对决在约 16 秒内跑完,总成本约 0.01 美元。这个数字比任何论述都更能说明问题:当你把"每一步都要做一次判断"的任务交给决策层,单次成本低到可以让"成千上万次模拟"变成一件随手可做的事。如果用生成模型逐场写一段"XX 因为……所以获胜"的解释,成本会是另一个量级。

详解二:Jev Chess——让非法着法变成不可能

Jev Chess 是一个很有趣的玩法:一张共享棋盘,整个互联网集体轮流执子,对手是 Jev。它把"类型安全"这件事展示得最直白——每一个合法着法都是同一道 Choice 题的一个选项,因此"走一步非法棋"在结构上就是不可能的,而不是"被拒绝"。

legal = board.legal_moves()            # 引擎算出的全部合法着法
d = client.systemone(
    state=board.to_fen(),
    questions={
        "move": {
            "type": "choice",
            "question": "这一局面走哪一步?",
            "choices": [m.uci() for m in legal],   # 选项就是着法空间本身
        }
    },
    instructions="下出当前局面最合理的一步,评估子力与局面得失。",
)
move = board.parse_uci(d["move"].choice)   # 返回值必然落在合法集合里

它还做了一件值得称道的事:把概率画在棋盘上。返回的概率不是被藏起来,而是直接用来给棋子着色,让你一眼看到模型对这步棋有多笃定。更关键的是页面上的一个实时校准面板——它拿每一次声明的置信度去对照一个"一层的子力检查"(one-ply material check),也就是活生生地把"你说你有 80% 把握,结果真的对吗"这件事摆在用户面前。

这恰好呼应了决策层的一个核心理念:概率是可用信息,别丢掉。选中的那一项只是一个点,整个分布才是判断的完整形态。

详解三:kNES——900 次决策,一次都没越出菜单

kNES(ArturSkowronski)是一个用 Kotlin 写的 NES 模拟器,它的 agent 通过 SemIf(Jev 接口的开源实现)在本地 Qwen3.5-4B 上直接读屏,玩《超级马里奥兄弟》和《最终幻想》。要点同样是"选项即动作空间":这一回合适用的目标,直接成为那道类型化 Choice 题的声明选项,于是"按下游戏根本没提供的键"是不可能的,而不只是"概率很低"。

它的实测数据很有说服力:900 次记录的决策,每次约 400 毫秒(在 M5 Pro 上),从头到尾没有一次回答了菜单之外的选项。

# 本回合适用的按钮,就是这道题的选项集
options = env.legal_buttons()
d = client.systemone(
    state=env.screen_text(),             # 本地模型自己看画面
    questions={
        "button": {
            "type": "choice",
            "question": "这一步按哪个键?",
            "choices": options,          # 游戏当前真正可用的按键
        }
    },
    instructions="选择能推进当前目标的操作。",
)
env.press(d["button"].choice)            # 菜单外的按键不可能被选中

把这三个项目放在一起,游戏的决策层有一个共性:合法动作集由引擎保证,Jev 只在其中做语义选择。这也是为什么在游戏这个场景里,"类型安全"不只是工程洁癖,而是直接对应着"不会卡在无效输入上"的可靠性。

更多项目一瞥

同一分类下还有一批项目,各自把决策用在不同环节:

  • PlayJev(OmniJev):一个开源 0.8B 视觉语言模型,读入一张 448px 的游戏画面,在一次前向里返回游戏所列动作上的概率分布,不生成任何文本;遇到低置信度的步骤,就把它交给一个搜索程序兜底,覆盖十款浏览器游戏。
  • jev-plays-pokemon(milanboers):把《宝可梦 红》的游戏状态读成文本,每一回合回答类型化问题,再由确定性代码把答案翻译成动作——注意这里控制流依然在代码手里。
  • JEV-Star(sc2musa):在《星际争霸 II》的 35 张 SMAC-Hard 地图上用 Jev Choice 做宏观与微观控制,选出的动作要对着"当前可用动作"校验,并可选地遵循 GPT-6 写的长线计划。
  • typesafe-mario(fhshaik)与 tsai-sc(phyous):前者从结构化模拟器状态里选动作,后者用键盘鼠标驱动原版《星际争霸》,并把每次决策的动作概率都记录下来。
  • Jevtown(gaborishka):严格说是"观众模拟"——从 id 生成 10,000 个角色读一条动态,一次请求里问 60 道 Score 题(谁会在意)加 7 道 Noul 审核题(0.5 就把内容挡在公开流外,0.85 直接拦截),再用分批 Choice 按 600 / 1,500 / 3,000 三波返回每个角色的反应,只有当"叫好"至少比"唱衰"多出这一波 0.1 的比例时,才把内容送入下一波。
  • Jev Driver(reinhard-z):浏览器里的俯视驾驶游戏,Florence-2 给路面图像写标题,Cloudflare Worker 就着这条标题问 Jev 三道 Choice(动作、类别、限速),没有任何规则表去覆盖模型答案;线上 median 约 330 毫秒、每次决策 0.00004 美元。
  • 还有 2048 × Jev(每步在四个方向上做 Choice,无启发式兜底,靠用户设定的置信阈值暂停交给人工)、AI Hold'em(完整合法走法与下注额构成 Choice 选项,扑克引擎校验后再落子)、Magic Jev Ball(20 个经典答案里 Choice)等一堆例子。

公平性与一致性

游戏里的"公平"有两层。一层是规则公平——谁能格挡、伤害怎么结算,这些由引擎代码保证,和决策层无关。另一层是判断一致性:同一个局面,这一次和下一次、这个 NPC 和那个 NPC,应该得到相近的决策。如果同一个血量的守卫这次死守、下次掉头就跑,玩家会立刻感到"这个 AI 在乱来"。生成模型做这种判断时抖动很大——温度、措辞、上下文的细微差别都会影响它给出的动作;而决策层返回的是选项上的概率分布,同一局面下分布通常高度稳定,这一步天然更利于一致性。

Jev Chess 的实时校准面板把一致性摆到了明面上:它拿每一次声明的置信度去对照一个"一层的子力检查",长期公开地展示"你说你有这么多把握,到底准不准"。这种把声明的置信度与实际结果对照的做法,是维持一个可信对手的关键——它让玩家对 AI 的强度有稳定的预期,而不是面对一个时而天才时而失误的黑箱。想调难度也简单:不是换模型,而是调阈值和指令,把决策往保守或激进的一侧推,同时保持判断分布的稳定性。

大规模模拟里的成本账

游戏场景最能体现第 2 章那句"决策成本极低"的实际意义。当你要跑的是几千场、上万次的对局,或者上万个人物的反应,逐次调用生成模型的成本会直接爆炸;而决策调用的成本低到让"多跑几百次以求稳健"成为可行策略。

flowchart LR
    S["引擎状态
(规则/血量/位置)"] --> L["代码算出
合法动作集"] L --> J["Jev:在合法集上
给出带概率的 Choice"] J --> A["代码执行
(确定、可校验)"] A --> S J -.低置信.-> H["交给搜索程序
或暂停人工复核"]

上图是游戏决策层的通用骨架,也是本章三个详解项目的共同形状:状态喂进来,合法集由代码算,Jev 只在集合内做语义选择,低置信度再走一条兜底分支。它把"聪明"用一个软件工程能接住的容器装了起来。

小结

  • 游戏里大量判断是"像个值得做的决定吗"这类语义题,写不成规则,而生成模型又会返回越界输出,正好落在决策层的主场。
  • 把合法动作集直接声明成 Choice 的选项,能让"选中不存在的动作"从"不太可能"变为"不可能",这是类型系统的保证,不是调参。
  • jev-torneo-animales 用"一次请求问完 K 个挑战者、冠军落败即丢弃后续"的技巧,跑出 1,999 场约 16 秒、约 0.01 美元的成绩。
  • Jev Chess 把每个合法着法做成单选题选项,并用实时校准面板把每次声明的置信度对照一层的子力检查;kNES 用本地模型跑了 900 次决策、约 400 毫秒/次、0 次越出菜单。
  • PlayJev(0.8B VLM,448px 画面,无生成文本)、Jevtown(10,000 角色,60 Score + 7 Noul)等项目说明:游戏与模拟天然适合"代码掌控制,模型只判定"。
  • 决策层极低的单次成本,让"成千上万次对局/模拟"的批量做法在经济上成立——这正是下一章实时应用要延续的成本优势。

下一章把镜头转向实时应用,看看当预算被压缩到 150 毫秒时,决策层要怎么组织本地模型、并行与批处理。

📑 Jev 实战:用类型化决策重构软件

1 第 1 章:为什么需要决策层——从"生成一切"到"只做判定" 2 第 2 章:三种类型化输出——Choice、Score、Noul 与调用契约 3 第 3 章:十个决策形态——什么时候该把判断交给 Jev 4 第 4 章:Classification 与 Routing——把分诊做成一等公民 5 第 5 章:Detection 与 Scoring——二元判断与有序评分 6 第 6 章:Search 与 Retrieval——在候选集里找相关 7 第 7 章:Ranking 与 Verification——排序与校验 8 第 8 章:ML Feature Extraction 与 Structured Data Extraction——把文本变特征与结构化数据 9 第 9 章:检索与知识图谱——Search and retrieval / Graphs and knowledge graphs 10 第 10 章:模型路由与 LLM 护栏——Model routing / LLM guardrails 11 第 11 章:语义代码检查——Semantic code linting 12 第 12 章:特征提取与需求预测——Feature extraction / Demand forecasting 13 第 13 章:招聘与线索生成——Recruiting / Lead generation 14 第 14 章:客户支持——Customer support 15 第 15 章:理赔、金融犯罪与风险评估——Insurance claims / Financial crime / Risk assessment 16 第 16 章:法律与合规——Legal and compliance 17 第 17 章:电商市场与广告——E-commerce marketplaces / Advertising 18 第 18 章:内容审核与信任安全——Moderation and trust and safety 19 第 19 章:游戏——Gaming 20 第 20 章:实时应用——150 毫秒预算 21 第 21 章:科学发现——Scientific discovery 22 第 22 章:大数据上的 AI Map Reduce——为什么便宜 100 倍 23 第 23 章:通用验证——Universal Verification 24 第 24 章:Harness Engineering——把决策织进智能体骨架
← 返回本书大纲