Skip to content

feat(local-models): apply OpenAI execution guidance to Qwen / DeepSeek / GLM families - #35087

Open
intelac wants to merge 1 commit into
NousResearch:mainfrom
intelac:feat/qwen-execution-discipline
Open

feat(local-models): apply OpenAI execution guidance to Qwen / DeepSeek / GLM families#35087
intelac wants to merge 1 commit into
NousResearch:mainfrom
intelac:feat/qwen-execution-discipline

Conversation

@intelac

@intelac intelac commented May 30, 2026

Copy link
Copy Markdown

Problem

PR #27797 extended the tier-2 OPENAI_MODEL_EXECUTION_GUIDANCE block from {gpt, codex} to {grok} after observing Grok hits the same failure modes that this block addresses: claiming completion without tool calls, suggesting workarounds instead of using existing tools, replying with plans instead of executing.

Local-model users running Qwen / DeepSeek / GLM via oMLX, LM Studio, or OpenRouter hit identical failure modes. These families are already in the tier-1 TOOL_USE_ENFORCEMENT_MODELS tuple but were excluded from the tier-2 gate, which remained a hard-coded substring check in agent/system_prompt.py.

Solution

  1. Promote the substring match to OPENAI_EXECUTION_DISCIPLINE_MODELS tuple in agent/prompt_builder.py (mirrors the shape of TOOL_USE_ENFORCEMENT_MODELS). Adds qwen, deepseek, glm to the defaults alongside gpt / codex / grok.

  2. Add an agent.execution_discipline config knob with the same value semantics as agent.tool_use_enforcement:

    • "auto" (default) — substring-match against the tuple above
    • true / "true" / "always" / "yes" / "on" — always inject
    • false / "false" / "never" / "no" / "off" — never inject
    • [list] — custom substring list

    Plumbed in agent/agent_init.py alongside _tool_use_enforcement.

  3. Refactor the tier-2 gate in agent/system_prompt.py from a hard-coded if into a config-driven block that mirrors the existing tier-1 logic. The escape hatch (list / true) lets users whose local model has been loaded under a renamed identifier opt in without having to fork.

  4. Update website/docs/user-guide/configuration.md with the new agent.execution_discipline key + correct the tool_use_enforcement default list (was missing the glm / qwen / deepseek entries that have been in TOOL_USE_ENFORCEMENT_MODELS).

The OPENAI_ prefix on the guidance constant is retained for backwards compat with imports/tests, following the precedent set in #27797.

Files changed

File Change
agent/prompt_builder.py + OPENAI_EXECUTION_DISCIPLINE_MODELS tuple
agent/agent_init.py + _execution_discipline plumbing
agent/system_prompt.py hard-coded gate → config-driven
tests/run_agent/test_run_agent.py + 6 tests
website/docs/user-guide/configuration.md new section + fix default list

+196 / -10, no API changes, no breaking behavior for existing users.

Tests

6 new tests in TestToolUseEnforcementConfig:

  • test_auto_injects_execution_guidance_for_qwen
  • test_auto_injects_execution_guidance_for_qwen_bare_local_name
  • test_auto_injects_execution_guidance_for_deepseek
  • test_auto_injects_execution_guidance_for_glm
  • test_execution_discipline_explicit_on_for_claude
  • test_execution_discipline_custom_list

Full tests/run_agent/test_run_agent.py: 359/359 green.
Full tests/run_agent/test_provider_parity.py: 89/89 green in isolation.

How to test

# config.yaml
agent:
  tool_use_enforcement: "auto"     # already triggers tier-1 for qwen
  execution_discipline: "auto"     # NEW — now triggers tier-2 for qwen too
hermes chat -m qwen/qwen-plus -q "test query"
# system prompt now contains the execution-discipline block
# (verify via -v --dump-prompt or local debug)

Related

…k / GLM families

PR NousResearch#27797 extended the tier-2 OPENAI_MODEL_EXECUTION_GUIDANCE block from
{gpt, codex} to {grok} after observing Grok hits the same failure modes
that this block addresses: claiming completion without tool calls,
suggesting workarounds instead of using existing tools, replying with
plans instead of executing.

Local-model users running Qwen / DeepSeek / GLM via oMLX, LM Studio, or
OpenRouter hit identical failure modes. These families are already in
the tier-1 TOOL_USE_ENFORCEMENT_MODELS tuple but were excluded from the
tier-2 gate, which remained a hard-coded substring check in
agent/system_prompt.py.

This change:

  1. Promotes the substring match to OPENAI_EXECUTION_DISCIPLINE_MODELS
     tuple in agent/prompt_builder.py (mirrors the shape of
     TOOL_USE_ENFORCEMENT_MODELS).  Adds qwen, deepseek, glm to the
     defaults alongside gpt / codex / grok.

  2. Adds an `agent.execution_discipline` config knob with the same
     value semantics as `agent.tool_use_enforcement`:
       "auto" (default) — substring-match against the tuple above
       true / "true" / "always" / "yes" / "on" — always inject
       false / "false" / "never" / "no" / "off" — never inject
       [list] — custom substring list
     Plumbed in agent/agent_init.py alongside `_tool_use_enforcement`.

  3. Refactors the tier-2 gate in agent/system_prompt.py from a hard-
     coded `if` into a config-driven block that mirrors the existing
     tier-1 logic.  The escape hatch (list / true) lets users whose
     local model has been loaded under a renamed identifier opt in
     without having to fork.

  4. Updates website/docs/user-guide/configuration.md with the new
     `agent.execution_discipline` key + corrects the
     `tool_use_enforcement` default list (was missing the glm / qwen /
     deepseek entries that have been in TOOL_USE_ENFORCEMENT_MODELS).

The OPENAI_ prefix on the guidance constant is retained for backwards
compat with imports/tests, following the precedent set in PR NousResearch#27797.

Tests: 6 new tests in TestToolUseEnforcementConfig:
  - test_auto_injects_execution_guidance_for_qwen
  - test_auto_injects_execution_guidance_for_qwen_bare_local_name
  - test_auto_injects_execution_guidance_for_deepseek
  - test_auto_injects_execution_guidance_for_glm
  - test_execution_discipline_explicit_on_for_claude
  - test_execution_discipline_custom_list

Full tests/run_agent/test_run_agent.py: 359/359 green.
Full tests/run_agent/test_provider_parity.py: 89/89 green in isolation.

Related: NousResearch#27797 (Grok extension precedent), NousResearch#29677 (Qwen/DeepSeek local
models on llama.cpp — adjacent context on the local-model substring set).
@alt-glitch alt-glitch added type/feature New feature or request comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists labels May 30, 2026
@rcouture27

Copy link
Copy Markdown

When running Qwen 3.6 27B Q8 via llama.cpp, often times the model just starts running the same tool call over and over and over again. It gets stuck in these loops. Then sometimes I'll stop it and ask for a status report or something, and it will just ignore me and keep running the tool calls. Not sure why it's doing this, but do you think this PR fixes these problems as well?

@intelac

intelac commented Jun 18, 2026

Copy link
Copy Markdown
Author

Good question! Sadly this PR probably won't help with that one. 😅

This PR is for the opposite problem — models that give up too early (say "done" without actually doing the work). What you're hitting is the model getting stuck repeating a tool call, which is a different beast.

That loop is usually a model/inference thing, not a prompt thing. A few things that have helped me with Qwen on llama.cpp:

  • Lower the context length — long context / full KV cache makes it degenerate into loops (hit this myself, dropping context fixed it).
  • Lower the stream/inactivity timeout so a stuck loop gets cut off automatically.

The "it ignores me when I ask for a status report" part is separate — that's about whether the harness interrupts a running turn, not the prompt.

If you can reproduce it, opening a separate issue with your model + llama.cpp flags + context length would be the best way to get it looked at. Hope that helps! 🙂

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for identifying a real tier mismatch. Current main still has glm, qwen, and deepseek in TOOL_USE_ENFORCEMENT_MODELS (agent/prompt_builder.py:302), while the execution-discipline gate remains limited to gpt, codex, and grok (agent/system_prompt.py:289).

Problems

  • The new documented agent.execution_discipline setting is read by the proposed agent/agent_init.py change, but the PR does not add it to DEFAULT_CONFIG. Current defaults place tool_use_enforcement at hermes_cli/config.py:1027 immediately before the next agent setting, and the repository configuration contract requires new config.yaml options to be registered in DEFAULT_CONFIG (AGENTS.md:584-590).

Suggested changes

  • Add "execution_discipline": "auto" beside tool_use_enforcement in hermes_cli/config.py when salvaging this change, then retain the behavior tests for the default and explicit override paths.

This is an automated hermes-sweeper review.

@alt-glitch alt-glitch added the area/config Config system, migrations, profiles label Jul 13, 2026
@teknium1 teknium1 added sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform labels Jul 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

area/config Config system, migrations, profiles comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists sweeper:blast-moderate Sweeper blast radius: moderate — a subsystem or single platform sweeper:risk-compatibility Sweeper risk: may break existing users, config, migrations, defaults, or upgrades type/feature New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants