Skip to contents

Rbebelm is organized around persistent agents. A BebelAgent keeps token history, generation settings, and decode caches across turns. Tools are layered on top of agents: the model emits a BebeLM tool-call block, R parses the call, runs the matching R function, appends the tool result, and continues generation.

Agent state

library(Rbebelm)
model <- bebel_model_load(Sys.getenv("BEBELM_WEIGHTS_FILE"), num_threads = 2)
agent <- bebel_agent(model, greedy = TRUE, max_gen = 48, max_think = 16)

bebel_append_user(agent, "Say exactly: Paris noted.")
turn1 <- bebel_assistant_turn(agent, on_event = NULL)

bebel_append_user(agent, "Say exactly: second turn complete.")
turn2 <- bebel_assistant_turn(agent, on_event = NULL)

bebel_agent_info(agent)[c("history_tokens", "processed_tokens", "kv_tokens")]
#> $history_tokens
#> [1] 82
#> 
#> $processed_tokens
#> [1] 80
#> 
#> $kv_tokens
#> [1] 80

# Direct methods are available on the agent object.
length(agent$history())
#> [1] 82
substr(agent$transcript(), 1, 80)
#> [1] "<|startoftext|><|im_start|>user\nSay exactly: Paris noted.<|im_end|>\n<|im_start|>"

# Helper functions provide the same operations.
length(bebel_history(agent))
#> [1] 82
substr(bebel_transcript(agent), 1, 80)
#> [1] "<|startoftext|><|im_start|>user\nSay exactly: Paris noted.<|im_end|>\n<|im_start|>"

# Reset the conversation while keeping the loaded weights and generation settings.
agent$clear()[c("history_tokens", "processed_tokens", "kv_tokens")]
#> $history_tokens
#> [1] 0
#> 
#> $processed_tokens
#> [1] 0
#> 
#> $kv_tokens
#> [1] 0

Use bebel_append_system() for a ChatML system-role instruction. Raw appends do not add user framing, so the low-level bebel_append() form below is equivalent apart from being more explicit about the tokens.

system_agent <- bebel_agent(model)
bebel_append_system(system_agent, "You are concise.")
bebel_transcript(system_agent)
#> [1] "<|startoftext|><|im_start|>system\nYou are concise.<|im_end|>\n"

raw_system_agent <- bebel_agent(model)
bebel_append(raw_system_agent, "<|im_start|>system\nYou are concise.<|im_end|>\n")
identical(bebel_transcript(system_agent), bebel_transcript(raw_system_agent))
#> [1] TRUE

raw_agent <- bebel_agent(model, greedy = TRUE, max_gen = 16, max_think = 0)
bebel_append(raw_agent, "The capital of Mali is")
raw_turn <- bebel_agent_generate(raw_agent, on_event = NULL)
raw_turn[c("stop", "generated_tokens")]
#> $stop
#> [1] "max_new"
#> 
#> $generated_tokens
#> [1] 16

ids <- bebel_tokenize(model, " and Italy is", add_bos = FALSE)
bebel_append_tokens(raw_agent, ids)
bebel_history(raw_agent)[1:8]
#> [1] 124894    597   5205    302  46628    355  50593   6261

Tool definitions

A tool is an R function plus metadata. The context environment is private to R and is not appended to the model transcript.

library(Rbebelm)
ctx <- new.env(parent = emptyenv())
ctx$thread_id <- "thread-001"
ctx$log <- character()

tools <- list(
  lookup_capital = bebel_tool(
    "lookup_capital",
    function(args, context, call) {
      context$log <- c(context$log, paste("tool", call$name, args$country))
      c(Mali = "Bamako", Italy = "Rome")[[args$country]]
    },
    description = "Return a capital city for a country."
  )
)

tools$lookup_capital
#> <bebelTool> lookup_capital 
#>   Return a capital city for a country.

The default parser accepts JSON calls, simple function calls, and BebeLM’s bracketed tool-call form.

bebel_parse_tool_call('{"name":"lookup_capital","arguments":{"country":"Italy"}}')
#> $name
#> [1] "lookup_capital"
#> 
#> $arguments
#> $arguments$country
#> [1] "Italy"
#> 
#> 
#> $raw
#> [1] "{\"name\":\"lookup_capital\",\"arguments\":{\"country\":\"Italy\"}}"
bebel_parse_tool_call('lookup_capital({"country":"Italy"})')
#> $name
#> [1] "lookup_capital"
#> 
#> $arguments
#> $arguments$country
#> [1] "Italy"
#> 
#> 
#> $raw
#> [1] "lookup_capital({\"country\":\"Italy\"})"
bebel_parse_tool_call('[lookup_capital(country="Italy")]')
#> $name
#> [1] "lookup_capital"
#> 
#> $arguments
#> $arguments$country
#> [1] "Italy"
#> 
#> 
#> $raw
#> [1] "[lookup_capital(country=\"Italy\")]"

Run a tool loop

bebel_agent_run() dispatches tools only when generation emits a BebeLM tool_call_end event. The prompt below asks directly for the tool-call form so the example exercises the dispatch path.

hooks <- list(
  tool_request = function(call, context, ...) {
    context$log <- c(context$log, paste("request", call$name))
  },
  tool_result = function(call, result, context, ...) {
    context$log <- c(context$log, paste("result", call$name, result))
  }
)

tool_prompt <- paste(
  "Return exactly this tool call and no other text:",
  "lookup_capital({\"country\":\"Italy\"})"
)

agent <- bebel_agent(model, greedy = TRUE, max_gen = 64, max_think = 0)
bebel_append_user(agent, tool_prompt)
run <- bebel_agent_run(agent, tools = tools, context = ctx, hooks = hooks, max_steps = 2)

length(run$tool_calls)
#> [1] 1
ctx$log
#> [1] "request lookup_capital"     "tool lookup_capital Italy" 
#> [3] "result lookup_capital Rome"

If a model uses a different tool-call format, pass a custom parse_tool_call function to bebel_agent_run().