| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
TECH-1588: agent retry + usage tracking refactor (#595) * TECH-1588: agent retry + usage tracking refactor Make AgentRunOutput the single source of truth for usage; rebuild the retry path so failed attempts no longer drop tokens from agent.usage. - Add Task.reset_run_state() (per-attempt state cleanup) and call it from task_start() and Agent.do_async's retry block. _run_id is deliberately not reset — do_async assigns it before InitStep. - Add TaskUsage.snapshot() / subtract() for delta-based accumulation. - Add _agent_usage_baseline on AgentRunOutput (with to_dict/from_dict round-trip) so cross-process resume after retry exhaustion does not double-count. - Flip _prepare_continuation_context to task._usage = output.usage (output is canonical) via output._ensure_usage(). - Make _finalize_agent_usage baseline-aware: delta = usage - baseline when a baseline is present. - Add @retryable exhaustion hook (sync + async); Agent implements _on_retries_exhausted to capture the last failed attempt's usage, snapshot the baseline, and idempotently upsert the output to storage (avoiding save_session_async's message-append side effect). - Capture each prior failed attempt's usage in the retry block before reset_run_state discards its TaskUsage. - Prefix injected test errors with 'INJECTED ERROR:' so test assertions distinguish injected vs real failures. - Delegate Chat retries entirely to Agent.retry: drop Chat(retry_attempts, retry_delay), _execute_with_retry, _is_retryable_error and the stream-retry wrappers. - New smoke tests: tests/smoke_tests/agent/test_retry_metrics.py (7 scenarios) and tests/smoke_tests/chat/test_chat_retry.py (3 scenarios). Stale unit tests for Chat's retry validation / _is_retryable_error removed. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(chat): use agent-first storage/memory resolution - Chat reuses agent.memory (and its storage) when present; storage= and memory-config kwargs apply only when the agent has no memory. - Realign Chat.session_id / user_id with agent.memory and emit UserWarning on mismatch. SessionManager now keys against the aligned ids. - AutonomousAgent.autonomous_memory / autonomous_storage become live properties tracking self.memory (no more stale snapshot after a Memory swap). - Add 12 smoke regressions across chat and autonomous_agent suites covering the resolution order and id alignment. * fix(tools,messages): tolerate gemma stream artifacts in HQ chat - shell_toolkit.run_command / run_python: @tool(timeout=None, max_retries=0) so the generic tool-layer asyncio.wait_for stops duplicating the subprocess's own timeout and leaking child processes on cancel (was triggering a 6x retry loop on legitimate run_command calls with no observable progress). - messages.ToolCallPart.args_as_dict: extend the existing trailing-junk fallback to also handle truncated / unterminated args (stream cut mid-string) by returning {} instead of tearing down the whole stream. - Trim verbose comment blocks in agent.py, tasks.py and run/agent/output.py to reflect the 'concise comments' preference. * docs(claude): track pending explanation/ doc syncs for TECH-1588 Three documents/ai/explanation/ files still describe pre-TECH-1588 contracts: - chat.md: agent-first storage/memory wiring and session_id / user_id alignment with UserWarning. - messages.md: ToolCallPart.args_as_dict() now falls back to {} on truncated/unterminated args instead of asserting. - tools.md: ShellToolKit overrides ToolConfig defaults with @tool(timeout=None, max_retries=0). Surface them in CLAUDE.md so a future session reconciles all three in one 'docs: sync explanation/' pass before relying on the old text. * docs: sync explanation/ docs with TECH-1588 contracts - chat.md: rewrite the storage/memory wiring section to describe the new agent-first policy (Chat reuses agent.memory and its storage when present; storage= and memory-config kwargs only apply when agent.memory is None) and the new session_id / user_id alignment + UserWarning. Update the dependency tree and integration notes that still said 'Chat replaces agent.memory'. - messages.md: ToolCallPart.args_as_dict() no longer asserts dict; document the trailing-junk + truncated-args fallback to {}. - tools.md: ToolConfig defaults table now flags the ShellToolKit override (timeout=None, max_retries=0) alongside the existing MCPTool override. - CLAUDE.md: drop the 'Outstanding documentation updates' TODO now that the three docs are in sync. * docs(claude): require documents/ai/explanation/ sync on contract changes Add a permanent rule under 'AI Operational Guides' so future sessions know: when a code change alters an observable contract (public API, default, side effect, error shape, new emission, conditional invariant), the matching documents/ai/explanation/<subsystem>.md must be resynced in the same commit/PR or an immediate follow-up 'docs: sync explanation/...' commit. Lists the common triggers and a 4-step how-to (grep, edit surgically, bundle with the change, surface what you found at the start of the reply). * refactor(pipeline): move error-injection scaffolding out of production The error-injection helpers used to live in src/upsonic/agent/pipeline/ step.py as a global _ERROR_INJECTIONS dict + inject/clear/check funcs + a try/except wrapper in Step.run. That's test-only code on the hot path; ship it from production and move it to tests/. - step.py: drop _ERROR_INJECTIONS, inject_error_into_step, clear_error_injection, check_error_injection, and the try/except around the check call in Step.run. Net -75 lines from runtime. - tests/_pipeline_injection.py: new shared helper with the same surface API (inject_error_into_step / clear_error_injection). Monkey-patches Step.run on first use and restores it once the registry is empty. Crucially, mirrors the old behaviour of finalizing an ERROR StepResult on the context before raising so get_problematic_step() and continue_run_async can still find a resume point. - tests/conftest.py: add project root to sys.path so suites can do 'from tests._pipeline_injection import …'. - test_retry_metrics.py, test_chat_retry.py, test_comprehensive_hitl.py, usage_durable_execution.py: change the import path only; test bodies untouched. (test_comprehensive_hitl also splits the StepResult/StepStatus import which stays under upsonic.agent.pipeline.) - documents/ai/explanation/agent/agent.md: resync the step.run description per the CLAUDE.md doc-sync rule — no more check_error_injection in production. Verified: 15 retry/chat-retry/task-metric tests + 34 HITL tests (comprehensive + usage_metrics + delay + storage_verification) all pass against the new helper. --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com> | 1 个月前 |