fix(agent): inject tool-use-enforcement guidance model-driven, not to… - #53857
Open
tauros1983 wants to merge 1 commit into
Open
tauros1983 wants to merge 1 commit into
tauros1983 wants to merge 1 commit into
Conversation
…ol-driven
Telegram session 20260628_003118_249c66d5 stalled after one assistant
turn on z-ai/glm-5.2: the system prompt was 20,401 chars and contained
NONE of 'Tool-use enforcement' / 'Execution discipline' / 'Parallel
tool calls' markers. The model emitted its tool intent as a literal
bash markdown code block (different syntax than the earlier
[TOOL_CALL] markers but identical underlying failure mode), the
runtime saw tool_calls=None, and the conversation loop ended with
finish_reason=stop after one turn.
Root cause: the tool-use-enforcement block in
agent/system_prompt.py was wrapped in 'if agent.valid_tool_names:'
so the entire guidance was skipped when the session happened to have
no tools loaded. Telegram/Discord gateway entry points commonly
start without tools, so every GLM/Qwen/DeepSeek session arriving
through those channels got zero guidance.
The fix decouples the guidance injection from valid_tool_names: the
model-name gate (TOOL_USE_ENFORCEMENT_MODELS substring check) now
drives whether the block lands, independent of whether the current
session has tools loaded. The valid_tool_names check is preserved
only for:
- Google-operational sub-block (content is about file paths and
edit commands — irrelevant without tools)
- parallel_tool_call_guidance (no point steering a no-tool session
to batch calls)
- task_completion_guidance (no point steering a session with no
work to 'finish')
The model needs the instruction regardless of whether the current
session happens to have tools loaded — a no-tools GLM session today
may turn into a tools session tomorrow, and even within a single
session the model needs to know it should emit structured tool_calls
when those become available rather than falling back to markdown.
This is independent of PR NousResearch#35087 (which widens the OPENAI_ gate to
cover glm/qwen/deepseek via a config-driven tuple) — that PR was
open for 28 days before this issue surfaced. PR NousResearch#35087 addresses
the wrong-family gate; this PR addresses the wrong-gate wrapper.
Both are needed; both can land in any order.
Files:
agent/system_prompt.py +49/-22
tests/agent/test_system_prompt.py +103/-0
Tests: 14/14 pass in test_system_prompt.py, 170/170 pass in
test_system_prompt.py + test_prompt_builder.py. ruff + ty clean.
9 new regression tests in TestToolUseEnforcementInjectionWithoutTools
cover glm/qwen/deepseek positive, opus negative, parallel-call still
tools-gated, enforce-off respected, explicit-on escape hatch, and
google-operational block still tools-gated.
E2E confirmation: after merge, send a fresh Telegram prompt with
z-ai/glm-5.2 and verify the assistant turn is followed by tool
execution instead of a 1-turn stall.
tonydwb
approved these changes
Jun 28, 2026
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Tool-use enforcement guidance injection fix (2 files). Important correctness fix:
- Removes the
if agent.valid_tool_names:guard that was causing zero guidance for gateway sessions starting without tools loaded - Makes enforcement model-driven, not tool-driven — correct for models like GLM-5.2 that need the guidance regardless of loaded tools
- Google-specific guidance still gated on
valid_tool_names(correct: it's about how to use tools) - Good explanation in the docstring of the root cause (stall after one assistant turn)
This fixes a real regression for Z.AI/GLM models on Telegram/Discord.
Collaborator
|
Thanks for isolating the current Problems
Suggested changes
This is an automated hermes-sweeper review. |
23 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
…ol-driven
Telegram session 20260628_003118_249c66d5 stalled after one assistant turn on z-ai/glm-5.2: the system prompt was 20,401 chars and contained NONE of 'Tool-use enforcement' / 'Execution discipline' / 'Parallel tool calls' markers. The model emitted its tool intent as a literal bash markdown code block (different syntax than the earlier [TOOL_CALL] markers but identical underlying failure mode), the runtime saw tool_calls=None, and the conversation loop ended with finish_reason=stop after one turn.
Root cause: the tool-use-enforcement block in
agent/system_prompt.py was wrapped in 'if agent.valid_tool_names:' so the entire guidance was skipped when the session happened to have no tools loaded. Telegram/Discord gateway entry points commonly start without tools, so every GLM/Qwen/DeepSeek session arriving through those channels got zero guidance.
The fix decouples the guidance injection from valid_tool_names: the model-name gate (TOOL_USE_ENFORCEMENT_MODELS substring check) now drives whether the block lands, independent of whether the current session has tools loaded. The valid_tool_names check is preserved only for:
The model needs the instruction regardless of whether the current session happens to have tools loaded — a no-tools GLM session today may turn into a tools session tomorrow, and even within a single session the model needs to know it should emit structured tool_calls when those become available rather than falling back to markdown.
This is independent of PR #35087 (which widens the OPENAI_ gate to cover glm/qwen/deepseek via a config-driven tuple) — that PR was open for 28 days before this issue surfaced. PR #35087 addresses the wrong-family gate; this PR addresses the wrong-gate wrapper. Both are needed; both can land in any order.
Files:
agent/system_prompt.py +49/-22
tests/agent/test_system_prompt.py +103/-0
Tests: 14/14 pass in test_system_prompt.py, 170/170 pass in test_system_prompt.py + test_prompt_builder.py. ruff + ty clean. 9 new regression tests in TestToolUseEnforcementInjectionWithoutTools cover glm/qwen/deepseek positive, opus negative, parallel-call still tools-gated, enforce-off respected, explicit-on escape hatch, and google-operational block still tools-gated.
E2E confirmation: after merge, send a fresh Telegram prompt with z-ai/glm-5.2 and verify the assistant turn is followed by tool execution instead of a 1-turn stall.
What does this PR do?
Related Issue
Fixes #
Type of Change
Changes Made
How to Test
Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/AFor New Skills
hermes --toolsets skills -q "Use the X skill to do Y"Screenshots / Logs