lvren-jev 0.2.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lvren_jev-0.2.0/LICENSE +21 -0
- lvren_jev-0.2.0/PKG-INFO +628 -0
- lvren_jev-0.2.0/README.md +595 -0
- lvren_jev-0.2.0/pyproject.toml +24 -0
- lvren_jev-0.2.0/setup.cfg +4 -0
- lvren_jev-0.2.0/src/lvren_jev/__init__.py +33 -0
- lvren_jev-0.2.0/src/lvren_jev/_version.py +3 -0
- lvren_jev-0.2.0/src/lvren_jev/classifier.py +3 -0
- lvren_jev-0.2.0/src/lvren_jev/decision.py +268 -0
- lvren_jev-0.2.0/src/lvren_jev/definitions.py +203 -0
- lvren_jev-0.2.0/src/lvren_jev/errors.py +18 -0
- lvren_jev-0.2.0/src/lvren_jev/loader.py +37 -0
- lvren_jev-0.2.0/src/lvren_jev/results.py +94 -0
- lvren_jev-0.2.0/src/lvren_jev/runtime.py +268 -0
- lvren_jev-0.2.0/src/lvren_jev.egg-info/PKG-INFO +628 -0
- lvren_jev-0.2.0/src/lvren_jev.egg-info/SOURCES.txt +23 -0
- lvren_jev-0.2.0/src/lvren_jev.egg-info/dependency_links.txt +1 -0
- lvren_jev-0.2.0/src/lvren_jev.egg-info/requires.txt +2 -0
- lvren_jev-0.2.0/src/lvren_jev.egg-info/top_level.txt +1 -0
- lvren_jev-0.2.0/tests/test_classifier.py +105 -0
- lvren_jev-0.2.0/tests/test_decision.py +135 -0
- lvren_jev-0.2.0/tests/test_loader.py +170 -0
- lvren_jev-0.2.0/tests/test_results.py +25 -0
- lvren_jev-0.2.0/tests/test_runtime.py +75 -0
- lvren_jev-0.2.0/tests/test_version.py +31 -0
lvren_jev-0.2.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BlueLvRen
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
lvren_jev-0.2.0/PKG-INFO
ADDED
|
@@ -0,0 +1,628 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lvren-jev
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Business-neutral semantic decision runtime for TypeSafe Jev
|
|
5
|
+
License: MIT License
|
|
6
|
+
|
|
7
|
+
Copyright (c) 2026 BlueLvRen
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
|
26
|
+
|
|
27
|
+
Requires-Python: >=3.11
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
License-File: LICENSE
|
|
30
|
+
Requires-Dist: PyYAML>=6.0
|
|
31
|
+
Requires-Dist: typesafe-sdk>=0.1
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
# lvren-jev:面向应用与 LLM 的 Jev 决策原语库
|
|
35
|
+
|
|
36
|
+
**核心思想:以完整业务操作作为 LLM 的委派边界,将决策细节和执行过程封装在程序内部。**
|
|
37
|
+
|
|
38
|
+
## 为什么使用 lvren-jev
|
|
39
|
+
|
|
40
|
+
官方 Jev SDK 解决“如何调用 Jev API”;`lvren-jev` 将 Jev 的分类、评分和条件判断封装为可复用的决策原语,供普通程序构建业务能力,再交由 LLM 按需调用。
|
|
41
|
+
|
|
42
|
+
以“读取 Excel 工时内容、判断类型并写回文件”为例,三种接入方式的区别在于:谁组织决策,谁处理结果,以及这些工作能否复用。
|
|
43
|
+
|
|
44
|
+
| 场景 | LLM 承担什么 | 程序承担什么 | 接入与维护成本 |
|
|
45
|
+
| --- | --- | --- | --- |
|
|
46
|
+
| LLM + Jev(直接使用 SDK) | 理解 Jev 参数、构造判断指令和类别、解释响应,再协调后续读写操作 | 尚未封装完整业务流程,文件操作需另行接入 | LLM 仍参与决策细节,增加参数生成、响应解释和过程协调的开销 |
|
|
47
|
+
| LLM + Jev + 普通程序 | 若程序只负责读写,LLM 仍需协调判断;若程序已封装完整流程,LLM 可以只委派任务 | 自行实现 Jev 请求构造、响应解析、分类回退及业务执行 | 可以实现完整业务委派,但决策接口与复用机制需要项目自行设计和维护 |
|
|
48
|
+
| LLM + 集成 lvren-jev 的普通程序 | 选择业务操作,提供必要信息,根据完成状态继续任务 | 普通程序获取上下文、完成读写和状态回报;`lvren-jev` 加载决策定义、调用 Jev 并返回结构化结果 | 复用现成的决策定义、调用接口与结果类型,减少各项目重复封装的工作 |
|
|
49
|
+
|
|
50
|
+
普通程序直接使用官方 SDK 也能实现同样的职责划分。`lvren-jev` 将其中通用的决策封装做成可复用组件,使项目更容易把完整业务留在程序内部,避免每接入一种判断都重新设计参数、解析逻辑和调用约定。仅仅引入本包不会自动减少 LLM 的参与,业务入口仍应由程序封装完整。
|
|
51
|
+
|
|
52
|
+
例如,LLM 将文件路径交给“完成 Excel 工时分类”工具,程序自行读取、分类、写回并保存,完成后回报结果。上下文可由 LLM 提供,也可由程序自行获取;只有失败或需要上层决策时,才需交回必要信息。业务工具与流程由宿主程序实现。
|
|
53
|
+
|
|
54
|
+
程序通过 YAML/JSON 维护决策定义,以 `classify()` 或 `evaluate()` 执行单个判断,也可用 `DecisionRequest` 在一次请求中组合多个判断。决策定义、运行配置与业务代码彼此分离,LLM 无需参与逐条参数构造和中间结果处理。
|
|
55
|
+
|
|
56
|
+
本包仍通过官方 `typesafe-sdk` 调用 Jev,不增加额外的模型推理层、Agent 或服务部署,也不收取额外服务费用。实际 Jev 调用遵循 TypeSafe 的计费与限制,请求次数取决于调用、缓存和重试配置;成本收益需结合实际任务衡量。
|
|
57
|
+
|
|
58
|
+
## 能力概览
|
|
59
|
+
|
|
60
|
+
| 能力 | 业务示例 | 调用入口 | 返回对象 |
|
|
61
|
+
| --- | --- | --- | --- |
|
|
62
|
+
| Choice 分类 | 判断工时类型、选择工单处理部门 | `SemanticClassifier.classify()` | `ClassificationResult` |
|
|
63
|
+
| Score 评分 | 评估风险、紧急程度或质量等级 | `ScoreEvaluator.evaluate()` | `ScoreResult` |
|
|
64
|
+
| Noul 条件判断 | 判断是否需要人工复核 | `NoulEvaluator.evaluate()` | `NoulResult` |
|
|
65
|
+
| 组合决策 | 一次请求同时判断类别、紧急程度和人工介入需求 | `JevRuntime.execute()` | `JevResponse` |
|
|
66
|
+
|
|
67
|
+
决策可通过 YAML/JSON 文件维护;运行时统一管理客户端复用、超时、重试、缓存和响应标准化。Excel、数据库、HTTP 等数据读写及后续业务动作由应用负责。
|
|
68
|
+
|
|
69
|
+
## 阅读导航
|
|
70
|
+
|
|
71
|
+
- [为什么使用 lvren-jev](#为什么使用-lvren-jev)
|
|
72
|
+
- [安装](#安装)
|
|
73
|
+
- [快速上手:填写 Excel 工时类型](#快速上手填写-excel-工时类型)
|
|
74
|
+
- [运行配置](#运行配置)
|
|
75
|
+
- [决策定义与调用](#决策定义与调用)
|
|
76
|
+
- [接口与返回结果](#接口与返回结果)
|
|
77
|
+
- [进阶:一次请求组合多个决策](#进阶一次请求组合多个决策)
|
|
78
|
+
- [源码开发与构建](#源码开发与构建)
|
|
79
|
+
- [许可证](#许可证)
|
|
80
|
+
|
|
81
|
+
## 安装
|
|
82
|
+
|
|
83
|
+
需要 Python 3.11 或更高版本,安装后使用 `import lvren_jev` 导入。
|
|
84
|
+
|
|
85
|
+
### 从仓库安装
|
|
86
|
+
|
|
87
|
+
下载仓库源码后,在仓库根目录安装:
|
|
88
|
+
|
|
89
|
+
```powershell
|
|
90
|
+
git clone https://github.com/BlueLvRen/lvren-jev.git lvren_jev
|
|
91
|
+
cd lvren_jev
|
|
92
|
+
python -m pip install .
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### 从 PyPI 安装
|
|
96
|
+
|
|
97
|
+
安装已发布的版本,无需下载仓库:
|
|
98
|
+
|
|
99
|
+
```powershell
|
|
100
|
+
python -m pip install lvren-jev
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
## 快速上手:填写 Excel 工时类型
|
|
104
|
+
|
|
105
|
+
以填写 Excel 工时类型为例:`工时.xlsx` 的 `sheet1` 第一行是表头,A 列是工时内容,B 列用于写入工时类型。从第二行开始逐行分类,跳过空内容,完成后保存并关闭文件。
|
|
106
|
+
|
|
107
|
+
| 行 | A 列:工时内容 | B 列:工时类型(示意结果) |
|
|
108
|
+
| --- | --- | --- |
|
|
109
|
+
| 2 | 处理生产 Redis 连接异常 | 运维 |
|
|
110
|
+
| 3 | 开发用户导出接口 | 开发 |
|
|
111
|
+
|
|
112
|
+
先在业务脚本的工作目录准备三个文件:
|
|
113
|
+
|
|
114
|
+
- `worklog.yaml`:告诉 Jev 有哪些工时类型、每个类型的含义,以及低置信度时如何处理。将[Choice 决策定义](#choice分类)保存为此文件,或复制仓库的 [`worklog.yaml`](examples/worklog_classifier/worklog.yaml) 示例。
|
|
115
|
+
- `typesafe.toml`:告诉运行时使用哪个服务、模型及请求参数。
|
|
116
|
+
- `typesafe.secrets.toml`:保存你的 API Key。
|
|
117
|
+
|
|
118
|
+
`typesafe.toml` 内容:
|
|
119
|
+
|
|
120
|
+
```toml
|
|
121
|
+
[typesafe]
|
|
122
|
+
api_key_file = "typesafe.secrets.toml"
|
|
123
|
+
base_url = "https://api.typesafe.ai"
|
|
124
|
+
model = "jev-1.13.0"
|
|
125
|
+
|
|
126
|
+
[runtime]
|
|
127
|
+
timeout = 30
|
|
128
|
+
retry = 1
|
|
129
|
+
cache = false
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
在同目录创建 `typesafe.secrets.toml`:
|
|
133
|
+
|
|
134
|
+
```toml
|
|
135
|
+
[typesafe]
|
|
136
|
+
api_key = "替换为你的 API Key"
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
下面展示完整的业务调用流程。`business_excel` 是你自己的 Excel 适配模块占位名,**不由本包提供**;示例省略其实现,只约定各方法的输入输出。接入已有 Excel 读写代码后即可运行。示例会覆盖非空 A 列所在行的 B 列,并在全部分类成功后保存原文件;任一行失败则不执行保存,但仍关闭文件。
|
|
140
|
+
|
|
141
|
+
```python
|
|
142
|
+
from lvren_jev import (
|
|
143
|
+
ClassificationResult,
|
|
144
|
+
JevRuntime,
|
|
145
|
+
SemanticClassifier,
|
|
146
|
+
load_decision_definition,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
# 业务方提供的 Excel 操作。
|
|
150
|
+
from business_excel import (
|
|
151
|
+
open_workbook, # 打开 Excel
|
|
152
|
+
iter_work_descriptions, # 逐行读取 A 列,跳过空内容
|
|
153
|
+
write_work_type, # 将工时类型写入 B 列
|
|
154
|
+
save_workbook, # 保存到原文件
|
|
155
|
+
close_workbook, # 关闭文件,不隐式保存
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def get_work_type_name(result: ClassificationResult) -> str:
|
|
160
|
+
# 输入:ClassificationResult 对象,完整结构见下方 classify() 调用处。
|
|
161
|
+
# 输出:写入 B 列的名称,例如 "运维";低置信度时为 "待确认"。
|
|
162
|
+
return result.label
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
# 加载类别及策略,返回 DecisionDefinition;该文件不是 Excel 数据文件。
|
|
166
|
+
definition = load_decision_definition("worklog.yaml")
|
|
167
|
+
|
|
168
|
+
# JevRuntime 读取连接配置并管理客户端;退出 with 时关闭客户端。
|
|
169
|
+
# 整个文件复用一个运行时和分类器,无需每行重新创建。
|
|
170
|
+
with JevRuntime.from_config("typesafe.toml") as runtime:
|
|
171
|
+
# SemanticClassifier 将类别定义和运行时组合成可调用的文本分类器。
|
|
172
|
+
classifier = SemanticClassifier.from_definition(definition, runtime=runtime)
|
|
173
|
+
|
|
174
|
+
# 输入:路径 str;输出:工作簿对象。
|
|
175
|
+
workbook = open_workbook("工时.xlsx")
|
|
176
|
+
try:
|
|
177
|
+
# 输入:工作簿对象、工作表名 str、起始行 int。
|
|
178
|
+
# 输出:迭代器,每项为 (int, str),如 (2, "处理生产 Redis 连接异常")。
|
|
179
|
+
for row_number, description in iter_work_descriptions(
|
|
180
|
+
workbook, sheet_name="sheet1", start_row=2
|
|
181
|
+
):
|
|
182
|
+
# 输入:单条工时文本 str;内部调用 Jev,返回 ClassificationResult 对象。
|
|
183
|
+
# 返回对象示意(实际内容由 Jev 返回,并经过分类策略处理):
|
|
184
|
+
# ClassificationResult(
|
|
185
|
+
# value="operations", # 稳定的类别 ID,用于程序判断和保存
|
|
186
|
+
# label="运维", # 展示名称,用于填写 Excel 的 B 列
|
|
187
|
+
# confidence=0.91, # 置信度,范围为 0 到 1
|
|
188
|
+
# probabilities={ # 各类别的概率;未提供时为 None
|
|
189
|
+
# "operations": 0.91,
|
|
190
|
+
# "development": 0.09,
|
|
191
|
+
# },
|
|
192
|
+
# fallback=False, # True 表示已按低置信度策略回退
|
|
193
|
+
# )
|
|
194
|
+
result = classifier.classify(description)
|
|
195
|
+
work_type = get_work_type_name(result)
|
|
196
|
+
|
|
197
|
+
# 输入:工作簿对象、"sheet1" (str)、2 (int)、"运维" (str);输出:None。
|
|
198
|
+
write_work_type(workbook, "sheet1", row_number, work_type)
|
|
199
|
+
|
|
200
|
+
# 输入:工作簿对象;输出:None。
|
|
201
|
+
save_workbook(workbook)
|
|
202
|
+
finally:
|
|
203
|
+
# 输入:工作簿对象;输出:None。
|
|
204
|
+
close_workbook(workbook)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
`classify()` 已经将 Jev 选中的类别转换为结果中的 `value` 和 `label`,并应用配置中的置信度阈值。业务方法 `get_work_type_name()` 只提取最终名称,不再发起请求或重新计算概率。置信度低于 `policy.threshold` 时,结果会改为配置的“待确认”;`probabilities` 保留各类别概率,便于业务方复核。这里的结果和概率均为示意,实际由 Jev 返回。
|
|
208
|
+
|
|
209
|
+
`typesafe.toml` 只保存地址、模型和运行参数;API Key 放在同目录的 `typesafe.secrets.toml` 中,并由 `api_key_file` 引用。应用启动时只需保证这两个文件路径正确。
|
|
210
|
+
|
|
211
|
+
## 运行配置
|
|
212
|
+
|
|
213
|
+
### 服务连接与请求参数
|
|
214
|
+
|
|
215
|
+
通用包最简单的配置形式如下:
|
|
216
|
+
|
|
217
|
+
```toml
|
|
218
|
+
[typesafe]
|
|
219
|
+
api_key_file = "typesafe.secrets.toml"
|
|
220
|
+
base_url = "https://api.typesafe.ai"
|
|
221
|
+
model = "jev-1.13.0"
|
|
222
|
+
|
|
223
|
+
[runtime]
|
|
224
|
+
timeout = 30
|
|
225
|
+
retry = 1
|
|
226
|
+
cache = false
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
配置含义:
|
|
230
|
+
|
|
231
|
+
- `[typesafe]`:API 地址和模型配置。
|
|
232
|
+
- `api_key_file`:相对于 `typesafe.toml` 的密钥文件路径。
|
|
233
|
+
- `base_url`:TypeSafe API 地址。
|
|
234
|
+
- `model`:使用的 Jev 模型。
|
|
235
|
+
- `[runtime].timeout`:单次请求超时时间,单位为秒。
|
|
236
|
+
- `[runtime].retry`:运行时对可重试连接错误的重试次数。
|
|
237
|
+
- `[runtime].cache`:是否启用相同请求的进程内缓存。
|
|
238
|
+
|
|
239
|
+
### API Key 文件
|
|
240
|
+
|
|
241
|
+
使用方的 `typesafe.toml` 只保存地址、模型和 Profile,不保存 API Key。首次使用时,在业务项目目录创建 `typesafe.secrets.toml`:
|
|
242
|
+
|
|
243
|
+
```powershell
|
|
244
|
+
notepad .\typesafe.secrets.toml
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
填入以下内容,并将占位文本替换为官方 API Key:
|
|
248
|
+
|
|
249
|
+
```toml
|
|
250
|
+
[typesafe]
|
|
251
|
+
api_key = "替换为你的 TypeSafe API Key"
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
如果配置了其他 Profile,为其创建对应的密钥文件,内容格式相同。请将 `typesafe.secrets.toml` 和 `typesafe.secrets.*.toml` 加入业务项目的 `.gitignore`;请勿提交真实密钥。
|
|
255
|
+
|
|
256
|
+
### 多来源配置(Profile)
|
|
257
|
+
|
|
258
|
+
主配置文件是 `typesafe.toml`,通过 Profile 区分不同来源:
|
|
259
|
+
|
|
260
|
+
```toml
|
|
261
|
+
[typesafe]
|
|
262
|
+
default_profile = "official"
|
|
263
|
+
|
|
264
|
+
[typesafe.profiles.official]
|
|
265
|
+
api_key_file = "typesafe.secrets.toml"
|
|
266
|
+
base_url = "https://api.typesafe.ai"
|
|
267
|
+
model = "jev-1.13.0"
|
|
268
|
+
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
每个 Profile 的 API Key 位于同级独立密钥文件。例如官方 Profile:
|
|
272
|
+
|
|
273
|
+
```toml
|
|
274
|
+
[typesafe]
|
|
275
|
+
api_key = "替换为你的 API Key"
|
|
276
|
+
```
|
|
277
|
+
|
|
278
|
+
可新增 `[typesafe.profiles.<名称>]` 配置其他来源,并通过 `default_profile` 选择默认来源。
|
|
279
|
+
|
|
280
|
+
## 决策定义与调用
|
|
281
|
+
|
|
282
|
+
决策定义文件描述“要判断什么”以及答案的含义。它属于业务层,由业务方维护;通用包只负责加载、构造原语请求和解析结果,不关心数据来自 Excel、数据库还是 HTTP。
|
|
283
|
+
|
|
284
|
+
目前支持三种 `kind`:
|
|
285
|
+
|
|
286
|
+
| `kind` | 对应原语 | 适合场景 | Python 包装类 | 结果类型 |
|
|
287
|
+
| --- | --- | --- | --- | --- |
|
|
288
|
+
| `classifier` | Choice | 从互斥类别中选择一个结果 | `SemanticClassifier` | `ClassificationResult` |
|
|
289
|
+
| `score` | Score | 对一个维度进行有序程度评分 | `ScoreEvaluator` | `ScoreResult` |
|
|
290
|
+
| `noul` | Noul | 判断一个条件成立的概率 | `NoulEvaluator` | `NoulResult` |
|
|
291
|
+
|
|
292
|
+
三种定义都有以下公共字段:
|
|
293
|
+
|
|
294
|
+
- `version`:决策文件格式版本,目前为 `1`。
|
|
295
|
+
- `kind`:决定使用哪种原语和包装类。
|
|
296
|
+
- `name`:问题名称,也是 Jev 返回答案中的问题标识。
|
|
297
|
+
- `input.type`:当前支持 `text`。
|
|
298
|
+
- `input.field`:输入文本在 `state` 中使用的字段名。
|
|
299
|
+
- `input.instructions`:告诉 Jev 如何进行判断的说明。
|
|
300
|
+
|
|
301
|
+
### Choice:分类
|
|
302
|
+
|
|
303
|
+
适合工时分类、工单路由、内容类型识别等“只能选一个类别”的场景。
|
|
304
|
+
|
|
305
|
+
示例文件:[`examples/worklog_classifier/worklog.yaml`](examples/worklog_classifier/worklog.yaml)
|
|
306
|
+
|
|
307
|
+
```yaml
|
|
308
|
+
version: 1
|
|
309
|
+
kind: classifier
|
|
310
|
+
name: worklog_classifier
|
|
311
|
+
|
|
312
|
+
input:
|
|
313
|
+
type: text
|
|
314
|
+
field: description
|
|
315
|
+
instructions: Classify the work description into the category that best matches it.
|
|
316
|
+
|
|
317
|
+
categories:
|
|
318
|
+
operations:
|
|
319
|
+
label: 运维
|
|
320
|
+
description: 生产维护、故障处理、监控和基础设施问题
|
|
321
|
+
development:
|
|
322
|
+
label: 开发
|
|
323
|
+
description: 功能开发、代码修改和缺陷修复
|
|
324
|
+
|
|
325
|
+
policy:
|
|
326
|
+
threshold: 0.65
|
|
327
|
+
fallback:
|
|
328
|
+
value: pending_review
|
|
329
|
+
label: 待确认
|
|
330
|
+
```
|
|
331
|
+
|
|
332
|
+
关键数据结构:
|
|
333
|
+
|
|
334
|
+
- `categories` 是“程序值 -> 类别定义”的对象。
|
|
335
|
+
- `categories.<value>.label` 是展示名称。
|
|
336
|
+
- `categories.<value>.description` 是类别含义,供 Jev 判断。
|
|
337
|
+
- `policy.threshold` 低于该置信度时使用 `fallback`。
|
|
338
|
+
|
|
339
|
+
调用方式:
|
|
340
|
+
|
|
341
|
+
```python
|
|
342
|
+
from lvren_jev import JevRuntime, SemanticClassifier, load_decision_definition
|
|
343
|
+
|
|
344
|
+
definition = load_decision_definition("worklog.yaml")
|
|
345
|
+
with JevRuntime.from_config("typesafe.toml") as runtime:
|
|
346
|
+
classifier = SemanticClassifier.from_definition(definition, runtime=runtime)
|
|
347
|
+
result = classifier.classify("处理生产 Redis 连接异常")
|
|
348
|
+
|
|
349
|
+
print(result.value, result.label, result.confidence)
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
输出 `ClassificationResult`,主要字段为 `value`、`label`、`confidence`、`probabilities` 和 `fallback`。
|
|
353
|
+
|
|
354
|
+
### Score:评分
|
|
355
|
+
|
|
356
|
+
适合风险程度、紧急程度、质量等级、影响范围等“从低到高有顺序”的场景。Score 的 `criteria` 必须按从低到高排列;返回的 `score` 可以是两个等级之间的小数。
|
|
357
|
+
|
|
358
|
+
示例文件:[`examples/score_evaluator/risk_score.yaml`](examples/score_evaluator/risk_score.yaml)
|
|
359
|
+
|
|
360
|
+
```yaml
|
|
361
|
+
version: 1
|
|
362
|
+
kind: score
|
|
363
|
+
name: risk_score
|
|
364
|
+
|
|
365
|
+
input:
|
|
366
|
+
type: text
|
|
367
|
+
field: description
|
|
368
|
+
instructions: Evaluate the operational risk described in the text.
|
|
369
|
+
|
|
370
|
+
criteria:
|
|
371
|
+
- 0: No meaningful operational risk
|
|
372
|
+
- 1: Minor issue with a local impact
|
|
373
|
+
- 2: Limited impact or workaround available
|
|
374
|
+
- 3: Material impact requiring prompt action
|
|
375
|
+
- 4: Major impact across an important workflow
|
|
376
|
+
- 5: Critical production risk requiring immediate response
|
|
377
|
+
```
|
|
378
|
+
|
|
379
|
+
关键数据结构:
|
|
380
|
+
|
|
381
|
+
- `criteria` 是有序数组,位置代表从低到高的评分等级。
|
|
382
|
+
- 每个等级可以是字符串,也可以是包含分值和描述的对象。
|
|
383
|
+
- Score 不使用 `categories` 和分类 fallback 策略。
|
|
384
|
+
|
|
385
|
+
调用方式:
|
|
386
|
+
|
|
387
|
+
```python
|
|
388
|
+
from lvren_jev import JevRuntime, ScoreEvaluator, load_decision_definition
|
|
389
|
+
|
|
390
|
+
definition = load_decision_definition("risk_score.yaml")
|
|
391
|
+
with JevRuntime.from_config("typesafe.toml") as runtime:
|
|
392
|
+
evaluator = ScoreEvaluator.from_definition(definition, runtime=runtime)
|
|
393
|
+
result = evaluator.evaluate("生产 Redis 连接持续失败")
|
|
394
|
+
|
|
395
|
+
print(result.score, result.confidence)
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
输出 `ScoreResult`,主要字段为 `score`、`confidence`、`legend` 和 `probabilities`。
|
|
399
|
+
|
|
400
|
+
### Noul:条件判断
|
|
401
|
+
|
|
402
|
+
适合“是否需要人工介入”“是否违反规则”“是否属于高风险”等二元条件判断。Noul 返回的是条件为真的概率,不是绝对布尔值。
|
|
403
|
+
|
|
404
|
+
示例文件:[`examples/noul_evaluator/needs_review.yaml`](examples/noul_evaluator/needs_review.yaml)
|
|
405
|
+
|
|
406
|
+
```yaml
|
|
407
|
+
version: 1
|
|
408
|
+
kind: noul
|
|
409
|
+
name: needs_review
|
|
410
|
+
|
|
411
|
+
input:
|
|
412
|
+
type: text
|
|
413
|
+
field: description
|
|
414
|
+
instructions: Determine whether the case requires human review.
|
|
415
|
+
|
|
416
|
+
criteria:
|
|
417
|
+
"true": The case is ambiguous, sensitive, high risk, or explicitly asks for a human.
|
|
418
|
+
"false": The case is clear and can be handled without human review.
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
关键数据结构:
|
|
422
|
+
|
|
423
|
+
- `criteria` 可省略,也可以用 `true`/`false` 两个键分别描述两种情况。
|
|
424
|
+
- YAML 中建议给 `true` 和 `false` 加引号,避免被 YAML 解析成布尔键。
|
|
425
|
+
- 返回的 `probability` 范围为 `0` 到 `1`,`NoulResult.is_true()` 默认使用 `0.5` 作为判断阈值。
|
|
426
|
+
|
|
427
|
+
调用方式:
|
|
428
|
+
|
|
429
|
+
```python
|
|
430
|
+
from lvren_jev import JevRuntime, NoulEvaluator, load_decision_definition
|
|
431
|
+
|
|
432
|
+
definition = load_decision_definition("needs_review.yaml")
|
|
433
|
+
with JevRuntime.from_config("typesafe.toml") as runtime:
|
|
434
|
+
evaluator = NoulEvaluator.from_definition(definition, runtime=runtime)
|
|
435
|
+
result = evaluator.evaluate("客户明确要求转人工处理")
|
|
436
|
+
|
|
437
|
+
print(result.probability)
|
|
438
|
+
print(result.is_true())
|
|
439
|
+
```
|
|
440
|
+
|
|
441
|
+
输出 `NoulResult`,主要字段为 `probability`。如果业务需要不同阈值,可以调用 `result.is_true(threshold=0.8)`。
|
|
442
|
+
|
|
443
|
+
## 接口与返回结果
|
|
444
|
+
|
|
445
|
+
### 调用层次
|
|
446
|
+
|
|
447
|
+
```text
|
|
448
|
+
业务输入: str 或 JSON 对象
|
|
449
|
+
-> 对应的原语包装类
|
|
450
|
+
-> DecisionRequest(state, questions)
|
|
451
|
+
-> JevRuntime.execute(request)
|
|
452
|
+
-> JevResponse(answers, usage, model)
|
|
453
|
+
-> 对应的结果类型
|
|
454
|
+
```
|
|
455
|
+
|
|
456
|
+
各层接口如下:
|
|
457
|
+
|
|
458
|
+
| 层 | 输入 | 输出 |
|
|
459
|
+
| --- | --- | --- |
|
|
460
|
+
| 业务层 | Excel、数据库或 HTTP 等来源的数据 | 传给包装类的文本或 JSON 对象,以及后续业务处理 |
|
|
461
|
+
| `SemanticClassifier` | `classify(text: str)` | `ClassificationResult` |
|
|
462
|
+
| `ScoreEvaluator` | `evaluate(state)` | `ScoreResult` |
|
|
463
|
+
| `NoulEvaluator` | `evaluate(state)` | `NoulResult` |
|
|
464
|
+
| `JevRuntime` | `DecisionRequest`,包含 `state` 和 `questions` | `JevResponse`,包含 `answers`、`usage`、`model` |
|
|
465
|
+
|
|
466
|
+
### 分类结果与字典转换
|
|
467
|
+
|
|
468
|
+
`classify()` 返回的是 `ClassificationResult` 对象,使用 `result.label` 等属性读取字段;调用 `result.to_dict()` 才会转换为字典,使用 `result.to_dict()["label"]` 等方式读取。转换示例:
|
|
469
|
+
|
|
470
|
+
```python
|
|
471
|
+
result = classifier.classify("处理生产 Redis 连接异常")
|
|
472
|
+
print(result.to_dict())
|
|
473
|
+
```
|
|
474
|
+
|
|
475
|
+
```python
|
|
476
|
+
{
|
|
477
|
+
"value": "operations",
|
|
478
|
+
"label": "运维",
|
|
479
|
+
"confidence": 0.91,
|
|
480
|
+
"probabilities": {
|
|
481
|
+
"operations": 0.91,
|
|
482
|
+
"development": 0.09,
|
|
483
|
+
},
|
|
484
|
+
"fallback": False,
|
|
485
|
+
}
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
其中:
|
|
489
|
+
|
|
490
|
+
- `value`:稳定的程序值,用于分支判断和保存。
|
|
491
|
+
- `label`:面向用户的展示名称。
|
|
492
|
+
- `confidence`:整体置信度,范围为 `0` 到 `1`。
|
|
493
|
+
- `probabilities`:各分类的概率。
|
|
494
|
+
- `fallback`:是否因置信度低于阈值进入待确认状态。
|
|
495
|
+
|
|
496
|
+
### 运行时与客户端
|
|
497
|
+
|
|
498
|
+
正式使用时不需要手动创建或替换 `TypeSafeClient`。下面这行会读取配置,并自动创建真实的官方 SDK 客户端:
|
|
499
|
+
|
|
500
|
+
```python
|
|
501
|
+
runtime = JevRuntime.from_config("typesafe.toml")
|
|
502
|
+
```
|
|
503
|
+
|
|
504
|
+
调用链是:
|
|
505
|
+
|
|
506
|
+
```text
|
|
507
|
+
typesafe.toml
|
|
508
|
+
-> JevRuntime.from_config()
|
|
509
|
+
-> typesafe_sdk.TypeSafeClient
|
|
510
|
+
-> TypeSafeClient.system_one()
|
|
511
|
+
-> JevResponse
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
## 进阶:一次请求组合多个决策
|
|
515
|
+
|
|
516
|
+
`JevRuntime` 使用通用的 `DecisionRequest`,可以在一次请求中组合三种原语:
|
|
517
|
+
|
|
518
|
+
```python
|
|
519
|
+
from lvren_jev import DecisionRequest, JevRuntime
|
|
520
|
+
|
|
521
|
+
runtime = JevRuntime.from_config("typesafe.toml")
|
|
522
|
+
response = runtime.execute(
|
|
523
|
+
DecisionRequest(
|
|
524
|
+
state={"message": "页面加载很慢,用户要求今天解决,并希望转人工。"},
|
|
525
|
+
questions={
|
|
526
|
+
"team": {
|
|
527
|
+
"type": "choice",
|
|
528
|
+
"instructions": "Which team should handle this request?",
|
|
529
|
+
"criteria": {
|
|
530
|
+
"billing": "Charges and payments",
|
|
531
|
+
"technical": "Software failures",
|
|
532
|
+
"other": "None of these",
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
"urgency": {
|
|
536
|
+
"type": "score",
|
|
537
|
+
"instructions": "How urgent is this request?",
|
|
538
|
+
"criteria": ["Can wait", "This week", "Today"],
|
|
539
|
+
},
|
|
540
|
+
"needs_human": {
|
|
541
|
+
"type": "noul",
|
|
542
|
+
"instructions": "Does the user want to talk to a human?",
|
|
543
|
+
"criteria": {
|
|
544
|
+
"true": "The user explicitly asks for a person.",
|
|
545
|
+
"false": "The user does not ask for a person.",
|
|
546
|
+
},
|
|
547
|
+
},
|
|
548
|
+
},
|
|
549
|
+
)
|
|
550
|
+
)
|
|
551
|
+
|
|
552
|
+
print(response.answers["team"])
|
|
553
|
+
print(response.answers["urgency"])
|
|
554
|
+
print(response.answers["needs_human"])
|
|
555
|
+
runtime.close()
|
|
556
|
+
```
|
|
557
|
+
|
|
558
|
+
三种原语的输入和返回字段分别是:
|
|
559
|
+
|
|
560
|
+
- `Choice`:`criteria` 是“程序值 -> 描述”的对象;返回 `choice`、`confidence` 和 `probabilities`。
|
|
561
|
+
- `Score`:`criteria` 是从低到高排列的描述数组;返回 `score`、`confidence`、`legend` 和 `probabilities`。`score` 可以是小数。
|
|
562
|
+
- `Noul`:`criteria` 可选,描述 `true` 和 `false`;返回 `noul`,范围为 `0` 到 `1`,表示回答为“是”的概率。Noul 没有单独的 `confidence` 字段。
|
|
563
|
+
|
|
564
|
+
每次只处理一种决策时,可使用前文的 `SemanticClassifier`、`ScoreEvaluator` 或 `NoulEvaluator`;需要组合多个问题时,直接构造 `DecisionRequest`。
|
|
565
|
+
|
|
566
|
+
## 源码开发与构建
|
|
567
|
+
|
|
568
|
+
以下步骤适用于修改源码、运行测试或构建安装包,在仓库根目录执行命令。
|
|
569
|
+
|
|
570
|
+
### 1. 获取项目
|
|
571
|
+
|
|
572
|
+
如果尚未下载项目:
|
|
573
|
+
|
|
574
|
+
```powershell
|
|
575
|
+
git clone https://github.com/BlueLvRen/lvren-jev.git lvren_jev
|
|
576
|
+
cd lvren_jev
|
|
577
|
+
```
|
|
578
|
+
|
|
579
|
+
如果项目已经存在,进入你本地的仓库根目录即可。
|
|
580
|
+
|
|
581
|
+
### 2. 创建虚拟环境并安装依赖
|
|
582
|
+
|
|
583
|
+
```powershell
|
|
584
|
+
python -m venv .venv
|
|
585
|
+
.\.venv\Scripts\Activate.ps1
|
|
586
|
+
python -m pip install --upgrade pip
|
|
587
|
+
python -m pip install -e .
|
|
588
|
+
```
|
|
589
|
+
|
|
590
|
+
验证 SDK 可以导入:
|
|
591
|
+
|
|
592
|
+
```powershell
|
|
593
|
+
python -c "from typesafe_sdk import TypeSafeClient; print('typesafe-sdk ready')"
|
|
594
|
+
```
|
|
595
|
+
|
|
596
|
+
如果 PowerShell 阻止激活脚本,可以只对当前窗口放行后重新激活:
|
|
597
|
+
|
|
598
|
+
```powershell
|
|
599
|
+
Set-ExecutionPolicy -Scope Process -ExecutionPolicy Bypass
|
|
600
|
+
.\.venv\Scripts\Activate.ps1
|
|
601
|
+
```
|
|
602
|
+
|
|
603
|
+
### 3. 检查安装
|
|
604
|
+
|
|
605
|
+
```powershell
|
|
606
|
+
python -c "import lvren_jev; print('lvren-jev', lvren_jev.__version__)"
|
|
607
|
+
```
|
|
608
|
+
|
|
609
|
+
正常情况下会显示已安装的 `lvren-jev` 版本。
|
|
610
|
+
|
|
611
|
+
### 4. 运行测试
|
|
612
|
+
|
|
613
|
+
```powershell
|
|
614
|
+
python -m unittest discover -s tests -v
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
### 5. 构建并安装 wheel
|
|
618
|
+
|
|
619
|
+
```powershell
|
|
620
|
+
python -m pip install build
|
|
621
|
+
python -m build --wheel
|
|
622
|
+
# dist 下会生成 lvren_jev-<版本号>-py3-none-any.whl,使用实际生成的文件路径安装。
|
|
623
|
+
python -m pip install .\dist\lvren_jev-0.2.0-py3-none-any.whl
|
|
624
|
+
```
|
|
625
|
+
|
|
626
|
+
## 许可证
|
|
627
|
+
|
|
628
|
+
本项目采用 [MIT License](LICENSE)。你可以自由使用、复制、修改和分发本项目,包括用于商业项目;使用时请保留版权和许可证声明。项目按现状提供,不附带任何担保。
|