| 1 | # Agent 核心精简 |
| 2 | |
| 3 | 本文档记录 Agent 核心精简改造:精简后循环的行为契约、用于前后对比的指标、 |
| 4 | 以及每个契约项的测试位置。英文版见 `AGENT_CORE_SIMPLIFICATION.md`。 |
| 5 | |
| 6 | ## 目标循环 |
| 7 | |
| 8 | ```text |
| 9 | 构造请求 |
| 10 | → provider stream |
| 11 | → clean final:结束 |
| 12 | → tool call:执行并进入下一 step |
| 13 | → 连接、服务或空响应错误:返回失败,由用户决定重试 |
| 14 | → 未处理错误:明确失败 |
| 15 | ``` |
| 16 | |
| 17 | ## 产品取舍 |
| 18 | |
| 19 | - 连接失败、HTTP 错误(包括 429/5xx)、流中断和空响应直接结束本次请求。 |
| 20 | 主会话、子任务及辅助搜索/摘要请求都不自动退避、不等待网络恢复,也不原样 |
| 21 | 重发失败请求。保留原始错误、已经收到的部分内容和实际请求用量;用户再次 |
| 22 | 提交时可以重试。针对明确协议或上下文问题的有界修正与传输重试分开处理。 |
| 23 | |
| 24 | - 普通请求默认 executor-only;planner 改为显式启用(`planner_model`)。 |
| 25 | - 普通 Agent 默认关闭 synthetic continuation;Goal、review、guardian、 |
| 26 | typed report 等显式流程保留自己的约束。 |
| 27 | - compaction 默认单次摘要;chunked/tree-reduce 高级恢复仅限显式场景 |
| 28 | (手动 `/compact` 与明确标记的 recovery workflow)。 |
| 29 | - final readiness、工具安全、取消、预算和显式全文读取的有界暂停继续作为硬边界; |
| 30 | 普通部分读取不冻结独立工作,详见 [读取证据生命周期](READ_EVIDENCE_LIFECYCLE.zh-CN.md)。 |
| 31 | - 旧配置/旧状态保留一版读取兼容;新运行时不再执行旧 fallback。 |
| 32 | |
| 33 | ## 指标基线 |
| 34 | |
| 35 | 复用现有 usage 与 e2ebench 埋点,不新增专用 fallback telemetry。每个阶段 |
| 36 | 前后对比使用: |
| 37 | |
| 38 | - `reasonix run --metrics <path>`:单次运行 `RunMetrics`:token/成本、 |
| 39 | `usage_by_source`(executor/planner/subagent/compaction/... 的请求调用数)、 |
| 40 | `retries`、`compactions`、`steps`。 |
| 41 | - `go run ./cmd/e2ebench -task <task> -json`:每任务请求数、 |
| 42 | `usage_by_source`、trajectory 摘要(stream retry、reasoning replay、 |
| 43 | empty-final retry、TTFT、按 source 的请求数)、墙钟时间、cache 命中。 |
| 44 | |
| 45 | | 指标 | 来源 | |
| 46 | |---|---| |
| 47 | | 每个普通 turn 的模型请求数 | `usage_by_source["executor"].Calls` / trajectory `ExecutorRequests` | |
| 48 | | planner 请求数 | `usage_by_source["planner"].Calls` / trajectory `PlannerRequests` | |
| 49 | | reviewer/evaluator/guardian 请求数 | `usage_by_source["recovery_reviewer"|"goal_evaluator"]`、guardian assessment usage | |
| 50 | | synthetic continuation 次数 | clean turn 的 executor 请求数超出 1 的部分;trajectory `EmptyFinalRetries` | |
| 51 | | stream retry 次数 | trajectory `StreamRetries` / `RunMetrics.Retries` | |
| 52 | | compaction 请求数与 summary spans | `RunMetrics.Compactions`、compaction telemetry 通知(`spans=`、`reqs=`) | |
| 53 | | 首个文本事件延迟 | trajectory `TTFTMs` | |
| 54 | | 总 turn 延迟 | `RunMetrics.DurationMs` / bench `WallMs` | |
| 55 | | tool 执行成功率 | bench 任务 solved 率 / `SolvedThenBroken` | |
| 56 | | 因协议错误终止次数 | trajectory retry-exhausted 结果 | |
| 57 | |
| 58 | 至少对比三类场景:普通问答、普通代码修改、长上下文/工具密集 |
| 59 | (`context-pressure` 任务)。每阶段目标:普通请求一条 executor 请求链,无 |
| 60 | 隐式 planner/reviewer/evaluator 请求,clean final 无额外 continuation, |
| 61 | 默认 compaction 不进入多段 summary,硬安全失败率不上升。 |
| 62 | |
| 63 | ## 契约测试 |
| 64 | |
| 65 | 新增的汇总套件:`internal/agent/agent_contract_test.go`。 |
| 66 | |
| 67 | | 契约项 | 测试 | |
| 68 | |---|---| |
| 69 | | clean final 恰好一次模型请求 | `TestContractCleanFinalMakesOneModelRequest` | |
| 70 | | tool call 执行后进入下一 step | `TestContractToolCallAdvancesToNextStep` | |
| 71 | | 用户手动重试保留正常 thinking,不残留降级状态 | `TestContractExplicitRetryPreservesThinking` | |
| 72 | | 所有 Agent 角色遇到服务失败均只请求一次 | `TestProviderFailureReturnsWithoutWaitingOrRetrying` | |
| 73 | | 流中断保留部分内容,等待用户决定重试 | `TestInterruptedStreamStopsUntilUserRetries` | |
| 74 | | clean final 不追加 synthetic continuation | `TestContractCleanFinalAddsNoSyntheticContinuation` | |
| 75 | | reasoning-only clean stop 直接完成 | `TestRunAcceptsReasoningOnlyFinalAnswer` | |
| 76 | | 空响应由用户决定是否重试 | `TestEmptyResponseLeavesRetryToUser` | |
| 77 | | 空响应直接报错,不提交空消息 | `TestRunStopsOnZeroContentWithoutCommittingEmptyMessages` | |
| 78 | | strict provider 缺失 reasoning 只做一次冻结请求重试 | `TestRunSilentlyRecoversMissingToolCallReasoning` 及 `loop_e2e_test.go`/`retry_e2e_test.go` 的 replay 套件(#9776 修复) | |
| 79 | | incomplete-read 门 | `incomplete_read_test.go` | |
| 80 | | final readiness | `final_readiness_test.go` | |
| 81 | | 取消 | `cancel_test.go` | |
| 82 | | task/token/cost 预算 | `run_budget_test.go` | |
| 83 | | 工具权限/畸形参数 | `argument_validation_test.go`、gate 相关测试 | |
| 84 | | 普通请求不调用 planner | `TestCoordinatorOrdinaryRequestDoesNotCallPlanner`、`TestDecidePlannerRouteExplicitOnly` | |
| 85 | | planner 无 `submit_plan` 的散文失败 | `TestCoordinatorPlanAndExecuteRequiresSubmittedPlan` | |
| 86 | | planner 失败不跑 executor | `TestCoordinatorFailsClosedWhenPlannerFails` | |
| 87 | | 不可用的 `planner_model` 是配置错误 | `TestBuildFailsWhenPlannerModelIsUnresolvable` | |
| 88 | | 普通 Agent 不追加 todo continuation | `TestStandardTodoContinuationDisabledByDefault` | |
| 89 | | 普通 compaction 保持单次摘要 | `TestPressureCompactionDoesNotCallChunkedFold` | |
| 90 | | 缺失的 guardian/recovery 模型 fail-closed | `TestBuildFailsWhenGuardianModelIsUnresolvable`、`TestBuildFailsWhenRecoveryModelIsUnresolvable` | |
| 91 | |
| 92 | ## 门禁 |
| 93 | |
| 94 | 每个阶段执行: |
| 95 | |
| 96 | ```bash |
| 97 | go test -count=1 ./internal/agent/... ./internal/control/... ./internal/config/... ./internal/boot/... |
| 98 | go test -race ./internal/agent/... ./internal/provider/... |
| 99 | go vet ./... |
| 100 | go run ./tools/repolint |
| 101 | ``` |
| 102 |