@master0071/dsh-systemone 0.4.1 → 0.4.2
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.
- package/lib/auto.js +22 -10
- package/lib/client.js +27 -77
- package/lib/index.js +7 -7
- package/package.json +1 -1
package/lib/auto.js
CHANGED
|
@@ -42,6 +42,16 @@ const MAX_AUTO_CACHE = 200
|
|
|
42
42
|
/** 当前请求注入 state 的最大字符数(历史逐条限 2000,这里限制本轮原文)。 */
|
|
43
43
|
const MAX_CURRENT_CHARS = 8000
|
|
44
44
|
|
|
45
|
+
/** 自动决策数字配置的兜底默认值(与 lib/index.js Config schema 的 default 保持同一份来源)。 */
|
|
46
|
+
export const AUTO_DEFAULTS = {
|
|
47
|
+
autoMaxMessages: 6,
|
|
48
|
+
autoTimeoutMs: 8000,
|
|
49
|
+
autoCacheTtlMs: 60000,
|
|
50
|
+
autoRouteMinConfidence: 0.35,
|
|
51
|
+
autoMinConfidence: 0.6,
|
|
52
|
+
autoMinChars: 4,
|
|
53
|
+
}
|
|
54
|
+
|
|
45
55
|
/**
|
|
46
56
|
* 安装自动决策。返回卸载函数。
|
|
47
57
|
*
|
|
@@ -99,7 +109,7 @@ export function installAutoDecide(ctx, deps) {
|
|
|
99
109
|
const newText = extractUserText([event?.message])
|
|
100
110
|
if (!newText) return
|
|
101
111
|
if (newText.trimStart().startsWith('/')) return
|
|
102
|
-
if (isTrivialInput(newText, readNumber(config, 'autoMinChars',
|
|
112
|
+
if (isTrivialInput(newText, readNumber(config, 'autoMinChars', AUTO_DEFAULTS.autoMinChars))) return
|
|
103
113
|
// 返回 promise 便于测试等待;宿主事件总线忽略返回值
|
|
104
114
|
return computeAndStore(agent, newText)
|
|
105
115
|
}))
|
|
@@ -141,13 +151,14 @@ export function installAutoDecide(ctx, deps) {
|
|
|
141
151
|
const newText = extractUserText(claimed)
|
|
142
152
|
if (!newText) return decision // 工具结果步骤 / 无新用户消息
|
|
143
153
|
if (newText.trimStart().startsWith('/')) return decision // 斜杠命令
|
|
144
|
-
if (isTrivialInput(newText, readNumber(config, 'autoMinChars',
|
|
154
|
+
if (isTrivialInput(newText, readNumber(config, 'autoMinChars', AUTO_DEFAULTS.autoMinChars))) return decision // 寒暄/致谢等无业务含义的输入
|
|
145
155
|
|
|
146
156
|
let injected = null
|
|
147
157
|
try {
|
|
148
158
|
// 双保险:既给上游传 abort signal,也用 race 兜底,
|
|
149
159
|
// 即使提供商忽略 signal 也不会让这一步的推理被无限阻塞。
|
|
150
|
-
const
|
|
160
|
+
const timeoutMs = readNumber(config, 'autoTimeoutMs', AUTO_DEFAULTS.autoTimeoutMs)
|
|
161
|
+
const timeout = combineSignal(payload?.signal, timeoutMs)
|
|
151
162
|
try {
|
|
152
163
|
injected = await raceTimeout(
|
|
153
164
|
computeDecision({
|
|
@@ -162,7 +173,7 @@ export function installAutoDecide(ctx, deps) {
|
|
|
162
173
|
log,
|
|
163
174
|
warnedScenarios,
|
|
164
175
|
}),
|
|
165
|
-
|
|
176
|
+
timeoutMs,
|
|
166
177
|
)
|
|
167
178
|
} finally {
|
|
168
179
|
timeout.dispose()
|
|
@@ -197,7 +208,8 @@ export function installAutoDecide(ctx, deps) {
|
|
|
197
208
|
/** 入箱预计算:异步算好并存进 perAgent,供 context 通道渲染。 */
|
|
198
209
|
async function computeAndStore(agent, newText) {
|
|
199
210
|
try {
|
|
200
|
-
const
|
|
211
|
+
const timeoutMs = readNumber(config, 'autoTimeoutMs', AUTO_DEFAULTS.autoTimeoutMs)
|
|
212
|
+
const timeout = combineSignal(undefined, timeoutMs)
|
|
201
213
|
try {
|
|
202
214
|
const injected = await raceTimeout(
|
|
203
215
|
computeDecision({
|
|
@@ -212,7 +224,7 @@ export function installAutoDecide(ctx, deps) {
|
|
|
212
224
|
log,
|
|
213
225
|
warnedScenarios,
|
|
214
226
|
}),
|
|
215
|
-
|
|
227
|
+
timeoutMs,
|
|
216
228
|
)
|
|
217
229
|
if (injected) perAgent.set(agent?.id ?? 'unknown', injected)
|
|
218
230
|
} finally {
|
|
@@ -244,7 +256,7 @@ export function installAutoDecide(ctx, deps) {
|
|
|
244
256
|
* 计算决策(带并发去重)。相同 state 只发一次上游请求。
|
|
245
257
|
*/
|
|
246
258
|
async function computeDecision({ agent, newText, provider, config, scenarios, cache, inFlight, signal, log, warnedScenarios }) {
|
|
247
|
-
const state = captureState(agent, newText, readNumber(config, 'autoMaxMessages',
|
|
259
|
+
const state = captureState(agent, newText, readNumber(config, 'autoMaxMessages', AUTO_DEFAULTS.autoMaxMessages))
|
|
248
260
|
const key = hashString(state)
|
|
249
261
|
const existing = inFlight.get(key)
|
|
250
262
|
if (existing) return existing
|
|
@@ -268,7 +280,7 @@ async function computeDecision({ agent, newText, provider, config, scenarios, ca
|
|
|
268
280
|
}
|
|
269
281
|
|
|
270
282
|
async function decide({ agent, newText, state, key, provider, config, scenarios, cache, signal, log, warnedScenarios }) {
|
|
271
|
-
const ttl = readNumber(config, 'autoCacheTtlMs',
|
|
283
|
+
const ttl = readNumber(config, 'autoCacheTtlMs', AUTO_DEFAULTS.autoCacheTtlMs) || 0
|
|
272
284
|
if (ttl > 0) {
|
|
273
285
|
const hit = cache.get(key)
|
|
274
286
|
if (hit && Date.now() - hit.at < ttl) return hit.result
|
|
@@ -279,7 +291,7 @@ async function decide({ agent, newText, state, key, provider, config, scenarios,
|
|
|
279
291
|
if (!routed) return null
|
|
280
292
|
|
|
281
293
|
// 路由置信度不足:宁可不注入,也不要塞一个瞎猜的结论
|
|
282
|
-
const floor = readNumber(config, 'autoRouteMinConfidence',
|
|
294
|
+
const floor = readNumber(config, 'autoRouteMinConfidence', AUTO_DEFAULTS.autoRouteMinConfidence)
|
|
283
295
|
if (Number.isFinite(floor) && floor > 0
|
|
284
296
|
&& Number.isFinite(routed.confidence) && routed.confidence < floor) {
|
|
285
297
|
log?.('info', `路由置信度 ${(routed.confidence * 100).toFixed(1)}% 低于阈值 ${(floor * 100).toFixed(0)}%,跳过注入`)
|
|
@@ -294,7 +306,7 @@ async function decide({ agent, newText, state, key, provider, config, scenarios,
|
|
|
294
306
|
scenario: routed.scenario.id,
|
|
295
307
|
state,
|
|
296
308
|
exec: { signal },
|
|
297
|
-
minConfidence: readNumber(config, 'autoMinConfidence',
|
|
309
|
+
minConfidence: readNumber(config, 'autoMinConfidence', AUTO_DEFAULTS.autoMinConfidence),
|
|
298
310
|
})
|
|
299
311
|
if (!result?.ok) {
|
|
300
312
|
log?.('warn', `场景 ${routed.scenario.id} 执行失败,已放行:${result?.error || '未知错误'}`)
|
package/lib/client.js
CHANGED
|
@@ -352,7 +352,7 @@ window.__ModuleLoader__.load({
|
|
|
352
352
|
: null
|
|
353
353
|
if (keys === null) issues.push(`问题 ${qid}(choice)需要 criteria 对象`)
|
|
354
354
|
else if (keys.length < 2) issues.push(`问题 ${qid}(choice)至少 2 个选项`)
|
|
355
|
-
else if (keys.length > 26) issues.push(`问题 ${qid}(choice
|
|
355
|
+
else if (keys.length > 26) issues.push(`问题 ${qid}(choice)选项不能超过 26 个(当前 ${keys.length})`)
|
|
356
356
|
}
|
|
357
357
|
if (q.type === 'score'
|
|
358
358
|
&& (!Array.isArray(q.criteria) || q.criteria.length < 2)) {
|
|
@@ -495,7 +495,7 @@ window.__ModuleLoader__.load({
|
|
|
495
495
|
control = h('input', {
|
|
496
496
|
id: controlId,
|
|
497
497
|
style: inputStyle,
|
|
498
|
-
type: field.kind === 'password' ? 'password' :
|
|
498
|
+
type: field.kind === 'password' ? 'password' : 'text',
|
|
499
499
|
inputMode: field.kind === 'number' ? 'decimal' : undefined,
|
|
500
500
|
value,
|
|
501
501
|
disabled,
|
|
@@ -679,6 +679,18 @@ window.__ModuleLoader__.load({
|
|
|
679
679
|
],
|
|
680
680
|
})
|
|
681
681
|
|
|
682
|
+
// 三张卡片共用同一套 footer:save(可选 revert / 额外状态)+ 状态行
|
|
683
|
+
const renderFooter = ({ showRevert, extra }) => hs('div', {
|
|
684
|
+
key: 'footer',
|
|
685
|
+
style: footerStyle,
|
|
686
|
+
children: [
|
|
687
|
+
h('button', { key: 'save', type: 'button', style: { ...primaryButton, opacity: canSave ? 1 : 0.5 }, disabled: !canSave, onClick: onSave, children: busy ? t('saving') : t('save') }),
|
|
688
|
+
...(showRevert ? [h('button', { key: 'revert', type: 'button', style: ghostButton, disabled: !dirty || busy, onClick: () => { setDraft(null); setNotice(null) }, children: t('revert') })] : []),
|
|
689
|
+
...(extra ? [extra] : []),
|
|
690
|
+
statusLine,
|
|
691
|
+
],
|
|
692
|
+
})
|
|
693
|
+
|
|
682
694
|
return hs('ul', {
|
|
683
695
|
style: listStyle,
|
|
684
696
|
children: [
|
|
@@ -692,32 +704,7 @@ window.__ModuleLoader__.load({
|
|
|
692
704
|
children: hs(Fragment, {
|
|
693
705
|
children: [
|
|
694
706
|
...renderFields(SERVICE_FIELDS, numberExtras),
|
|
695
|
-
|
|
696
|
-
key: 'footer',
|
|
697
|
-
style: footerStyle,
|
|
698
|
-
children: [
|
|
699
|
-
h('button', {
|
|
700
|
-
key: 'save',
|
|
701
|
-
type: 'button',
|
|
702
|
-
style: { ...primaryButton, opacity: canSave ? 1 : 0.5 },
|
|
703
|
-
disabled: !canSave,
|
|
704
|
-
onClick: onSave,
|
|
705
|
-
children: busy ? t('saving') : t('save'),
|
|
706
|
-
}),
|
|
707
|
-
h('button', {
|
|
708
|
-
key: 'revert',
|
|
709
|
-
type: 'button',
|
|
710
|
-
style: ghostButton,
|
|
711
|
-
disabled: !dirty || busy,
|
|
712
|
-
onClick: () => {
|
|
713
|
-
setDraft(null)
|
|
714
|
-
setNotice(null)
|
|
715
|
-
},
|
|
716
|
-
children: t('revert'),
|
|
717
|
-
}),
|
|
718
|
-
statusLine,
|
|
719
|
-
],
|
|
720
|
-
}),
|
|
707
|
+
renderFooter({ showRevert: true }),
|
|
721
708
|
],
|
|
722
709
|
}),
|
|
723
710
|
}),
|
|
@@ -779,40 +766,17 @@ window.__ModuleLoader__.load({
|
|
|
779
766
|
}),
|
|
780
767
|
},
|
|
781
768
|
}),
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
}),
|
|
794
|
-
h('button', {
|
|
795
|
-
key: 'revert',
|
|
796
|
-
type: 'button',
|
|
797
|
-
style: ghostButton,
|
|
798
|
-
disabled: !dirty || busy,
|
|
799
|
-
onClick: () => {
|
|
800
|
-
setDraft(null)
|
|
801
|
-
setNotice(null)
|
|
802
|
-
},
|
|
803
|
-
children: t('revert'),
|
|
804
|
-
}),
|
|
805
|
-
customCheck.ok
|
|
806
|
-
? h('span', {
|
|
807
|
-
key: 'ok',
|
|
808
|
-
style: statusStyle,
|
|
809
|
-
children: customCheck.count === 0
|
|
810
|
-
? t('jsonEmpty')
|
|
811
|
-
: `${t('jsonOk', { count: customCheck.count })} ${t('customIdsUsed')}`,
|
|
812
|
-
})
|
|
813
|
-
: null,
|
|
814
|
-
statusLine,
|
|
815
|
-
],
|
|
769
|
+
renderFooter({
|
|
770
|
+
showRevert: true,
|
|
771
|
+
extra: customCheck.ok
|
|
772
|
+
? h('span', {
|
|
773
|
+
key: 'ok',
|
|
774
|
+
style: statusStyle,
|
|
775
|
+
children: customCheck.count === 0
|
|
776
|
+
? t('jsonEmpty')
|
|
777
|
+
: `${t('jsonOk', { count: customCheck.count })} ${t('customIdsUsed')}`,
|
|
778
|
+
})
|
|
779
|
+
: null,
|
|
816
780
|
}),
|
|
817
781
|
],
|
|
818
782
|
}),
|
|
@@ -826,21 +790,7 @@ window.__ModuleLoader__.load({
|
|
|
826
790
|
children: hs(Fragment, {
|
|
827
791
|
children: [
|
|
828
792
|
...renderFields(AUTO_FIELDS, numberExtras),
|
|
829
|
-
|
|
830
|
-
key: 'footer',
|
|
831
|
-
style: footerStyle,
|
|
832
|
-
children: [
|
|
833
|
-
h('button', {
|
|
834
|
-
key: 'save',
|
|
835
|
-
type: 'button',
|
|
836
|
-
style: { ...primaryButton, opacity: canSave ? 1 : 0.5 },
|
|
837
|
-
disabled: !canSave,
|
|
838
|
-
onClick: onSave,
|
|
839
|
-
children: busy ? t('saving') : t('save'),
|
|
840
|
-
}),
|
|
841
|
-
statusLine,
|
|
842
|
-
],
|
|
843
|
-
}),
|
|
793
|
+
renderFooter({ showRevert: false }),
|
|
844
794
|
],
|
|
845
795
|
}),
|
|
846
796
|
}),
|
package/lib/index.js
CHANGED
|
@@ -16,7 +16,7 @@ import z from '@deepseek-ai/schemastery'
|
|
|
16
16
|
import { createProvider } from './provider.js'
|
|
17
17
|
import { createTools } from './tools.js'
|
|
18
18
|
import { resolveScenarios } from './scenarios.js'
|
|
19
|
-
import { installAutoDecide } from './auto.js'
|
|
19
|
+
import { installAutoDecide, AUTO_DEFAULTS } from './auto.js'
|
|
20
20
|
import { readBoolean, readString, unwrap } from './volatile.js'
|
|
21
21
|
|
|
22
22
|
/** 合法的提供商类型;其他值会按 unisound 处理并告警一次。 */
|
|
@@ -82,17 +82,17 @@ const Config = z.object({
|
|
|
82
82
|
/** 注入通道:message=本步生效(pre-step 注入);context=官方动态上下文(晚一步生效) */
|
|
83
83
|
autoInject: z.string().default('message').volatile(),
|
|
84
84
|
/** 自动决策超时(毫秒),超时即放行,绝不阻塞会话 */
|
|
85
|
-
autoTimeoutMs: z.number().min(500).default(
|
|
85
|
+
autoTimeoutMs: z.number().min(500).default(AUTO_DEFAULTS.autoTimeoutMs).volatile(),
|
|
86
86
|
/** 捕获的历史消息条数(不含本轮新消息) */
|
|
87
|
-
autoMaxMessages: z.number().min(1).max(50).default(
|
|
87
|
+
autoMaxMessages: z.number().min(1).max(50).default(AUTO_DEFAULTS.autoMaxMessages).volatile(),
|
|
88
88
|
/** 相同内容的结果缓存时长(毫秒),0 表示不缓存 */
|
|
89
|
-
autoCacheTtlMs: z.number().min(0).default(
|
|
89
|
+
autoCacheTtlMs: z.number().min(0).default(AUTO_DEFAULTS.autoCacheTtlMs).volatile(),
|
|
90
90
|
/** 自动决策使用的置信度阈值 */
|
|
91
|
-
autoMinConfidence: z.number().min(0).max(1).default(
|
|
91
|
+
autoMinConfidence: z.number().min(0).max(1).default(AUTO_DEFAULTS.autoMinConfidence).volatile(),
|
|
92
92
|
/** 自动路由的置信度门槛:低于该值宁可不注入,避免塞入瞎猜的结论 */
|
|
93
|
-
autoRouteMinConfidence: z.number().min(0).max(1).default(
|
|
93
|
+
autoRouteMinConfidence: z.number().min(0).max(1).default(AUTO_DEFAULTS.autoRouteMinConfidence).volatile(),
|
|
94
94
|
/** 触发自动决策的最小输入长度(字符);低于该长度且整体是寒暄才跳过 */
|
|
95
|
-
autoMinChars: z.number().min(0).default(
|
|
95
|
+
autoMinChars: z.number().min(0).default(AUTO_DEFAULTS.autoMinChars).volatile(),
|
|
96
96
|
})
|
|
97
97
|
|
|
98
98
|
export default class SystemOneService extends Service {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@master0071/dsh-systemone",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "DeepSeek Harness 插件:将 SystemOne 决策模型(默认云知声 u2-decision,可切换提供商)接入软件开发、客服、内容审核、路由、销售线索、风控、招聘、数据治理、教育、需求变更等 10 大业务场景,为 Agent 返回带概率分布的决策与处置建议。",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|