-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathagents.qmd
More file actions
474 lines (357 loc) · 13.9 KB
/
agents.qmd
File metadata and controls
474 lines (357 loc) · 13.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
---
title: "Agent"
format:
html:
toc: true
execute:
echo: true
warning: false
error: false
---
```{r}
#| include: false
if (requireNamespace("pkgload", quietly = TRUE)) pkgload::load_all()
library(mini007)
# helper for examples; adjust your API key retrieval
retrieve_open_ai_credential <- function() {
Sys.getenv("OPENAI_API_KEY")
}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
```
### Creating an Agent
An Agent is built upon an LLM object created by the `ellmer` package, in the following examples, we'll work with the `OpenAI` models, however you can use any model/combination of models you want:
```{r}
# no need to provide the system prompt, it will be set when creating the
# agent (see the 'instruction' parameter)
retrieve_open_ai_credential <- function() {
Sys.getenv("OPENAI_API_KEY")
}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
```
After initializing the `ellmer` LLM object, creating the Agent is straightforward:
```{r}
polar_bear_researcher <- Agent$new(
name = "POLAR BEAR RESEARCHER",
instruction = "You are an expert in polar bears, you task is to collect information about polar bears. Answer in 1 sentence max.",
llm_object = openai_4_1_mini
)
```
Each created Agent has an `agent_id` (among other meta information):
```{r}
polar_bear_researcher$agent_id
```
At any time, you can tweak the `llm_object`:
```{r}
polar_bear_researcher$llm_object
```
An agent can provide the answer to a prompt using the `invoke` method:
```{r}
polar_bear_researcher$invoke("Are polar bears dangerous for humans?")
```
You can also retrieve a list that displays the history of the agent:
```{r}
polar_bear_researcher$messages
```
Or the `ellmer` way:
```{r}
polar_bear_researcher$llm_object
```
## Agents and Messages
#### Managing Agent Conversation History
The `clear_and_summarise_messages` method allows you to compress an agent's conversation history into a concise summary and clear the message history while preserving context. This is useful for maintaining memory efficiency while keeping important conversation context.
```{r}
# After several interactions, summarise and clear the conversation history
polar_bear_researcher$clear_and_summarise_messages()
polar_bear_researcher$messages
```
This method summarises all previous conversations into a paragraph and appends it to the system prompt, then clears the conversation history. The agent retains the context but with reduced memory usage.
#### Keep only the most recent messages with **keep_last_n_messages()**
When a conversation grows long, you can keep just the last N messages while preserving the system prompt. This helps control token usage without fully resetting context.
```{r}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "history_manager",
instruction = "You are a concise assistant.",
llm_object = openai_4_1_mini
)
agent$invoke("What is the capital of Italy?")
agent$invoke("What is the capital of Germany?")
agent$invoke("What is the capital of Algeria?")
agent$messages
```
```{r}
# Keep only the last 2 messages (system prompt is preserved)
agent$keep_last_n_messages(n = 2)
agent$messages
```
#### Manually Adding Messages to an Agent’s History
You can inject any message (system, user, or assistant) directly into an Agent’s history with `add_message(role, content)`. This is helpful to reconstruct, supplement, or simulate conversation steps.
- **add_message(role, content)**:
- `role`: "user", "assistant", or "system"
- `content`: The text message to add
```{r, eval=TRUE}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "Pizza expert",
instruction = "You are a Pizza expert",
llm_object = openai_4_1_mini
)
# Add a user message, an assistant reply, and a system instruction:
agent$add_message("user", "Where can I find the best pizza in the world?")
agent$add_message("assistant", "You can find the best pizza in the world in Algiers, Algeria. It's tasty and crunchy.")
# View conversation history
agent$messages
```
This makes it easy to reconstruct or extend sessions, provide custom context, or insert notes for debugging/testing purposes.
```{r, eval=TRUE}
agent$invoke("summarise the previous conversation")
```
#### Sharing context between agents
###### Available at the moment only in the development version
You can copy recent messages from one agent to another. The transferred entries
are added as user messages in the receiving agent.
```{r}
agent_1 <- Agent$new(
name = "first",
instruction = "You are a friendly bot",
llm_object = openai_4_1_mini
)
agent_2 <- Agent$new(
name = "second",
instruction = "You are another bot",
llm_object = openai_4_1_mini
)
agent_1$invoke("Hello there")
agent_1$invoke("How are you?")
# share the last two exchanges with a2
# messages arrive as 'user' entries
agent_1$share_context_with(agent_2, n = 2)
agent_2$messages
```
#### Sync between **messages** and **turns**
You can modify the `messages` object as you please, this will be automatically translated to the suitable `turns` required by `ellmer`:
```{r}
agent$messages[[5]]$content <- "Obivously you asked me about the best pizza in the world which is of course in Algiery!"
agent$messages
```
The underlying ellmer object:
```{r}
agent$llm_object
```
#### Resetting conversation history
If you want to clear the conversation while preserving the current system prompt, use `reset_conversation_history()`.
```{r}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "session_reset",
instruction = "You are an assistant.",
llm_object = openai_4_1_mini
)
agent$invoke("Tell me a short fun fact about dates (the fruit).")
agent$invoke("And one more.")
# Clear all messages except the system prompt
agent$reset_conversation_history()
agent$messages
```
#### Exporting and Loading Agent Conversation History
You can save an agent’s conversation history to a file and reload it later. This allows you to archive, transfer, or resume agent sessions across R sessions or machines.
- **export_messages_history(file_path)**: Saves the current conversation to a JSON file.
- **load_messages_history(file_path)**: Loads a saved conversation history from a JSON file, replacing the agent’s current history.
In both methods, if you omit the `file_path` parameter, a default file named `"<getwd()>/<agent_name>_messages.json"` is used.
```{r, eval=FALSE}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "session_agent",
instruction = "You are a persistent researcher.",
llm_object = openai_4_1_mini
)
# Interact with the agent
agent$invoke("Tell me something interesting about volcanoes.")
# Save the conversation
agent$export_messages_history("volcano_session.json")
# ...Later, or in a new session...
# Restore the conversation
agent$load_messages_history("volcano_session.json")
# agent$messages # Displays current history
```
#### Updating the system instruction during a session
Use `update_instruction(new_instruction)` to change the Agent’s system prompt mid-session. The first system message and the underlying `ellmer` system prompt are both updated.
```{r}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "reconfigurable",
instruction = "You are a helpful assistant.",
llm_object = openai_4_1_mini
)
agent$update_instruction("You are a strictly concise assistant. Answer in one sentence.")
agent$messages
```
## Budget and cost control
You can limit how much an `Agent` is allowed to spend and decide what should happen as the budget is approached or exceeded. Use `set_budget()` to define the maximum spend (in USD), and `set_budget_policy()` to control warnings and over-budget behavior.
- **set_budget(amount_in_usd)**: sets the absolute budget for the agent.
- **set_budget_policy(on_exceed, warn_at)**:
- **on_exceed**: one of `"abort"`, `"warn"`, or `"ask"`.
- **abort**: stop with an error when the budget is exceeded.
- **warn**: emit a warning and continue.
- **ask**: interactively ask what to do when the budget is exceeded.
- **warn_at**: a fraction in (0, 1); triggers a one-time warning when spending reaches that fraction of the budget (default `0.8`).
```{r}
# An API KEY is required to invoke the Agent
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
agent <- Agent$new(
name = "cost_conscious_assistant",
instruction = "Answer succinctly.",
llm_object = openai_4_1_mini
)
# Set a 5 USD budget
agent$set_budget(5)
# Warn at 90% of the budget and ask what to do if exceeded
agent$set_budget_policy(on_exceed = "ask", warn_at = 0.9)
# Normal usage
agent$invoke("Give me a one-sentence fun fact about Algeria.")
```
The current policy is echoed when setting the budget. You can update the policy at any time before or during an interaction lifecycle to adapt to your workflow's tolerance for cost overruns.
#### Inspecting usage and estimated cost
Call `get_usage_stats()` to retrieve the estimated cost, and budget information (if set).
```{r}
stats <- agent$get_usage_stats()
stats
```
## Generate and execute R code from natural language
`generate_execute_r_code()` lets an `Agent` translate a natural-language task description into R code, optionally validate its syntax, and (optionally) execute it.
- **code_description**: a plain-English description of the R code to generate.
- **validate**: `TRUE` to run a syntax validation step on the generated code first.
- **execute**: `TRUE` to execute the generated code (requires successful validation).
- **interactive**: if `TRUE`, shows the code and asks for confirmation before executing.
- **env**: environment where code will run when `execute = TRUE` (default `globalenv()`).
Safety notes:
- Set `validate = TRUE` and review the printed code before execution.
- Keep `interactive = TRUE` to require an explicit confirmation before running code.
```{r}
openai_4_1_mini <- ellmer::chat(
name = "openai/gpt-4.1-mini",
credentials = retrieve_open_ai_credential,
echo = "none"
)
r_assistant <- Agent$new(
name = "R Code Assistant",
instruction = "You are an expert R programmer.",
llm_object = openai_4_1_mini
)
agent$generate_execute_r_code(
code_description = "using ggplot2, generate a scatterplot of hwy and cty in red",
validate = TRUE,
execute = TRUE,
interactive = FALSE
)
```
## Cloning an Agent
If you want to create a new agent with the exact same characteristics, you can use the `clone_agent` method. Note that the new Agent can have the same name but it'll have a different `ID`:
```{r}
rai_agent <- Agent$new(
name = "Rai musician",
instruction = "You are an expert in Algerian Rai music",
llm_object = openai_4_1_mini
)
result <- rai_agent$invoke("Give me a rai song in 1 sentence. Don't explain")
rai_agent$agent_id
rai_agent$name
rai_agent$instruction
rai_agent$messages
```
```{r}
new_rai_agent <- rai_agent$clone_agent(new_name = "Just Rai")
new_rai_agent$agent_id
new_rai_agent$name
new_rai_agent$instruction
new_rai_agent$messages
```
## Response Validation
The `validate_response()` method provides intelligent LLM-based validation of
agent responses against custom criteria. This powerful feature uses the agent's own
LLM to evaluate whether a response meets specified validation standards, returning
both a score and detailed feedback.
### How it works
The method evaluates the response against your criteria. It returns a validation score (0-1) and determines if the response is valid based on your threshold.
### Parameters
- **prompt**: The original prompt that generated the response
- **response**: The response text to validate
- **validation_criteria**: Your validation requirements (e.g., "Must be factual and under 50 words")
- **validation_score**: Score threshold for validity (0-1, default 0.8)
#### Example 1: Factual Content Validation
```{r}
fact_checker <- Agent$new(
name = "fact_checker",
instruction = "You are a factual assistant.",
llm_object = openai_4_1_mini
)
prompt <- "What is the capital of Algeria?"
response <- fact_checker$invoke(prompt)
validation <- fact_checker$validate_response(
prompt = prompt,
response = response,
validation_criteria = "The response must be factually accurate and mention Algiers as the capital",
validation_score = 0.8
)
validation
```
#### Example 2: Content Length and Style Validation
```{r}
content_agent <- Agent$new(
name = "content_creator",
instruction = "You are a creative writing assistant.",
llm_object = openai_4_1_mini
)
prompt <- "Write a 1 sentence advertisment about an Algerian dates (the fruid)"
response <- content_agent$invoke(prompt)
validation <- content_agent$validate_response(
prompt = prompt,
response = response,
validation_criteria = "Response must be under 100 words, professional tone, and highlight Algerian dates",
validation_score = 0.75
)
validation
```
### Use Cases
- **Quality Control**: Validate responses meet content standards before
publication
- **Factual Accuracy**: Ensure responses contain correct information
- **Style Compliance**: Check responses follow tone, length, or format
requirements
- **Safety Filtering**: Validate content meets safety and appropriateness criteria
- **A/B Testing**: Compare response quality across different models or prompts
The validation results include the original prompt, response, criteria, score,
feedback, and validity status, making it easy to audit and improve your agent's
performance.