第 12 章:特征提取与需求预测——Feature extraction / Demand forecasting
第 12 章:特征提取与需求预测——Feature extraction / Demand forecasting
评分卡、梯度提升树、时序预测这类模型擅长处理数字,却天生看不懂文本。"这条新闻够不够正面""这个供应商是不是在放鹰""这一期促销的力度比上一期强多少"——真正有解释力的信号全在非结构化内容里,却进不了特征矩阵。本章讲怎么用 Jev 把文本判成能直接拼进特征表的列,再把这些语义信号送进需求预测。
预测模型卡在"结构化"这一关
传统预测模型的输入是一张数值表:历史销量、价格、滞后项、星期几、节假日标记。它们的学习方式决定了这件事——梯度提升树在数值轴上切分,逻辑回归给每个特征配一个权重,时序模型看的是数值的走势。文本进不来,不是因为模型弱,而是因为它没有能承接文本的位置。
于是长期只有两条老路:
- 训一个文本模型:为每个场景单独标注、单独微调。重,慢,换一个业务口径就得重来一遍。
- 让 LLM 抽成 JSON:把一段文本丢给生成式模型,让它吐结构化字段。贵,且不稳定——同一段输入两次,字段可能不一样,数字还可能被"编"出来。
Jev 给的是第三条路:把每个判断变成一列概率。一次 Noul 得到 P(true),一次 Score 得到 score = Σ i·p_i,一次 Choice 得到每个选项的概率。这些值天然是 0 到 1 之间的数字,能直接当特征用,而且校准过、可复现、按决策计价。生成式模型负责"写出各种可能",这一步只需要它"在具体这一格选一个数"。
两种粒度:直接问,还是多维打分
同样的文本要变成特征,先要决定问几个问题、变成几列。
最简的粒度是每行一个问题、出一列:问一个 Noul"这条内容是不是促销",得到一列 is_promotion_p,直接拼进表里就行。它便宜、解释清晰,但一列只承载一个语义。
更细的粒度是把判断拆成多个维度分别打分。agentjournal.dev 上的一篇实测("Jev judge call vs dimension scores")就对比了这两种做法:每行只问一个直接问题,与把 12–14 个维度分别打分、再用本地拟合的权重合成,在三个分类任务上较量。结果是多维打分在日文 NLI 上达到 0.9076,而单问题只有 0.8373——维度拆得越细,信号越多,准确率越高。但代价也写在同一篇里:多维方案把大约 25 倍的"难判的良性样本"误标成了攻击。
这正是一对典型的特征工程取舍:更多的列意味着更多的信号,也意味着更高的成本与更脆的误报面。上线时该问几个问题,取决于下游模型缺多少信息、以及你能忍多少误报。
flowchart LR
A["非结构化文本
评论 / 公告 / 日志 / 工单"] --> B["Jev 判定
Noul / Choice / Score"]
B --> C["概率 → 特征列
0–1"]
C --> D["拼进数值特征表"]
D --> E["下游模型
GBDT / 时序 / TabPFN"]
E --> F["预测结果
并由概率列回溯解释"]实例一:tab-jev——把每个选项的概率变成一列
edamame-labs/tab-jev 把这条路径做到了极简。它对每一行的文本问一个针对目标的 Noul,再加一组 Score 评分维度,然后做一件关键的事:把每个选项的概率直接转成该行旁边的一列,和原有的数字字段并排摆在一起。接着,它让一个表格基础模型(如 TabPFN)在上下文里直接从这些带标签的行学习——不训练、不改权重,全靠拼接好的特征表。
在 Kickstarter 众筹预测上,它的数字很说明问题:256 个标签下达到 0.745 AUC,而只靠 Jev 本身加校准是 0.682。两个信息点:一是 Jev 单独就能给一个不弱的下限;二是把语义列交给下游表格模型后,它在极低标签量(256)下就把上限又抬了一截。这正是"特征提取喂给下游模型"最想看到的形状——不是让 Jev 去做预测,而是让它把预测所需的语义补上去。
实例二:jevextract 与 JevSpan——先把实体抽出来,再当特征
如果特征不是"整段文本的一个分数",而是"文本里出现了哪些实体、各是什么",就需要先做抽取。两个项目把抽取的分工讲得很清楚。
gabazureus/jevextract 是 LangExtract 的替代方案,它的做法是:由代码提出候选 span(带精确 offset),Jev 对每个 span 只回答一个 Choice(属于某个 schema 类别,或"都不是"),再加一个句级的 Noul,然后把高于阈值的答案留下、把接近临界的标出来交人工复核。它的基准显示,成本比在 Gemini 3.5 Flash 上跑 LangExtract 低 10–26 倍,但 F1 略低(84.2 对 88.5)。这个取舍之所以可接受,是因为候选位置由确定性代码给出,模型只做分类——控制流留在代码里,判定交给 Jev,成本因此被压了下来。
lzq-0529/jev-span 则把这条路推到零样本命名实体识别(NER):在标点处切分文本,对每种实体类型、每个候选窗口问一个 Choice,再用第二个 Choice 复核这个提名(是该类型 / 不是 / 混合 / 部分),最后用第三个 Choice 定下边界。在 12 个中英文 NER 基准上,它平均 73.7 严格 F1,而对标的 Qwen3.8-27B 直接抽取是 72.1。
顺带一提 xyzzzh/GroundingJev:它借用同样的"一次非自回归前向"思路,用一个 Jev 式的 Qwen3.5-0.8B 小模型,把图像加指代短语一次映射成四个边界框坐标,相对自回归的基座模型有 8.61 倍的推理加速。它不是直接调 Jev API,而是说明了这个决策范式可以蒸馏进一个极小的模型里——当你要抽的特征量极大时,这条路能同时拿下成本和延迟。
从抽取到特征:给评分卡补上语义列
把上面的做法拼起来,一段文本进入特征表的全过程是这样:先按业务定义一组判定,一次请求问完,再把每种输出的结果摊成列。
FEATURE_SPEC = {
"sentiment": ("score", "这段内容整体是正面还是负面?"),
"urgency": ("score", "这条信息的紧急程度如何?"),
"is_promotion": ("noul", "这条内容是不是一次促销或打折?"),
"competitor_move": ("noul", "这条内容是否包含竞品的价格或动作?"),
}
def text_features(text: str) -> dict[str, float]:
d = client.systemone(
state=text,
questions={k: {"type": v[0], "question": v[1]} for k, v in FEATURE_SPEC.items()},
instructions="只依据给出的文本判断;每个问题独立作答,不要相互迁就。",
)
feats: dict[str, float] = {}
for key, (kind, _) in FEATURE_SPEC.items():
ans = d[key]
# Noul 出概率,Score 出加权分——两者都是 0–1 的数字,可直接当特征
feats[f"{key}_p" if kind == "noul" else f"{key}_score"] = (
ans.probability if kind == "noul" else ans.score
)
return feats返回的 feats 就是一个字典,直接作为一行拼到原有的数字特征后面即可。注意 Score 的 score = Σ i·p_i 这一步——它把一整条概率分布压成一个有序分数,天然落在选项数对应的区间里,不用你手动归一化;而且因为它来自分布,一列分数随时能拆回各选项的概率来解释。
实例三:jev-curate 与 jev-align——数据管道里的判定
特征提取在数据管道里其实是同一件事的两个方向:一边是往里筛,一边是给人改。
AkashPriyadarshii/jev-curate 做的是筛选:它用 Jev 的 Noul 检查加校准过的置信度来筛合成的 JSONL 与 Parquet 行,把通过的行和被拒的行直接流式写盘。这等于把"哪些样本配得上进训练集"这件判断变成了一次判定调用,而且不需要把整批数据先读进内存。
Sutro 的 jev-align 走得更远:它用 Jev 的 Choice、Score 或 Boolean 评估 CSV、Parquet、JSONL 的每一行,把含糊的和用于审计的样本送给人工,然后用被接受的人工标注,通过 GEPA 去优化那份保存下来的定义。这里有个关键设计——人的修正不是去重训一个模型,而是去改进"定义"本身:定义越写越准,同一套判定就跟着变好。这条"人工纠错回流到定义"的闭环,是所有长期运行的特征管道都必须有的。
实例四:jlink、jevgrep、typeful-triage——把判定铺到记录、日志与工单
同一套特征化思路可以铺到更细的粒度上。
keltokhy/jlink 处理的是记录去重与关联:用一句大白话的匹配规则把两条记录判成"是否同一条",每个记录对问一个 Noul,并辅以**本地候选分块(blocking)**先缩小要比较的范围。分块是确定性的、免费的,模型只负责规则覆盖不到的那些对——又一次"规则先行、模型兜底"。
allebee/jevgrep 把这一招用到了日志流:对着一个大白话问题,逐行问一个 Noul,把概率达到阈值的行打印出来,连 tail -f 的实时输出也能过滤,仓库里还带了一份对 Claude 的人工标注基准。日志特征化因此可以在流上跑,而不是先落库再算。
cephalization/jev-triage(typeful-triage)把它变成了团队工具:一个多人协作的分诊面板,对每个 issue 问一组固定的类型化问题——kind、severity、urgency、duplicate、next step——而且每一次人工纠正都被保存下来、在后续运行时回显给模型。这就是特征提取的在线版:判定出的字段既是给下游用的特征,也是给后续判定用的上下文,人会越用越准。
需求预测:把语义信号喂给预测模型
需求预测(Demand forecasting)是官方 use-case-map 里 19 个行业用例之一,但它在这个社区里还没有一个高星的独立项目可引。所以这一节用通用示例讲清模式,落地时的口径要随业务定义调整。
需求预测的困境是:数字能告诉你"过去卖了多少",但说不清"这一期为什么会跳"。一次竞品降价、一条负面新闻、一个节假日的政策变动——这些都会让需求偏离历史趋势,而它们在时序数值里根本不可见。语义列补的就是这块解释性特征。
tab-jev 的 Kickstarter 众筹预测其实就是需求预测的一个变体:众筹额是需求信号,它靠语义列在低标签量下把 AUC 从 0.682 拉到 0.745。另一个量级上的证据来自 pjmenon45/Jev-IOT:它用超低成本的非自回归遥测分类器在 1000 万台以上智能电表上做 sub-150ms 的异常分诊与自动处置,成本低于每月 35 美元。遥测本身就是一类需求与用量信号,它证明了这类判定能在极端规模与延迟预算下运行。
把语义列接进时序预测,形状大致如下——这里的代码是通用示例,用来说明模式:
import pandas as pd
from sklearn.ensemble import GradientBoostingRegressor
FEATURE_COLS = ["sales_lag_1", "sales_lag_7", "sales_lag_30",
"sentiment_score", "is_promotion_p", "competitor_move_p"]
def build_demand_frame(sales: pd.DataFrame, semantic_rows: pd.DataFrame) -> pd.DataFrame:
df = sales.copy()
for lag in (1, 7, 30): # 数值滞后特征:过去的需求
df[f"sales_lag_{lag}"] = df["sales"].shift(lag)
df = df.merge(semantic_rows, on="date", how="left") # 语义列:为什么这一期会变
return df.dropna()
frame = build_demand_frame(sales, semantic_rows) # semantic_rows 来自 text_features
model = GradientBoostingRegressor().fit(frame[FEATURE_COLS], frame["sales"])要点是别把语义列当成时序数值的替代品。数值滞后项负责"惯性的延续",语义列负责"拐点的解释"。二者拼在一起,模型才既看到趋势、也看到趋势之外的那一下扰动。
让语义列可监控、可解释
用概率列做特征,最大的好处是可审计:任何一个预测,都能沿着它用到的那几列往下拆——这一行的 sentiment_score 是多少、competitor_move_p 是多少、Score 的分数来自哪个选项占了多少概率。特征不再是黑箱里的一团数字,而是能逐条讲清来源的判定结果。
但也正因为它是概率,校准必须被当成一等公民来监控。scienthoon/jev-ood-calibration 做了一份独立的校准测试:在 900 条规则生成的支持工单加三个公开基准上跑 Jev,公开了每一次原始响应,并给出每条类型的偏差方向——Choice 和 Score 偏自信,Boolean 偏保守。这条结论对特征提取很实用:用 Noul 出的列,系统性上会比它声称的更容易触发阈值;用 Score 出的列,则可能把分布压得更陡。 上线后按批次复核校准,比上线前调一次阈值更重要。
小结
- 预测模型吃不下文本;Jev 的第三种解法是把每个判断变成一列 0–1 的概率或分数,便宜、可复现、可校准。
- 两种粒度:每行一个问题出一列;或 12–14 维分别打分再拟合权重——后者在日文 NLI 上 0.9076 对 0.8373,但把约 25 倍的难判良性样本误标成攻击,信号与代价同时变大。
- tab-jev 把每个选项的概率转成列,交给 TabPFN 在上下文里学:256 标签下 0.745 AUC,对照 Jev 单独加校准的 0.682。
- 抽取层的关键分工是"候选由代码给、分类由 Jev 判":jevextract 成本低 10–26 倍但 F1 84.2 对 88.5;JevSpan 在 12 个中英文 NER 基准上平均 73.7 对 72.1;GroundingJev 用一次前向换 8.61 倍加速。
- 数据管道里,jev-curate 用
Noul加校准置信度流式筛 row,jev-align 把人工修正经 GEPA 回流去优化定义;jlink、jevgrep、typeful-triage 把判定铺到记录、日志流与工单。 - 需求预测是官方用例之一,当前缺高星项目,用"数值滞后特征 + 语义列"的通用模式落地;上线后要按
jev-ood-calibration的结论监控校准——Choice/Score偏自信、Boolean偏保守。
把文本变成可进模型的特征之后,下一章我们转向另一个"筛人"的场景:招聘与线索生成——简历怎么分诊、线索怎么打分与去重。