hermes-connect-services 1.2.0 → 1.3.1
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/stall_detector.py +76 -16
- package/package.json +1 -1
package/lib/stall_detector.py
CHANGED
|
@@ -32,15 +32,19 @@ MIN_WEIGHTED_CHARS = 14.0
|
|
|
32
32
|
# 请求用户裁决 / 提供输入的措辞。命中即否决。
|
|
33
33
|
REQUEST_PATTERNS = [
|
|
34
34
|
# 中文:把决定权交回用户
|
|
35
|
-
r"请你?(
|
|
35
|
+
r"请你?(?:确认|批准|授权|选择|决定|提供|粘贴|发送|告知|告诉|现在|立刻|马上|配合|扫码|扫|在)",
|
|
36
36
|
r"你(?:可以|需要|来)(?:确认|决定|选择|提供|先)",
|
|
37
37
|
r"需要你(?:确认|决定|操作|提供|回复|选择)",
|
|
38
38
|
r"(?:是否|要不要|需不需要|要不要我|需要我)(?:继续|执行|我|帮|先|再)?",
|
|
39
|
-
r"等你(
|
|
40
|
-
r"告诉我(
|
|
39
|
+
r"等你(?:确认|回复|决定|操作|安排)",
|
|
40
|
+
r"告诉我(?:你|是否|要|结果|选择|一声|什么时候)",
|
|
41
|
+
r"(?:说|吱|喊)一声",
|
|
42
|
+
r"你(?:点个?头|说[「“\"']?开[」”\"']?)(?:我就|我立刻|我马上)?",
|
|
43
|
+
r"要我[^。!?\n]{0,24}(?:就|的话|吗)",
|
|
44
|
+
r"扫完(?:不用回复|告诉我|说一声)",
|
|
41
45
|
r"让我知道",
|
|
42
|
-
r"由你(
|
|
43
|
-
r"你(?:先|来)(
|
|
46
|
+
r"由你(?:决定|来|选|定)",
|
|
47
|
+
r"你(?:先|来)(?:确认|决定|操作|选|说)",
|
|
44
48
|
r"确认后(?:我)?再",
|
|
45
49
|
r"(?:你|您)看(?:看|一下)?(?:是否|要不要|行不行)",
|
|
46
50
|
# English: handing the decision back
|
|
@@ -55,11 +59,18 @@ REQUEST_PATTERNS = [
|
|
|
55
59
|
r"\bwaiting (?:for|on) your\b",
|
|
56
60
|
]
|
|
57
61
|
|
|
62
|
+
# 确认/复述长期规则或未来条件约定(「以后遇到…我会先…」「可以,主人。以后…」)。
|
|
63
|
+
# 这是回答用户的规则设定,不是当前回合宣布下一步动作后漏调工具。
|
|
64
|
+
STANDING_RULE_PATTERNS = [
|
|
65
|
+
r"(?:以后|今后|之后|下次|后续|往后|接下来每次|每当|每次)[^。!?\n]{0,40}(?:提到|遇到|收到|让我|你让我|要求|需要|开始|做|时|的时候)[^。!?\n]{0,30}我(?:会|将|都会|先|就)",
|
|
66
|
+
r"^(?:可以|好的?|明白|没问题|收到|记下了|知道了)[,,。!!\s]*(?:主人|小主|老板)?[,,。!!\s]*(?:以后|今后|之后|下次|后续|往后|每当|每次)",
|
|
67
|
+
]
|
|
68
|
+
|
|
58
69
|
# 「已经做完」的收尾。命中且无未来标记时否决。
|
|
59
70
|
DONE_PATTERNS = [
|
|
60
71
|
r"(?:已|已经)(?:经)?(?:完成|修好|改好|搞定|处理完|就绪|生效|上线|发布|通过)",
|
|
61
|
-
r"(?:全部|都)(
|
|
62
|
-
r"(
|
|
72
|
+
r"(?:全部|都)(?:完成|通过|搞定|就绪|闭合)",
|
|
73
|
+
r"(?:搞定|收工|完事|可以收尾)(?:了)?[。.!!]?$",
|
|
63
74
|
r"(?:到|就)这里(?:为止|结束)?",
|
|
64
75
|
r"(?:没有|无需)(?:其他|更多|别的)(?:改动|问题|需要)",
|
|
65
76
|
r"\b(?:all )?done\b",
|
|
@@ -77,6 +88,8 @@ FUTURE_STRONG = [
|
|
|
77
88
|
r"让(?:我们|我)先",
|
|
78
89
|
r"(?:接下来|下面|现在)我(?:来|将|会|要)",
|
|
79
90
|
r"开始(?:执行|处理|修改|实现|写|改|跑|检查)",
|
|
91
|
+
r"(?:^|[。!?\n]\s*)(?:开始[。,,\s]*)?先(?:把|将|从|去|给|对|用|跑|看|查|改|写|读|登记|核对|整理|创建|执行|检查)[^。!?\n]{1,60}再",
|
|
92
|
+
r"(?:^|[。!?\n]\s*)现在(?:重新|开始|把|将|先|去|来|给|对|跑|开|改|写|整理|执行|检查)",
|
|
80
93
|
r"\blet me\b",
|
|
81
94
|
r"\bi'?ll\b",
|
|
82
95
|
r"\bi will\b",
|
|
@@ -98,11 +111,11 @@ FUTURE_WEAK = [
|
|
|
98
111
|
|
|
99
112
|
# 动作动词:把「未来时」落到「要做一件具体的事」上。
|
|
100
113
|
ACTION_VERB = [
|
|
101
|
-
r"(
|
|
102
|
-
r"(
|
|
103
|
-
r"(
|
|
114
|
+
r"(?:检查|查看|看看|查一下|读取|打开|搜索|定位|核对|登记|整理)",
|
|
115
|
+
r"(?:修改|改成|改掉|编辑|写入|创建|新建|删除|移除|重写|写进)",
|
|
116
|
+
r"(?:执行|运行|跑一下|跑起|启动|重启|安装|部署|发布|提交|开窗口|重新开)",
|
|
104
117
|
r"(?:修复|实现|添加|增加|补充|更新|替换|迁移|重构)",
|
|
105
|
-
r"(
|
|
118
|
+
r"(?:验证|测试|校验|确认一下|跑通|编译|上机)",
|
|
106
119
|
r"\b(?:check|read|open|inspect|look at|search|grep)\b",
|
|
107
120
|
r"\b(?:edit|write|create|add|remove|delete|rewrite|patch|update)\b",
|
|
108
121
|
r"\b(?:run|execute|install|deploy|build|start|restart|publish)\b",
|
|
@@ -114,6 +127,11 @@ COLON_TAIL = re.compile(r"[::]\s*\Z")
|
|
|
114
127
|
ELLIPSIS_TAIL = re.compile(r"(?:\.{3,}|…)\s*\Z")
|
|
115
128
|
QUESTION_TAIL = re.compile(r"[??]\s*\Z")
|
|
116
129
|
|
|
130
|
+
# action-colon 判定用:末行收尾的冒号、分句切分、列表项前缀。
|
|
131
|
+
_COLON_AT_END = re.compile(r"[::]\s*\Z")
|
|
132
|
+
_CLAUSE_SPLIT = re.compile(r"[,,。;;!!??]")
|
|
133
|
+
_LIST_ITEM = re.compile(r"^[-*•\d|]")
|
|
134
|
+
|
|
117
135
|
# 阈值。只有弱未来标记时必须靠额外信号才能过线。
|
|
118
136
|
THRESHOLD_STRONG = 3.0
|
|
119
137
|
THRESHOLD_WEAK_ONLY = 4.5
|
|
@@ -140,6 +158,7 @@ def _any_match(group: str, text: str) -> bool:
|
|
|
140
158
|
|
|
141
159
|
_PATTERN_GROUPS = {
|
|
142
160
|
"request": REQUEST_PATTERNS,
|
|
161
|
+
"standing_rule": STANDING_RULE_PATTERNS,
|
|
143
162
|
"done": DONE_PATTERNS,
|
|
144
163
|
"future_strong": FUTURE_STRONG,
|
|
145
164
|
"future_weak": FUTURE_WEAK,
|
|
@@ -207,10 +226,14 @@ def detect_stall(raw_text: Any, tool_call_count: int = 0) -> Dict[str, Any]:
|
|
|
207
226
|
if _any_match("request", tail):
|
|
208
227
|
return _result("asked-user")
|
|
209
228
|
|
|
229
|
+
# ── 否决 ③:确认/复述长期规则约定(「以后遇到…我会先…」)──────────────
|
|
230
|
+
if _any_match("standing_rule", tail):
|
|
231
|
+
return _result("standing-rule")
|
|
232
|
+
|
|
210
233
|
has_strong = _any_match("future_strong", tail)
|
|
211
234
|
has_weak = _any_match("future_weak", tail)
|
|
212
235
|
|
|
213
|
-
# ── 否决
|
|
236
|
+
# ── 否决 ④:明确宣告完成且没有未来时 ──────────────────────────────────
|
|
214
237
|
if not has_strong and not has_weak and _any_match("done", tail):
|
|
215
238
|
return _result("already-done")
|
|
216
239
|
|
|
@@ -224,20 +247,42 @@ def detect_stall(raw_text: Any, tool_call_count: int = 0) -> Dict[str, Any]:
|
|
|
224
247
|
if has_weak:
|
|
225
248
|
score += 1.5
|
|
226
249
|
signals.append("future-weak")
|
|
227
|
-
|
|
250
|
+
has_verb = _any_match("action_verb", tail)
|
|
251
|
+
if has_verb:
|
|
228
252
|
score += 1.0
|
|
229
253
|
signals.append("action-verb")
|
|
230
|
-
|
|
254
|
+
has_colon = bool(COLON_TAIL.search(raw))
|
|
255
|
+
if has_colon:
|
|
231
256
|
score += 1.5
|
|
232
257
|
signals.append("colon-tail")
|
|
233
258
|
if ELLIPSIS_TAIL.search(raw):
|
|
234
259
|
score += 0.5
|
|
235
260
|
signals.append("ellipsis-tail")
|
|
236
261
|
|
|
237
|
-
|
|
262
|
+
# 末行非列表项以「动作动词 + 冒号」收尾(例如「……整份重写干净:」):
|
|
263
|
+
# 即使省略了显式「我先/接下来」代词,也是典型的即将发起工具调用。
|
|
264
|
+
#
|
|
265
|
+
# 动词必须落在冒号前的**最后一个分句**里,否则「已定位问题并修好,根因有
|
|
266
|
+
# 三层:」这类说明性收尾会被误判 —— 它的动词讲的是已完成的事,冒号引出的
|
|
267
|
+
# 是名词性解释。宁可漏掉一次续跑,也不要把说明性收尾当成漏执行。
|
|
268
|
+
lines = [line.strip() for line in raw.split("\n") if line.strip()]
|
|
269
|
+
last_line = lines[-1] if lines else ""
|
|
270
|
+
tail_clause = _CLAUSE_SPLIT.split(_COLON_AT_END.sub("", last_line))[-1] if last_line else ""
|
|
271
|
+
is_action_colon = (
|
|
272
|
+
has_colon
|
|
273
|
+
and has_verb
|
|
274
|
+
and _any_match("action_verb", tail_clause)
|
|
275
|
+
and not _LIST_ITEM.match(last_line)
|
|
276
|
+
and len(last_line) <= 160
|
|
277
|
+
)
|
|
278
|
+
if not has_strong and not has_weak and is_action_colon:
|
|
279
|
+
score += 1.0
|
|
280
|
+
signals.append("action-colon")
|
|
281
|
+
|
|
282
|
+
if not has_strong and not has_weak and not is_action_colon:
|
|
238
283
|
return _result("no-future-cue", score, signals)
|
|
239
284
|
|
|
240
|
-
threshold = THRESHOLD_STRONG if has_strong else THRESHOLD_WEAK_ONLY
|
|
285
|
+
threshold = THRESHOLD_STRONG if (has_strong or is_action_colon) else THRESHOLD_WEAK_ONLY
|
|
241
286
|
if score < threshold:
|
|
242
287
|
return _result("weak-only", score, signals)
|
|
243
288
|
|
|
@@ -254,3 +299,18 @@ CONTINUE_PROMPT = "\n".join([
|
|
|
254
299
|
"如果它其实已经完成,或不需要执行,用一句话说明结论即可,不要重述计划。",
|
|
255
300
|
"如果需要用户先决定,提出一个具体的问题,不要继续描述待办。",
|
|
256
301
|
])
|
|
302
|
+
|
|
303
|
+
# 空回合的续跑提示:模型只留下思考过程(thinking),既没有正文也没有工具调用。
|
|
304
|
+
#
|
|
305
|
+
# 实测故障模式(2026-10-30,pi 会话 01a1880e,provider=workbuddy / hy4-preview):
|
|
306
|
+
# usage 显示 output 有 286 / 667 个 token、reasoning 145 / 457,耗时 7.6s、15.5s
|
|
307
|
+
# (不是超时被切),但 content 里只有 thinking 块,正文为空。用户看到的就是
|
|
308
|
+
# 「思考着,突然不工作了」。
|
|
309
|
+
#
|
|
310
|
+
# 与 CONTINUE_PROMPT 的区别:那一条假定还有可见文本可以判断意图;这里什么都
|
|
311
|
+
# 没有,所以不能说「执行你宣布的操作」—— 只能要求它把结论说出来。
|
|
312
|
+
EMPTY_TURN_PROMPT = "\n".join([
|
|
313
|
+
"你上一条回复只留下了思考过程,没有给出任何可见内容,也没有调用任何工具。",
|
|
314
|
+
"请把你的结论直接说出来:已经做了什么、接下来要做什么,或者一句话说明这一轮的结果。",
|
|
315
|
+
"不要重复思考过程,也不要只描述计划而不动手。",
|
|
316
|
+
])
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "hermes-connect-services",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.1",
|
|
4
4
|
"description": "Bring zcode-ai-connect's four loopback endpoints to hermes, and add the missing turn-end gate that re-runs a turn when the model announces a next step but calls no tool.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|