第 3 章

第 3 章:System One:判断的核心心智模型

第 3 章:System One:判断的核心心智模型

本文整理自 Datawhale 开源项目 datawhalechina/jev-cookbook(CC BY-NC-SA 4.0),源文件:main/02_核心概念/01_SystemOne.ipynb。

针对官方文档对应章节的可运行实验笔记,全部实验使用中文场景与中文提示词。 面向会基础 Python、刚接触 AI Agent 的读者。

学习目标: 复刻消息、交易、政策的退款例子,理解共享状态、独立问题和确定性检查的边界。

官方原文 · 中文参考。本章以中文重述理论、复刻对应场景;扩展实验会单独说明。 所有客户、订单及消息均为教学合成数据。

笔记本结构

章节 内容
0. 准备 安装库、配置客户端、连通性测试与离线示例
1 三个退款判断
2 代码组合与人工复核
3 只改 ID 的对照实验
练习与小结 练习、自查、总结与本次执行记录

实验按原理 → 理论根基 → 定义数据 → 定义问题 → 调用 → 解读结果展开,每个代码单元格只做一件事。

运行要求

  • Python ≥ 3.10;本章使用 typesafe-sdk==0.7.0。
  • 真实实验需要启动进程的 TYPESAFE_API_KEY 环境变量,密钥不要写进 Notebook。

在本仓库 notebooks/ 目录创建环境并打开本文件:

./setup_env.sh
.venv/bin/python -m pip install -r requirements.txt -c generators/constraints-foundations.txt
.venv/bin/jupyter lab 04_SystemOne.ipynb

产品名、字段名和选项 key 保持英文,state、提示词与解说使用中文。 默认 JEV_RUN_MODE=auto(在线优先):检测到 TYPESAFE_API_KEY 即调用真实模型;未检测到才回退离线替身,每格输出都标注来源。 正式验收设置 JEV_RUN_MODE=live(无密钥直接报错、不回退);强制纯离线学习设置 JEV_RUN_MODE=offline。

验证状态:2026-09-25 已用真实 API 在线执行本章全部调用,下方输出即实测结果。 批量执行、离线预览和验收记录见本目录 MAINTENANCE.md。

0. 准备

本节可折叠阅读,但独立运行时不能跳过。客户端、辅助对象和示例数据都在本文件中定义。

0.1 安装依赖

推荐先运行 setup_env.sh。只有当前内核缺少 SDK 时,本格才安装依赖。

import importlib.util
if importlib.util.find_spec("typesafe_sdk") is None:
    %pip install -q typesafe-sdk==0.7.0

观察与理解: 安装包的名字是 typesafe-sdk,Python 导入名是 typesafe_sdk。安装成功不代表 API 已连通。

0.2 导入与配置

默认模型固定版本,便于记录实验条件;可通过环境变量更换。不要从 Notebook 输入密钥。

import os
import json
import time
import math
from datetime import datetime, timezone
from importlib.metadata import version
from typesafe_sdk import (
    Choice, Score, Noul, NoulCriteria, TypeSafeClient,
    TypeSafeAuthenticationError, RetryPolicy,
)

MODEL = os.environ.get("TYPESAFE_DEFAULT_MODEL", "jev-1.13.0")
RUN_MODE = os.environ.get("JEV_RUN_MODE", "auto")
API_KEY = os.environ.get("TYPESAFE_API_KEY", "")
if RUN_MODE not in {"live", "offline", "auto"}:
    raise ValueError("JEV_RUN_MODE 只能是 live、offline 或 auto")
if RUN_MODE == "live" and not API_KEY:
    raise RuntimeError("JEV_RUN_MODE=live 需要真实密钥:请配置 TYPESAFE_API_KEY;仅学习可改用默认 auto(无密钥自动离线)")
client = None if (RUN_MODE == "offline" or not API_KEY) else TypeSafeClient(
    api_key=API_KEY, model=MODEL, timeout=30, retry=RetryPolicy(max_retries=0))
mode_note = ("在线优先:本次会话调用真实模型" if client is not None else
             ("强制离线(JEV_RUN_MODE=offline)" if RUN_MODE == "offline" else
              "未检测到 TYPESAFE_API_KEY → 离线替身;配置密钥后重跑本格即切在线实测"))
print("模式:", RUN_MODE, "|", mode_note, "|SDK:", version("typesafe-sdk"), "|模型配置:", MODEL)
模式: auto | 在线优先:本次会话调用真实模型 |SDK: 0.7.1 |模型配置: jev-1.13.0

默认在线优先:有密钥即走真实模型,每次调用的来源(live/offline)都记录在 CALL_LOG 与输出中;auto 仅在未配置密钥或 401 时回退离线替身。正式验收设 JEV_RUN_MODE=live(禁用回退、不自动重试,请求数量有界)。

0.3 连通性测试

用一条 Noul 检查真实响应能否返回。网络、限流与输入错误直接抛出,不伪装成不确定判断。

PING = {"source": "offline", "reason": "未发起连通性请求(无 client:未配置密钥或强制离线)"}
if client is not None:
    try:
        ping = client.system_one("你好", {"greeting": Noul(
            instructions="这段文字是否在打招呼?")})
        PING = {"source": "live", "model": ping.model,
                "input_tokens": ping.usage.input_tokens,
                "output_tokens": ping.usage.output_tokens}
    except TypeSafeAuthenticationError:
        if RUN_MODE == "live":
            raise
        client.close()
        client = None
        PING["reason"] = "401 鉴权失败,仅教学模式允许回退"
print(json.dumps(PING, ensure_ascii=False))
{"source": "live", "model": "jev-1.13.0", "input_tokens": 278, "output_tokens": 22}

观察与理解: source=live 表示这一次连通性请求成功;仍要查看后续实验记录,不能用它代替整章验收。

0.4 离线替身

沿用参考模板的 _FakeAnswer 与 _FakeResponse 访问方式。人工数字仅用来检验读取字段和代码分支。

class _FakeAnswer:
    def __init__(self, type_, **values):
        self.type = type_
        for name, value in values.items():
            setattr(self, name, value)


class _FakeResponse:
    def __init__(self, answers):
        self.answers = answers
        self.choices = {k: v for k, v in answers.items() if v.type == "choice"}
        self.scores = {k: v for k, v in answers.items() if v.type == "score"}
        self.nouls = {k: v for k, v in answers.items() if v.type == "noul"}
        self.model = "人工示例,非模型预测"
        self.usage = _FakeAnswer("usage", input_tokens=0, output_tokens=0)

人工 Score 由概率计算期望,避免模板中的分数与分布不一致。人工 confidence 只是指定的演示字段,不是在复现服务端的计算公式。

定义两种示例答案构造器;Noul 可直接用 _FakeAnswer。所有具体答案集中在下一节。

def fake_choice(probabilities, confidence):
    return _FakeAnswer("choice", choice=max(probabilities, key=probabilities.get),
                       probabilities=probabilities, confidence=confidence)


def fake_score(probabilities, legend, confidence):
    return _FakeAnswer("score", score=sum(k * p for k, p in probabilities.items()),
                       probabilities=probabilities, legend=dict(enumerate(legend)),
                       confidence=confidence)

观察与理解: 例如概率 {0:0.05, 1:0.26, 2:0.69} 对应 1.64。不能把另一个数与该分布配在一起。

0.5 统一调用入口

每次调用记录来源、模型与 token 用量。离线耗时记为 None,不把本地字典访问当成模型速度。

CALL_LOG = []


class TS:
    def call(self, state, questions, offline_answers, label):
        start = time.perf_counter()
        source = "live"
        if client is None:
            response, source = _FakeResponse(offline_answers), "offline"
        else:
            try:
                response = client.system_one(state, questions)
            except TypeSafeAuthenticationError:
                if RUN_MODE != "auto":
                    raise
                response, source = _FakeResponse(offline_answers), "offline"
        validate_response(response, questions)
        CALL_LOG.append({"case": label, "source": source, "model": response.model,
                         "seconds": time.perf_counter() - start if source == "live" else None,
                         "input_tokens": response.usage.input_tokens,
                         "output_tokens": response.usage.output_tokens})
        if source == "offline":
            print("离线替身(未调用真实模型):", label, ";人工答案,仅演示代码路径")
        return response


ts = TS()

与参考模板相比,这里增加了严格 live 模式和逐次记录。保留 401 教学回退,但超时、429 等错误继续失败,防止验收被回退掩盖。

校验结构与数值契约;只断言接口应满足的性质,不断言真实模型必须预测某个标签。

def validate_response(response, questions):
    if set(response.answers) != set(questions):
        raise ValueError("答案 ID 与问题 ID 不一致")
    for key, question in questions.items():
        answer = response.answers[key]
        if isinstance(question, Noul):
            if not 0 <= answer.noul <= 1:
                raise ValueError("Noul 超出概率范围")
            continue
        probabilities = answer.probabilities
        if not all(math.isfinite(p) and 0 <= p <= 1 for p in probabilities.values()):
            raise ValueError("概率值无效")
        if not math.isclose(sum(probabilities.values()), 1, abs_tol=0.02):
            raise ValueError("概率之和偏离 1")
        if not 0 <= answer.confidence <= 1:
            raise ValueError("confidence 超出范围")
        if isinstance(question, Choice):
            if set(probabilities) != set(question.criteria):
                raise ValueError("Choice 选项集合不一致")
            if answer.choice not in probabilities:
                raise ValueError("Choice 标签不在选项中")
        else:
            expected = sum(int(k) * p for k, p in probabilities.items())
            if not math.isclose(answer.score, expected, abs_tol=0.03):
                raise ValueError("Score 与概率加权期望不一致")

观察与理解: 容差用于服务端数值舍入。结构检查通过只说明响应可读取,不证明语义判断正确。

显示结果时统一列出类型、概率和置信度;Noul 不额外制造 confidence 字段。

def show(response):
    rows = {}
    for key, answer in response.answers.items():
        rows[key] = {name: getattr(answer, name) for name in
                     ("type", "choice", "score", "noul", "confidence", "probabilities", "legend")
                     if hasattr(answer, name)}
    print(json.dumps(rows, ensure_ascii=False, indent=2))

0.6 本章离线示例数据

以下数值全部人工构造,专门测试分支;不来自 Jev,也不能用于估计中文准确率或校准情况。正式 live 运行不会使用这些答案。

REFUND_OFFLINE = {
    "refund_requested": _FakeAnswer("noul", noul=0.98),
    "duplicate_charge": _FakeAnswer("noul", noul=0.94),
    "policy_supports_refund": _FakeAnswer("noul", noul=0.95),
}
RENAMED_OFFLINE = {"q1": REFUND_OFFLINE["refund_requested"]}

📖 理论根基:聚焦判断与组合

System One 强调快速、受约束的判断。模型理解文本,但不会替本例生成退款解释或执行支付操作。 名称借用了“系统 1”的直觉判断概念;这不是对人的思考过程的科学复现声明。

原文退款流程是:构造客户消息、相关交易、退款政策;一起提出独立问题;由代码结合确定性检查再路由。 每个问题必须独立读完 state 就能回答,不能把“上一个问题为真”作为隐含前提。 Noul 返回概率,不返回独立 confidence。

官方原文 · 中文参考 · 官方原文 · 中文参考

与 LLM 的区别(官方对比)

官方概念页专门有一节讲它和普通大模型(LLM)的不同。同样的客户消息,聊天模型会写一段回复, System One 只返回你问的那几个数:

  1. 训练目标不同:System One 为「校准决策」而训练——返回的概率针对真实结果优化,用来反映不确定性。 注意校准是按一组预测衡量的:概率 0.8 的事件长期发生频率接近 80%,但不保证某一次一定发生。
  2. 能力边界不同:它不撰写回复、不产出代码,也不解释自己的推理过程。判断之外的话一概不说。
  3. 答案空间由你定义:你能得到什么,取决于用三种原语问了什么——
原语 问题示例 示例答案空间 示例输出
Choice 哪个团队应当处理这张工单? billing / technical / account choice: "billing"
Score 这位客户有多沮丧? 0=平静,1=沮丧,2=非常沮丧 score: 1.4
Noul 这条消息是否要求退款? 真 / 假 noul: 0.95

(表格来自官方概念页;答案空间只是示例配置,完整选项见原语各页。)

两个边界注意:① 目前只接受文本输入(字符串、JSON 对象、文本数组),图像、音频、视频暂不支持; ② 名字借自《思考,快与慢》的「系统 1」——快速直觉判断,不是对人类思考过程的科学复现声明。 需要写回复、写代码、做开放推理时,那是大模型的活;Jev 负责在旁边把「该走哪条路」快速定下来。

1. 复刻退款场景

原理

客户是否要求退款、是否存在重复收费的证据、政策是否支持退款,是三个不同属性。 把证据放在一起,让每个问题各自检查;支付权限和交易唯一性则由程序核验。

第一步:准备状态

字段中的 captured 是交易系统状态码,保留英文;消息与政策使用中文。

REFUND_STATE = {
    "message": "订单 A-104 被扣了两次款,请退还重复扣取的那一笔。",
    "order": {
        "id": "A-104",
        "charges": [
            {"id": "C-1", "amount_usd": 49, "status": "captured"},
            {"id": "C-2", "amount_usd": 49, "status": "captured"},
        ],
    },
    "refund_policy": "同一订单的重复扣款可以退还重复部分。",
}

第二步:每个问题直接引用证据

REFUND_QUESTIONS = {
    "refund_requested": Noul(instructions="客户在 `message` 中是否明确要求退款?"),
    "duplicate_charge": Noul(instructions="`message` 与 `order.charges` 是否表明同一订单被重复扣款?"),
    "policy_supports_refund": Noul(instructions=(
        "根据 `message`、`order.charges` 与 `refund_policy`,政策是否支持退还本案重复扣款?")),
}

观察与理解: 第三问直接看事实与政策,没有引用前两问的预测。这就是同一次请求里的独立性。

第三步:一次调用

refund_response = ts.call(REFUND_STATE, REFUND_QUESTIONS, REFUND_OFFLINE, "退款三问")

第四步:逐项观察

show(refund_response)
{
  "refund_requested": {
    "type": "noul",
    "noul": 0.99
  },
  "duplicate_charge": {
    "type": "noul",
    "noul": 0.95
  },
  "policy_supports_refund": {
    "type": "noul",
    "noul": 0.98
  }
}

观察与理解: 三个概率都高不等于可以立即转账。金额、权限、幂等记录及支付接口约束仍是业务系统的责任。

2. 在代码中组合

📖 理论根基

能精确计算的条件直接用代码。下面只核对本例所需的基础记录,并输出处理建议, 不连接支付系统,也不声称覆盖真实退款的全部业务条件。 0.8 是教学阈值,需要用真实业务标签与错误成本另行评估。

先定义确定性检查。

def basic_record_check(state):
    charges = state["order"]["charges"]
    return (
        len(charges) == 2
        and len({x["id"] for x in charges}) == 2
        and all(x["status"] == "captured" for x in charges)
        and charges[0]["amount_usd"] == charges[1]["amount_usd"]
    )


DECISION_THRESHOLD = 0.8

组合函数只接受已返回的答案。

def refund_recommendation(state, response):
    if not basic_record_check(state):
        return {"route": "review", "reason": "交易记录需要核对"}
    probabilities = [response.nouls[key].noul for key in REFUND_QUESTIONS]
    if all(p >= DECISION_THRESHOLD for p in probabilities):
        return {"route": "refund_review", "reason": "建议进入退款审核流程"}
    return {"route": "review", "reason": "至少一个语义判断未达到教学阈值"}

观察与理解: 多个概率的乘积不自动成为总体成功概率;问题被独立评估不代表这些事件在统计上相互独立。

应用组合函数。

recommendation = refund_recommendation(REFUND_STATE, refund_response)

显示建议与触发原因。

print(json.dumps(recommendation, ensure_ascii=False, indent=2))
{
  "route": "refund_review",
  "reason": "建议进入退款审核流程"
}

观察与理解: 如果实际概率未达到阈值,应保留复核结果。不能为了展示‘通过’而改写模型返回。

3. 问题 ID 的对照实验

原理:ID 用来匹配结果。完整语义必须放进 instructions。 先前请求中的 refund_requested 改名为 q1,问题文本保持不变。 这是两次服务请求的观察;即使出现数值差异,也不能只凭一对结果认定 ID 改变了模型语义。

只更换问题 ID。

RENAMED_QUESTIONS = {"q1": REFUND_QUESTIONS["refund_requested"]}

发送对照请求。

renamed_response = ts.call(REFUND_STATE, RENAMED_QUESTIONS, RENAMED_OFFLINE, "问题 ID 对照")

比较字段映射和实际差值。

original_p = refund_response.nouls["refund_requested"].noul
renamed_p = renamed_response.nouls["q1"].noul
print({"原 ID 概率": original_p, "新 ID 概率": renamed_p,
       "绝对差": abs(original_p - renamed_p),
       "实际返回 ID": list(renamed_response.answers)})
{'原 ID 概率': 0.99, '新 ID 概率': 0.99, '绝对差': 0.0, '实际返回 ID': ['q1']}

观察与理解: 不要在真实实验中断言两个浮点数必须完全相等。本例验证访问方式,并用于记录重复请求的实际行为。

练习与自查

如果第三个问题必须根据第一个问题的答案改写,应该怎样实现?为什么不能写‘如果 refund_requested 为真就……’?

参考思路:先完成练习再展开

先执行第一轮请求,在 Python 中读取答案,再构造第二轮 state 与问题。同请求的 ID 不会把一个问题的答案传给另一个问题。

小结

工作 负责方
解读消息与政策 模型的原子问题
核对金额、交易 ID、权限 确定性代码
组合与路由 应用程序

下一章:State。

离线运行只说明教材代码能执行。正式交付必须实际运行 live,并阅读每条输出;缺失的分支应记为未观察到。

本次执行记录

先关闭连接,再生成记录。下面的 JSON 由实际运行计算,批量执行器会据此检查来源。

if client is not None:
    client.close()

本章拿几句话试了试真实模型,看它给的概率怎么反应——这只说明模型对这类输入的反应方式,不构成准确率评测。特别提醒:如果哪句答得合心意就专门挑出来当考题,再拿这些挑过的句子去算准确率,数字必然虚高。输出里的耗时也只是当时网络的快照,每次都会不一样。

AUDIT = {
    "kind": "jev_execution_audit",
    "executed_at_utc": datetime.now(timezone.utc).isoformat(),
    "sdk": version("typesafe-sdk"), "requested_model": MODEL,
    "mode": RUN_MODE, "ping": PING,
    "real_calls": sum(x["source"] == "live" for x in CALL_LOG),
    "offline_calls": sum(x["source"] == "offline" for x in CALL_LOG),
    "cases": CALL_LOG,
    "coverage": globals().get("COVERAGE", {}),
    "validation_status": "live_executed_requires_review" if (
        PING["source"] == "live" and CALL_LOG
        and all(x["source"] == "live" for x in CALL_LOG)
    ) else "offline_only_not_model_evidence",
}
print(json.dumps(AUDIT, ensure_ascii=False, indent=2))
{
  "kind": "jev_execution_audit",
  "executed_at_utc": "2026-09-25T09:08:02.005122+00:00",
  "sdk": "0.7.1",
  "requested_model": "jev-1.13.0",
  "mode": "auto",
  "ping": {
    "source": "live",
    "model": "jev-1.13.0",
    "input_tokens": 278,
    "output_tokens": 22
  },
  "real_calls": 2,
  "offline_calls": 0,
  "cases": [
    {
      "case": "退款三问",
      "source": "live",
      "model": "jev-1.13.0",
      "seconds": 0.3085615420714021,
      "input_tokens": 501,
      "output_tokens": 58
    },
    {
      "case": "问题 ID 对照",
      "source": "live",
      "model": "jev-1.13.0",
      "seconds": 0.2808937500230968,
      "input_tokens": 426,
      "output_tokens": 21
    }
  ],
  "coverage": {},
  "validation_status": "live_executed_requires_review"
}

读完输出后,在本仓库 notebooks/MAINTENANCE.md 的验收表中记录日期、真实模型、观察到的分支和偏离预期之处。不要把人工演示数值抄进实测记录。

📑 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 章:知识库
← 返回本书大纲