Class: Truffle::Agent
- Inherits:
-
Object
- Object
- Truffle::Agent
- Defined in:
- lib/truffle/agent.rb
Overview
A stateful agent: a provider, a system prompt, a running message history, and a toolbox. Calling #run drives the agent loop to completion.
The loop is the port of pi's agent-core runtime:
run(text)
emit :agent_start
append user message
loop:
emit :turn_start
response = provider.chat(messages, tools)
append assistant message; emit :message
if response has tool calls:
for each call: emit :tool_call, run tool, append tool result,
emit :tool_result
emit :turn_end ; continue # feed results back to the model
else:
emit :turn_end ; emit :agent_end ; return assistant text
Events let a UI (TUI, web, logs) observe the run without the harness knowing anything about how it is rendered. Subscribe with #on.
Constant Summary collapse
- DEFAULT_MAX_TURNS =
12- EVENTS =
%i[agent_start turn_start message tool_call tool_result turn_end agent_end].freeze
Instance Attribute Summary collapse
-
#max_turns ⇒ Object
readonly
Returns the value of attribute max_turns.
-
#messages ⇒ Object
readonly
Returns the value of attribute messages.
-
#provider ⇒ Object
readonly
Returns the value of attribute provider.
-
#system_prompt ⇒ Object
readonly
Returns the value of attribute system_prompt.
-
#toolbox ⇒ Object
readonly
Returns the value of attribute toolbox.
Instance Method Summary collapse
-
#initialize(provider:, system_prompt: nil, tools: [], model: nil, max_turns: DEFAULT_MAX_TURNS) ⇒ Agent
constructor
A new instance of Agent.
-
#on(event = nil, &block) ⇒ Object
Register a listener.
-
#reset ⇒ Object
Reset history back to just the system prompt (keeps tools + listeners).
-
#run(user_input) ⇒ Object
Send a user message and run the loop until the model answers without requesting a tool.
Constructor Details
#initialize(provider:, system_prompt: nil, tools: [], model: nil, max_turns: DEFAULT_MAX_TURNS) ⇒ Agent
Returns a new instance of Agent.
32 33 34 35 36 37 38 39 40 41 42 43 |
# File 'lib/truffle/agent.rb', line 32 def initialize(provider:, system_prompt: nil, tools: [], model: nil, max_turns: DEFAULT_MAX_TURNS) @provider = provider @system_prompt = system_prompt @model = model @max_turns = max_turns @toolbox = tools.is_a?(Toolbox) ? tools : Toolbox.new(tools) @listeners = Hash.new { |h, k| h[k] = [] } @messages = [] @messages << Message.system(system_prompt) if system_prompt end |
Instance Attribute Details
#max_turns ⇒ Object (readonly)
Returns the value of attribute max_turns.
30 31 32 |
# File 'lib/truffle/agent.rb', line 30 def max_turns @max_turns end |
#messages ⇒ Object (readonly)
Returns the value of attribute messages.
30 31 32 |
# File 'lib/truffle/agent.rb', line 30 def @messages end |
#provider ⇒ Object (readonly)
Returns the value of attribute provider.
30 31 32 |
# File 'lib/truffle/agent.rb', line 30 def provider @provider end |
#system_prompt ⇒ Object (readonly)
Returns the value of attribute system_prompt.
30 31 32 |
# File 'lib/truffle/agent.rb', line 30 def system_prompt @system_prompt end |
#toolbox ⇒ Object (readonly)
Returns the value of attribute toolbox.
30 31 32 |
# File 'lib/truffle/agent.rb', line 30 def toolbox @toolbox end |
Instance Method Details
#on(event = nil, &block) ⇒ Object
Register a listener. on(:tool_call) { |payload| ... } for one event, or
on { |type, payload| ... } (no event arg) for every event.
47 48 49 50 51 52 53 54 55 56 57 58 59 60 |
# File 'lib/truffle/agent.rb', line 47 def on(event = nil, &block) raise ArgumentError, "on requires a block" unless block if event.nil? @listeners[:_all] << block else event = event.to_sym unless EVENTS.include?(event) raise ArgumentError, "unknown event #{event.inspect}, expected one of #{EVENTS.inspect}" end @listeners[event] << block end self end |
#reset ⇒ Object
Reset history back to just the system prompt (keeps tools + listeners).
97 98 99 100 101 |
# File 'lib/truffle/agent.rb', line 97 def reset @messages = [] @messages << Message.system(@system_prompt) if @system_prompt self end |
#run(user_input) ⇒ Object
Send a user message and run the loop until the model answers without requesting a tool. Returns the final assistant text.
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 |
# File 'lib/truffle/agent.rb', line 64 def run(user_input) emit(:agent_start, input: user_input) @messages << Message.user(user_input) final_text = nil turns = 0 loop do turns += 1 if turns > max_turns raise Error, "exceeded max_turns (#{max_turns}) without a final answer" end emit(:turn_start, turn: turns) response = @provider.chat(messages: @messages, tools: @toolbox.to_schema, model: @model) @messages << response. emit(:message, message: response., usage: response.usage) unless response.tool_calls? final_text = response.text emit(:turn_end, turn: turns, tool_results: []) break end tool_results = run_tool_calls(response.tool_calls) emit(:turn_end, turn: turns, tool_results: tool_results) end emit(:agent_end, output: final_text, messages: @messages) final_text end |