Packages

Lux is a powerful framework for building and orchestrating LLM-powered agent workflows. It provides a robust set of tools for creating, managing, and coordinating AI agents in complex business processes.

Current section

Files

Jump to
lux lib lux reflection.ex
Raw

lib/lux/reflection.ex

defmodule Lux.Reflection do
@moduledoc """
A Reflection represents a Agent's decision-making process and self-awareness.
It can evolve over time as the Agent learns and adapts to new situations.
"""
@type t :: %__MODULE__{
id: String.t(),
name: String.t(),
description: String.t(),
context: map(),
llm_config: map(),
history: [map()],
last_reflection_time: DateTime.t() | nil,
# Learned patterns and behaviors
patterns: [map()],
# Performance metrics
metrics: map(),
state: :idle | :reflecting | :learning | :adapting
}
defstruct id: nil,
name: "",
description: "",
context: %{},
llm_config: %{
provider: :openai,
model: "gpt-4",
temperature: 0.7,
max_tokens: 1000
},
history: [],
last_reflection_time: nil,
patterns: [],
metrics: %{
successful_actions: 0,
failed_actions: 0,
avg_response_time: 0,
learning_rate: 0.1,
total_reflections: 0,
total_actions: 0
},
state: :idle
@doc """
Creates a new reflection module.
"""
def new(attrs \\ %{}) do
struct(
__MODULE__,
Map.merge(attrs, %{
id: attrs[:id] || Lux.UUID.generate(),
last_reflection_time: DateTime.utc_now()
})
)
end
@doc """
Performs a reflection cycle for a agent, deciding on next actions.
"""
def reflect(%__MODULE__{} = reflection, %Lux.Agent{} = agent, context) when is_map(context) do
reflection = %{reflection | state: :reflecting}
prompt = build_reflection_prompt(reflection, agent, context)
case call_llm(prompt, reflection.llm_config) do
{:ok, response} ->
try do
{actions, new_patterns} = parse_reflection_response(response)
updated_reflection =
reflection
|> update_history(response)
|> update_patterns(new_patterns)
|> update_metrics(actions)
|> Map.put(:last_reflection_time, DateTime.utc_now())
|> Map.put(:state, :idle)
{:ok, actions, updated_reflection}
rescue
error -> {:error, error, %{reflection | state: :idle}}
end
end
end
def reflect(%__MODULE__{} = reflection, _agent, _context) do
{:error, :invalid_context, %{reflection | state: :idle}}
end
@doc """
Updates the reflection's context with new information.
"""
def update_context(%__MODULE__{} = reflection, new_context) do
merged_context = Map.merge(reflection.context, new_context)
%{reflection | context: merged_context}
end
@doc """
Analyzes the reflection's history to identify patterns and improve decision making.
"""
def learn(%__MODULE__{} = reflection) do
reflection = %{reflection | state: :learning}
# Analyze history and update patterns
new_patterns = analyze_history(reflection.history)
# Update metrics based on learning
new_metrics = update_learning_metrics(reflection.metrics, new_patterns)
%{reflection | patterns: new_patterns, metrics: new_metrics, state: :idle}
end
# Private helpers
defp build_reflection_prompt(reflection, agent, context) do
"""
You are #{agent.name}'s reflection process.
Your goal is to help achieve: #{agent.goal}
Current context:
#{inspect(context)}
Recent history:
#{format_history(reflection.history)}
Learned patterns:
#{format_patterns(reflection.patterns)}
Performance metrics:
#{inspect(reflection.metrics)}
Based on this information, what actions should the agent take?
Respond in the following JSON format:
{
"reflection": {
"thoughts": "your reasoning process",
"patterns_identified": [],
"improvement_suggestions": []
},
"actions": [
{
"type": "prism|beam|lens",
"name": "action_name",
"params": {},
"expected_outcome": "description"
}
]
}
"""
end
# This is here so that the compiler doesn't complain about the SamplePrism
# not being an existing atom. It will be replaced once we implemt call_llm
SamplePrism
defp call_llm(_prompt, _config) do
# TODO: Implement actual LLM call
{:ok,
%{
"reflection" => %{
"thoughts" => "Based on past patterns and current context...",
"patterns_identified" => ["Pattern1", "Pattern2"],
"improvement_suggestions" => ["Suggestion1"]
},
"actions" => [
%{
"type" => "prism",
"name" => "SamplePrism",
"params" => %{},
"expected_outcome" => "Expected result"
}
]
}}
end
defp parse_reflection_response(response) do
actions =
Enum.map(response["actions"], fn action ->
{String.to_existing_atom("Elixir.#{action["name"]}"), action["params"]}
end)
patterns = response["reflection"]["patterns_identified"]
{actions, patterns}
end
defp update_history(reflection, response) do
new_history_entry = %{
timestamp: DateTime.utc_now(),
reflection: response["reflection"],
actions: response["actions"]
}
# Keep last 100 reflections
updated_history = Enum.take([new_history_entry | reflection.history], 100)
%{reflection | history: updated_history}
end
# Make testable
@doc false
def format_history(history) do
history
# Show only last 5 reflections
|> Enum.take(5)
|> Enum.map_join("\n", fn entry ->
"#{entry.timestamp}: #{inspect(entry.reflection["thoughts"])}"
end)
end
defp format_patterns(patterns) do
Enum.map_join(patterns, "\n", &inspect/1)
end
# Make testable
@doc false
def update_metrics(reflection, actions) do
# Update metrics based on new actions
new_metrics = %{
reflection.metrics
| total_reflections: (reflection.metrics[:total_reflections] || 0) + 1,
total_actions: (reflection.metrics[:total_actions] || 0) + length(actions)
}
%{reflection | metrics: new_metrics}
end
# Make testable
@doc false
def update_patterns(reflection, new_patterns) do
# Merge new patterns with existing ones, removing duplicates
updated_patterns =
(reflection.patterns ++ new_patterns)
|> Enum.uniq()
# Keep only top 50 patterns
|> Enum.take(50)
%{reflection | patterns: updated_patterns}
end
defp analyze_history(_history) do
# TODO: Implement pattern recognition from history
[]
end
defp update_learning_metrics(metrics, _new_patterns) do
# Update metrics based on learning process
Map.merge(metrics, %{
# Decrease learning rate over time
learning_rate: metrics.learning_rate * 0.9,
total_reflections: metrics.total_reflections + 1
})
end
end