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:
1 parent
a6186e9505
commit
d55941561f
2 files changed
+30
-5
No files matched your search
@@ -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 次还说没做的,停止再问并标成「疑似误拆」。
|
||||
|
||||
Reference in new issue
Block a user