Class: Phronomy::Context::Assembler

Inherits:
Object
  • Object
show all
Defined in:
lib/phronomy/context/assembler.rb

Overview

Assembler collects all four context regions and produces the final messages: hash consumed by Agent::Base.

Regions:

  1. Instruction — system prompt text set via #add_instruction
  2. Capability — tool definitions (handled by RubyLLM, not here)
  3. Knowledge — external facts injected via #add_knowledge (generates XML tags)
  4. Conversation — historical messages added via #add_messages

Token budgeting: When a budget is given, conversation messages are trimmed from oldest to newest until they fit. Knowledge chunks are always included in full (they are assumed to be pre-screened by the caller). When no budget is given all messages are passed through unchanged.

Examples:

assembler = Phronomy::Context::Assembler.new(budget: budget)
assembler.add_instruction("You are a helpful assistant.")
assembler.add_knowledge("The user lives in Tokyo.", type: :entity, trusted: false)
assembler.add_messages(manager.load(thread_id: "t1", query: user_input))
context = assembler.build
# => { system: "You are ...\n<context ...>...</context>", messages: [...] }

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(budget: nil) ⇒ Assembler

Returns a new instance of Assembler.

Parameters:



42
43
44
45
46
47
# File 'lib/phronomy/context/assembler.rb', line 42

def initialize(budget: nil)
  @budget = budget
  @instruction = nil
  @knowledge_chunks = []
  @messages = []
end

Class Method Details

.xml_tag(text, type:, trusted: false) ⇒ String

Builds a single XML context tag string. Exposed as a class method so callers (e.g. Agent::Base) can build static knowledge XML tags independently of an Assembler instance.

Parameters:

  • text (String)
  • type (Symbol, String)
  • trusted (Boolean) (defaults to: false)

Returns:

  • (String)


36
37
38
# File 'lib/phronomy/context/assembler.rb', line 36

def self.xml_tag(text, type:, trusted: false)
  "<context type=\"#{type}\" trusted=\"#{trusted}\">\n#{text}\n</context>"
end

Instance Method Details

#add_instruction(text) ⇒ self

Set the system instruction text (Region 1). Calling this multiple times replaces the previous value.

Parameters:

  • text (String)

Returns:

  • (self)


54
55
56
57
# File 'lib/phronomy/context/assembler.rb', line 54

def add_instruction(text)
  @instruction = text.to_s
  self
end

#add_knowledge(text, type:, trusted: false, source: nil) ⇒ self

Append a knowledge chunk (Region 3). The chunk is wrapped in an XML context tag automatically.

Parameters:

  • text (String)
  • type (Symbol, String)

    semantic label for the context tag (e.g. :entity, :rag, :static)

  • trusted (Boolean) (defaults to: false)

    false (default) indicates externally sourced data

  • source (String, nil) (defaults to: nil)

    optional source label (e.g. filename); included in the XML tag so the LLM can produce grounded citations. Omitted when nil.

Returns:

  • (self)


68
69
70
71
# File 'lib/phronomy/context/assembler.rb', line 68

def add_knowledge(text, type:, trusted: false, source: nil)
  @knowledge_chunks << {text: text.to_s, type: type.to_s, trusted: trusted, source: source}
  self
end

#add_messages(messages) ⇒ self

Set conversation messages (Region 4). Replaces any previously set messages.

Parameters:

  • messages (Array)

    message-like objects with #role and #content

Returns:

  • (self)


77
78
79
80
# File 'lib/phronomy/context/assembler.rb', line 77

def add_messages(messages)
  @messages = Array(messages)
  self
end

#buildHash{Symbol => Object}

Assemble the context.

Returns:

  • (Hash{Symbol => Object})

    :system [String, nil] combined system prompt (instruction + knowledge XML tags) :messages [Array] conversation messages, trimmed to budget if set



87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'lib/phronomy/context/assembler.rb', line 87

def build
  knowledge_text = @knowledge_chunks.map { |c| xml_context_tag(c) }.join("\n\n")
  system_parts = [@instruction, knowledge_text.empty? ? nil : knowledge_text].compact
  system_text = system_parts.join("\n\n")

  messages = if @budget
    trim_messages_to_budget(@messages, system_text)
  else
    @messages
  end

  {
    system: system_text.empty? ? nil : system_text,
    messages: messages
  }
end