Getting Started with AI Agents
ai-agents.RmdMattermostR provides a complete R interface to the Mattermost Agents v2.0 plugin, letting you discover, configure, and invoke AI agents (Grok, Gemini, Perplexity, and others) directly from R.
This vignette covers:
- How the plugin transport works
- Discovering agents on the server
- Creating agents with v2.0 configuration options
- Invoking agents for LLM completions
- Multi-turn conversations
- Best practices for automated pipelines
Prerequisites
You need a Mattermost server (v11.5+) with the Agents plugin installed and at least one LLM service configured by your server administrator.
library(MattermostR)
# Authenticate (or set MATTERMOST_TOKEN and MATTERMOST_URL env vars)
auth <- authenticate_mattermost(
base_url = "https://your-mattermost.example.com",
token = "your-token"
)Architecture: How the Plugin Transport Works
MattermostR talks to the Agents plugin through the same HTTP stack
used for the core API. Under the hood, plugin_api_request()
prefixes every request with
/plugins/<plugin_id>/api/v1, then delegates to
mattermost_api_request() — inheriting rate-limiting, retry
with exponential backoff, and structured mattermost_error
conditions for free.
┌──────────────────────────────┐
│ invoke_agent() │ ← User function
│ create_agent(), list_agents()│
└──────────┬───────────────────┘
│
▼
┌──────────────────────────────┐
│ plugin_api_request() │ ← Constructs plugin path
│ /plugins/<id>/api/v1/... │
└──────────┬───────────────────┘
│
▼
┌──────────────────────────────┐
│ mattermost_api_request() │ ← Core HTTP handler
│ Rate-limit, retry, errors │
└──────────────────────────────┘
The plugin ID defaults to "mattermost-ai-agents" and can
be overridden system-wide:
# Check the current plugin ID
get_agents_plugin_id()
# Override if your server uses a different plugin identifier
options(MattermostR.agents_plugin_id = "com.mattermost.plugin-ai")Discovering Agents
Use list_agents() to retrieve all configured agents on
the server:
agents <- list_agents(auth = auth)
print(agents)
#> id bot_user_id display_name model reasoning_enabled
#> 1 agent_abc1 bot_user_001 Grok grok-3 TRUE
#> 2 agent_abc2 bot_user_002 Gemini gemini-2.0-f. FALSE
#> 3 agent_abc3 bot_user_003 Perplexity sonar-pro-... TRUERetrieve a single agent’s full configuration:
agent <- get_agent("agent_abc1", auth = auth)
agent$display_name
#> [1] "Grok"
agent$reasoning_enabled
#> [1] TRUE
agent$model
#> [1] "grok-3"Creating an Agent
Create an agent by binding it to an existing bot account. The v2.0 schema supports reasoning controls, tool policy, vision, and structured output:
# First, create a bot (if one doesn't already exist)
bot <- create_bot(
username = "quant-copilot",
display_name = "Quant Copilot",
auth = auth
)
# Then create an agent using that bot
agent <- create_agent(
bot_user_id = bot$user_id,
display_name = "Quant Copilot",
instructions = paste(
"You are a quantitative finance assistant.",
"Explain financial implications in 3 short bullet points.",
"Be cynical and objective. Format in Markdown."
),
model = "grok-3",
reasoning_enabled = TRUE,
reasoning_effort = "medium",
disable_tools = TRUE, # Recommended for automated pipelines
auth = auth
)Key Parameters
| Parameter | Default | Description |
|---|---|---|
reasoning_enabled |
TRUE |
Enable extended thinking (longer but deeper responses) |
reasoning_effort |
NULL |
"low", "medium", or
"high" (provider-specific) |
thinking_budget |
NULL |
Token budget for the reasoning phase |
disable_tools |
FALSE |
Disable MCP tool calls — set to
TRUE for headless automation
|
enable_vision |
FALSE |
Enable image understanding |
structured_output_enabled |
FALSE |
Enable JSON-formatted output |
Note: On some LLM providers (notably Anthropic),
structured_output_enabledandreasoning_enabledare mutually exclusive. The server will reject the request if both areTRUE.
Invoking an Agent
Send a prompt and receive a synchronous LLM completion:
response <- invoke_agent(
bot_user_id = agent$bot_user_id,
message = "What is the current Fed funds rate and how does it affect tech valuations?",
auth = auth
)
cat(response$message)
#> - **Fed funds rate**: 4.25–4.50% (unchanged since Dec 2024). Markets
#> pricing 2 cuts in H2 2025.
#> - **Tech impact**: Higher discount rates compress growth stock DCFs;
#> mega-cap P/E multiples remain historically elevated at ~30x.
#> - **Contrarian view**: Real rates are restrictive but earnings growth
#> (AI capex cycle) may sustain valuations through 2025.Custom System Prompt
Override the agent’s default instructions for a single request:
response <- invoke_agent(
bot_user_id = agent$bot_user_id,
message = "Summarize the ECB rate decision impact on EUR/USD",
system_prompt = "You are a macro FX strategist. Be extremely concise. Max 2 sentences.",
auth = auth
)
cat(response$message)
#> ECB cut 25bp to 3.50%; EUR/USD dropped 40 pips to 1.0820 on dovish
#> forward guidance. Yield differential widening favours USD into Q3.Extended Timeout for Reasoning Models
LLM completions with reasoning enabled can take 30–120+ seconds. The
default timeout = 120 seconds handles most cases, but
increase it for complex prompts:
response <- invoke_agent(
bot_user_id = agent$bot_user_id,
message = "Build a simplified DCF model for Boeing with 5-year projections.",
timeout = 300, # 5 minutes for complex reasoning
auth = auth
)Multi-Turn Conversations
Provide prior conversation context to enable follow-up questions:
# First turn
response1 <- invoke_agent(
bot_user_id = agent$bot_user_id,
message = "What are the key risks for European banks in 2025?",
auth = auth
)
# Second turn — provide the prior exchange as context
response2 <- invoke_agent(
bot_user_id = agent$bot_user_id,
message = "Which specific banks have the highest CRE exposure?",
context = list(
list(role = "user", content = "What are the key risks for European banks in 2025?"),
list(role = "assistant", content = response1$message)
),
auth = auth
)
cat(response2$message)Managing Agents
Update an agent
Only the provided fields are modified; everything else is preserved:
update_agent(
agent_id = agent$id,
model = "grok-3-mini", # Switch to a faster model
instructions = "Be brief. Max 1 sentence per point.",
auth = auth
)Delete an agent
This removes the agent configuration. The underlying bot account is unaffected:
delete_agent(agent_id = agent$id, auth = auth)Best Practices for Automated Pipelines
When calling invoke_agent() from Airflow DAGs, cron
jobs, or other headless automation, follow these guidelines:
1. Disable tools
Agents v2.0 supports MCP (Model Context Protocol) tool calling. Some tools require interactive UI approval — a human clicking “Accept” in the Mattermost chat interface. In a headless script, this causes the request to hang indefinitely.
Always create pipeline agents with
disable_tools = TRUE:
pipeline_agent <- create_agent(
bot_user_id = bot$user_id,
display_name = "Pipeline Analyst",
instructions = "Summarize financial news in 3 bullet points. Be cynical.",
model = "grok-3",
disable_tools = TRUE,
auth = auth
)2. Set generous timeouts
Reasoning models (the v2.0 default) think before they respond. Budget at least 120 seconds for standard prompts and 300+ for complex analytical queries:
response <- invoke_agent(
bot_user_id = pipeline_agent$bot_user_id,
message = news_body,
timeout = 180,
auth = auth
)3. Handle the full response
invoke_agent() returns the complete parsed JSON
response, not just the message text. This future-proofs your
code against new fields the plugin may add (tool results, token counts,
metadata):
response <- invoke_agent(
bot_user_id = pipeline_agent$bot_user_id,
message = "Analyze this earnings report.",
auth = auth
)
# The text response
analysis <- response$message
# Post it as a threaded reply in Mattermost
send_mattermost_message(
channel_id = channel_id,
message = analysis,
root_id = parent_post_id,
auth = auth
)4. Forward-compatible experimental fields
The invoke_agent() function accepts ... for
passing experimental or newly-added plugin API fields without waiting
for a package update:
# If the plugin adds a 'conversation_id' field in a future version:
response <- invoke_agent(
bot_user_id = pipeline_agent$bot_user_id,
message = "Continue where we left off.",
conversation_id = "conv_abc123",
auth = auth
)Plugin ID Configuration
Different Mattermost versions or custom installations may use different plugin identifiers. MattermostR resolves the plugin ID from:
-
getOption("MattermostR.agents_plugin_id")— if set - Default:
"mattermost-ai-agents"
To discover the actual plugin ID on your server:
# List all installed plugins
plugins <- mattermost_api_request(auth, "/api/v4/plugins", method = "GET")
# Find the agents plugin
agents_plugins <- Filter(
function(p) grepl("agent|ai", p$id, ignore.case = TRUE),
plugins$active
)Summary
| Function | Purpose |
|---|---|
get_agents_plugin_id() |
Resolve the plugin identifier |
list_agents() |
Discover all configured agents |
get_agent() |
Get a single agent’s configuration |
create_agent() |
Create an agent with v2.0 schema |
update_agent() |
Partially update an agent |
delete_agent() |
Remove an agent |
invoke_agent() |
Send a prompt and get an LLM completion |