第 33 章

第 33 章:智能家居应用实战

第 33 章:智能家居应用实战

本文整理自 Datawhale 开源项目 datawhalechina/jev-cookbook(CC BY-NC-SA 4.0),源文件:main/07_实战应用。

本章用 12 个可运行项目,把 Jev 放进游戏、控制任务和应用工作流中观察。每个项目都沿同一条链展开:环境提供什么状态 → Jev 判断什么 → 普通代码如何执行与兜底 → 结果怎样回到下一步。

这章不是 12 个彼此无关的 API 示例。前三个游戏展示不同的决策结构,第四个 Jev Games Web 把它们统一呈现;接着三个项目观察闭环控制和网页操作,最后五个项目把同一套边界带到策略游戏、约束求解、模拟器复现和智能家居中。

本章学习路线

  1. 先看贪吃蛇和扫雷:理解有限动作、规则校验与概率判断的分工。
  2. 再看迷宫、移动靶、浏览器:观察动作如何改变环境,反馈怎样进入下一轮。
  3. 最后看斗地主、21 点、数独、Mario 与智能家居:练习识别隐藏信息、约束条件、延迟反馈和代码侧安全边界。

读每个项目时问四个问题:

环节 要观察的问题
State / Observation 模型实际看到了哪些状态?哪些信息被隐藏或摘要?
Decision 输出是有限动作、类别,还是候选项分布?有没有“等待 / 拒绝 / 未知”?
Policy / Engine 谁检查动作是否合法、更新状态、执行副作用并处理错误?
Feedback 成功或失败由什么裁定?反馈如何影响下一次判断?
环境真实状态 → 可见观测 / State → 类型化问题与有限选项 → Jev 输出选择或概率
      ↑                                                                   ↓
      └── 环境裁定结果 ← 执行动作 ← 普通代码做合法性与权限检查 ←──────
                                             └─ 非法 / 高风险 / 低置信:拒绝、重试或人工处理

在不完全信息游戏里,环境真值和代理观测不能混为一谈。比如斗地主裁判可以知道全部手牌,策略只能看到自己的手牌和公开出牌;把对手的暗牌传给模型,再把胜率当成可部署结果,会造成信息泄漏。这个区分来自部分可观测决策过程(POMDP)的基本设定,可参考 Kaelbling、Littman 与 Cassandra(1998)。

项目总览

# 项目 类型 主要观察点
1 Gridloop / 贪吃蛇 实时游戏 局部观测、动作合法性、碰撞反馈
2 扫雷 逻辑游戏 数字约束、确定性规则与风险概率
3 狼人杀 多轮博弈 公开证据、角色隐藏、置信度与发言来源
4 Jev Games Web React 统一入口 游戏入口、对照实验、trace 展示
5 迷宫 闭环控制 局部判断与代码规划分离
6 移动靶 / 位置预测 连续控制 时序、转向、开火与命中反馈
7 Browser Use 网页智能体 选择操作与目标元素、执行后校验
8 斗地主 不完全信息博弈 暗牌隔离、合法牌型与队友配合
9 21 点 概率策略 牌靴组成、爆牌风险、资金曲线
10 数独 约束求解 行列宫约束、候选集、试错记忆
11 Mario 复现 模拟器控制 帧同步、动作持续时间、环境混杂因素
12 智能家居 应用工作流 意图路由、投机提示、确定性派发

项目展示

1. Gridloop / 贪吃蛇

看点: 每一步都是方向选择,但动作是否安全要由游戏环境判断。读图时关注蛇身、食物、自动驾驶提示和回合状态;不要把一次长回合等同于策略已具备泛化能力。

贪吃蛇自动驾驶界面:游戏状态与决策面板

  • 项目代码与说明
  • 边界: 模型给方向判断;碰撞、吃到食物、得分和状态更新由游戏规则负责。

2. 扫雷

看点: 已打开数字格提供邻域约束,旗子与未翻开的格子则构成风险选择。要区分“规则已能确定安全”和“模型只是在多个候选中选风险较低的一格”。

扫雷自动驾驶界面:棋盘、已打开格数与最近动作

  • 项目代码与说明
  • 边界: 界面里的概率或选择不替代扫雷规则;是否踩雷由棋盘状态裁定。

3. 狼人杀

看点: 每轮发言和投票都改变后续信息。观察玩家卡片中的最近判断、证据与身份状态,特别留意界面标出的模板 fallback:模板发言不是模型生成内容,不能把它当作 Jev 的语言能力证据。

狼人杀对局界面:公开发言、玩家状态与近期判断

  • 项目代码与说明
  • 边界: 身份是隐藏信息;复盘只能使用当时可见的发言和公开事件。

4. Jev Games Web:统一入口

看点: React 页面把游戏入口和对照实验放在同一个导航里,便于比较原始策略与接入决策模型后的 trace。它是前三个游戏的入口,不是额外一种游戏策略。

Jev Games Web 统一入口

5. 迷宫:闭环导航

看点: 模型看局部窗口并判断相邻方向是否安全,路线搜索、重站位和物理碰撞仍由代码处理。截图中的 50×50、2738 步、1044 次撞墙是这段冻结回放的记录,适合用来观察反馈成本,而不是只看“最终到达出口”。

迷宫 50×50 本地回放:路线、步数和碰撞反馈

6. 移动靶 / 位置预测

看点: 可选动作包括左转、右转、开火和等待。模型给出的动作概率不等于命中概率;命中必须由游戏环境确认。完整测试集记录为 11 / 128 命中,报告分析了过早开火、转向不足等失败模式。

移动靶回放:动作概率、tick 与命中边界;场景图形是明确标注的示意画面

7. Browser Use:网页智能体

看点: 每个步骤分别选择“做什么操作”和“操作哪个元素”,执行器只消费可见元素索引;最后还要通过独立校验确认目标完成。下图是本地 travel 夹具的中间步骤,页面可拖动时间轴继续看完三步。

Browser Use 本地夹具回放:筛选条件与可见元素目标

  • 项目代码与说明 · 实验报告
  • 边界: 回放只访问本地夹具;真实浏览器任务另有 API、站点和权限约束,不能从夹具截图推出线上成功率。

8. 斗地主

看点: 桌面展示公开牌局,玩家手牌只属于当前玩家观测;右侧可以对照裁判回答、动作问题和合法候选。图中标明的是本地规则替身,不是真实 Jev 在线调用。

斗地主牌桌:公开牌局、玩家手牌与合法动作分布

9. 21 点

看点: 当前手牌、庄家明牌、剩余牌靴组成和爆牌风险共同影响 hit / stand / double 等动作。资金曲线是策略结果的一部分;比较时要使用同一牌靴规则、初始资金和随机种子。

21 点牌桌:玩家与庄家状态、动作候选和爆牌概率

10. 数独

看点: 先由 MRV(最少候选优先)选格,再结合同行、同列、同宫排除数字。时间轴可以逐步回放本地裁判记录;右侧展示的是候选约束和本步记录答案,不把候选概率冒充成“答对概率”。

数独逐步回放:9×9 棋盘、当前候选、行列宫约束

11. Mario 复现:模拟器闭环

看点: 参考上游 typesafe-mario,沿着“内存遥测 → 结构化 state → Choice 选择控制宏 → Python 推进模拟器 → 新状态反馈”看完整决策闭环。NES 截图只供人观察;原始 local_grid 只用于调试,解析后的地形与危险字段才进入 state。按键边沿、相机滚动和每拍推进帧数都会影响闭环表现。

NES 模拟器中的 Mario 游戏画面,Jev 使用结构化 state 而不是这张截图

  • 本地案例说明 · 八节实验报告 · 完整排查记录
  • 边界: 浏览器页面默认使用本地 scripted pilot,Choice / Noul / Score 数值不是 Jev 输出。按键与模拟器现象可作为工程观察,模型表现需运行上游项目的真实 Jev 路径后单独评测。

12. 智能家居:从判断到设备派发

看点: 从自然语言输入开始,依次观察意图、类型化问题、投机动作、无关分支剪枝、确定性派发和 3D 设备状态。截图明确显示“模拟引擎 · 未配 KEY”,用于讲数据流和 UI,不是一次真实 API 延迟或成本测量。

智能家居演练场:3D 房间、决策追踪、投机分支与设备状态

运行与复盘

本章项目混合了静态回放、离线裁判、UI 模拟和可选在线 API:

  • 零密钥阅读: 先读各项目 README 与实验报告,并打开 maze/final.html、predict_position/final.html、browser-use/final.html 和 sudoku/sudoku_replay.html 查看本地回放。数独回放直接展示离线记录;移动靶在缺少逐帧原图时明确切换到场景示意。
  • 需要运行服务时: 按具体项目 README 和统一测试指南启动单个项目。不要把启动了网页等同于已经完成在线模型验证。
  • 需要在线 API 时: 先检查项目说明中的模型、密钥和成本要求;不要把密钥写入代码或截图。智能家居和浏览器 smoke 的在线路径可能触发真实请求。
  • 看实验结果时: 区分样例回放、离线规则裁判、mock、模板 fallback 与真实模型响应;成功判定应以项目裁判和冻结测试集指标为准。

本章总结

12 个项目覆盖三类不同问题:局部游戏判断(贪吃蛇、扫雷、狼人杀;由 Jev Games Web 统一展示),动作闭环与工具控制(迷宫、移动靶、浏览器),以及结构化策略落地(斗地主、21 点、数独、Mario、智能家居)。共同的方法不是“让模型接管一切”,而是把判断题做成有限、可观测的决策,再由普通代码承担规则、权限、执行和反馈。

来源与许可

  • Gridloop、扫雷、狼人杀与 Jev Games Web 收录自 jev-games。上游仓库未附 LICENSE;版权归原作者,收录仅供学习参考。
  • 其余项目按项目内声明与 jev-playground 的 CC0-1.0 约定收录。Browser Use 与 Mario 复现另见各自项目内的来源说明。
  • 改动或再分发前检查具体项目的 LICENSE 与来源说明;仓库级设计见 app/ARCHITECTURE.playground.md。

本笔记本分四部分:① 官方案例讲解 → ② 实现原理与逻辑 → ③ 我们的实验 → ④ 启动 Web 端体验(末尾把整个 3D 应用内嵌进笔记本)。

运行前置:本目录下已启动 serve_smart_home.py(第 4 节的启动单元格也可以一键拉起)。

① 官方案例讲解

TypeSafe 官方的智能家居演示(教学视频)做了四件事:

  1. 单次调用多重评估:用户说 "get the coffee boiling",系统只调用 TypeSafe API 一次, 却在同一发请求里塞进一大批问题:意图是什么?是不是复合指令?作用范围?哪类设备?哪个具体设备?
  2. choice 原语定意图:choice 返回每个选项的概率分布—— smart_home_command 99% / info_request / smart_home_query,取 argmax 后由代码分支。
  3. 投机提示(Speculative Prompting):明明只涉及咖啡机,也"顺带"问了门锁——返回 "79% unlock / 21% lock"这种不确定分布。视频里的原话是:反正快且便宜,问都问了, 真用到门锁的指令来时就不用再跑一趟。代码端剪枝掉不相关的答案即可。
  4. 该出手的才出手:复合指令("关厨房灯然后锁书房门",复合概率 98%)先用 178ms 的 TypeSafe 初筛,再交 Claude Haiku 拆成两条原子指令,并行回 TypeSafe 评估; 常识问题("1989 世界大赛冠军")则路由给大模型回答——快而确定的走 Jev,开放式的走 LLM。

② 实现原理与逻辑

2.1 一次请求长什么样

POST /v1/systemone,请求 = state(当前世界状态)+ questions(问题包); 回复 = 每个问题的答案,带完整概率分布:

import json, urllib.request

payload = {
  "state": {"utterance": "把咖啡烧上", "home": {"rooms": ["living_room", "kitchen"]}},
  "model": "jev-latest",
  "questions": {
    "intent": {"type": "choice", "criteria": {
        "smart_home_command": "控制设备", "information_request": "与家居无关的常识"}},
    "category": {"type": "choice", "criteria": {
        "lighting": "灯", "appliance": "家电", "not_device_specific": "不涉及"}},
    "appliance_action": {"type": "choice", "criteria": {
        "turn_on": "打开", "turn_off": "关闭", "leave_unchanged": "保持"}}
  }
}
req = urllib.request.Request("http://127.0.0.1:8810/api/jev",
    data=json.dumps(payload).encode(), headers={"Content-Type": "application/json"})
wire = json.loads(urllib.request.urlopen(req, timeout=20).read())
print(f"上游耗时 {wire['upstream_ms']}ms")
for name, a in wire["body"]["answers"].items():
    dist = a.get("probabilities") or {"noul": a.get("noul")}
    print(f"  {name:18s} -> {a.get('choice', '—'):22s} {json.dumps(dist, ensure_ascii=False)}")
上游耗时 201ms
  intent             -> smart_home_command     {"smart_home_command": 0.97, "information_request": 0.02, "smart_home_query": 0.01}
  is_compound        -> —                      {"noul": 0.074}
  scope              -> specific_device        {"whole_house": 0.05, "single_room": 0.05, "specific_device": 0.9}
  target_room        -> not_room_specific      {"living_room": 0.02, "kitchen": 0.02, "office": 0.02, "bedroom": 0.02, "entrance": 0.02, "not_room_specific": 0.9}
  category           -> appliance              {"lighting": 0.02, "appliance": 0.902, "lock": 0.02, "fan": 0.02, "speaker": 0.02, "not_device_specific": 0.02}
  light_action       -> turn_on                {"turn_on": 0.942, "turn_off": 0.029, "leave_unchanged": 0.029}
  fan_action         -> turn_on                {"turn_on": 0.907, "turn_off": 0.047, "leave_unchanged": 0.047}
  speaker_action     -> turn_on                {"turn_on": 0.906, "turn_off": 0.047, "leave_unchanged": 0.047}
  appliance_action   -> turn_on                {"turn_on": 0.908, "turn_off": 0.046, "leave_unchanged": 0.046}
  lock_action        -> lock                   {"lock": 0.919, "unlock": 0.041, "leave_unchanged": 0.041}

2.2 三种原语就是全部词汇

原语 回答什么 返回
choice 多选一(意图/设备类别/动作) argmax + 全分布 + 置信
noul 是/否(复合吗?A 必须先于 B 吗?) 一个 0~1 概率
score 有序评分(优先级/紧急度) 期望分 + 各档概率

没有文本生成、没有对话——所以快、便宜、输出天然结构化。开放式的活再转包给大模型。

2.3 我们页面里的完整管线

指令 ──► ① Jev 投机调用(10 问捆绑:意图/复合/范围/房间/类别 + 五类设备动作)
          ├─ intent=信息请求 ──► step-3.5-flash 直答(Jev 只当 141ms 的路由守卫)
          ├─ intent=状态查询 ──► 投机答案定位 + 本地 3D 状态直读(零额外调用)
          └─ intent=控制指令
               ├─ is_compound ≥ 50% ──► LLM 拆原子指令
               │      └─ ② Jev 编排调用:每对子指令 noul「A 必须先于 B 吗」
               │           + 每条 score 优先级(安全最先)
               │           任一依赖 ≥60% → 串行执行栈(拓扑序逐个派发)
               │           全部 <60%    → 并行执行
               └─ 单一指令 ──► 直接取投机答案里相关类别的动作派发(剪枝其余)

串行/并行的意义:「先开客厅灯再关掉」两步作用于同一设备,顺序决定最终状态 → 必须串行; 「关厨房灯 + 锁书房门」互不相关 → 并行省时间。依赖判定这件事本身也交给 Jev 的原语完成。

③ 我们的实验:智能家居 3D 演练场

参考官方案例 UI 复刻的 Three.js 三维户型(客厅/厨房/书房/卧室/玄关,设备可点选), 把演示视频里的"假数据"全部换成真实调用:

  • 语音直接输入:阶跃星辰流式 ASR(stepaudio-2.5-asr,16k 裸 PCM,首字 ~0.52s), 中英文自动检测;打字输入保留。
  • 串行/并行任务编排:同灯先开后关判 93% 依赖 → 串行栈执行,最终灯为关(顺序保持); 异类指令并行 + 按优先级排序。编排分析仅 133–141ms。
  • 传统 LLM 对照:一键切换 step-5-preview 一次直答做同样的事——JSON 遵从完美, 但延迟 3–20×、每单多几百输出 token(页面右下角实时显示成本:Jev 输入 $0.042/M、输出免费)。
  • 无 Key 降级:不设 TYPESAFE_API_KEY 自动切本地模拟引擎,功能照常。

实测明细见 README.md。

④ 启动 Web 端 + 内嵌体验

可以内嵌:我们的本地服务不发 X-Frame-Options,Jupyter 里直接用 <iframe> 即可。 注意要加 allow="microphone",否则框里麦克风会被浏览器禁掉。

import subprocess, sys, time, urllib.request, os
from pathlib import Path

PORT = 8810
BASE = f"http://127.0.0.1:{PORT}"

def server_up():
    try:
        return urllib.request.urlopen(f"{BASE}/api/health", timeout=2).status == 200
    except Exception:
        return False

if not server_up():
    env = dict(os.environ)
    subprocess.Popen([sys.executable, "serve_smart_home.py", "--port", str(PORT),
                      "--host", "127.0.0.1", "--no-open"],
                     cwd=str(Path.cwd()), env=env,
                     stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
    for _ in range(30):
        if server_up():
            break
        time.sleep(0.5)

import json
health = json.loads(urllib.request.urlopen(f"{BASE}/api/health", timeout=2).read())
print("服务状态:", "运行中" if server_up() else "启动失败")
print("  真实 Jev :", "已配置" if health["jev"] else "未配置(页面用本地模拟引擎)")
print("  阶跃 ASR :", health["asr_model"] if health["asr"] else "未配置")
服务状态: 运行中
  真实 Jev : 已配置
  阶跃 ASR : stepaudio-2.5-asr
from IPython.display import HTML
HTML(f'''

3D 场景可拖拽旋转;麦克风需在 独立标签页打开时体验更稳(部分浏览器限制 iframe 内的录音权限)。

''')

尾注

  • 代码与实验报告:Bald0Wang/jev-playground → smart-home/
  • Jev 定价:输入 $0.042/M tokens、输出免费;阶跃定价见官方 docs/zh/guides/pricing/details
  • 环境搭建:TYPESAFE_API_KEY(console.typesafe.ai)、STEPFUN_API_KEY(platform.stepfun.com)

📑 Jev Cookbook:System One 判断模型实战教程

1 第 1 章:认识 Jev:模型、上手与场景 2 第 2 章:核心概念总览 3 第 3 章:System One:判断的核心心智模型 4 第 4 章:状态:让判断连续可追溯 5 第 5 章:原语:Choice、Score 与 Noul 6 第 6 章:置信度:让概率可信 7 第 7 章:应用构建:从原语到完整系统 8 第 8 章:架构模式 9 第 9 章:实战指南总览 10 第 10 章:自一致性 · Noul 11 第 11 章:自一致性 · Choice 12 第 12 章:并行提问 13 第 13 章:重排序 14 第 14 章:逐行语义搜索 15 第 15 章:结构恢复 16 第 16 章:函数调用 17 第 17 章:技能推荐 18 第 18 章:实体对齐 19 第 19 章:RAG 段落分类 20 第 20 章:引用核查 21 第 21 章:LLM 防护栏 22 第 22 章:SDE 级联 23 第 23 章:日期抽取 24 第 24 章:预解析值抽取 25 第 25 章:层级分类 26 第 26 章:自动研究特征发现 27 第 27 章:基于置信度的分类 28 第 28 章:智能家居实验 29 第 29 章:模型评测总览 30 第 30 章:模型评测实验 31 第 31 章:Laya vs Jev 对比基准 32 第 32 章:JevBench:LLM 评测体系 33 第 33 章:智能家居应用实战 34 第 34 章:Jev-Mem 研究总览 35 第 35 章:Jev-Mem 缩放实验 36 第 36 章:Jev-Mem 完整走查 37 第 37 章:Agent 集成总览 38 第 38 章:Pi 集成实验 39 第 39 章:DSH 决策协作 40 第 40 章:本地模型总览 41 第 41 章:本地模型介绍与对比 42 第 42 章:中文数据集构建方案 43 第 43 章:微调指南 44 第 44 章:RLCD 原理与实验优化 45 第 45 章:中文数据集构建实验 46 第 46 章:中文 Head 微调实验 47 第 47 章:全量 v2 微调实验 48 第 48 章:知识库
← 返回本书大纲