fix(chat): forbid fake tool calls in tool-less (Just chat) mode #29

Merged
will.anderson merged 1 commits from propose/no-fake-tools-in-chat-mode into main 2026-06-22 16:36:49 +00:00
Member

Fix: stop the model faking tool calls in "Just chat" mode (for review — don't merge without sign-off)

The bug (reproduced on the local soul)

In the non-agentic path (agentic:false — Tools off / "Just chat", or the router judged no tools needed), asking for tool-work makes the model role-play tool use: it emits a fake json {…} "tool call" and says "let me search/query/pull your recent sessions" while nothing runs. To a user it reads as a broken/lying app.

Repro on live local soul (POST /api/chat, agentic:false, "Summarize the key themes across my recent conversations"):

I need to query your conversation history… json {"node_types":["ConversationTurn","Memory"]…} … Let me search through your recent sessions.

The agentic path is healthy — verified it calls search_memory and reports honestly ("memory came back empty… I won't invent themes"). So this is non-agentic-only.

Root cause

build_system_prompt (used by handle_chat, the tool-less path) never tells the model it has no tools this turn, so it fabricates.

Fix

A NO TOOLS THIS TURN directive appended to the non-agentic system prompt: never emit tool calls / JSON tool blocks / "let me pull…" narration; answer from context only; if a tool is genuinely needed, say so in one sentence and tell the user to turn Tools on. Applied to:

  • chat.el (source of truth)
  • dist/soul.c (the curated translation unit the CI compiles) — so the CI-built binary carries it.

7 lines, additive, scoped to the non-agentic prompt only (the agentic path builds its own system inline and is untouched).

Verification honesty

  • The fabrication repro is confirmed on the live local soul.
  • I could not verify the patched binary locally: this machine has no copy of the pinned el-runtime version, and a hand-link against the el repo's origin/main runtime 404s on every route (HTTP-layer drift). It builds correctly through CI, which links soul.c against the pinned runtime. Please sanity-check the built binary on your side.

Related (separate, bigger)

"Summarize my recent conversations" returns empty even with tools because conversations aren't stored as searchable per-session episodic nodes (conv_history_persist writes one overwriting conv:history blob). I'm writing that up as a separate design proposal.


⚠️ Source↔artifact skew found (please confirm which path is canonical)

While verifying the fix is on the real path, I found a divergence:

  • dist/soul.c (the TU the CI actually compiles → the shipped binary): non-agentic /api/chat runs handle_chat → build_system_prompt. My repro on the running binary confirms this is the live path, and the soul.c edit fixes it.
  • .el sources on main (routes.el): non-agentic routes through layered_cycle (soul.el) → steward_*imprint_respond, which appends a marker and does not call build_system_prompt or an LLM. handle_chat isn't on that path.

So the chat.el edit matches the shipped behavior but may be a no-op on the .el layered_cycle path. Net: the dist/soul.c change is the one that fixes the running/CI binary (verified); the .el tree and soul.c have skewed and likely need reconciling. Can you confirm which path is canonical, and if layered_cycle is meant to be live, where its non-agentic system prompt is built so the no-tools rule lands there too?

## Fix: stop the model faking tool calls in "Just chat" mode (for review — don't merge without sign-off) ### The bug (reproduced on the local soul) In the **non-agentic** path (`agentic:false` — Tools off / "Just chat", or the router judged no tools needed), asking for tool-work makes the model **role-play tool use**: it emits a fake ```json {…}``` "tool call" and says *"let me search/query/pull your recent sessions"* while **nothing runs**. To a user it reads as a broken/lying app. Repro on live local soul (`POST /api/chat`, `agentic:false`, "Summarize the key themes across my recent conversations"): > I need to query your conversation history… ```json {"node_types":["ConversationTurn","Memory"]…}``` … Let me search through your recent sessions. The **agentic path is healthy** — verified it calls `search_memory` and reports honestly ("memory came back empty… I won't invent themes"). So this is non-agentic-only. ### Root cause `build_system_prompt` (used by `handle_chat`, the tool-less path) never tells the model it has **no** tools this turn, so it fabricates. ### Fix A `NO TOOLS THIS TURN` directive appended to the non-agentic system prompt: never emit tool calls / JSON tool blocks / "let me pull…" narration; answer from context only; if a tool is genuinely needed, say so in one sentence and tell the user to turn Tools on. Applied to: - `chat.el` (source of truth) - `dist/soul.c` (the curated translation unit the CI compiles) — so the CI-built binary carries it. 7 lines, additive, scoped to the non-agentic prompt only (the agentic path builds its own system inline and is untouched). ### Verification honesty - The **fabrication repro** is confirmed on the live local soul. - I could **not** verify the patched *binary* locally: this machine has no copy of the pinned `el-runtime` version, and a hand-link against the el repo's `origin/main` runtime 404s on every route (HTTP-layer drift). It builds correctly through CI, which links `soul.c` against the pinned runtime. Please sanity-check the built binary on your side. ### Related (separate, bigger) "Summarize my recent conversations" returns empty **even with tools** because conversations aren't stored as searchable per-session episodic nodes (`conv_history_persist` writes one overwriting `conv:history` blob). I'm writing that up as a separate design proposal. --- ### ⚠️ Source↔artifact skew found (please confirm which path is canonical) While verifying the fix is on the real path, I found a divergence: - **`dist/soul.c`** (the TU the CI actually compiles → the shipped binary): non-agentic `/api/chat` runs `handle_chat → build_system_prompt`. **My repro on the running binary confirms this is the live path**, and the `soul.c` edit fixes it. - **`.el` sources on `main`** (`routes.el`): non-agentic routes through `layered_cycle` (soul.el) → `steward_*` → `imprint_respond`, which appends a marker and does **not** call `build_system_prompt` or an LLM. `handle_chat` isn't on that path. So the `chat.el` edit matches the shipped behavior but may be a no-op on the `.el` `layered_cycle` path. Net: **the `dist/soul.c` change is the one that fixes the running/CI binary** (verified); the `.el` tree and `soul.c` have skewed and likely need reconciling. Can you confirm which path is canonical, and if `layered_cycle` is meant to be live, where its non-agentic system prompt is built so the no-tools rule lands there too?
tim.lingo added 1 commit 2026-06-21 16:58:08 +00:00
fix(chat): forbid fake tool calls in tool-less (Just chat) mode
Neuron Soul CI / build (pull_request) Successful in 4m47s
f6c4ea70a0
REPRODUCED: in the non-agentic path (Tools off / 'Just chat'), asking for
tool-work makes the model role-play tool use — it emits a fake ```json {...}```
'tool call' and says 'let me search/query/pull your sessions' while NOTHING
runs. Reads as a broken/lying app. (The agentic path is fine: verified it
calls search_memory and reports honestly.)

Root cause: build_system_prompt (handle_chat, the tool-less path) never told
the model it has no tools this turn, so it fabricated.

Fix: add a NO-TOOLS directive to the non-agentic system prompt — never emit
tool calls / JSON tool blocks / 'let me pull...' narration; answer from context
only; if a tool is truly needed, say so in one sentence and tell the user to
turn Tools on. Applied to chat.el (source) AND dist/soul.c (the curated TU the
CI compiles), so the CI-built binary carries it.

Verified the FABRICATION repro on the live local soul; could not verify the
patched binary locally (no matching el-runtime version on this machine — a
hand-link against origin/main runtime 404s on all routes). Builds correctly via
CI, which links soul.c against the pinned runtime.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
will.anderson merged commit f3069b481d into main 2026-06-22 16:36:49 +00:00
Sign in to join this conversation.
No Reviewers
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: neuron-technologies/neuron#29