april.a digital gardenAPRILNOTE!
↖ 全部笔记
April

从 Jev 到 Laya:搭建面向智能体的本地决策引擎

# Laya# Jev# 智能体# 意图识别# 决策引擎

在搭建智能体时,我们经常把不同性质的任务交给同一个大模型:理解用户需求、选择工具、判断紧急程度、生成答案,甚至决定要不要继续执行。

其中一些任务需要开放式推理,但另一些问题的答案其实早已确定在一个有限集合里。例如,一条请求应该进入知识库问答、账单处理还是技术支持?工具执行失败后应该重试、追问还是转人工?

这类判断可以独立成服务。本文结合 Jev 的 System One 思路,以及已经搭建的本地 laya-api,介绍如何把 Laya 用作智能体前面的决策层。

一、为什么要把“判断”单独拆出来

假设用户说:

我被重复扣款了,请尽快退款。

一个客服系统至少要完成四件事:识别问题属于哪个业务领域、判断是否包含退款意图、决定处理优先级,以及回复用户。

前三件事可以拆成结构化判断;最后一件事通常仍然需要生成式模型。拆开之后,我们可以分别修改分类标准、评估误判和调整执行策略。

工作 适合的处理方式 示例
语义分类 决策模型 账单、技术支持、销售咨询
明确规则校验 普通代码 用户是否拥有查询该订单的权限
外部数据读取 业务接口 订单是否确实发生重复扣款
回答组织 生成式大模型 解释查询结果并说明后续步骤

这里的目标不是把所有工作都交给小模型,而是让每一步的输入、输出和责任更明确。

二、Jev 的原理:从生成文本到输出决策

TypeSafe 将 Jev 定位为 System One 模型:输入状态和预定义问题,返回类型化答案及概率。官方介绍强调并行输出决策,以及面向概率校准的训练方法 RLCD。它不靠生成一段解释文字来交付结果。来源:TypeSafe 官方介绍

可以把其接口思路概括为:

1
2
3
4
5
状态 + 判断问题 + 允许的答案
↓
类型化结果与概率
↓
程序选择后续分支

RLCD 的全称是 Reinforcement Learning for Calibrated Decisions。理解“校准”可以用一个统计例子:如果模型对一批判断都报出约 80% 的正确把握,理想情况下,这批判断的实际正确比例也应接近 80%。这是群体统计性质,不是对某个请求的正确性担保。

公开资料能够支持上述接口与训练目标描述,但不足以完整还原 Jev 的网络结构和训练过程。因此,不能把 Jev 直接描述成某一种 BERT 分类器。

另一个容易混淆的概念是“不会产生非法输出”和“不会判断错误”。当选项只有 billing、technical、sales 时,模型可以被限制为只返回其中一个值,但仍有可能选错。接入业务时,类型校验与语义评估都需要保留。

LangChain 的实践展示了这类决策如何进入智能体循环:通过分类器或中间件处理模型路由、工具调用检查等限定任务。对本文而言,最有价值的是这种职责拆分,而不是某个宣传中的速度倍数。来源:Building a Harness with Jev

三、为什么选择 Laya 做本地实践

Laya 提供开放权重,模型卡标注 Apache-2.0 许可,并提供英文、多语言和特定决策任务版本。它采用非自回归的决策路线,而不是逐字生成回复。来源:Laya 模型卡

对这个项目,优势主要体现在以下几个方面。

第一,部署位置可以自己控制。 当前服务从本地目录读取权重,并开启 Hugging Face 和 Transformers 离线模式。权重与依赖准备完毕后,推理不必依赖外部模型 API。这与需要内网部署的场景比较匹配。

第二,业务接口比较直接。 调用方定义选项,服务返回分类、评分或是非概率,接入端不用再从自然语言回答中提取部门名称。

第三,可以为中文请求选择多语言权重。 当前项目启动时预加载 multilingual,并允许调用方显式指定版本。相比只加载一个英文模型,这让语言选择成为可控制、可记录的一步。

第四,可以在不重写服务的情况下调整问题定义。 questions 由请求传入,部门分类可以换成智能体路由或知识库选择。不过,改变候选项不等于模型已经学会新的业务边界,仍然需要重新评估。

这些优势并不意味着 Laya 的所有判断都比 Jev 或通用大模型准确。这里选择它,是因为开放权重、本地运行和结构化接口适合当前实践;效果高低需要用同一批业务样本比较。

四、现有 laya-api 实际做了什么

项目结构很轻量:

1
2
3
4
5
6
7
8
laya-api/
├── server.py # 加载本地模型并创建 HTTP 服务
├── client.py # 标准库 HTTP 客户端
├── start.ps1 / stop.ps1 # 启停脚本
├── install.ps1 # 环境安装脚本
├── smoke_test.py # 集成检查
├── verification.json # 已保存的检查结果
└── requirements*.txt # 依赖与版本记录

server.py 使用 Laya SDK 的 Router 和 create_app,复用其 HTTP 服务能力,并额外提供 /v1/decide 路径。当前固定的直接依赖包括 laya[serve]==0.3.21、transformers==4.57.6 和 httpx==0.28.1。

模型目录布局如下:

1
2
3
4
F:\laya\
├── 根目录权重 # english
├── multilingual\ # 多语言
└── typed-decisions\ # 特定决策任务版本

服务启动前会检查三套目录中的必要文件。虽然不是所有模型都会立即加载,但当前实现要求三套权重文件都准备齐全。

核心配置包括:

项目 当前实现
默认地址 http://127.0.0.1:8000
推理接口 POST /v1/decide,兼容路径 /v1/systemone
交互文档 /docs
健康检查 /health
启动预加载 multilingual
默认驻留模型数 2
推理配置 默认请求 CUDA,fast=False、compile=False
请求 token 上限配置 默认 LAYA_MAX_TOKEN_BUDGET=2048
访问控制 设置 LAYA_API_KEY 后使用 Bearer 认证

这里有两种不同的“路由”。 Laya 的 Router 选择的是英文或多语言等模型权重;业务路由选择的才是账单智能体、技术支持智能体或某个工作流。前者已经实现,后者需要调用方根据返回结果编排。

在项目目录运行:

1
.\start.ps1

然后打开 /docs 即可测试。需要 CPU 运行时,现有脚本也支持:

1
.\start.ps1 -Port 8001 -Device cpu

这些是现有服务的使用方式,不是从零安装教程;在新机器复现前,需要按照项目 README 准备 Conda 环境和本地权重。

五、用一条请求同时完成三种判断

以下是面向当前 /v1/decide 接口的请求示例,问题 ID 和中文说明由调用方定义:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
{
"state": "我被重复扣款了,请尽快退款。",
"model": "multilingual",
"questions": {
"department": {
"type": "choice",
"instructions": "应该交给哪个部门处理?",
"criteria": {
"billing": "账单、付款和退款",
"technical": "软件故障和技术问题",
"sales": "购买咨询",
"other": "以上均不适用,或信息不足以判断"
}
},
"urgency": {
"type": "score",
"instructions": "请求有多紧急?",
"criteria": ["不紧急", "需要尽快处理", "严重紧急"]
},
"refund_requested": {
"type": "noul",
"instructions": "用户是否明确要求退款?"
}
}
}

三种问题对应三个不同用途:

示例加入了 other,让模型有机会表达“不属于已定义类别”。这只是一个候选项,不是可靠拒判机制;还需要在后续策略中处理不确定结果。

本项目已有验证记录使用的是三个部门选项、英文问题说明和中文用户输入,与上面新增加 other 的请求不同。记录中部门判断的实际响应节选如下:

1
2
3
4
5
6
7
8
9
10
{
"choice": "billing",
"probabilities": {
"billing": 0.982,
"technical": 0.0091,
"sales": 0.0088
},
"confidence": 0.9068,
"answer_confidence": 0.982
}

这条记录说明服务能够将该中文请求识别为账单问题。它不能证明所有退款请求都会得到正确结果,也不能作为新问题配置的测试结果。

还要注意,confidence 与 answer_confidence 的数值不同,不能混用。接入时应确认当前 SDK 对字段的定义,选择固定的指标做验证,再据此设阈值。本项目 README 也注明,action.act_probability 暂无可靠信号,不应拿它直接决定是否执行。

六、从模型服务到决策引擎

一个可以返回分类结果的 API,是决策引擎的基础,但还不是完整引擎。

在本文的设计中,决策引擎包含三部分:模型提供语义判断,策略代码决定如何使用判断,执行适配层连接真正的业务系统。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
用户输入 + 必要的会话上下文
│
▼
输入整理与候选范围确定
│
▼
Laya:分类、评分、概率
│
▼
策略层:检查结果与条件
├─ 信息不足 → 追问
├─ 判断不稳 → 大模型复核或人工处理
└─ 条件满足 → 指定智能体 / 工作流
│
▼
业务接口执行与反馈

这与 LangGraph 文章里“模型负责限定判断、代码负责工作流”的思路相近。LangGraph 可以承担编排,但不是这一架构的必要依赖;当前服务仍是独立 HTTP API。来源:Building Prod with Jev and LangGraph

1. 先定义意图的边界

“账单问题”和“退款请求”不是互斥概念:前者是业务领域,后者是具体动作意图。如果把它们并列在同一个单选问题中,模型的答案会变得难以解释。

一种更清晰的设计是分别判断:

字段 含义 候选或输出
domain 涉及哪个业务领域 账单、技术、销售、其他
refund_requested 是否要求退款 是的概率
needs_clarification 信息是否不足 是的概率
urgency 需要多快响应 有序评分

如果用户同时提出“查询账单”和“修改发票”,可以通过多个独立是非问题识别多个意图,或者先按业务规则拆分任务。不要期待单个 choice 自动表达全部需求。

2. 模型结果只是策略的输入

例如,策略层可以按以下顺序工作:

这里不直接给出“超过 0.8 就自动执行”的通用规则。阈值应在标注样本上确定,而且“允许进入账单处理流程”和“允许实际退款”应是两种不同的策略。

用户要求退款,只能证明存在这个意图。订单是否属于该用户、是否确实重复扣款、是否满足退款条件,都要由业务系统核实。

3. 与 Dify 的接入方式

基于现有接口,可以设计一个 Dify 工作流:开始节点接收问题,HTTP 请求节点调用 /v1/decide,条件分支读取 answers.department.choice 等字段,再进入知识库问答或业务处理流程。

这是接入方案,不是当前项目已完成的功能。部署时还要处理网络地址:Dify 如果运行在容器里,容器中的 127.0.0.1 并不指向宿主机上的 Laya 服务。需要使用容器可访问的宿主机或内网地址;对外监听时同步配置访问控制。

七、验证记录能说明什么

项目 README 记录的环境为 Python 3.11、PyTorch 2.10.0+cu128 和 RTX 5070 Ti。已有 verification.json 保存了以下请求结果:

输入与模型 部门结果 服务端记录耗时
中文退款请求,自动路由到 multilingual billing 491.53 ms
英文退款请求,自动路由到 english billing 1858.41 ms
英文退款请求,显式 typed-decisions billing 1997.09 ms

这些时间来自响应头 X-Inference-Time-Ms,不是完整客户端往返时间。按照当前项目说明,该计时可能包含首次模型加载,因此不能由这三次调用推导稳定吞吐、平均延迟,或得出多语言版本总是更快的结论。

smoke_test.py 检查了模型路由、答案取值范围、错误请求、两个并发请求、兼容路径和 OpenAPI。它验证的是基本接口行为,并没有完成大规模意图分类准确率评估;其中部门分类的断言检查“是否属于允许选项”,并非完整的业务正确性判定。

下一步我会优先补充一套业务评估集,覆盖:清晰意图、多意图、否定表达、指代、省略信息、无关输入,以及看起来像指令的用户原文。特别值得测试的是:

我不是要退款,只想知道为什么扣了两次钱。

这条输入仍然属于账单问题,但不应简单归为“明确要求退款”。

评估时同时记录准确率、误路由率、需要复核的比例、冷启动与预热后的 P50/P95 延迟。对于高风险动作,更应关注被错误放行的请求,而不只是总准确率。

八、当前实现的几个边界

长上下文可能被截断。 当前服务的 max_len 请求上限默认配置为 2048,模型默认读取预算还可能更小。上游文档出现更长上下文支持,并不意味着本地服务已经按那个配置运行。传入完整聊天记录前,应先明确保留哪些用户话语和业务事实。

并发调用不代表并行推理。 当前方案按单进程推理队列组织请求;多启动几个 worker 可能重复加载模型。真实容量需要压测,不能用“两条并发请求成功”替代吞吐测试。

模型选择本身也需要检查。 /health 中的 checkpoint_devices 与 cpu_fallbacks 能帮助确认是否实际使用 GPU。显存不足后的设备回退会改变延迟表现。

概率要和问题配置一起版本化。 候选描述、选项数量、模型版本和输入截断方式发生变化,都会影响结果。建议保存模型与问题配置版本、最终业务处理结果,便于复盘误判。

分类与事实核验应分开。 决策模型可以判断“用户是否声称已经付款”,但核验付款是否真实发生,仍应查询交易系统。

九、这次实践的收获

这次搭建完成的是一个可本地运行的 Laya 决策 API:输入用户状态和问题定义,输出可由程序读取的分类、评分与概率。

它为后续智能体系统提供了一个可独立替换和评估的判断环节。下一步值得投入的工作,是定义清晰的意图边界、建立评估集,再把决策结果与真实业务流程连接起来。

对我而言,决策引擎的价值在于让判断过程变得可观察、可调整:哪里适合模型处理语义,哪里需要确定性规则,哪里应该追问,都能在系统里明确表达出来。

参考资料

资料核对日期:2026-09-29。模型与 SDK 更新较快,复现时应同时记录依赖版本、权重版本和问题配置。

查找笔记