Skip to content
🎯 场景实战第 13 / 16 节

自然语言点单:转成结构化购物车 ​

💡 一句话总结:用 Jev 把一句话点单转成结构化购物车 —— Choice 收缩选项循环找出每样商品,再对每样商品发一批请求填齐字段,最后由代码拼装进你的订单系统。

顾客会说「Can I get a large oat milk latte and a blueberry muffin please」(大杯燕麦奶拿铁加一个蓝莓麦芬,谢谢),但你的收银系统需要的是一件件分开、字段各就各位的结构化购物车。官方 Shopping cart cookbook 的解法只用 Choice 选择题这一种题型,分两段完成自然语言转结构化:

  1. 找商品:商品数量事先不知道,就用一个收缩选项循环——每轮问「订单里最先提到的是哪样剩余商品」,选中一样就从下一轮的选项里删掉,直到模型选出哨兵选项 nothing_else。
  2. 填字段:知道了每样商品,也就知道了它有哪些字段(拿铁有 size 和 milk),每样商品一次批量请求把所有字段题并行问完。

官方实测(jev-1.14.0,2026-10-29):3 次请求找齐 2 样商品(拿铁 0.99、蓝莓麦芬 1.00、哨兵 1.00),再加 1 次请求填完拿铁两个字段(large 1.00、oat 1.00)——一共 4 次请求拼出整个购物车。本课约 15 分钟整套跑通,而且下载官方缓存后不设 API Key 也能复现官方全部数字(本页实测通过)。

学完你能做什么 ​

  • 写出 find_items(order):给一句自然语言订单,不管她点了几样,循环找出每样商品、自带把握度,nothing_else 哨兵兜住「点完了」
  • 写出 fill_fields(order, item):每样商品一次请求并行填齐所有已知字段,顾客没提的字段明确答 unspecified,而不是脑补一个值
  • 把菜单写成纯数据(描述 + 字段 schema):加商品、加字段改字典就行,问题一个字不用动
  • 不花一分钱复现官方数字:官方 json_cache.json 缓存重放,重跑零请求

为什么用 Jev,而不是让 LLM 直接生成购物车 JSON? ​

「让 LLM 把这句话解析成 JSON 购物车」是很多人的第一反应——能跑,但你拿到的是一段要祈祷的文本。

能力让 LLM 生成 JSONJev 收缩选项循环
商品数量❌ 「她点了几样」要靠模型数,数错就漏项多项✅ 循环一次找一样、找完自动停,件数由代码数
字段值合法性❌ 可能生成 venti、燕麦奶——不在你的菜单里✅ 选项就是合法值(size ∈ small/medium/large),答案永远落在选项里
没提的字段❌ 倾向于替顾客补一个「默认中杯」✅ unspecified 选项明确报「订单没说」,交给你按默认规则处理
下游解析❌ JSON 解析失败、幻觉字段,层层校验✅ 类型化答案 + 概率分布,代码直接用
把握度❌ 要追问「你确定吗」✅ 每个判断自带 confidence,低置信送店员人工确认

一句话:LLM 会替你重写订单,Jev 只替你读订单——每样商品、每个字段都是一次可校准的选择题。

你现在的困境 ​

  • 「让 LLM 输出购物车 JSON,十次有九次对,漏掉的那次是顾客点了三样它只吐两样——投诉都找不到是哪行代码的错」
  • 「顾客说『来杯拿铁』没说大小,模型每次都自信地编一个 medium 写进订单」
  • 「菜单一上新,解析 prompt 就要跟着改一遍,改完还得全量回归测试」

什么时候用这一招 ​

当你需要:

  • 把一句自然语言变成结构化数据:点单、配置项、表单填写、商品属性选择
  • 候选集合是已知的闭集——菜单是你定的,字段合法值是你定的
  • 数量不定:可能点 1 样,可能点 5 样,事先不知道

而且不想:

  • 解析 LLM 吐出来的 JSON,再写一层「它是不是又编了」的校验
  • 让生成模型做计数——官方踩坑手册明说计数不可靠,误差随规模增大
  • 每次菜单变动都重调 prompt

🎒 开始前的准备 ​

  • 完成了 Choice 选择题:会写 criteria 选项 map、读得懂 choice 和 confidence,知道一题最多 255 个选项
  • 建议读过 Confidence 置信度:知道低置信答案怎么转人工复核
  • 跑通过安装与第一个请求:.venv 可用。本课全程不设 Key 也能跑(官方缓存重放);想 live 跑设 TYPESAFE_API_KEY
  • 一杯水 ☕——本课是一个完整小项目:一个循环加一批题,把「一句话」变成「购物车」

跟我做 ​

先说好怎么跑:新建 cart.py,把下面每一步的代码块按顺序贴进同一个文件,每完成一步运行一次 python cart.py。所有 API 调用走 json_cache.json 缓存,重复运行零花费。

第 1 步:装环境、下官方缓存,备好菜单和订单 ​

为什么:官方为这个案例录制了全部 4 次请求的答案缓存 json_cache.json,放对位置就能不调 API 复现官方全部数字;没有它则设 TYPESAFE_API_KEY 真跑。

安装依赖(官方 cookbook 原命令;cooksafe 是官方配套工具库,PyPI 自述「Shared helpers for the TypeSafe cookbooks」,它的依赖声明里带 typesafe-sdk<0.8.0,>=0.7.0,所以 SDK 不用单独写):

bash
pip install ipython 'cooksafe>=0.2.0,<0.3.0'

下载官方缓存(raw 直链;不设 Key 复现官方数字全靠它):

bash
curl -sL -o json_cache.json https://raw.githubusercontent.com/typesafe-ai/typesafe-public-examples/main/cookbooks/cart_from_primitives/json_cache.json

新建 cart.py,贴入文件头——客户端、缓存、常量:

python
import os
from pathlib import Path

from cooksafe import JsonCache, make_playground_link
from IPython.display import Markdown, display
from typesafe_sdk import Choice, TypeSafeClient

TYPESAFE_MODEL = "jev-1.14.0"  # 官方数字的录制版本
NOTHING_ELSE = "nothing_else"  # the sentinel option that ends the item loop
MAX_ITEM_REQUESTS = 6  # a hard stop, so a bad answer can never loop forever

client = TypeSafeClient(
    api_key=os.environ.get("TYPESAFE_API_KEY", "cache-only"),  # cached re-renders need no key
    model=TYPESAFE_MODEL,
    timeout=30.0,
)
json_cache = JsonCache(Path("json_cache.json"))


def fmt_confidence(confidence: float | None) -> str:
    """A confidence for printing. The API can return None, so never format it blind."""
    return f"{confidence:.2f}" if confidence is not None else "n/a"


print("setup ok")

运行 python cart.py。

你应该看到:setup ok——还没发任何请求。

三个名字先记住:NOTHING_ELSE 是结束循环的哨兵选项;MAX_ITEM_REQUESTS = 6 是硬上限——官方注释写得很清楚,防的是一个坏答案让循环永远停不下来;fmt_confidence 提醒你 confidence 可能为 None,别盲格式化。

  • setup ok 打印出来了,json_cache.json 就在运行目录里

第 2 步:把菜单写成数据——描述给 Jev 看,字段就是选项 ​

为什么:自然语言转结构化的「结构」住在哪里?官方的答案是一个字典:每个商品的描述写上顾客会怎么点它(latte 也被叫 flat white 或 milky coffee),fields 是它自己的 schema——字段名映射到合法值列表,而这些值就是那道字段题的 criteria 选项。菜单是数据不是 prompt,上新改字典就行。

贴入菜单和订单:

python
MENU = {
    "latte": {
        "description": "Espresso with steamed milk. Also ordered as a flat white or a milky coffee.",
        "fields": {
            "size": ["small", "medium", "large"],
            "milk": ["whole", "oat", "almond"],
        },
    },
    "filter_coffee": {
        "description": "Plain brewed drip coffee, sometimes ordered as a black coffee.",
        "fields": {},
    },
    "blueberry_muffin": {
        "description": "A blueberry muffin.",
        "fields": {},
    },
    "almond_croissant": {
        "description": "An almond croissant, sometimes ordered as a pastry.",
        "fields": {},
    },
}

ORDER = "Can I get a large oat milk latte and a blueberry muffin please"

两个设计点:

  • 描述承载点单用语:Jev 拿订单和这些描述做匹配——顾客说「a milky coffee」,命中的是 latte 的描述,不是这个词的字面。这就是为什么描述要写「顾客会怎么说」而不是「这是什么」
  • 只有 latte 有字段:size 和 milk 各自是闭集;其余三样没有字段,第 4 步会直接跳过它们的请求

运行 python cart.py。

你应该看到:还是只有 setup ok——菜单只是数据定义,一个请求都没发。

  • 我能说出 MENU 里 description 和 fields 各自的作用,以及为什么描述要写点单的同义词

第 3 步:收缩选项循环——自然语言转结构化的第一段,找出每样商品 ​

为什么:顾客点几样事先不知道。直接问「她点了几样」是让模型做计数——官方踩坑手册(Jev 1.13 版,9 种失败模式见 Jev 1.13 踩坑手册)明说:Jev 不可靠地计数,误差随被数对象的规模增大;替代做法是代码迭代候选、每个一问。收缩选项循环就是这个做法:每轮一道 Choice 问「订单里最先提到的是哪样剩余商品」,选中就存下并从下轮选项里删掉,把整句话原样再发一遍。这也是官方判据下的真依赖——官方原话:只有当代码拿到上一个答案才能构造下一个请求(比如决定下一题的选项)时,第二次请求才是真依赖。

先看循环全貌,再贴代码:

text
第 1 轮  选项 = latte / filter_coffee / blueberry_muffin / almond_croissant + nothing_else
         → latte (0.99)            存下,从下轮选项里删掉
第 2 轮  选项 = filter_coffee / blueberry_muffin / almond_croissant + nothing_else
         → blueberry_muffin (1.00) 存下,从下轮选项里删掉
第 3 轮  选项 = filter_coffee / almond_croissant + nothing_else
         → nothing_else (1.00)     ← 哨兵被选中,循环停

贴入问题构造器、单次请求函数和主循环:

python
def item_question(remaining: dict[str, dict], found: list[str]) -> Choice:
    """Build one request's question from the items not found yet."""
    already = ", ".join(found) if found else "nothing yet"
    criteria = {name: item["description"] for name, item in remaining.items()}
    criteria[NOTHING_ELSE] = (
        "The order mentions no menu item other than the ones already accounted for."
    )
    return Choice(
        instructions=(
            f"This is a cafe order. Items already accounted for: {already}. "
            "Which remaining menu item does the order mention? If the order mentions more "
            "than one remaining item, choose the one mentioned first."
        ),
        criteria=criteria,
    )


@json_cache
def ask_item(order: str, question: dict) -> dict:
    """One item request. The question arrives as its API JSON, which also keys the cache."""
    answer = client.system_one(state=order, questions={"item": question}).choices["item"]
    return {"choice": answer.choice, "confidence": answer.confidence}


def find_items(order: str) -> list[dict]:
    remaining = list(MENU)
    found: list[dict] = []
    item_requests = 0
    for _ in range(MAX_ITEM_REQUESTS):
        if not remaining:
            break
        question = item_question(
            {name: MENU[name] for name in remaining},
            [item["name"] for item in found],
        )
        result = ask_item(order, question.model_dump(mode="json", exclude_none=True))
        item_requests += 1
        print(
            f"item request {item_requests}: {len(remaining)} items + sentinel "
            f"-> {result['choice']} (confidence {fmt_confidence(result['confidence'])})"
        )
        if result["choice"] == NOTHING_ELSE:
            break
        remaining.remove(result["choice"])
        found.append({"name": result["choice"], "confidence": result["confidence"]})
    return found


items = find_items(ORDER)
print(f"\nfound {len(items)} items: {[item['name'] for item in items]}")

运行 python cart.py——全部命中官方缓存,一个请求都不发。

你应该看到(官方 jev-1.14.0 录制;本页实测逐字复现):

text
item request 1: 4 items + sentinel -> latte (confidence 0.99)
item request 2: 3 items + sentinel -> blueberry_muffin (confidence 1.00)
item request 3: 2 items + sentinel -> nothing_else (confidence 1.00)

found 2 items: ['latte', 'blueberry_muffin']

四个设计点值得停下来看:

  • 哨兵是循环的刹车:Choice 必须选一个选项,没有「都不选」——所以每轮都带 nothing_else,它的描述写明「订单里没有提到已算过之外的商品」。选中它,循环停
  • instructions 报进度:「Items already accounted for: nothing yet → latte → latte, blueberry_muffin」——每轮告诉模型前面找到了什么,避免它重复报已找到的商品
  • 「最先提到的选一个」消歧:订单提到多于一样剩余商品时,只选最先提到的那样——确定的规则,不是让模型自由发挥
  • 三重保险的退出:哨兵被选中、菜单清空、MAX_ITEM_REQUESTS 上限——循环一定会停

💡 这就是 Jev 的独有价值

这是软件循环,不是 agent 循环——官方构建指南明说「能用软件工作流表达的,就别用 agent 的 while 循环」。控制流、退出条件、去重全在你的代码里,模型每轮只做一件极窄的事:在当轮选项里挑一个。挑错了,代码的硬上限兜底;挑对了,confidence 告诉你这次挑选有多稳。

  • 零 Key 跑出 3 行 item request 和 found 2 items;我能说出循环的三个退出条件和哨兵选项的作用

第 4 步:每样商品一批量请求填字段 ​

为什么:商品找齐后,每样商品该问哪些字段就确定了——拿铁有 size 和 milk,麦芬没有字段。官方的填法是每样商品一次请求带上它全部字段题(fan-out 模式:多题对同一 state 并行评估),而不是一个字段一次请求。每个字段题还带 unspecified 选项——顾客没提的字段明确答「订单没说」,而不是被迫挑一个具体值。

贴入字段题构造和填空代码:

python
UNSPECIFIED = "unspecified"


def field_questions(item_name: str) -> dict[str, Choice]:
    questions = {}
    for field, values in MENU[item_name]["fields"].items():
        criteria = {value: None for value in values}
        criteria[UNSPECIFIED] = (
            f"The order does not say which {field} for the {item_name}."
        )
        questions[field] = Choice(
            instructions=f"Which {field} did the order ask for, for the {item_name}?",
            criteria=criteria,
        )
    return questions


@json_cache
def ask_fields(order: str, questions: dict[str, dict]) -> dict:
    """One field request. The questions arrive as their API JSON, which also keys the cache."""
    response = client.system_one(state=order, questions=questions)
    return {
        field: {"choice": answer.choice, "confidence": answer.confidence}
        for field, answer in response.choices.items()
    }


def fill_fields(order: str, item_name: str) -> dict:
    if not MENU[item_name]["fields"]:
        return {}
    questions = {
        field: question.model_dump(mode="json", exclude_none=True)
        for field, question in field_questions(item_name).items()
    }
    return ask_fields(order, questions)


cart = []
for item in items:
    fields = fill_fields(ORDER, item["name"])
    cart.append(
        {"name": item["name"], "confidence": item["confidence"], "fields": fields}
    )

运行。拿铁发 1 次请求(size + milk 两题并行),蓝莓麦芬 fields 为空——直接跳过请求。加上第 3 步的 3 次,全案例共 4 次请求。

你应该看到:没有新输出——这一步只把 cart 列表在内存里拼好(拿铁带着两个字段答案,麦芬 fields 为空字典),第 5 步才打印。

  • 我能说出为什么麦芬不发请求、unspecified 选项解决的是哪类「脑补默认值」问题

第 5 步:代码拼装结构化购物车 ​

为什么:拼装是纯代码的活,一次 API 都不发。每样商品带自己的 confidence,每个字段也带——哪个判断拖后腿一目了然。

贴入打印代码:

python
print(f'order: "{ORDER}"\n')
for line in cart:
    print(f"{line['name']}  (item confidence {fmt_confidence(line['confidence'])})")
    for field, answer in line["fields"].items():
        conf = fmt_confidence(answer["confidence"])
        print(f"    {field:<6} {answer['choice']:<12} confidence {conf}")

运行。

你应该看到(官方 jev-1.14.0 录制;本页实测逐字复现):

text
order: "Can I get a large oat milk latte and a blueberry muffin please"

latte  (item confidence 0.99)
    size   large        confidence 1.00
    milk   oat          confidence 1.00
blueberry_muffin  (item confidence 1.00)

对照第 2 步的菜单 schema 检查:商品是菜单里的合法 key,size 是 large(合法值之一),milk 是 oat(合法值之一)——不可能出现菜单外的商品或字段值,因为答案只能从选项里选。这就是「结构化」的底气:下游订单系统拿到的永远是 schema 内的数据。

  • 我的输出和官方数字逐字一致:latte 0.99 / large 1.00 / oat 1.00 / blueberry_muffin 1.00

📋 魔法 Prompt ​

本页的「Prompt」是给 API 的问题模板——抄走、替换【】占位符(带 ? 的是选填)就能接到你自己的菜单或商品目录上。三件套:找商品题、填字段题、循环骨架。

📝 找商品题(Choice 收缩版) ​

预期效果:凡是「从一句话里找出所有命中的已知条目」的需求——点单、选配置项、勾选服务——都套这一题,放进循环里逐个找出。

python
from typesafe_sdk import Choice

# 必填项:remaining 传还没找到的商品(名字 -> 顾客会怎么点它的描述)
criteria = {【name】: 【商品描述,写顾客的同义叫法】 for name in 【remaining】}
criteria[【哨兵名】] = "【订单里没有提到已算过之外的商品】"
item_q = Choice(
    instructions=(
        f"【这是什么场景的一句话。已算过的:{【found 或 'nothing yet'】}。】"
        "【订单里最先提到的剩余商品是哪样?提到多件时选最先说的】"
    ),
    criteria=criteria,
)
# 选填项:场景词换掉 cafe order——配置单、服务预约、商品咨询?
# 输出规范:answer.choice 是商品名或哨兵名;answer.confidence 接复核线
# 约束条件:
# ✅ 描述写「顾客会怎么点它」,同义词写进去(flat white 也是 latte)
# ✅ 每轮把已找到的报进 instructions,防止重复报
# ❌ 别问「她点了几样」——计数交给循环,模型只做挑选

✅ 填字段题(字段 schema 版) ​

预期效果:把每样商品的字段合法值变成一题一题的 Choice,一次请求并行问完;顾客没提的字段明确报 unspecified。

python
# 必填项:fields 是该商品的 schema:字段名 -> 合法值列表
questions = {}
for field, values in 【商品["fields"]】.items():
    criteria = {value: None for value in values}
    criteria[【"unspecified"】] = f"The order does not say which {field} for the {【商品名】}."
    questions[field] = Choice(
        instructions=f"【订单为这个商品点了哪个 {field}?】",
        criteria=criteria,
    )
# 选填项:unspecified 的下游处理——按你的默认值规则补,或转人工确认?
# 输出规范:每字段 answer.choice ∈ 合法值 ∪ {unspecified},各带 confidence
# 约束条件:
# ✅ 选项就是函数/系统接受的合法值——答案不可能越界
# ✅ 没有字段的商品跳过请求,别发空请求
# ❌ 不给 unspecified = 逼模型编一个值

🔧 循环骨架(代码版) ​

预期效果:找齐不定数量的条目、防死循环、缓存省钱——一段纯代码,可单测。

python
MAX_REQUESTS = 【6:硬上限,防坏答案死循环】
remaining, found = list(【MENU】), []
for _ in range(MAX_REQUESTS):
    if not remaining:
        break
    result = ask(【order】, build_question(remaining, found))
    if result["choice"] == 【哨兵名】:
        break
    remaining.remove(result["choice"])
    found.append(result)
# 输出规范:found 是 [{name, confidence}, ...];件数 = len(found),由代码数
# 约束条件:
# ✅ 三个出口缺一不可:哨兵、候选清空、硬上限
# ✅ 请求函数挂 @json_cache——改菜单/改问题会换缓存键自动真跑,重跑零花费
# ❌ 别把循环交给模型自己「决定何时结束」——退出条件是代码的事

检查点 ✅ ​

  • 不设 TYPESAFE_API_KEY、放好官方 json_cache.json,我逐字复现了官方全部数字:latte 0.99 / blueberry_muffin 1.00 / nothing_else 1.00 / size large 1.00 / milk oat 1.00
  • 全案例共 4 次请求:3 次找商品(含 1 次哨兵)+ 1 次填拿铁字段;麦芬没发请求
  • 我能说出为什么用循环而不是问「点了几样」:计数不可靠,代码迭代 + 每个一问才是官方替代做法
  • 我能解释这是「真依赖」:下一轮的选项要等上一轮的答案才能构造——这正是官方判据认可第二次请求的情形
  • 我知道 unspecified 和 nothing_else 的分工:前者是字段没说,后者是商品找完了

踩坑提醒 ​

❌ 让模型输出整个购物车 JSON ​

一次生成把计数、字段合法性、格式三件事搅在一起:数错件、编造 venti 这种菜单外的值、JSON 解析失败——错了都不知道错在哪一环。官方拆法让每件事单独可测:件数由循环数、合法性由选项保证、拼装由代码做。生成模型做不了这个保证,因为它的每个字都是重新打出来的。

❌ 忘了哨兵选项或硬上限 ​

没有 nothing_else,Choice 被迫在剩余商品里硬挑一个——点完的订单会「多出」一件。没有 MAX_ITEM_REQUESTS,一个坏答案能让循环永远停不下来。两个都写上,循环才既正确又必然终止。

❌ 顾客没说的字段,模型替她选了 ​

不给 unspecified 出口,模型只能挑一个具体值——「来杯拿铁」会自信地变成 medium whole milk latte。官方的解法就是每道字段题带 unspecified,下游按你的默认值规则补或转店员确认。同场景的另一个官方做法见 Agent 决策:函数调用 cookbook 用一道单独的是非题判断「用户提没提这个参数」,两种出口选一种。

❌ 直接拿中文订单上线 ​

官方说明:Jev 主要用英语训练,英语准确度最好;其他语言(含中文)能用但准确度较低——上线前先拿自己的中文订单跑一批看概率分布,重点关注 confidence,语言支持说明见模型与价格。另外改菜单或改问题会换缓存键:cooksafe 的缓存键由函数名和参数构成,改 MENU 后重跑会自动真请求(设了 Key 才会成功),想强制重跑就删掉 json_cache.json。

进阶技巧(可选) ​

数量:加一个 quantity 字段。 官方明说本例的局限:购物车没有数量字段,「two flat whites」也只产出一件 latte。官方给的解法:给每样商品的 schema 加 quantity 字段,合法值如 one / two / three——第 4 步的批量请求会自动把它一起问了。

大菜单:先分块再挑。 一道 Choice 最多 255 个选项(Choice 选择题有完整说明)。菜单更大时先挑区块(哪个类目),再挑区块内的商品——每段都在 255 以内。搜索与匹配的逐行搜索是同一思想的实战:给 218 行各标 ID,让 Choice 指向匹配的行。

免代码先试。 官方 cookbook 页面底部有做好的 Playground 分享链接——完整订单 + 第一轮找商品题(四样商品加哨兵),点开(https://console.typesafe.ai/playground)改订单措辞,答案跟着变。想生成自己的链接:

python
playground_link = make_playground_link(ORDER, {"item": item_question(MENU, [])})
# Print a usable URL in a script; render a clickable link in a notebook.
if __name__ == "__main__" and "__file__" in globals():
    print(f"Open in the TypeSafe playground:\n{playground_link}")
else:
    display(Markdown(f"🔗 [Open the first item-finding request in the TypeSafe playground]({playground_link})"))

链接生成离线完成、不需要 API Key;URL 里带着完整 state 和问题,别放敏感数据。看官方实拍:TypeSafe shopping cart demo。

接进你的点单流。 把 ORDER 换成聊天消息、语音转写、表单备注,MENU 换成你的商品目录;答案结构不变,下游订单系统照单全收。字段多了成本涨?并行提问与投机扇出实测过批量提问的收益:13 题一次请求比逐题问 12.2 倍便宜、10.0 倍快,答案不变。

本课小结 ​

✅ 自然语言转结构化,官方拆成两段:收缩选项循环找商品(件数由代码数,nothing_else 哨兵刹车),每样商品一批量请求填字段(合法值就是选项,unspecified 报「没说」)。

✅ 每个判断可校准:商品 0.99 / 1.00、字段 1.00 / 1.00——低置信的判断送店员人工确认,而不是硬进订单。

✅ 官方数字零 Key 复现:4 次请求(3 找商品 + 1 填字段)全部命中官方缓存,本页实测逐字重放。

✅ 菜单是数据不是 prompt:上新改 MENU 字典,问题模板一个字不动;改了会自动换缓存键真跑。

关键配置字段回顾:

字段本页的值说明
TYPESAFE_MODEL"jev-1.14.0"官方数字录制版本(2026-10-29);live 跑锁版本 ID,别用会漂移的 jev-latest
NOTHING_ELSE"nothing_else"哨兵选项:订单里没有剩余商品时被选中,循环停
MAX_ITEM_REQUESTS6硬上限:哨兵、菜单清空之外的第三重保险
UNSPECIFIED"unspecified"字段没提时的出口,防模型脑补默认值
MENU4 样商品,仅 latte 有 size/milk描述写顾客的点单叫法;fields 即 schema
每单请求数找商品 N+1 次 + 每样有字段的商品 1 次本例 3+1=4 次;全走缓存,改菜单/改题才 miss

相关教程:

下一课预告 ​

一句话点单变成了购物车,下一个问题是一大堆候选里哪几条真正匹配。下一课:搜索与匹配:重排序、逐行搜索、实体对齐。

你会学到:

  • 重排序把 CLERC 法律查询 top-1 命中从 5% 提到 18%、top-10 从 38% 提到 62%
  • GitHub ToS 218 行逐行语义搜索:给每行标 ID,一道 Choice 指向匹配的行——本页「选项即候选」的大规模版
  • 两份啤酒目录 450 个候选对怎么用一道 Score 加三道 Noul 找出同款
搞懂AI关于我们怎么写隐私说明

独立的第三方教程,与文中提到的任何产品及其公司没有隶属或合作关系;产品名称和商标归各自所有者。价格和政策以官方页面为准。