cd /news/ai-agents/thursdays-with-koog-prompts-everywhe… · home topics ai-agents article
[ARTICLE · art-84370] src=pac.commonsware.com ↗ pub= topic=ai-agents verified=true sentiment=· neutral

Thursdays with Koog: Prompts Everywhere

Mark Murphy's 'Thursdays with Koog' column explains that in the AIAgent framework, the user's instruction is only a 'user message' within a larger prompt that includes a system prompt and conversation history, and that AIAgent handles assembling these automatically. For multi-turn interactions, PromptExecutor offers more control, allowing developers to build a complete Prompt using Koog's DSL and pass it to execute(), whereas AIAgent's callback approach for subsequent user messages is considered awkward. The column highlights the trade-offs between using AIAgent's convenience and PromptExecutor's flexibility for managing prompts in coding agents.

read4 min views1 publishedJul 23, 2026

Last Thursday, we explored the differences between LLMClient, PromptExecutor, and AIAgent in the

AIAgent

, mostly for its tool support.It turns out that how you work with prompts differs depending on which of these layers you work with. You have a richer API at lower layers, mostly because AIAgent

is designed to handle much of prompt management for you.

As users of coding agents, we are used to thinking of "the prompt" as being "what we tell the agent to do". So, for example:

knosh "Make me a sandwich"

(obligatory XKCD shout-out)

Here, Make me a sandwich

is a prompt. It is a fairly lousy prompt, in that coding agents generally are ill-suited for meal prep, but, hey.

While that is a prompt, it is not the prompt that gets sent to the LLM. Make me a sandwich

is a "user message": something you (the user) is sending to the LLM. With a one-shot agent like Knosh, that is the one and only user message. However, most LLM clients are "multi-turn", with multiple user messages interspersed with LLM replies. Plus, many LLM clients have a "system prompt": a message that is supplied by the agent's developers, that forms the first text in the prompt sent to the LLM.

You can envision this in the form of a chat transcript:

| You are a helpful coding agent.
> Make me a sandwich.
< Sorry, Dave, I can't do that. Would you like me to open the pod bay door instead?
> Um, I don't have a "pod bay door", and my name is not Dave.
< I like to think that all my users are Dave, each in their own way.
> Are you *sure* that you are coding agent?
< Well, you asked for a sandwich. I assumed that the normal "coding agent" guidance was part of some other... mythos.

Here, |

represents the system prompt, >

prefixes user messages, and <

prefixes assistant messages (replies from the LLM).

The good news is: AIAgent

handles assembling all that for you.

An available constructor parameter on AIAgent

is systemPrompt

, where you supply that initial message. Knosh assembles this from multiple user-supplied sources:

AGENTS.md

filesprompt

commandTo send a user message to the LLM, you simply call run()

on the AIAgent

, and it attaches that to the system prompt and sends the whole thing to the LLM. For a one-shot (single-turn) agent harness like Knosh, you just get back the LLM's response.

For a multi-turn agent, AIAgent

will keep track of the LLM's response and add it to the growing transcript that represents the overall prompt. If the LLM's response represents a tool call, AIAgent

will invoke the tool, attach that result to the overall prompt, and send the whole thing back to the LLM.

However, this leads to two challenges:

If the first user message is supplied to run()

, how does AIAgent

get further messages from the user?

What happens if the prompt gets large?

Knosh neatly avoids those by having only one turn. This is good, because while dealing the large prompts via Koog's history compression is reasonable, Koog's callback-centric approach to future user messages is awkward IMHO. I might talk about that awkwardness in some future issue.

One way to avoid that awkwardness is to not use AIAgent

, and instead use PromptExecutor

. Turn management becomes straightforward:

execute()

to send a prompt to the LLM, and the return value is what the LLM sent backexecute()

to send the revised prompt to the LLM, and the return value is what the LLM sent backKoog has a prompt

DSL to assemble prompts from various pieces (system prompts, user messages, assistant responses). You wind up with a Prompt

that represents the whole exchange between user and assistant, which you pass to execute()

:

val response = promptExecutor.execute(
    prompt = prompt("demo") { user("Summarize this.") },
    model = OpenAIModels.Chat.GPT4o
)

(the above code snippet comes courtesy of the Koog documentation)

What you lose is tool call support. Technically speaking, you can blend that in — AIAgent

does that, using an underlying PromptExecutor

— but that's going to be a fair bit of work.

In fact, more details about "that fair bit of work" is what I plan to talk about next week: how you set up tools and how they tie into this whole prompt thing.

── more in #ai-agents 4 stories · sorted by recency
── more on @mark murphy 3 stories trending now
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain — perfect for shipping the agent you just read about.

$git push zahid main
Live at https://your-agent.zahid.host
Get free account → Pricing
from €0/mo · no card required
LIVE [news/thursdays-with-koog-…] indexed:0 read:4min 2026-07-23 ·