fix(chat): forbid fake tool calls in tool-less (Just chat) mode #29
Reference in New Issue
Block a user
Delete Branch "propose/no-fake-tools-in-chat-mode"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
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 fakejson {…}"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"):The agentic path is healthy — verified it calls
search_memoryand reports honestly ("memory came back empty… I won't invent themes"). So this is non-agentic-only.Root cause
build_system_prompt(used byhandle_chat, the tool-less path) never tells the model it has no tools this turn, so it fabricates.Fix
A
NO TOOLS THIS TURNdirective 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
el-runtimeversion, and a hand-link against the el repo'sorigin/mainruntime 404s on every route (HTTP-layer drift). It builds correctly through CI, which linkssoul.cagainst 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_persistwrites one overwritingconv:historyblob). 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/chatrunshandle_chat → build_system_prompt. My repro on the running binary confirms this is the live path, and thesoul.cedit fixes it..elsources onmain(routes.el): non-agentic routes throughlayered_cycle(soul.el) →steward_*→imprint_respond, which appends a marker and does not callbuild_system_promptor an LLM.handle_chatisn't on that path.So the
chat.eledit matches the shipped behavior but may be a no-op on the.ellayered_cyclepath. Net: thedist/soul.cchange is the one that fixes the running/CI binary (verified); the.eltree andsoul.chave skewed and likely need reconciling. Can you confirm which path is canonical, and iflayered_cycleis meant to be live, where its non-agentic system prompt is built so the no-tools rule lands there too?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>