fix: 验收把训练卡死了 —— 加冷却、降条数、追问收窄

实测:agentx 清单涨到 175 条,它每次收工被拦下答 6 条,答完又被拦。
22:02–22:08 六分钟全在答验收,一点活没干。
**验收本来是为了让工作更实,不是替代工作。**

三处一起收:
- 拦截加 15 分钟冷却(卸膛仍在最前面,防死循环结构不变)
- 注入上限 6 → 3
- 追问清单 77 条 → 5 条,全部以「继续」开头,只保证不停 + 目标通道打通
  (完整版备份在 scripts/probes/agentx-training-full.txt.bak)

用户原话:「只要保证最后的目标通道打通,让他围绕训练就好,
不要加另外的要求。」

468 个测试。

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
wuguanganandClaude Opus 5 committed 2026-08-09 03:39:28 +08:00
1 parent a6186e9505
commit d55941561f
2 files changed
+30 -5

No files matched your search

+27 -2
View File
@@ -61,6 +61,17 @@ public final class AcceptanceStore {
@ObservationIgnored
private var armedSessions: Set<String> = []
/// 上一次拦下某个会话的时间。
///
/// **拦截本身是有成本的**:它每被拦一次就要停下手上的活、逐条答一遍。
/// 实测清单涨到 175 条之后,它六分钟里全在答验收、一点活没干 ——
/// 验收本来是为了让工作更实,不是替代工作。
@ObservationIgnored
private var lastInterceptAt: [String: Date] = [:]
/// 两次拦截的最小间隔。到不了就放行,让它有整块时间干活。
private let interceptCooldown: TimeInterval = 900
/// 用户说话了 —— 给这个会话上膛。**唯一的上膛入口。**
public func arm(sessionId: String) {
armedSessions.insert(sessionId)
@@ -73,8 +84,21 @@ public final class AcceptanceStore {
public func disarmAndShouldIntercept(sessionId: String, projectPath: String) -> Bool {
let wasArmed = armedSessions.remove(sessionId) != nil
guard wasArmed else { return false }
// 冷却期内直接放行。**卸膛在前面已经做了**,所以这里 return false
// 不会破坏防死循环的结构 —— 只是这一次不拦。
let now = Date()
if let last = lastInterceptAt[sessionId],
now.timeIntervalSince(last) < interceptCooldown {
return false
}
// 清单里没有待办就别打扰 —— 没启用这功能的项目必须完全无感。
return ledger(for: projectPath).items.contains(where: \.needsAttention)
guard ledger(for: projectPath).items.contains(where: \.needsAttention) else {
return false
}
lastInterceptAt[sessionId] = now
return true
}
/// 只读地看一眼有没有上膛。给测试和排障用,不改状态。
@@ -350,7 +374,8 @@ public final class AcceptanceStore {
// 实测在一个真实项目上拦了 12 条,带完整验收条件之后糊了满屏 ——
// 要点一多就没人看(Claude 也一样),反而比不拦更容易被忽略。
// 存疑的排前面:那一档是「自报做完但代码里找不到」,最该先说清楚。
let cap = 6
// 从 6 降到 3:一次问太多它要停很久才能答完,而它本该在干活。
let cap = 3
// 存疑的优先;其余按**最久没问过**的排前面。
//
// 不轮换的话每轮问的都是同一批:老实答「没做」的条目保持 .open,
@@ -235,8 +235,8 @@ final class AcceptanceStoreTests: XCTestCase {
let text = store.injectionText(for: project) ?? ""
let listed = text.split(separator: "\n").filter { $0.hasPrefix("- [") }.count
XCTAssertEqual(listed, 6)
XCTAssertTrue(text.contains("另有 6 条未列出"), "省略了多少必须说出来")
XCTAssertEqual(listed, 3)
XCTAssertTrue(text.contains("另有 9 条未列出"), "省略了多少必须说出来")
}
/// **老实回答「没做」不能被惩罚。**
@@ -259,7 +259,7 @@ final class AcceptanceStoreTests: XCTestCase {
.filter { second.contains($0.id) }.map(\.id)
XCTAssertEqual(Set(firstIDs).intersection(secondIDs).count, 0, "第二轮该问没问过的那批")
XCTAssertEqual(secondIDs.count, 6)
XCTAssertEqual(secondIDs.count, 3)
}
/// 问过 3 次还说没做的,停止再问并标成「疑似误拆」。