From 1663dee6612fc87fd42270c922756b83312416e5 Mon Sep 17 00:00:00 2001 From: Valentin Haudiquet Date: Wed, 11 Nov 2026 00:06:40 +0100 Subject: [PATCH] fix(agent-tools): render tool schemas without HTML-safe escapes minijinja's tojson escapes `<`, `>`, `&` and `'` so its output can ride a script tag. A system prompt is not a script tag, so every apostrophe in a field description reached the model as \u0027 and every `` as \u003c: harder to read, and more tokens for the same text. The renderer registers a json filter that serializes through serde_json and leaves those characters alone, and both tool sections use it. tojson stays for a user template whose output does feed a page. The serialization is also compact, which drops the spaces tojson puts after every separator. The tool row is `name | arguments` with the description indented underneath, so a multi-line description can no longer push the schema column below another tool's prose, where it read as part of that tool. --- crates/rhema-claw/SYSTEM.md | 7 ++++--- crates/rhema-claw/src/bootstrap.rs | 5 +++-- crates/rhema-claw/src/interactive.rs | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/rhema-claw/SYSTEM.md b/crates/rhema-claw/SYSTEM.md index aaecb1e..6fb451b 100644 --- a/crates/rhema-claw/SYSTEM.md +++ b/crates/rhema-claw/SYSTEM.md @@ -19,11 +19,12 @@ it, is the reply the user sees. {% if tools %} ## Tools -You act through these tools (name | description | arguments). The -arguments are the JSON schema a call must satisfy. +You act through these tools: each entry is its name and arguments, then +what it is for. The arguments are the JSON schema a call must satisfy. {% for tool in tools %} -- {{ tool.name }} | {{ tool.description }} | {{ tool.parameters | tojson }} +- {{ tool.name }} | {{ tool.parameters | json }} +{{ tool.description | indent(2, true) }} {% endfor %} {% endif %} diff --git a/crates/rhema-claw/src/bootstrap.rs b/crates/rhema-claw/src/bootstrap.rs index 4087e83..11a7aa9 100644 --- a/crates/rhema-claw/src/bootstrap.rs +++ b/crates/rhema-claw/src/bootstrap.rs @@ -518,9 +518,10 @@ mod tests { None, ) .expect("renders"); - assert!(prompt.contains("- my_tool | its purpose | "), "{prompt}"); + assert!(prompt.contains("- my_tool | {"), "{prompt}"); + assert!(prompt.contains("\n its purpose"), "{prompt}"); assert!( - prompt.contains(r#""required": ["kind"]"#), + prompt.contains(r#""required":["kind"]"#), "the JSON schema rendered: {prompt}" ); } diff --git a/crates/rhema-claw/src/interactive.rs b/crates/rhema-claw/src/interactive.rs index 6ea8549..2ed2b6c 100644 --- a/crates/rhema-claw/src/interactive.rs +++ b/crates/rhema-claw/src/interactive.rs @@ -1218,7 +1218,7 @@ mod tests { ); } assert!( - prompt.contains(r#""required": ["command"]"#), + prompt.contains(r#""required":["command"]"#), "bash's schema renders: {prompt}" ); assert!(prompt.contains("final message"), "{prompt}");