3.9 KiB
3.9 KiB
| title | date | module | tags | problem_type | severity | resolution_type | category | |||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Gate without caller silently disables feature | 2026-07-06 | experts/orchestrator, core/reflexion |
|
logic_error | P1 | code_fix | logic-errors |
Gate without caller silently disables feature
Problem
_decompose_task 新增了 trigger_reason: str | None = None 参数和门控逻辑,用于控制 retrieve_prompt_reflection 的调用。但两个生产调用方都不传 trigger_reason,导致门控条件恒为 False,功能被静默禁用。
Symptoms
retrieve_prompt_reflection在生产中永远不会被调用(死代码)- 无错误日志,无异常 — 功能只是"消失"了
- 单元测试全部通过(因为测试直接传入
trigger_reason="verify_retry",未覆盖生产调用路径) - 修复前是"每次分解都检索"(浪费 token),修复后变成"永不检索"(功能丢失)
What Didn't Work
- 单元测试无法发现:测试直接调用
_decompose_task(..., trigger_reason="verify_retry"),验证门控逻辑本身正确,但未覆盖生产调用路径(两个调用方都不传 trigger_reason) - 功能测试无法发现:
retrieve_prompt_reflection返回空时不报错,只是降级到默认 prompt,行为差异不明显 - 类型检查无法发现:
trigger_reason有默认值None,类型检查通过
Solution
在 _rebalance 路径传入 trigger_reason="loop_detection",使重新规划时检索历史反思:
# 修复前 — 两个调用方都不传 trigger_reason
phases = await self._decompose_task(lead, task) # 正常分解
new_phases = await self._decompose_task(lead, augmented_task) # 重分解
# 修复后 — _rebalance 路径传入 trigger_reason
new_phases = await self._decompose_task(
lead, augmented_task, trigger_reason="loop_detection"
)
并在正常分解路径添加注释说明 verify/schema 重试路径待接入。
Why This Works
门控设计的核心假设是"有调用方会传入触发条件"。当这个假设不成立时,门控会静默禁用功能:
trigger_reason: str | None = None— 默认 NoneNone not in frozenset({"verify_retry", ...})— 门控条件恒为 False- 无错误、无日志、无异常 — 功能只是"消失"了
修复方式是确保至少有一个调用方传入触发条件。_rebalance 是重新规划路径,传入 loop_detection 语义合理(检测到规划问题后重新规划)。
Prevention
1. 门控设计检查清单
设计门控/开关时,必须:
- 列出所有调用方,确保至少有一个会传入触发条件
- 添加断言或日志:如果门控条件从未为 True,发出警告
- 在 docstring 中明确说明"调用方必须传入 X 才能触发 Y"
2. 测试覆盖生产调用路径
# 不要只测试门控逻辑本身
async def test_gate_with_trigger():
await decompose_task(lead, task, trigger_reason="verify_retry")
assert retrieve_called # 这只验证门控逻辑
# 还要测试生产调用路径
async def test_production_call_path_does_not_silently_disable():
# 模拟生产调用(不传 trigger_reason)
await decompose_task(lead, task)
# 如果功能应该被禁用,assert 它被禁用
# 如果功能不应该被禁用,assert 它仍然工作
3. 代码审查重点
审查门控逻辑时,追踪所有调用方:
- 门控条件是否可达?
- 是否有调用方传入触发条件?
- 默认值是否会导致功能被静默禁用?
4. 死代码检测
定期检查从未执行的代码路径。retrieve_prompt_reflection 如果从未被调用,是死代码的信号。
Related
docs/solutions/runtime-errors/async-generator-yield-before-await-deadlock.md— 同一 IQ-boost 工作中的另一个非显而易见 bug- PR #27 (feat/agent-iq-boost) — 原始 trigger_reason 门控引入
- PR fix/agent-iq-boost-gaps — 本次修复