Repository navigation
fix(agent): end the turn when the request is done - #4480
Conversation
|
Blocking regression found in end-to-end testing: with this PR, the agent can finish a task, get sent back to verify it, and then be forbidden from doing so. It ends with a boilerplate "task is incomplete" message instead of its answer. Measured on a combined build of the open fix PRs, DeepSeek V4.1 Flash,
Suggested fix:
🔍 Technical details
|
|
Fixed the regression above: when the agent is sent back to run a check after answering, it can now load the skill that check needs and look inside the folder the prompt named. A working folder named in the prompt also no longer switches on document extraction, which is what sent
|
|
Found a real regression, not CI noise: any agent built without running 🔍 Technical details
This reproduces deterministically in CI, not intermittently:
Root-caused the rest of the red X's while I was in there, both unrelated to this diff:
The turn-scope-guard logic itself — the drift limit, the failure-streak stop, |
6dc04ef to
e3dd542
Compare
|
Live recheck on DeepSeek V4.1 Flash, rebased onto today's |
A bugfix request loaded the coding skill, whose "find every call site" tip switched on document inventorying; after the fix was verified the agent spent minutes extracting every file it had read until the user cancelled. Only a skill that lists extract_document_items in tools_required turns inventorying on now. Two general stop conditions cover loops the identical-call guard misses: once the turn has answered, a call touching nothing the request touched is not run (a second ends the turn), and a tool the framework keeps rejecting with new arguments is stopped like an identical repeat. Closes amd#4448.
After answering, the agent was sometimes sent back to verify its work (run the tests, check a file) and then refused the very step that check needed: loading the skill that grants pytest, or browsing the folder the prompt named. The completion check then replaced a good answer with boilerplate. Seen in the everyday suite on 11-codereview and b3-triage. - Skill discovery and loading always count as serving the request. - browse_directory's directory_path is read as a path, so the prompt's working folder stays in scope. - A working folder named in the prompt no longer switches on document extraction; only files are inventory sources.
_process_query_impl called self._turn_scope.begin_turn() unconditionally, assuming Agent.__init__ had run. Test agents built with ChatAgent.__new__ to drive _process_query_impl without a Lemonade backend never get that attribute, so every such test crashed with an AttributeError. Lazily create the guard when it's missing, the same way _extraction_ledger is defended elsewhere in this file.
e3dd542 to
b043fb8
Compare
|
Verdict: Approve with suggestions. Removing the coding skill's accidental trigger for inventory mode fixes the root cause, and the before/after bench numbers show it. One edge case can stop the agent partway through a fix it was asked to make. 🟡 A fix the agent is sent back to finish can be blocked halfway. The agent is marked "answered" just before it is asked to run the tests. If those tests fail in a file it hasn't touched yet, reading or editing that file counts as off-request work. The first attempt is refused and the second ends the turn, so the user gets a closing summary while the tests are still failing. A folder named in the prompt doesn't help: only that exact folder is in scope, not the files inside it. So "the tests in Suggested fix: treat anything under a folder the request named as in scope. Also consider not blocking file work until the test re-run the agent was sent back for has finished. A test for the prompt → answer → failing pytest → edit a new file path would lock this down. 🟢 When the prompt names the working folder itself, its relative form is 🔍 Technical details
|
…d#4527) The turn guard ends a turn after the same tool is refused four times in a row. It counted every shell command as one tool, so an agent that tried `python`, then `ls`, then `pip` and was refused each time looked like it was stuck in a loop, and its turn ended. On a SWE-bench pylint task this ended the run with no patch. Streaks are now counted per program run through the shell (a leading `cd <dir> &&` is skipped), so a real loop on one program still ends the turn and trying different programs doesn't. The data is a single task. The same pylint task (pylint-4970, DeepSeek V4.1 Flash) ended with no patch in all 3 runs made with the guard (amd#4480) before this change, and was resolved in the run after it. ## Test plan - [ ] `python -m pytest tests/unit/agents/test_turn_scope.py`
After finishing what the user asked, the flagship could start an unrelated job and run until cancelled. In one TUI session a one-line bugfix was done and verified in 40 s, followed by 16 minutes of "extract every source into an inventory". The trigger was the
codingskill: its "find every call site" tip switched on document-inventory mode, so every coding session was pushed toward it. Now only a skill built on the extraction tool turns that mode on, and two general stop conditions end work the request didn't ask for.Closes #4448
extract_document_itemsintools_required. None of the starter skills do, while real "list every item in this transcript" requests still get full, source-quoted extraction (fix(agents): verify completion and preserve exhaustive inventories #4374's guarantees are kept).Test plan
pytest tests/unit/agents tests/unit/eval: 3001 passed, 78 skipped, 1 failed. The failure,test_symlinked_directory_is_the_same_output, also fails on untouchedmainon Windows (symlink privileges).New tests: turn scope (drift after the answer, failing-tool loop, what counts as related); the coding skill's wording no longer enables extraction while an extraction skill still does; code read along the way isn't an inventory source; bench-harness hooks.
Before/after on DeepSeek V4.1 Flash via Fireworks, same machine,
gaia eval tasks run --suite everyday --tasks 02-bugfix,01-refactor,b6-config-env --no-judge:main)n=1, but the deltas are far larger than run-to-run noise.
python util/lint.py --black --isort.Judged quality pass (
gaia eval tasks judge) once the judge CLI is signed in on the eval box.