第 17 章

第 17 章:电商市场与广告——E-commerce marketplaces / Advertising

第 17 章:电商市场与广告——E-commerce marketplaces / Advertising

电商和广告是 Jev 落地最密集的两个场景,原因很直接:判断量极大、判定标准又大多能用一句话讲清。"这个字段对不对得上""这条 listing 算不算违规""这条评论在夸还是在骂",都是能被类型化、被批量执行的判断。本章用几个真实项目讲清商品归类、属性对齐、买家意图与广告合规的工程做法。

电商判断的共同结构

把电商里的判断摊开,会发现它们反复出现同一种结构:一堆候选对象,一个标准,逐条判定。商品字段要映射到目标表的列,listing 要对照规则查违规,评论要归到某个主题,商品要挂到类目树的某个节点。这类任务的输出空间是封闭的,天然适合 Jev。

底下还叠着一层分层执行:先用确定性的规则或字符串匹配把能定的定掉,只剩真正模棱两可的项才交给 Jev。这个模式在电商里到处出现,因为电商数据噪声大、规则明确的占比高,先跑规则能省下大量判定。

商品属性抽取与表格导入映射

jev-table-import-mapper(DuvInc/jev-table-import-mapper)是理解这个模式的好例子。它解决的问题很具体:把一份上传 CSV 的列,映射到目标表的列上。

它分两层执行。第一层是严格的确定性名称相等匹配——列名完全一致的直接映射,不问模型。第二层才对剩下的 (source, destination) 配对,逐对问一个 Noul:"这一列是不是对应目标表的这一列?",并且给每个来列加一个 guard Noul 做保护。

实测数据值得记下来:一个 23 列的导出文件里,它一次调用问了 253 个问题,映射对了 10 of 10 列,耗时 915 ms,成本 $0.0012。关键在于阈值设计——低于 0.75 的映射不猜,直接留在界面上让人看。宁可让用户手动确认几列,也不把不确定的映射悄悄写进去。这和合规里的 fail-closed 是同一个思路。

# 第一层:代码里的确定性匹配
mapping = {}
remaining_src = []
for src in source_columns:
    same = [d for d in dest_columns if normalize(src) == normalize(d)]
    if same:
        mapping[src] = same[0]          # 名称相等,直接定
    else:
        remaining_src.append(src)

# 第二层:只对剩下的配对问 Jev
questions = {}
for s in remaining_src:
    for d in dest_columns:
        questions[f"{s}__{d}"] = {
            "type": "noul",
            "question": f"源列“{s}”是否对应目标列“{d}”?"
        }

d = client.systemone(
    state=build_state(remaining_src, dest_columns, sample_rows),
    questions=questions,
    instructions="你是数据导入助手,只看列名与样例值判断对应关系,拿不准判否。"
)

for key, ans in d.items():
    s, dst = key.split("__")
    if ans.probability >= 0.75:        # 0.75 以下不猜
        mapping[s] = dst

AI-decision-maker(zlZayn/AI-decision-maker)做的是邻近的事:用 Choice 把 CSV 的列归入一个 13 种类型的编码表,再把整个数据集归到 6 种场景之一,所有写入都在本地执行。它有一个反直觉的实测结果:在这类任务上,Jev 的 token 成本是 LLM 的 6.6–12.7 倍。原因不是 Jev 变贵了,而是这个任务里每个问题都要重复一遍判定标准("这个列名是否符合以下的 13 类定义……"),标准文本被逐列重复发送,token 反而上去了。

这个数字很有价值——它是一份诚实的失效边界。Jev 省的是"生成"的钱,不是"输入"的钱。 当任务的特点是问题短、标准长、还要对每一列重复标准时,token 成本可能反而更高。判断要不要用 Jev,不能只看"决策比生成便宜"这句话,要看你发出去多少 token。

类目树映射:分层 Choice 与兜底桶

商品的类目树往往很深——"服饰 > 男装 > 上衣 > 卫衣 > 连帽卫衣"。指望模型一次输出完整路径,等于让它在一个组合爆炸的空间里做单一选择。更稳的做法是把类目树拆成逐层 Choice:先在第一层选大类,再在选中的大类下选子类,一层层收敛。

CATEGORY_TREE = {
    "服饰": ["男装", "女装", "童装"],
    "男装": ["上衣", "裤装", "鞋靴"],
    "上衣": ["卫衣", "衬衫", "T恤"],
}

def classify_path(title, tree, root_labels):
    path, candidates = [], root_labels
    while candidates:
        d = client.systemone(
            state={"title": title, "path_so_far": path},
            questions={"pick": {
                "type": "choice",
                "question": f"「{title}」应归入以下哪一类?",
                "choices": candidates + ["以上都不是"],
            }},
            instructions="你是电商类目归类助手,只按商品标题判断,拿不准选「以上都不是」。"
        )
        if d["pick"].choice == "以上都不是" or d["pick"].probability < 0.7:
            break                      # 拿不准就停,回落到人工队列
        chosen = d["pick"].choice
        path.append(chosen)
        candidates = tree.get(chosen, [])
    return path

逐层判定的好处是每一层的候选只有几个,判定更准;代价是要多轮请求,所以现实里常见的是把确定的前几层用规则表定掉(品牌到类目的固定映射),只对模糊层问 Jev。

文档侧有同类做法的印证:DocJev(jerryjliu/docjev)用自然语言写的类目规则把文档归类,40 篇文档的试点全部 40/40 判对,Jev 决策的 p50 约 182 ms。规则用自然语言表达、类别可以随目录调整,这对类目经常变动的电商目录很实用。Tab Sorter(AstonyCat/jev-tab-grouper)的兜底桶策略同样该抄:分不出来的项不要硬塞进某个类目,落进一个"待归类"桶,等人工或后续规则处理。

买家意图与商品匹配

买家侧也是一堆类型化判断。hearth-jev-rental-search(Nancy-Chauhan/hearth-jev-rental-search)是个多源租房搜索的例子:它自主地从多个来源聚合房源,让 Jev 逐条判定"这条房源是否符合用户条件"。把"房源"换成"商品",就是电商里的搜索匹配——把筛选条件写成自然语言("预算 500 以内、全新、支持七天无理由"),对每个候选商品问一个 Noul,命中的进候选集。

这里有个和类目树相反的策略取舍:类目树要唯一归属,所以用 Choice 收敛到一条路径;买家筛选要多标签,一条商品可能同时"符合预算"又"符合成色要求",所以拆成一串独立的 Noul,各判各的。第 2 章讲过 Choice 和多个 Noul 的区别,电商场景里这两种用法几乎一半一半。

条件本身还经常是"半结构化"的——有的能直接比数字(预算、库存),有的只能靠语义("看起来不像正品""描述有夸大")。工程上的做法是把能算的用代码算,算不了的转成自然语言陈述交给 Jev,两者结果在代码里合并成一个候选集。

评价与买家意向的行级标注

电商的第三类判断落在用户生成内容上。Feed Lens(SkywalkerDarren/feed-lens)是个直接可参考的例子:它用 Jev 的 Noul 判定,对 Weibo、Threads、X 上的帖子打每个平台、用户自定义的主题词和表达标签。

这里的要点是"用户自定义标签"——标签不是预先写死的枚举,而是每个用户自己写的一串陈述(比如"在询问价格""在投诉物流"),Jev 逐条判断这条帖子是否符合。这其实就是把 Detection 形态用户化了:一个标签一个问题,返回一个概率。

user_labels = [
    ("询问价格", "这条帖子是否在向他人询问价格或优惠?"),
    ("物流投诉", "这条帖子是否在抱怨发货慢或物流问题?"),
    ("买家秀", "这条帖子是否在展示自己收到的商品?"),
]

questions = {
    f"label_{i}": {"type": "noul", "question": q}
    for i, (_, q) in enumerate(user_labels)
}

d = client.systemone(
    state=post_text,
    questions=questions,
    instructions="你是社媒内容标注助手,逐条判断帖子是否符合给定标签,不做推测。"
)

hits = [label for i, (label, _) in enumerate(user_labels)
        if d[f"label_{i}"].probability >= 0.6]

注意这是多标签——一条帖子可能同时命中"询问价格"和"买家秀",所以用多个 Noul,不是一个大 Choice。

同样的模式可以搬到后台数据表上。Vibefilter(vibefilter/filament)是个 Filament 后台的表格过滤器:它对每一行问一个 Noul,题干就是一句大白话,比如 "The customer is angry.",把概率达到 0.8 的行留在筛选结果里。它的实测很具体:在 1,000 条评论里,判定结果与 demo 自带的情绪标签吻合 861 条,每条陈述约 $0.003、一秒,判定按内容缓存。查差评、查疑似刷单、查需要人工复核的 SKU,无非是换一句陈述而已——业务规则写成自然语言,一行一个布尔判定。

Listing 质量与广告创意合规

商品详情页和广告创意要过的审,本质上是同一类判断:这段内容算不算违规、算不算低质。jev-slop-guard(davertor/jev-slop-guard)给出了一个干净的实现:对每条帖子问一个 Choice(slop / not_slop),达到用户设定的阈值(默认 0.7)就打码加标记,并留一个"显示这条内容"的覆盖按钮。它还用了一个很实用的工程约束——三个并发请求的上限,每条内容只缓存一次判定结果,保证滚动浏览时不会被判定拖住。

阈值在这里的作用是让用户自己权衡误报和漏报:觉得误伤太多就调高阈值,觉得漏放了就调低。代码握着"打码/放行"的分叉,模型只回概率。

广告侧目前还没有高星开源项目,官方把它的关键判断归为几类:创意合规审核、定向与相关度、低质流量判定,以及在实时出价里把语义信号当作特征。"创意合规"和"定向相关度"可以直接复用上面的 Detection/Ranking 做法。有一个真实项目能印证"广告识别"的判定思路:Jev Wrapped(gaborishka/jev-wrapped)在分析 Telegram 频道的帖子时,用三个 Noul 判断每篇是否含付费广告、标题党、情绪施压——广告从 0.7 起算,但当帖子类型本身也判为广告时放宽到 0.4;标题党和情绪施压从 0.5 起算。

这种"主判定 + 条件放宽"的阈值组合,是广告识别里很有借鉴价值的策略:单一信号弱,但信号之间相互印证时可以降低门槛。

定向相关度则更接近 Ranking/Scoring——给定一条广告和一群受众画像,逐对判一个相关性 Score,再在代码里排序取头部。低质流量判定可以看成"多条 Noul 叠加":把"标题党""货不对板""诱导跳转"各写成一个独立布尔,任一命中降权。这里没有免费的准确率,误伤广告主的代价(少赚曝光)和放过低质内容的代价(伤用户体验)是一对权衡,只能靠阈值调。

实时出价里的语义信号,落在 ML Feature Extraction 形态上——把广告文案、落地页文本转成 Score/Noul 特征,喂给下游的竞价模型。特征本身的语义判定交给 Jev,出价逻辑仍然在你自己手里。

一个商品批量入库的编排

把前面几节串起来,一个"商品批量入库"的管线大致是这样:先用确定性规则处理标题格式、SKU 校验;需要语义判断的字段走 Jev(Choice 归到类目、Noul 判违规、Score 评质量分);不确定的留在人工队列。这个编排和 Tab Sorter 的思路一模一样——它对窗口里的每个标签页问一个 Choice,对照用户可编辑的分组标准,分不出来的一律落到一个固定兜底桶,绝不硬归。商品入库同理:拿不准的不要塞进某个类目,丢到"待归类"桶里。

flowchart TD
    CSV["商品数据(多列)"] --> RULE["确定性规则层
SKU / 格式 / 名称相等"] RULE -->|可判定| WRITE["写入目标表"] RULE -->|需语义判断| JEV["Jev
Choice 归类 + Noul 违规 + Score 质量"] JEV -->|p ≥ 阈值| WRITE JEV -->|p < 阈值| REVIEW["人工队列 / 待归类桶"] WRITE --> DONE["入库完成"] REVIEW --> DONE

阈值是这根管线的调节旋钮:调高,自动化程度下降但更准;调低,吞吐上来,但人工复核的压力前移到了下游。

小结

  • 电商判断的共同结构是"一堆候选 + 一个标准 + 逐条判定",输出空间封闭,天然适合 Jev 的类型化决策。
  • DuvInc/jev-table-import-mapper 用"确定性名称匹配 + 逐对 Noul"两层执行,23 列导出 253 问、915 ms、$0.0012,低于 0.75 的映射一律不猜。
  • zlZayn/AI-decision-maker 暴露了一个反直觉的边界:Jev 省的是生成的钱,不是输入的钱——标准文本被逐列重复发送时,token 成本可能反而更高。
  • 类目树映射用逐层 Choice 收敛 + 兜底桶,参考 jerryjliu/docjev(40/40、p50 约 182 ms)与 AstonyCat/jev-tab-grouper;买家匹配则相反,用多个 Noul 做多标签,参考 Nancy-Chauhan/hearth-jev-rental-search。
  • 行级标注用"自然语言陈述 + 逐行 Noul",参考 SkywalkerDarren/feed-lens 与 vibefilter/filament(1,000 条评论吻合 861 条、约 $0.003/条)。
  • Listing / 创意合规用 Choice + 用户可调阈值,参考 davertor/jev-slop-guard(默认 0.7);广告识别的阈值可"主判定 + 条件放宽",参考 gaborishka/jev-wrapped(广告 0.7,类型为广告时 0.4)。

下一章进入判断量最大的内容审核与信任安全,看分级审核与逃逸对抗怎么做。

📑 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——把决策织进智能体骨架
← 返回本书大纲