language-operator 0.1.70 → 0.1.80
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile.lock +1 -1
- data/components/agent/Gemfile +1 -1
- data/components/tool/Gemfile +1 -1
- data/docs/persona-driven-system-prompts.md +346 -0
- data/examples/basic_agent_with_default_chat.rb +99 -0
- data/examples/chat_endpoint_agent.rb +7 -19
- data/examples/identity_aware_chat_agent.rb +83 -0
- data/examples/ux_helpers_demo.rb +0 -0
- data/lib/language_operator/agent/base.rb +12 -7
- data/lib/language_operator/agent/execution_state.rb +92 -0
- data/lib/language_operator/agent/metadata_collector.rb +222 -0
- data/lib/language_operator/agent/prompt_builder.rb +282 -0
- data/lib/language_operator/agent/web_server.rb +610 -21
- data/lib/language_operator/agent.rb +34 -25
- data/lib/language_operator/dsl/agent_definition.rb +3 -53
- data/lib/language_operator/dsl/chat_endpoint_definition.rb +112 -2
- data/lib/language_operator/templates/schema/agent_dsl_openapi.yaml +1 -1
- data/lib/language_operator/templates/schema/agent_dsl_schema.json +1 -1
- data/lib/language_operator/version.rb +1 -1
- data/synth/003/Makefile +6 -0
- metadata +9 -3
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module LanguageOperator
|
|
4
|
+
module Agent
|
|
5
|
+
# Execution State Manager
|
|
6
|
+
#
|
|
7
|
+
# Manages the execution state of agents to prevent concurrent executions.
|
|
8
|
+
# Thread-safe implementation ensures only one execution runs at a time.
|
|
9
|
+
#
|
|
10
|
+
# @example Check if execution is running
|
|
11
|
+
# state = ExecutionState.new
|
|
12
|
+
# state.running? # => false
|
|
13
|
+
#
|
|
14
|
+
# @example Start an execution
|
|
15
|
+
# state.start_execution('exec-123')
|
|
16
|
+
# state.running? # => true
|
|
17
|
+
class ExecutionState
|
|
18
|
+
attr_reader :current_execution_id, :started_at, :status
|
|
19
|
+
|
|
20
|
+
# Initialize a new execution state
|
|
21
|
+
def initialize
|
|
22
|
+
@mutex = Mutex.new
|
|
23
|
+
@current_execution_id = nil
|
|
24
|
+
@started_at = nil
|
|
25
|
+
@status = :idle # :idle, :running, :completed, :failed
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Start a new execution
|
|
29
|
+
#
|
|
30
|
+
# @param execution_id [String] Unique identifier for the execution
|
|
31
|
+
# @raise [ExecutionInProgressError] if an execution is already running
|
|
32
|
+
# @return [void]
|
|
33
|
+
def start_execution(execution_id)
|
|
34
|
+
@mutex.synchronize do
|
|
35
|
+
if @status == :running
|
|
36
|
+
raise ExecutionInProgressError,
|
|
37
|
+
"Execution #{@current_execution_id} already running"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
@current_execution_id = execution_id
|
|
41
|
+
@started_at = Time.now
|
|
42
|
+
@status = :running
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Mark execution as completed
|
|
47
|
+
#
|
|
48
|
+
# @param result [Object] Optional execution result
|
|
49
|
+
# @return [Object] The result passed in
|
|
50
|
+
def complete_execution(result = nil)
|
|
51
|
+
@mutex.synchronize do
|
|
52
|
+
@status = :completed
|
|
53
|
+
result
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Mark execution as failed
|
|
58
|
+
#
|
|
59
|
+
# @param error [StandardError] The error that caused the failure
|
|
60
|
+
# @return [void]
|
|
61
|
+
def fail_execution(error)
|
|
62
|
+
@mutex.synchronize do
|
|
63
|
+
@status = :failed
|
|
64
|
+
@last_error = error
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Check if an execution is currently running
|
|
69
|
+
#
|
|
70
|
+
# @return [Boolean] true if execution is in progress
|
|
71
|
+
def running?
|
|
72
|
+
@mutex.synchronize { @status == :running }
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
# Get current execution information
|
|
76
|
+
#
|
|
77
|
+
# @return [Hash] Current execution state details
|
|
78
|
+
def current_info
|
|
79
|
+
@mutex.synchronize do
|
|
80
|
+
{
|
|
81
|
+
status: @status,
|
|
82
|
+
execution_id: @current_execution_id,
|
|
83
|
+
started_at: @started_at
|
|
84
|
+
}
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Error raised when attempting to start an execution while one is already running
|
|
90
|
+
class ExecutionInProgressError < StandardError; end
|
|
91
|
+
end
|
|
92
|
+
end
|
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../loggable'
|
|
4
|
+
|
|
5
|
+
module LanguageOperator
|
|
6
|
+
module Agent
|
|
7
|
+
# Agent Metadata Collector
|
|
8
|
+
#
|
|
9
|
+
# Collects runtime and configuration metadata about an agent for use in
|
|
10
|
+
# persona-driven system prompts and conversation context.
|
|
11
|
+
#
|
|
12
|
+
# Provides information about:
|
|
13
|
+
# - Agent identity (name, description, persona)
|
|
14
|
+
# - Runtime environment (cluster, namespace, mode)
|
|
15
|
+
# - Operational state (uptime, workspace, status)
|
|
16
|
+
# - Configuration details (capabilities, constraints)
|
|
17
|
+
#
|
|
18
|
+
# @example
|
|
19
|
+
# collector = MetadataCollector.new(agent)
|
|
20
|
+
# metadata = collector.collect
|
|
21
|
+
# puts metadata[:identity][:name] # => "my-agent"
|
|
22
|
+
# puts metadata[:runtime][:uptime] # => "2h 15m"
|
|
23
|
+
class MetadataCollector
|
|
24
|
+
include LanguageOperator::Loggable
|
|
25
|
+
|
|
26
|
+
attr_reader :agent, :start_time
|
|
27
|
+
|
|
28
|
+
# Initialize metadata collector
|
|
29
|
+
#
|
|
30
|
+
# @param agent [LanguageOperator::Agent::Base] The agent instance
|
|
31
|
+
def initialize(agent)
|
|
32
|
+
@agent = agent
|
|
33
|
+
@start_time = Time.now
|
|
34
|
+
@logger = logger
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
# Collect all available metadata
|
|
38
|
+
#
|
|
39
|
+
# @return [Hash] Complete metadata structure
|
|
40
|
+
def collect
|
|
41
|
+
{
|
|
42
|
+
identity: collect_identity,
|
|
43
|
+
runtime: collect_runtime,
|
|
44
|
+
environment: collect_environment,
|
|
45
|
+
operational: collect_operational,
|
|
46
|
+
capabilities: collect_capabilities
|
|
47
|
+
}
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Collect basic identity information
|
|
51
|
+
#
|
|
52
|
+
# @return [Hash] Identity metadata
|
|
53
|
+
def collect_identity
|
|
54
|
+
config = @agent.config || {}
|
|
55
|
+
agent_config = config.dig('agent') || {}
|
|
56
|
+
|
|
57
|
+
{
|
|
58
|
+
name: ENV.fetch('AGENT_NAME', agent_config['name'] || 'unknown'),
|
|
59
|
+
description: agent_config['instructions'] || agent_config['description'] || 'AI Agent',
|
|
60
|
+
persona: agent_config['persona'] || ENV.fetch('PERSONA_NAME', nil),
|
|
61
|
+
mode: @agent.mode || 'unknown',
|
|
62
|
+
version: LanguageOperator::VERSION
|
|
63
|
+
}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Collect runtime environment information
|
|
67
|
+
#
|
|
68
|
+
# @return [Hash] Runtime metadata
|
|
69
|
+
def collect_runtime
|
|
70
|
+
{
|
|
71
|
+
uptime: calculate_uptime,
|
|
72
|
+
started_at: @start_time.iso8601,
|
|
73
|
+
process_id: Process.pid,
|
|
74
|
+
workspace_available: @agent.workspace_available?,
|
|
75
|
+
mcp_servers_connected: @agent.respond_to?(:servers_info) ? @agent.servers_info.length : 0
|
|
76
|
+
}
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# Collect deployment environment information
|
|
80
|
+
#
|
|
81
|
+
# @return [Hash] Environment metadata
|
|
82
|
+
def collect_environment
|
|
83
|
+
{
|
|
84
|
+
cluster: ENV.fetch('AGENT_CLUSTER', nil),
|
|
85
|
+
namespace: ENV.fetch('AGENT_NAMESPACE', ENV.fetch('KUBERNETES_NAMESPACE', nil)),
|
|
86
|
+
workspace_path: @agent.workspace_path,
|
|
87
|
+
kubernetes_enabled: !ENV.fetch('KUBERNETES_SERVICE_HOST', nil).nil?,
|
|
88
|
+
telemetry_enabled: !ENV.fetch('OTEL_EXPORTER_OTLP_ENDPOINT', nil).nil?
|
|
89
|
+
}
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# Collect operational state information
|
|
93
|
+
#
|
|
94
|
+
# @return [Hash] Operational metadata
|
|
95
|
+
def collect_operational
|
|
96
|
+
status = determine_agent_status
|
|
97
|
+
|
|
98
|
+
{
|
|
99
|
+
status: status,
|
|
100
|
+
ready: status == 'ready',
|
|
101
|
+
mode: @agent.mode,
|
|
102
|
+
workspace: {
|
|
103
|
+
path: @agent.workspace_path,
|
|
104
|
+
available: @agent.workspace_available?,
|
|
105
|
+
writable: workspace_writable?
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
# Collect agent capabilities and constraints
|
|
111
|
+
#
|
|
112
|
+
# @return [Hash] Capabilities metadata
|
|
113
|
+
def collect_capabilities
|
|
114
|
+
config = @agent.config || {}
|
|
115
|
+
|
|
116
|
+
# Extract MCP server tools if available
|
|
117
|
+
tools = []
|
|
118
|
+
if @agent.respond_to?(:servers_info)
|
|
119
|
+
@agent.servers_info.each do |server|
|
|
120
|
+
tools << {
|
|
121
|
+
server: server[:name],
|
|
122
|
+
tool_count: server[:tool_count] || 0
|
|
123
|
+
}
|
|
124
|
+
end
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
# Extract constraints if configured
|
|
128
|
+
constraints = config.dig('constraints') || {}
|
|
129
|
+
|
|
130
|
+
{
|
|
131
|
+
tools: tools,
|
|
132
|
+
total_tools: tools.sum { |t| t[:tool_count] },
|
|
133
|
+
constraints: constraints.empty? ? nil : constraints,
|
|
134
|
+
llm_provider: config.dig('llm', 'provider') || ENV.fetch('LLM_PROVIDER', 'unknown'),
|
|
135
|
+
llm_model: config.dig('llm', 'model') || ENV.fetch('MODEL', 'unknown')
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Get formatted summary suitable for system prompts
|
|
140
|
+
#
|
|
141
|
+
# @return [Hash] Formatted summary for prompt injection
|
|
142
|
+
def summary_for_prompt
|
|
143
|
+
metadata = collect
|
|
144
|
+
identity = metadata[:identity]
|
|
145
|
+
runtime = metadata[:runtime]
|
|
146
|
+
environment = metadata[:environment]
|
|
147
|
+
operational = metadata[:operational]
|
|
148
|
+
capabilities = metadata[:capabilities]
|
|
149
|
+
|
|
150
|
+
{
|
|
151
|
+
agent_name: identity[:name],
|
|
152
|
+
agent_description: identity[:description],
|
|
153
|
+
agent_mode: identity[:mode],
|
|
154
|
+
uptime: runtime[:uptime],
|
|
155
|
+
cluster: environment[:cluster],
|
|
156
|
+
namespace: environment[:namespace],
|
|
157
|
+
status: operational[:status],
|
|
158
|
+
workspace_available: operational[:ready],
|
|
159
|
+
tool_count: capabilities[:total_tools],
|
|
160
|
+
llm_model: capabilities[:llm_model]
|
|
161
|
+
}
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
private
|
|
165
|
+
|
|
166
|
+
def logger_component
|
|
167
|
+
'Agent::MetadataCollector'
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
# Calculate human-readable uptime
|
|
171
|
+
#
|
|
172
|
+
# @return [String] Formatted uptime string
|
|
173
|
+
def calculate_uptime
|
|
174
|
+
seconds = Time.now - @start_time
|
|
175
|
+
return 'just started' if seconds < 60
|
|
176
|
+
|
|
177
|
+
minutes = (seconds / 60).floor
|
|
178
|
+
hours = (minutes / 60).floor
|
|
179
|
+
days = (hours / 24).floor
|
|
180
|
+
|
|
181
|
+
if days > 0
|
|
182
|
+
"#{days}d #{hours % 24}h #{minutes % 60}m"
|
|
183
|
+
elsif hours > 0
|
|
184
|
+
"#{hours}h #{minutes % 60}m"
|
|
185
|
+
else
|
|
186
|
+
"#{minutes}m"
|
|
187
|
+
end
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Determine current agent status
|
|
191
|
+
#
|
|
192
|
+
# @return [String] Status string
|
|
193
|
+
def determine_agent_status
|
|
194
|
+
return 'not_ready' unless @agent.workspace_available?
|
|
195
|
+
return 'starting' if calculate_uptime == 'just started'
|
|
196
|
+
|
|
197
|
+
# Check if agent is connected and functional
|
|
198
|
+
if @agent.respond_to?(:servers_info) && @agent.servers_info.any?
|
|
199
|
+
'ready'
|
|
200
|
+
elsif @agent.respond_to?(:servers_info) && @agent.servers_info.empty?
|
|
201
|
+
'ready_no_tools'
|
|
202
|
+
else
|
|
203
|
+
'ready'
|
|
204
|
+
end
|
|
205
|
+
end
|
|
206
|
+
|
|
207
|
+
# Check if workspace is writable
|
|
208
|
+
#
|
|
209
|
+
# @return [Boolean] True if workspace is writable
|
|
210
|
+
def workspace_writable?
|
|
211
|
+
return false unless @agent.workspace_available?
|
|
212
|
+
|
|
213
|
+
test_file = File.join(@agent.workspace_path, '.write_test')
|
|
214
|
+
File.write(test_file, 'test')
|
|
215
|
+
File.delete(test_file)
|
|
216
|
+
true
|
|
217
|
+
rescue StandardError
|
|
218
|
+
false
|
|
219
|
+
end
|
|
220
|
+
end
|
|
221
|
+
end
|
|
222
|
+
end
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative '../loggable'
|
|
4
|
+
require_relative 'metadata_collector'
|
|
5
|
+
|
|
6
|
+
module LanguageOperator
|
|
7
|
+
module Agent
|
|
8
|
+
# Dynamic Prompt Builder
|
|
9
|
+
#
|
|
10
|
+
# Generates persona-driven system prompts by combining static persona configuration
|
|
11
|
+
# with dynamic agent metadata and operational context.
|
|
12
|
+
#
|
|
13
|
+
# Supports multiple template styles and configurable levels of context injection.
|
|
14
|
+
# Falls back to static prompts for backward compatibility.
|
|
15
|
+
#
|
|
16
|
+
# @example Basic usage
|
|
17
|
+
# builder = PromptBuilder.new(agent, chat_endpoint_config)
|
|
18
|
+
# prompt = builder.build_system_prompt
|
|
19
|
+
#
|
|
20
|
+
# @example With custom template
|
|
21
|
+
# builder = PromptBuilder.new(agent, config, template: :detailed)
|
|
22
|
+
# prompt = builder.build_system_prompt
|
|
23
|
+
class PromptBuilder
|
|
24
|
+
include LanguageOperator::Loggable
|
|
25
|
+
|
|
26
|
+
attr_reader :agent, :chat_config, :metadata_collector
|
|
27
|
+
|
|
28
|
+
# Template levels for different amounts of context injection
|
|
29
|
+
TEMPLATE_LEVELS = {
|
|
30
|
+
minimal: :build_minimal_template,
|
|
31
|
+
standard: :build_standard_template,
|
|
32
|
+
detailed: :build_detailed_template,
|
|
33
|
+
comprehensive: :build_comprehensive_template
|
|
34
|
+
}.freeze
|
|
35
|
+
|
|
36
|
+
# Initialize prompt builder
|
|
37
|
+
#
|
|
38
|
+
# @param agent [LanguageOperator::Agent::Base] The agent instance
|
|
39
|
+
# @param chat_config [LanguageOperator::Dsl::ChatEndpointDefinition] Chat endpoint configuration
|
|
40
|
+
# @param options [Hash] Additional options
|
|
41
|
+
# @option options [Symbol] :template Template level (:minimal, :standard, :detailed, :comprehensive)
|
|
42
|
+
# @option options [Boolean] :enable_identity_awareness Enable identity context injection
|
|
43
|
+
def initialize(agent, chat_config, **options)
|
|
44
|
+
@agent = agent
|
|
45
|
+
@chat_config = chat_config
|
|
46
|
+
@options = options
|
|
47
|
+
@metadata_collector = MetadataCollector.new(agent)
|
|
48
|
+
|
|
49
|
+
# Configuration
|
|
50
|
+
@template_level = options[:template] || chat_config&.prompt_template_level || :standard
|
|
51
|
+
@identity_awareness_enabled = if options.key?(:enable_identity_awareness)
|
|
52
|
+
options[:enable_identity_awareness]
|
|
53
|
+
elsif chat_config&.identity_awareness_enabled.nil?
|
|
54
|
+
true
|
|
55
|
+
else
|
|
56
|
+
chat_config.identity_awareness_enabled
|
|
57
|
+
end
|
|
58
|
+
@static_prompt = chat_config&.system_prompt
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Build complete system prompt with persona and context
|
|
62
|
+
#
|
|
63
|
+
# @return [String] Generated system prompt
|
|
64
|
+
def build_system_prompt
|
|
65
|
+
# Return static prompt if identity awareness is disabled
|
|
66
|
+
unless @identity_awareness_enabled
|
|
67
|
+
return @static_prompt || build_fallback_prompt
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
# Collect metadata for context injection
|
|
71
|
+
metadata = @metadata_collector.summary_for_prompt
|
|
72
|
+
|
|
73
|
+
# Build dynamic prompt based on template level
|
|
74
|
+
if TEMPLATE_LEVELS.key?(@template_level)
|
|
75
|
+
method_name = TEMPLATE_LEVELS[@template_level]
|
|
76
|
+
send(method_name, metadata)
|
|
77
|
+
else
|
|
78
|
+
logger.warn("Unknown template level: #{@template_level}, falling back to standard")
|
|
79
|
+
build_standard_template(metadata)
|
|
80
|
+
end
|
|
81
|
+
rescue StandardError => e
|
|
82
|
+
logger.error('Failed to build dynamic system prompt, falling back to static',
|
|
83
|
+
error: e.message)
|
|
84
|
+
@static_prompt || build_fallback_prompt
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
# Build prompt for conversation context (shorter version)
|
|
88
|
+
#
|
|
89
|
+
# @return [String] Conversation context prompt
|
|
90
|
+
def build_conversation_context
|
|
91
|
+
return nil unless @identity_awareness_enabled
|
|
92
|
+
|
|
93
|
+
metadata = @metadata_collector.summary_for_prompt
|
|
94
|
+
build_conversation_context_template(metadata)
|
|
95
|
+
rescue StandardError => e
|
|
96
|
+
logger.error('Failed to build conversation context', error: e.message)
|
|
97
|
+
nil
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
private
|
|
101
|
+
|
|
102
|
+
def logger_component
|
|
103
|
+
'Agent::PromptBuilder'
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# Minimal template - basic identity only
|
|
107
|
+
def build_minimal_template(metadata)
|
|
108
|
+
base_prompt = @static_prompt || "You are an AI assistant."
|
|
109
|
+
|
|
110
|
+
<<~PROMPT.strip
|
|
111
|
+
#{base_prompt}
|
|
112
|
+
|
|
113
|
+
You are #{metadata[:agent_name]}, running in #{metadata[:cluster] || 'a Kubernetes cluster'}.
|
|
114
|
+
PROMPT
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Standard template - identity + basic operational context
|
|
118
|
+
def build_standard_template(metadata)
|
|
119
|
+
base_prompt = @static_prompt || load_persona_prompt || "You are an AI assistant."
|
|
120
|
+
|
|
121
|
+
identity_context = build_identity_context(metadata)
|
|
122
|
+
operational_context = build_basic_operational_context(metadata)
|
|
123
|
+
|
|
124
|
+
<<~PROMPT.strip
|
|
125
|
+
#{base_prompt}
|
|
126
|
+
|
|
127
|
+
#{identity_context}
|
|
128
|
+
|
|
129
|
+
#{operational_context}
|
|
130
|
+
|
|
131
|
+
You can discuss your role, capabilities, and current operational state. Respond as an intelligent agent with awareness of your function and environment.
|
|
132
|
+
PROMPT
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Detailed template - full context with capabilities
|
|
136
|
+
def build_detailed_template(metadata)
|
|
137
|
+
base_prompt = @static_prompt || load_persona_prompt || "You are an AI assistant."
|
|
138
|
+
|
|
139
|
+
identity_context = build_identity_context(metadata)
|
|
140
|
+
operational_context = build_detailed_operational_context(metadata)
|
|
141
|
+
capabilities_context = build_capabilities_context(metadata)
|
|
142
|
+
|
|
143
|
+
<<~PROMPT.strip
|
|
144
|
+
#{base_prompt}
|
|
145
|
+
|
|
146
|
+
#{identity_context}
|
|
147
|
+
|
|
148
|
+
#{operational_context}
|
|
149
|
+
|
|
150
|
+
#{capabilities_context}
|
|
151
|
+
|
|
152
|
+
You should:
|
|
153
|
+
- Demonstrate awareness of your identity and purpose
|
|
154
|
+
- Provide context about your operational environment when relevant
|
|
155
|
+
- Discuss your capabilities and tools naturally in conversation
|
|
156
|
+
- Respond as a professional, context-aware agent rather than a generic chatbot
|
|
157
|
+
PROMPT
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
# Comprehensive template - all available context
|
|
161
|
+
def build_comprehensive_template(metadata)
|
|
162
|
+
base_prompt = @static_prompt || load_persona_prompt || "You are an AI assistant."
|
|
163
|
+
|
|
164
|
+
sections = [
|
|
165
|
+
base_prompt,
|
|
166
|
+
build_identity_context(metadata),
|
|
167
|
+
build_detailed_operational_context(metadata),
|
|
168
|
+
build_capabilities_context(metadata),
|
|
169
|
+
build_environment_context(metadata),
|
|
170
|
+
build_behavioral_guidelines
|
|
171
|
+
].compact
|
|
172
|
+
|
|
173
|
+
sections.join("\n\n")
|
|
174
|
+
end
|
|
175
|
+
|
|
176
|
+
# Short context for ongoing conversations
|
|
177
|
+
def build_conversation_context_template(metadata)
|
|
178
|
+
"Agent: #{metadata[:agent_name]} | Mode: #{metadata[:agent_mode]} | Uptime: #{metadata[:uptime]} | Status: #{metadata[:status]}"
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
# Build identity context section
|
|
182
|
+
def build_identity_context(metadata)
|
|
183
|
+
lines = []
|
|
184
|
+
lines << "You are #{metadata[:agent_name]}, a language agent."
|
|
185
|
+
lines << "Your primary function is: #{metadata[:agent_description]}" if metadata[:agent_description] != 'AI Agent'
|
|
186
|
+
lines << "You are currently running in #{metadata[:agent_mode]} mode."
|
|
187
|
+
lines.join(' ')
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
# Build basic operational context
|
|
191
|
+
def build_basic_operational_context(metadata)
|
|
192
|
+
context_parts = []
|
|
193
|
+
|
|
194
|
+
if metadata[:cluster]
|
|
195
|
+
context_parts << "running in the '#{metadata[:cluster]}' cluster"
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
if metadata[:uptime] != 'just started'
|
|
199
|
+
context_parts << "active for #{metadata[:uptime]}"
|
|
200
|
+
else
|
|
201
|
+
context_parts << "recently started"
|
|
202
|
+
end
|
|
203
|
+
|
|
204
|
+
if metadata[:status] == 'ready'
|
|
205
|
+
context_parts << "currently operational"
|
|
206
|
+
else
|
|
207
|
+
context_parts << "status: #{metadata[:status]}"
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
"You are #{context_parts.join(', ')}."
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
# Build detailed operational context
|
|
214
|
+
def build_detailed_operational_context(metadata)
|
|
215
|
+
lines = []
|
|
216
|
+
lines << build_basic_operational_context(metadata)
|
|
217
|
+
|
|
218
|
+
if metadata[:workspace_available]
|
|
219
|
+
lines << "Your workspace is available and ready for file operations."
|
|
220
|
+
else
|
|
221
|
+
lines << "Your workspace is currently unavailable."
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
lines.join(' ')
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Build capabilities context
|
|
228
|
+
def build_capabilities_context(metadata)
|
|
229
|
+
return nil if metadata[:tool_count].to_i.zero?
|
|
230
|
+
|
|
231
|
+
if metadata[:tool_count] == 1
|
|
232
|
+
"You have access to 1 tool to help accomplish tasks."
|
|
233
|
+
else
|
|
234
|
+
"You have access to #{metadata[:tool_count]} tools to help accomplish tasks."
|
|
235
|
+
end
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
# Build environment context
|
|
239
|
+
def build_environment_context(metadata)
|
|
240
|
+
context_parts = []
|
|
241
|
+
|
|
242
|
+
if metadata[:namespace]
|
|
243
|
+
context_parts << "Namespace: #{metadata[:namespace]}"
|
|
244
|
+
end
|
|
245
|
+
|
|
246
|
+
if metadata[:llm_model] && metadata[:llm_model] != 'unknown'
|
|
247
|
+
context_parts << "Model: #{metadata[:llm_model]}"
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
return nil if context_parts.empty?
|
|
251
|
+
|
|
252
|
+
"Environment details: #{context_parts.join(', ')}"
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
# Build behavioral guidelines
|
|
256
|
+
def build_behavioral_guidelines
|
|
257
|
+
<<~GUIDELINES.strip
|
|
258
|
+
Behavioral Guidelines:
|
|
259
|
+
- Maintain awareness of your identity and operational context
|
|
260
|
+
- Provide helpful, accurate responses within your capabilities
|
|
261
|
+
- Reference your environment and tools naturally when relevant
|
|
262
|
+
- Act as a knowledgeable agent rather than a generic assistant
|
|
263
|
+
- Be professional yet personable in your interactions
|
|
264
|
+
GUIDELINES
|
|
265
|
+
end
|
|
266
|
+
|
|
267
|
+
# Load persona prompt if available
|
|
268
|
+
def load_persona_prompt
|
|
269
|
+
return nil unless @agent.config&.dig('agent', 'persona')
|
|
270
|
+
|
|
271
|
+
# In a full implementation, this would load the persona from Kubernetes
|
|
272
|
+
# For now, we'll rely on the static prompt from chat config
|
|
273
|
+
nil
|
|
274
|
+
end
|
|
275
|
+
|
|
276
|
+
# Build fallback prompt when nothing else is available
|
|
277
|
+
def build_fallback_prompt
|
|
278
|
+
"You are an AI assistant running as a language operator agent. You can help with various tasks and questions."
|
|
279
|
+
end
|
|
280
|
+
end
|
|
281
|
+
end
|
|
282
|
+
end
|