Skip to contents

deepagentsr provides a small Deep-Agents-style harness for R. A DeepAgent wraps a model, R tools, a virtual filesystem, subagents, memory, human approval, and execution events behind one entry point: create_deep_agent().

This vignette uses fake_chat() so it can run without a network connection. Use the same agent configuration with an ellmer chat object when you want live model calls.

A deterministic first agent

Tools are ordinary R functions wrapped with deep_tool(). If you do not provide an argument schema, deepagentsr infers one from the function formals and converts it to the provider-specific ellmer schema when needed.

library(deepagentsr)

calculator <- deep_tool(
  function(x, y) x + y,
  name = "add",
  description = "Add two numbers."
)

agent <- create_deep_agent(
  model = fake_chat(list(
    assistant_tool_call("write_todos", list(items = list("Call add", "Answer"))),
    assistant_tool_call("add", list(x = 19, y = 23)),
    assistant_message("The result is 42.")
  )),
  tools = list(calculator)
)

result <- agent$invoke("Add 19 and 23.")
result[c("status", "text")]
#> $status
#> [1] "completed"
#> 
#> $text
#> [1] "The result is 42."

Every run produces structured events. These events are useful for tests, logging, UI adapters, and JSONL traces.

vapply(result$events, function(event) event$type, character(1))
#> [1] "agent_start"   "tool_request"  "todo_update"   "tool_result"  
#> [5] "tool_request"  "tool_result"   "model_message" "agent_end"

The thread state includes turns, todos, pending interrupts, summaries, and other runtime data.

state <- agent$get_state(result$thread_id)
state$status
#> [1] "completed"
state$todos
#> [[1]]
#> [1] "Call add"
#> 
#> [[2]]
#> [1] "Answer"

Use OpenAI through ellmer

For live calls, pass an ellmer chat object or a model factory. The package does not own provider clients directly; it registers tools with ellmer and lets ellmer handle OpenAI, Anthropic, Ollama, and other providers.

library(ellmer)
library(deepagentsr)

# Optional for local development when OPENAI_API_KEY is stored in .env.
readRenviron(".env")

calculator <- deep_tool(
  function(x, y) x + y,
  name = "add",
  description = "Add two numbers."
)

agent <- create_deep_agent(
  model = ellmer::chat_openai(model = "gpt-4.1", echo = "none"),
  tools = list(calculator),
  system_prompt = "You are a concise R assistant."
)

agent$invoke("Use the add tool to calculate 19 + 23.")

The guarded live test suite has been run against GPT 4.1 and GPT 5 family models, including gpt-4.1, gpt-4.1-mini, gpt-4.1-nano, gpt-5, gpt-5-mini, gpt-5-nano, gpt-5.1, and gpt-5.2, subject to model access on the configured OpenAI account.

Streaming and async entry points

agent$stream() returns execution events for a run. When coro is installed it returns a generator; otherwise it returns a list-like event stream.

stream_agent <- create_deep_agent(
  model = fake_chat(list(assistant_message("streamed response")))
)

stream <- stream_agent$stream("hello")
events <- if (requireNamespace("coro", quietly = TRUE) &&
  inherits(stream, "coro_generator_instance")) {
  coro::collect(stream)
} else {
  stream
}

vapply(events, function(event) event$type, character(1))
#> [1] "agent_start"   "model_message" "agent_end"

agent$stream_async() is available when promises is installed and resolves to the same event stream.

Runtime context and middleware

Use context_schema to require caller-provided runtime fields. Use middleware callbacks to observe every emitted event.

seen <- character()

context_agent <- create_deep_agent(
  model = fake_chat(list(assistant_message("ok"))),
  context_schema = list(user_id = "character"),
  middleware = list(function(event) {
    seen <<- c(seen, event$type)
  })
)

context_run <- context_agent$invoke(
  "hello",
  context = list(user_id = "user-123")
)

context_run$status
#> [1] "completed"
seen
#> [1] "agent_start"   "model_message" "agent_end"