Knosh uses Koog to execute prompts. Koog has a PromptExecutor
, which executes prompts. Hence, it would seem like Knosh would use PromptExecutor
, right?
Nope.
PromptExecutor
indeed executes prompts. It even does so in a one-shot fashion, the way Knosh operates. You hand PromptExecutor
a prompt, it passes the prompt along to the LLM model via its provider, and PromptExecutor
hands back the response. Easy, peasy.
However, that is pretty much all that PromptExecutor
does. If you want anything else, you need to use something else.
For example, like any coding agent, Knosh has tools. If you have used Claude Code, Codex, OpenCode, or the like, you will be used to seeing mentions of the agent using tools like Read
or Write
or Bash
. You might think that tools are a dedicated communications channel between the LLM and the agent, completely independent from your prompt and its response.
Alas, no.
The way tool calls work is:
That covers a single tool call in a one-shot prompt. The LLM could elect to call tools several times in succession -- the agent handles all that back-and-forth. In an interactive coding agent or any other "chatbot"-style interface, there are lots of prompts in a conversation -- the agent handles chaining all this stuff into the "context" that the initial prompt evolves into.
PromptExecutor
does none of that. In Koog, AIAgent
offers that sort of high-level interface. For my Android developer audience: PromptExecutor
is to OkHttp as AIAgent
is to Retrofit. AIAgent
wraps a PromptExecutor
and handles multi-turn conversations, tool calls, and lots of other stuff that agents need.
Because Knosh needs tools, Knosh uses AIAgent
.
Knosh's AIAgentFactory
builds the AIAgent that a particular command uses. The core of that is the
AIAgent
constructor call:
return AIAgent(
promptExecutor = promptExecutor,
llmModel = resolveModel(llmProvider, agentConfig.modelName, agentConfig.contextLength),
toolRegistry =
toolSet.build(agentConfig, commandPermissions, commandExternalDirectories, logFullToolCalls, allowedToolIds),
temperature = temperature ?: agentConfig.temperature,
maxIterations = maxIterations,
systemPrompt =
assembleSystemPrompt(
listOf(agentConfig.systemPrompt) +
loadAgentsMarkdown(
configDir = System.getProperty("user.home").toPath() / ".config" / "knosh",
workingDir = System.getProperty("user.dir").toPath(),
fileSystem = fileSystem,
)
),
)
Knosh uses MultiLLMPromptExecutor
as its PromptExecutor
implementation. This wraps a map of LLMProvider
objects to LLMClient
objects — we covered those a week ago. The LLMProvider
is a simple identifier of a provider (e.g., LLMProvider.OpenAI
), while LLMClient
knows things like the API key to use. MultiLLMPromptExecutor
lets you configure all possible providers/clients that you wish to use.
We will explore many of those other AIAgent
constructor parameters in future issues. The two of importance for now is promptExecutor
and llmModel
. Those combine to let the AIAgent
know how to talk to the LLM:
PromptExecutor
with the LLModel
MultiLLMPromptExecutor
uses that to identify what LLMClient
to use, based on which LLMProvider
the LLModel
is tied toPromptExecutor
to execute a prompt, MultiLLMPromptExecutor
uses the LLMClient
To have an AIAgent
execute a prompt, you call run()
, passing in the desired prompt, perhaps just as a simple String
. We will explore prompts and how Koog works with them in next week's issue!