派活触发句收工闸:说了就必须把执行会话拉起来(实测栽过,补机器抓手)
一、起因(用户令逐字) 「重点问题是 会话接收到 调用执行会话完成目标 没有去调用对应技能去执行」。 实测会话 b232218f(vibe-product):用户 01:32:00 / 01:33:46 两次说触发句, 到 01:36:27 才建出排期 —— 中间那一轮 AI 只在回复里解释口径,没拉起执行会话。 根因:机制里**只有 "注入提醒"**,⛔ 没有任何机器抓手检查"到底做没做"。 二、改法(发现 → 拦住,两步) 1. scripts/hooks/skill-load-guard.py:命中**派活族**触发句时写一条待办 <工作区>/.workbuddy/.dispatch-pending(会话 id / 时间戳 / 命中词)。 ⚠️ 写在**冷却判定之前** —— 实测 01:33:46 那条最明确的触发句正被 300 s 冷却吞掉, 写在冷却之后就等于"最该拦的那一条反而没有标记"。 ⚠️ 比对前**去掉空白**:用户写的是「应该使用 执行会话的技能去完成工作」(中间有空格)。 ⛔ 不把裸词「执行会话」算进派活族(太宽,讨论机制时也出现)⇒ 误报面保持最小。 2. scripts/hooks/stop-dialog-guard.py:新增收工前闸 —— 同会话 + 超 90 s 宽限 + 宿主库该区此后**没有**新建排期 ⇒ 返回 {"continue": false} 拦一次,逼它先建排期。 ⚠️ 必须放在**自作用域判定之前**(那条作用域只认本工作区,而栽的会话在 vibe-product)。 ⛔ 三条安全底线:① 全程 fail-open(读不到库/解析失败/任何异常 ⇒ 一律放行); ② 只拦一次(复用本文件 10 分钟限流);③ 待办 30 分钟过期,⛔ 不长期纠缠。 三、判据(新增自测用例,5 项) selftest.py::t_dispatch_gate —— 派活句留待办(含带空格写法)/非派活句不留/ 超宽限且未建排期则拦/宽限内放行。三条断言都确定性,⛔ 不依赖现网存量。 验收:selftest.py rc=0 PASS 100 / FAIL 0(改前 99); 两组外部变异对照(发现侧 3 项、收工闸 4 项)全过; 另实测"10 分钟限流"有效(同一会话重复构造不再重复拦)。
This commit is contained in:
1 parent
f2691e9368
commit
9a665039a9
4 files changed
+164
-4
No files matched your search
@@ -111,6 +111,14 @@ STATE_REL = os.path.join('.workbuddy', '.skill-load-guard.state')
|
||||
DISABLE_REL = os.path.join('.workbuddy', 'skill-guard.disabled')
|
||||
COOLDOWN = 300 # 同会话注入冷却(秒)
|
||||
|
||||
# 🔴🔴 2026-10-07 加「派活待办」:命中**派活族**触发句 ⇒ 留一条机器可读的待办,
|
||||
# 交给 `stop-dialog-guard.py` 在**收工前拦一次**。
|
||||
# 起因(用户令逐字):「**重点问题是 会话接收到 调用执行会话完成目标 没有去调用对应技能去执行**」——
|
||||
# 本钩子原来**只注入提醒**,AI 照不照做没人管;实测会话 `b232218f` 里用户连说三遍,一次都没拉起执行会话。
|
||||
DISPATCH_REL = os.path.join('.workbuddy', '.dispatch-pending')
|
||||
_DISPATCH_WORDS = ('使用执行会话', '使用任务会话', '执行会话完成', '任务会话完成',
|
||||
'调用执行会话', '调用任务会话', '派任务会话', '建任务会话')
|
||||
|
||||
# 用户「点名方法 / 点名规则」的词表 —— 命中任一即注入。
|
||||
# ── 分组 1:点名「决策方法」(2026-09-16 立,最稳的一条)─────────────────────
|
||||
# ── 分组 2:点名「通用作业规则」(2026-09-22 用户拍板 B 后新增)─────────────────
|
||||
@@ -233,6 +241,27 @@ def _log(workdir, line):
|
||||
pass
|
||||
|
||||
|
||||
def _mark_dispatch(workdir, sid, prompt, hits):
|
||||
"""命中「派活族」触发句 ⇒ 写一条待办(TSV:会话 id / 时间戳 / 命中词)。
|
||||
|
||||
⚠️ 调用点必须在**冷却判定之前** —— 实测 01:33:46 那条最明确的触发句正被 300 s 冷却吞掉,
|
||||
写在冷却之后就等于"最该拦的那一条反而没有标记"。
|
||||
⚠️ 比对前**去掉空白**:实测用户写的是「**应该使用 执行会话的技能去完成工作**」(中间有空格),
|
||||
按原样比就漏了。⛔ 但**不把裸词「执行会话」算进派活族**(它太宽,讨论机制时也会出现)⇒ 误报面保持最小。
|
||||
⛔ 全程 fail-open:任何异常只吞掉,绝不影响本钩子的正常注入。
|
||||
"""
|
||||
try:
|
||||
seg = re.sub(r'\s+', '', prompt or '')
|
||||
if not any(w in seg for w in _DISPATCH_WORDS):
|
||||
return
|
||||
with io.open(os.path.join(_norm_path(workdir), DISPATCH_REL),
|
||||
'w', encoding='utf-8', newline='\n') as f:
|
||||
f.write('%s\t%.3f\t%s\n' % (sid, time.time(), ','.join(hits)))
|
||||
_log(workdir, 'DISPATCH_PENDING sid=%s hits=%s' % (sid[:8], ','.join(hits)))
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
|
||||
def _cooling(workdir, sid):
|
||||
"""同一会话 COOLDOWN 秒内不重复注入。"""
|
||||
try:
|
||||
@@ -287,6 +316,9 @@ def main():
|
||||
if not hits:
|
||||
return
|
||||
|
||||
# 🔴 先留待办(⛔ 必须在冷却之前,见 `_mark_dispatch` 注释)
|
||||
_mark_dispatch(workdir, sid, prompt, hits)
|
||||
|
||||
if _cooling(workdir, sid):
|
||||
return
|
||||
|
||||
|
||||
Reference in new issue
Block a user