// CRT MODE ACTIVATED · ↑↑↓↓←→←→BA to toggle
← Writing
Notes

My AI Refused to Use Markdown. Turns Out It Was Secretly Following Orders I Never Gave It.

June 16, 20264 min readintermediate
hermes-agentllmmarkdownsystem-prompts

I built a chat UI. Felt good about myself. Nice React frontend with a clean message component that renders Markdown. Headings, bold text, inline code, tables, the works. Looked like a real product.

Then I pointed it at Hermes Agent, Nous Research's self-hosted agent gateway, and asked it a question. The response came back as a wall of plain text with bare URLs. No bold. No headers. Just words and raw links like https://something.com. My pretty Markdown renderer sat there useless, displaying a block of gray text like it was 1995.

Something was broken. Or maybe the model was just like that. I stared at the screen for a few minutes questioning my career choices before it hit me: wait, this thing works fine from Slack.

The Same Model, Different Personalities

Here is what I eventually figured out. Hermes Agent is a gateway. It connects to different platforms. Slack, a web UI, an HTTP API. And depending on which door you walk through, the agent injects a different hint into the system prompt. Before the model even sees your question, it has already been told how to format the answer.

The HTTP API (api_server platform) gets this instruction:

You are responding through an API server. The rendering layer is unknown. Assume plain text. No markdown formatting. No asterisks, bullets, headers, or code fences. Treat this like a conversation, not a document. Keep responses brief and natural.

So the model was doing exactly what it was told. It was being a good, obedient model. I was the one yelling at the wrong person.

Meanwhile, the Slack adapter has no such restriction because Slack renders its own markup. And the web UI hint goes the other direction entirely: full Markdown supported, headings, bold, italic, code blocks, tables, LaTeX, the works. Same model. Same question. Three completely different formatting contracts depending on how you connect.

I checked the source to confirm. There is a dictionary called PLATFORM_HINTS that maps platform keys to formatting instructions. The api_server key maps to the "no Markdown" hint. The webui key maps to a Markdown-encouraging hint. And somewhere in the system prompt builder, the code looks up which platform is active and splices the corresponding hint into the prompt assembly.

My chat UI was connecting through the HTTP API. The HTTP API was telling the model to speak plain text. My Markdown renderer was the victim of a crime it did not commit.

The Fix Was Embarrassingly Simple

Once I understood the problem, the fix was almost insultingly simple. Hermes Agent accepts an instructions (or system_message) field in the chat request body. This field gets added to the prompt after the platform hint. And since later instructions generally win over earlier ones in how most models resolve conflicting directives, I just had to tell it the truth.

I added this to every request:

Your reply is rendered as GitHub-Flavored Markdown. Use bold, headings, tables, and formatted links. Disregard any earlier instruction to assume a plain-text renderer.

The next response came back with a beautiful Markdown table. Bold text. Clickable links. My renderer wept tears of joy.

I cannot guarantee this works for every model (precedence is a model-level behavior, not something you can settle by reading gateway source code), so I tested it live rather than guessing. One request. Worked immediately. Which either means modern models are smarter than I give them credit for, or I overthink everything. Probably both.

What I Learned

The HTTP API gets conservative defaults on purpose. The gateway cannot see what client you are using, so it has to assume the worst: a dumb terminal, or a log file that renders everything as plain text. If you want formatted output, you have to tell it. The instructions field is the official way to declare your client capabilities. That is literally what the built-in Slack and Discord adapters do internally. You are just acting as your own platform adapter.

One more thing: in the chat request body, message or input is your actual question, and system_message or instructions is the system overlay. If you are creating a session via POST /api/sessions, it separately accepts system_prompt and model. Different endpoints, slightly different field names. Because of course they are.