Class: Phronomy::Agent::ReactAgent
- Defined in:
- lib/phronomy/agent/react_agent.rb
Overview
ReAct pattern (Reasoning + Acting) agent. Repeats the LLM <-> Tool loop until no more tool calls are made.
Instance Attribute Summary
Attributes inherited from Base
Instance Method Summary collapse
-
#stream(input, config: {}) {|Phronomy::Agent::StreamEvent| ... } ⇒ Hash
Streaming version of #invoke for the ReAct loop.
Methods inherited from Base
#_add_handoff_tool, _before_completion, #_handoff_tools, _on_compact_callback, _on_compaction_trigger_callback, _on_trim_callback, #add_input_guardrail, #add_output_guardrail, before_completion, cache_instructions, context_overhead, context_window, instructions, #invoke, max_iterations, max_output_tokens, model, #on_approval_required, on_compact, on_compaction_trigger, on_trim, provider, retry_policy, static_knowledge, static_knowledge_sources, temperature, tool_aliases, tools
Methods included from Runnable
Instance Method Details
#stream(input, config: {}) {|Phronomy::Agent::StreamEvent| ... } ⇒ Hash
Streaming version of #invoke for the ReAct loop. Yields StreamEvent events while the LLM-tool loop runs.
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 |
# File 'lib/phronomy/agent/react_agent.rb', line 73 def stream(input, config: {}, &block) return invoke(input, config: config) unless block = {} [:user_id] = config[:user_id] if config[:user_id] [:session_id] = config[:session_id] if config[:session_id] trace("agent.invoke", input: input, **) do |_span| run_input_guardrails!(input) memory = config[:memory] thread_id = config[:thread_id] max_iter = self.class.max_iterations = if memory && thread_id load_from_memory(memory, thread_id: thread_id, query: (input)) else [] end = .dup user_asked = false total_usage = Phronomy::TokenUsage.zero iterations_exhausted = true max_iter.times do response = stream_step(, input, user_asked: user_asked, config: config, &block) user_asked = true = response[:messages] total_usage += response[:usage] if response[:done] iterations_exhausted = false break end end save_to_memory(memory, thread_id: thread_id, messages: ) if memory && thread_id # Fall back to the last message that carries non-nil content (same as # the non-streaming path above). output = .reverse.find { |m| m.content && !m.content.empty? }&.content run_output_guardrails!(output) result = {output: output, messages: , usage: total_usage, iterations_exhausted: iterations_exhausted} block.call(StreamEvent.new(type: :done, payload: result)) [result, total_usage] end rescue => e block&.call(StreamEvent.new(type: :error, payload: {error: e})) raise end |