The agent loop: prompt in, tool calls executed, final answer out.
The agent starts with no abilities at all. What it can do is exactly what you
pass in :capabilities - see jj.bettong.capability.
(require '[jj.bettong.agent :as agent] '[jj.bettong.capabilities :as caps])
(def assistant (agent/deepseek {:capabilities [(caps/filesystem)]})) (agent/run assistant "Create file /tmp/foo.txt with content 'foo'")
The agent loop: prompt in, tool calls executed, final answer out.
The agent starts with no abilities at all. What it can do is exactly what you
pass in :capabilities - see `jj.bettong.capability`.
(require '[jj.bettong.agent :as agent]
'[jj.bettong.capabilities :as caps])
(def assistant (agent/deepseek {:capabilities [(caps/filesystem)]}))
(agent/run assistant "Create file /tmp/foo.txt with content 'foo'")Something that can run a prompt to completion.
Implement it yourself when you want a stand-in - a canned agent for your own tests, one that caches, one that routes between models.
Something that can run a prompt to completion. Implement it yourself when you want a stand-in - a canned agent for your own tests, one that caches, one that routes between models.
(run this prompt)(run this prompt opts)Runs prompt and returns
{:output <final assistant text>
:steps <number of model calls>
:tool-calls [{:tool .. :capability .. :args .. :result ..} ...]
:messages <full transcript>
:usage {:requests .. :prompt-tokens .. :completion-tokens .. :total-tokens
:cache-hit-tokens .. :cache-miss-tokens .. :reasoning-tokens ..}
:done? <false if it hit :max-steps>}
opts overrides the agent's own configuration for this one call.
Runs `prompt` and returns
{:output <final assistant text>
:steps <number of model calls>
:tool-calls [{:tool .. :capability .. :args .. :result ..} ...]
:messages <full transcript>
:usage {:requests .. :prompt-tokens .. :completion-tokens .. :total-tokens
:cache-hit-tokens .. :cache-miss-tokens .. :reasoning-tokens ..}
:done? <false if it hit :max-steps>}
`opts` overrides the agent's own configuration for this one call.(approve-all _)The default approval hook: every tool call is allowed.
The default approval hook: every tool call is allowed.
(ask-in-terminal {:keys [tool args]})An :approve-fn that prints the tool call and waits for y/n on stdin. Anything other than y/yes denies the call.
An :approve-fn that prints the tool call and waits for y/n on stdin. Anything other than y/yes denies the call.
(clear! session)Forgets the conversation, keeping the agent. The next turn starts fresh.
Forgets the conversation, keeping the agent. The next turn starts fresh.
Everything an agent accepts, at construction or as a per-call override.
Everything an agent accepts, at construction or as a per-call override.
(deepseek)(deepseek config)An agent backed by DeepSeek's API. Configure it once, run it many times:
(def assistant (agent/deepseek {:api-key (System/getenv "DEEPSEEK_API_KEY") :model "deepseek-v4-flash" :capabilities [(caps/filesystem {:root "/srv/data"})]}))
(agent/run assistant "Summarise report.txt") (agent/run assistant "And again in French" {:max-steps 3}) ; per-call override
Options:
:capabilities what the agent may do. A seq of anything satisfying
jj.bettong.capability/Capability, including plain maps. Empty
by default: with no capabilities it has no tools and can only
talk back.
:api-key defaults to the DEEPSEEK_API_KEY environment variable
:model defaults to deepseek-v4-flash
:base-url defaults to https://api.deepseek.com - point it at any
OpenAI-compatible endpoint
:max-steps model calls before giving up (default 10)
:system replace the base system prompt; capability instructions are
still appended to it
:approve-fn (fn [{:keys [tool args capability]}]) -> anything truthy allows
the call; false/nil, or {:allowed? false :reason <string>},
denies it and hands the reason to the model
:on-event fn called with progress maps
:temperature passed through to the model
:chat-fn override the LLM call itself, for testing
An agent backed by DeepSeek's API. Configure it once, run it many times:
(def assistant
(agent/deepseek {:api-key (System/getenv "DEEPSEEK_API_KEY")
:model "deepseek-v4-flash"
:capabilities [(caps/filesystem {:root "/srv/data"})]}))
(agent/run assistant "Summarise report.txt")
(agent/run assistant "And again in French" {:max-steps 3}) ; per-call override
Options:
:capabilities what the agent may do. A seq of anything satisfying
`jj.bettong.capability/Capability`, including plain maps. Empty
by default: with no capabilities it has no tools and can only
talk back.
:api-key defaults to the DEEPSEEK_API_KEY environment variable
:model defaults to deepseek-v4-flash
:base-url defaults to https://api.deepseek.com - point it at any
OpenAI-compatible endpoint
:max-steps model calls before giving up (default 10)
:system replace the base system prompt; capability instructions are
still appended to it
:approve-fn (fn [{:keys [tool args capability]}]) -> anything truthy allows
the call; false/nil, or {:allowed? false :reason <string>},
denies it and hands the reason to the model
:on-event fn called with progress maps
:temperature passed through to the model
:chat-fn override the LLM call itself, for testing(parse-arguments arguments)Tool-call arguments arrive as a JSON string; decode to a keyword map.
Tool-call arguments arrive as a JSON string; decode to a keyword map.
(session agent)An agent that remembers. Each turn continues the transcript instead of starting one, so follow-ups work and the provider's cache keeps paying:
(def chat (agent/session assistant)) (agent/run chat "Describe Lithuania using wikipedia") (agent/run chat "Now in one sentence") ; knows what 'it' is
A session is itself an Agent, so anything that takes one takes this.
An agent that remembers. Each turn continues the transcript instead of starting one, so follow-ups work and the provider's cache keeps paying: (def chat (agent/session assistant)) (agent/run chat "Describe Lithuania using wikipedia") (agent/run chat "Now in one sentence") ; knows what 'it' is A session is itself an `Agent`, so anything that takes one takes this.
(session-usage session)What the conversation has cost in total, across every turn.
What the conversation has cost in total, across every turn.
(system-prompt capabilities override)The base prompt plus whatever the granted capabilities want the model to know.
The base prompt plus whatever the granted capabilities want the model to know.
(transcript session)Everything said so far, as the messages the API sees.
Everything said so far, as the messages the API sees.
cljdoc builds & hosts documentation for Clojure/Script libraries
| Ctrl+k | Jump to recent docs |
| ← | Move to previous article |
| → | Move to next article |
| Ctrl+/ | Jump to the search field |