派活触发句收工闸:说了就必须把执行会话拉起来(实测栽过,补机器抓手)
一、起因(用户令逐字) 「重点问题是 会话接收到 调用执行会话完成目标 没有去调用对应技能去执行」。 实测会话 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
@@ -7238,5 +7238,62 @@ def main() -> int:
|
||||
return 0 if nfail == 0 else 1
|
||||
|
||||
|
||||
@case("⛔ 派活触发句:说了就必须把执行会话拉起来(收工前闸)")
|
||||
def t_dispatch_gate():
|
||||
"""🔴 2026-10-07 立。
|
||||
|
||||
用户令逐字:「**重点问题是 会话接收到 调用执行会话完成目标 没有去调用对应技能去执行**」。
|
||||
实测(会话 `b232218f` · vibe-product):用户 01:32:00 / 01:33:46 两次说触发句,
|
||||
直到 01:36:27 才建出排期 —— 中间那一轮 AI 只在回复里解释口径,**没拉起执行会话**。
|
||||
|
||||
守三样(都确定性,⛔ 不依赖现网存量):
|
||||
① 派活族触发句 ⇒ `skill-load-guard.py` 留待办(含**带空格**的写法 —— 实测用户就是这么写的);
|
||||
② 非派活发言 ⇒ ⛔ 不留(⛔ 别把裸词「执行会话」当派活,那会误报);
|
||||
③ 收工闸:同会话 + 超 90 s 宽限 + 该区此后**没有**新建排期 ⇒ 拦(`continue:false`)。
|
||||
"""
|
||||
import json as _j
|
||||
import subprocess as _sp
|
||||
import tempfile as _tf
|
||||
import time as _t
|
||||
|
||||
gate = HERE / "hooks" / "stop-dialog-guard.py"
|
||||
mark = HERE / "hooks" / "skill-load-guard.py"
|
||||
sid = "selftest-gate-%d" % os.getpid()
|
||||
tmp = _tf.mkdtemp(prefix="dsh-gate-")
|
||||
mk = os.path.join(tmp, ".workbuddy", ".dispatch-pending")
|
||||
os.makedirs(os.path.dirname(mk), exist_ok=True)
|
||||
|
||||
def _feed(script, payload):
|
||||
return _sp.run([sys.executable, str(script)],
|
||||
input=_j.dumps(payload).encode("utf-8"),
|
||||
stdout=_sp.PIPE, stderr=_sp.PIPE, timeout=60
|
||||
).stdout.decode("utf-8", "replace")
|
||||
|
||||
def _mark(ts):
|
||||
with open(mk, "w", encoding="utf-8", newline="\n") as f:
|
||||
f.write("%s\t%.3f\t使用执行会话\n" % (sid, ts))
|
||||
|
||||
r = []
|
||||
for tag, prompt in (("直连写法", "调用执行会话完成目标:按 1-2-3 步骤做"),
|
||||
("带空格写法", "应该使用 执行会话的技能去完成工作")):
|
||||
if os.path.exists(mk):
|
||||
os.remove(mk)
|
||||
_feed(mark, {"prompt": prompt, "session_id": sid, "cwd": tmp})
|
||||
r.append(("派活句留待办(%s)" % tag, os.path.isfile(mk)))
|
||||
if os.path.exists(mk):
|
||||
os.remove(mk)
|
||||
_feed(mark, {"prompt": "重点是技能规则和机制也要和这几个文档匹配",
|
||||
"session_id": sid, "cwd": tmp})
|
||||
r.append(("非派活句⛔ 不留待办", not os.path.isfile(mk)))
|
||||
|
||||
pl = {"session_id": sid, "cwd": tmp, "stop_hook_active": False,
|
||||
"transcript_path": os.path.join(tmp, "x.jsonl")}
|
||||
_mark(_t.time() - 200)
|
||||
r.append(("超 90 s 宽限 + 该区未建排期 ⇒ 拦", '"continue": false' in _feed(gate, pl)))
|
||||
_mark(_t.time())
|
||||
r.append(("宽限内(刚说完)⇒ 放行", '"continue": false' not in _feed(gate, pl)))
|
||||
return r
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
sys.exit(main())
|
||||
Reference in new issue
Block a user