ADR-0010 Agent Intelligence Evolution
- Status: Accepted
- Date: 2025-03
- Decision Makers: EnerOS Intelligence Architecture Group
- Related Capabilities: Agent Intelligence Evolution, Physics Constraint Engine
- Related ADR: ADR-0002 Power-Native AgentOS
Context
Decision Timing
EnerOS v0.43 is about to be released, with three foundational capabilities stable: Power-Native kernel, Zero Trust security, and Agent runtime. Pilot grid companies have raised new requirements: Agents cannot just do rule-driven transactional work; they also need reasoning capabilities across multi-source heterogeneous data, prediction capabilities for renewable energy fluctuations, and natural language interaction capabilities with dispatchers.
These requirements mark EnerOS’s evolution from a “rule-driven automation system” to an “intelligence-driven autonomous system.” This ADR decides the architecture form of the Agent intelligence layer, the selection strategy for reasoning backends, and the enforcement mechanism for security constraints—a key node in EnerOS’s evolution path.
Current Pain Points
Before EnerOS v0.43, Agents were primarily rule-and-script-driven, performing well in structured scenarios but struggling with the following:
Pain Point 1: Fault root cause analysis requires reasoning across multi-source heterogeneous data
Fault analysis requires synthesizing protection action logs, SCADA measurements, equipment ledgers, weather data, maintenance records, and other multi-source information. Rule engines can only handle structured conditions and cannot understand unstructured text (such as maintenance records, weather warnings), resulting in root cause analysis coverage of only 65%.
// Rule engine limitation: can only handle predefined patterns, cannot cover combined faults and unknown patterns
fn diagnose_fault_oldschool(event: ProtectionEvent) -> Option<RootCause> {
match event.pattern {
ProtectionPattern::Overcurrent => Some(RootCause::ShortCircuit),
ProtectionPattern::Overvoltage => Some(RootCause::CapacitorBankFailure),
ProtectionPattern::Underfrequency => Some(RootCause::LoadImbalance),
ProtectionPattern::DistanceZone1 => Some(RootCause::CloseInFault),
ProtectionPattern::DistanceZone2 => Some(RootCause::MidLineFault),
ProtectionPattern::DistanceZone3 => Some(RootCause::RemoteFault),
ProtectionPattern::Differential => Some(RootCause::InternalFault),
ProtectionPattern::Directional => Some(RootCause::ReverseFault),
ProtectionPattern::EarthFault => Some(RootCause::GroundFault),
// Actual rule base has 1200 rules, but combined faults and unknown patterns still fall into None
_ => None, // 65% of faults fall into "unknown" classification
}
}
Pain Point 2: Collaborative dispatch under renewable energy fluctuations requires prediction + decision-making
Photovoltaic and wind power output are highly stochastic; traditional dispatch strategies based on deterministic load flow decrease efficiency by 20-30% in high renewable penetration scenarios. Coordination between prediction models and decision models is needed.
Pain Point 3: Operations conversational interaction requires natural language understanding
Dispatchers want to query grid status and get recommendations through natural language, rather than operating multiple GUIs. Rule engines cannot understand fuzzy expressions like “is the 110kV bus voltage a bit high right now.”
Pain Point 4: Poor long-tail scenario extensibility
Each new scenario requires new rules; as the rule base expands, maintenance costs surge. The v0.43 rule base has reached 1200 rules, with an average of 3 person-days per new rule.
Specific Event Triggering the Decision
In February 2025, a pilot grid experienced multiple line trips due to ice storms during the Spring Festival. The rule engine could not synthesize weather warnings and equipment historical data for root cause inference, ultimately relying on manual analysis taking 4 hours. The post-incident review explicitly required EnerOS to introduce intelligent reasoning capabilities before v0.50.
Constraints at Decision Time
| Constraint | Description |
|---|---|
| Security | All reasoning results must pass constraint engine validation; “intelligent but unsafe” operations are prohibited |
| Performance | Real-time scenario reasoning latency < 100ms; non-real-time scenarios acceptable at 5-10s |
| Cost | LLM calls require quota control to prevent single tenant from exhausting budget |
| Auditable | Reasoning process traceable, satisfying compliance forensics |
| Extensible | New reasoning backends should not require kernel modification |
| Privacy | Data does not leave the station; sensitive data requires local processing |
Decision
Adopt kernel-built-in Agent intelligence layer: introduce a unified intelligence interface between the Agent runtime and kernel, supporting pluggable rule, model, and LLM reasoning backends; all reasoning results must pass constraint engine validation before execution, ensuring “intelligent but not overstepping boundaries.”
Core Rationale
Reason 1: Intelligence layer in the kernel, security control in the kernel
Placing the intelligence layer between the Agent runtime and kernel forces all reasoning results to pass through the kernel’s constraint engine. Whether the backend is rules, models, or LLM, output must pass physical constraint validation, fundamentally eliminating “intelligence overstepping boundaries.”
Reason 2: Three pluggable backends, avoiding one-size-fits-all
Different scenarios suit different backends: protection logic uses rules (high determinism), load forecasting uses statistical models (low cost, low latency), root cause analysis and dialogue use LLM (strong understanding). The unified interface lets Agents switch backends by scenario without modifying business code.
Reason 3: Kernel-built-in avoids external hook risks
External LLM gateways bypass kernel constraints, posing security risks. Built-in intelligence layer makes all intelligent calls pass through unified audit and constraint chains, satisfying compliance requirements.
Reason 4: Reasoning process traceable
Each reasoning records input, backend type, model version, output, constraint validation result, satisfying the “explainable, traceable” requirements of compliance forensics.
Reason 5: Supports progressive upgrade
Rule backends ensure smooth migration of existing applications; model and LLM backends are gradually introduced by scenario, avoiding one-time rewrites.
Agent Intelligence Tier Model
EnerOS divides Agent intelligence into five tiers, each corresponding to different capabilities and backend choices:
| Tier | Name | Capability | Recommended Backend | Latency Requirement |
|---|---|---|---|---|
| L1 | Reactive | Single event triggers fixed action | Rules | < 1ms |
| L2 | Rule-based | Multi-condition combination judgment | Rules | < 10ms |
| L3 | Statistical prediction | Prediction based on historical data | Model | < 100ms |
| L4 | Reasoning analysis | Reasoning across multi-source data | LLM / Model | < 5s |
| L5 | Autonomous decision | Autonomous planning within constraint boundaries | LLM + Model | < 30s |
Higher is not always better—L1/L2 far exceed L4/L5 in performance and reliability for deterministic scenarios. EnerOS encourages “use the lowest sufficient tier” to solve problems.
Intelligence Layer Architecture
┌──────────────────────────────────────────────────┐
│ Agent Business Logic │
└──────────────────┬───────────────────────────────┘
│ ReasoningRequest
▼
┌──────────────────────────────────────────────────┐
│ Intelligence Interface (ReasoningTrait) │
│ ┌─────────┬─────────────┬────────────────────┐ │
│ │ Rule │ Model │ LLM │ │
│ │ Backend │ Backend │ Backend │ │
│ │ RuleEng │ ModelServer │ LlmGateway │ │
│ └─────────┴─────────────┴────────────────────┘ │
└──────────────────┬───────────────────────────────┘
│ ReasoningResult (unvalidated)
▼
┌──────────────────────────────────────────────────┐
│ Constraint Engine (ConstraintEngine) │
│ ┌────────────────────────────────────────────┐ │
│ │ Voltage / Current / Frequency / Thermal / │ │
│ │ Operation Sequence │ │
│ └────────────────────────────────────────────┘ │
└──────────────────┬───────────────────────────────┘
│ ValidatedResult
▼
┌──────────────────────────────────────────────────┐
│ Audit and Execution │
└──────────────────────────────────────────────────┘
Unified Intelligence Interface
All reasoning backends implement the unified ReasoningBackend trait:
use async_trait::async_trait;
/// Reasoning request
pub struct ReasoningRequest {
pub task: ReasoningTask,
pub context: ReasoningContext,
pub constraints: Vec<ConstraintSpec>,
pub deadline: Option<Deadline>,
pub preferred_backend: Option<BackendKind>,
}
/// Reasoning result (before constraint validation)
pub struct ReasoningResult {
pub action: ProposedAction,
pub confidence: f64,
pub explanation: String,
pub backend: BackendKind,
pub model_version: Option<String>,
pub latency: Duration,
}
/// Interface all reasoning backends must implement
#[async_trait]
pub trait ReasoningBackend: Send + Sync {
/// Backend type
fn kind(&self) -> BackendKind;
/// Whether it can handle a certain task type
fn can_handle(&self, task: &ReasoningTask) -> FitScore;
/// Execute reasoning
async fn reason(&self, req: &ReasoningRequest) -> Result<ReasoningResult>;
/// Health check
async fn health_check(&self) -> Result<HealthStatus>;
}
/// Intelligence layer dispatcher: selects the most appropriate backend based on task
pub struct ReasoningDispatcher {
backends: Vec<Arc<dyn ReasoningBackend>>,
constraint_engine: Arc<ConstraintEngine>,
audit_logger: Arc<AuditLogger>,
}
impl ReasoningDispatcher {
pub async fn reason(&self, req: ReasoningRequest) -> Result<ValidatedResult> {
// 1. Select backend
let backend = self.select_backend(&req)?;
let start = Instant::now();
// 2. Call backend reasoning
let raw_result = backend.reason(&req).await?;
// 3. Constraint validation (cannot be bypassed)
let validated = self.constraint_engine.validate(&raw_result.action)?;
// 4. Audit recording
self.audit_logger.record(AuditEntry {
request: req.summary(),
backend: raw_result.backend,
model_version: raw_result.model_version.clone(),
raw_action: raw_result.action.summary(),
validated: validated.clone(),
latency: start.elapsed(),
timestamp: now_ns(),
}).await?;
Ok(validated)
}
fn select_backend(&self, req: &ReasoningRequest) -> Result<&Arc<dyn ReasoningBackend>> {
// If caller specifies backend and it's healthy, use directly
if let Some(kind) = req.preferred_backend {
if let Some(b) = self.backends.iter().find(|b| b.kind() == kind) {
if b.health_check().await?.is_healthy() {
return Ok(b);
}
}
}
// Otherwise select highest FitScore
let mut scores: Vec<_> = self.backends.iter()
.map(|b| (b, b.can_handle(&req.task)))
.collect();
scores.sort_by(|a, b| b.1.partial_cmp(&a.1).unwrap());
scores.first().map(|(b, _)| *b)
.ok_or_else(|| ReasoningError::NoSuitableBackend)
}
}
Three Reasoning Backend Implementations
Rule backend (L1-L2):
pub struct RuleBackend {
rules: Vec<Rule>,
engine: RuleEngine,
}
#[async_trait]
impl ReasoningBackend for RuleBackend {
fn kind(&self) -> BackendKind { BackendKind::Rule }
fn can_handle(&self, task: &ReasoningTask) -> FitScore {
match task {
ReasoningTask::ProtectionLogic => FitScore::perfect(),
ReasoningTask::DispatchWithinRules => FitScore::high(),
ReasoningTask::RootCauseAnalysis => FitScore::low(),
_ => FitScore::none(),
}
}
async fn reason(&self, req: &ReasoningRequest) -> Result<ReasoningResult> {
let matched = self.engine.evaluate(&req.context, &self.rules)?;
Ok(ReasoningResult {
action: matched.action,
confidence: 1.0, // Rule match = 100% confidence
explanation: matched.rule_text,
backend: BackendKind::Rule,
model_version: Some(format!("rules-v{}", matched.ruleset_version)),
latency: Duration::from_micros(150),
})
}
}
Model backend (L3):
pub struct ModelBackend {
registry: ModelRegistry,
runtime: Arc<ModelRuntime>,
}
#[async_trait]
impl ReasoningBackend for ModelBackend {
fn kind(&self) -> BackendKind { BackendKind::Model }
fn can_handle(&self, task: &ReasoningTask) -> FitScore {
match task {
ReasoningTask::LoadForecast => FitScore::perfect(),
ReasoningTask::RenewableGenerationForecast => FitScore::perfect(),
ReasoningTask::EquipmentHealthPredict => FitScore::high(),
_ => FitScore::low(),
}
}
async fn reason(&self, req: &ReasoningRequest) -> Result<ReasoningResult> {
let model = self.registry.select(&req.task)?;
let input = self.prepare_features(&req.context, &model)?;
let output = self.runtime.infer(&model, &input).await?;
Ok(ReasoningResult {
action: output.to_action(),
confidence: output.confidence,
explanation: format!("model {} inference", model.name),
backend: BackendKind::Model,
model_version: Some(model.version.clone()),
latency: output.latency,
})
}
}
LLM backend (L4-L5):
pub struct LlmBackend {
gateway: Arc<LlmGateway>,
quota: Arc<QuotaManager>,
prompt_builder: PromptBuilder,
}
#[async_trait]
impl ReasoningBackend for LlmBackend {
fn kind(&self) -> BackendKind { BackendKind::Llm }
fn can_handle(&self, task: &ReasoningTask) -> FitScore {
match task {
ReasoningTask::RootCauseAnalysis => FitScore::perfect(),
ReasoningTask::NaturalLanguageDialogue => FitScore::perfect(),
ReasoningTask::DispatchOptimization => FitScore::high(),
_ => FitScore::low(),
}
}
async fn reason(&self, req: &ReasoningRequest) -> Result<ReasoningResult> {
// 1. Quota check
self.quota.check_and_consume(&req.context.tenant_id, 1)?;
// 2. Build prompt (including grid context and constraint hints)
let prompt = self.prompt_builder.build(req)?;
// 3. Call LLM
let response = self.gateway.complete(&prompt).await?;
// 4. Parse structured output
let parsed = LlmResponseParser::parse(&response)?;
Ok(ReasoningResult {
action: parsed.action,
confidence: parsed.confidence,
explanation: parsed.reasoning_chain,
backend: BackendKind::Llm,
model_version: Some(self.gateway.model_version().to_string()),
latency: response.latency,
})
}
}
LLM Integration Approach
EnerOS’s LLM integration follows the “local first, on-demand external call” principle:
| Deployment Mode | Applicable Scenario | Latency | Cost | Privacy |
|---|---|---|---|---|
| Local small model | Real-time reasoning, privacy-sensitive | < 100ms | Low | Fully local |
| Private cloud large model | Complex reasoning, enterprise intranet | 1-5s | Medium | Intranet |
| Public cloud large model | Dialogue, non-sensitive scenarios | 2-10s | High | Requires desensitization |
# eneros.toml
[intelligence.llm]
default_mode = "local_first" # local_first / cloud_only / hybrid
[intelligence.llm.local]
model = "eneros-7b-instruct" # EnerOS fine-tuned 7B model
runtime = "onnx" # onnx / ggml / tensorrt
device = "cuda" # cuda / cpu / mps
max_tokens = 2048
temperature = 0.3
[intelligence.llm.cloud]
provider = "azure_openai" # azure_openai / anthropic / custom
endpoint = "https://llm.internal.example.com/v1"
model = "gpt-4o"
api_key_env = "ENEROS_LLM_API_KEY"
timeout = "30s"
[intelligence.llm.quota]
daily_token_limit = 1_000_000
per_tenant_limit = 100_000
overflow_action = "fallback_to_local" # fallback_to_local / reject
Reasoning Engine Selection
EnerOS supports multiple reasoning backend runtimes:
| Runtime | Applicable Models | Advantages | Disadvantages |
|---|---|---|---|
| ONNX Runtime | General | Cross-platform, mature ecosystem | Average performance for large models |
| GGML / llama.cpp | LLM | CPU-friendly, lightweight | Limited GPU acceleration |
| TensorRT-LLM | LLM | Optimal for NVIDIA GPU | NVIDIA only |
| Candle | Rust native | Pure Rust, memory safe | Small ecosystem |
| vLLM | LLM serving | High throughput, PagedAttention | Heavier deployment |
EnerOS defaults to ONNX Runtime + Candle combination: ONNX Runtime for served models, Candle for embedded scenarios.
Security Constraint Mechanism
All reasoning results must pass multiple constraint engine validations before execution:
pub struct ConstraintEngine {
physics_rules: Vec<PhysicsRule>,
operational_rules: Vec<OperationalRule>,
sequence_rules: Vec<SequenceRule>,
}
impl ConstraintEngine {
pub fn validate(&self, action: &ProposedAction) -> Result<ValidatedResult> {
// 1. Physical constraints (voltage, current, frequency, thermal stability)
for rule in &self.physics_rules {
rule.check(action)?;
}
// 2. Operational constraints (operation sequence, device state)
for rule in &self.operational_rules {
rule.check(action)?;
}
// 3. Temporal constraints (operation intervals, concurrency limits)
for rule in &self.sequence_rules {
rule.check(action)?;
}
Ok(ValidatedResult::from(action))
}
}
/// Handling policy when LLM output violates constraints
pub enum ConstraintViolationPolicy {
Reject, // Reject directly, return error
AutoCorrect, // Attempt auto-correction (e.g., reduce output within constraints)
Escalate, // Escalate to human review
FallbackRule, // Fall back to rule backend
}
impl ReasoningDispatcher {
pub async fn reason_with_policy(
&self,
req: ReasoningRequest,
policy: ConstraintViolationPolicy,
) -> Result<FinalResult> {
loop {
let result = self.reason(req.clone()).await;
match result {
Ok(validated) => return Ok(FinalResult::Validated(validated)),
Err(ReasoningError::ConstraintViolation(v)) => match policy {
ConstraintViolationPolicy::Reject => return Err(v.into()),
ConstraintViolationPolicy::AutoCorrect => {
req.constraints.push(v.to_constraint());
continue; // Re-reason with additional constraints
}
ConstraintViolationPolicy::Escalate => {
return Ok(FinalResult::NeedsHumanReview(v));
}
ConstraintViolationPolicy::FallbackRule => {
req.preferred_backend = Some(BackendKind::Rule);
continue;
}
},
Err(e) => return Err(e),
}
}
}
}
Consequences
Benefits
1. Intelligence and security decoupled, reusing kernel constraints and audit
Whether the backend is rules, models, or LLM, output passes through the same constraint engine and audit chain, providing unified security assurance. New backends need not duplicate security mechanisms.
2. Multiple reasoning backends switchable by scenario, avoiding one-size-fits-all
Measured in pilot grids, rule backends handle 78% of transactional tasks (latency < 1ms), model backends handle 18% of prediction tasks (latency < 100ms), and LLM backends handle 4% of complex reasoning (latency < 5s), optimizing overall cost and latency.
3. Reasoning process traceable, satisfying compliance forensics
Each reasoning records input summary, backend type, model version, raw output, constraint validation result, and final decision, forming a complete audit chain. Compliance forensics can trace the full process of any decision.
4. Fault root cause analysis coverage increased from 65% to 92%
LLM backends can synthesize unstructured data (maintenance records, weather warnings), covering combined faults and long-tail scenarios that rule engines cannot handle.
5. Long-tail scenario extension cost reduced
New scenarios need not write rules; supported through prompt engineering or fine-tuning small models, with average effort dropping from 3 person-days to 0.5 person-days.
Costs
1. Constraint validation adds extra latency; async prefetch needed for real-time scenarios
Constraint validation adds 10-50μs latency; for real-time domain (< 100μs) scenarios, optimized through async prefetch and caching.
2. Model version management and regression testing become new engineering burden
Need to establish model registry, A/B testing framework, regression test datasets; team adds 1 ML engineer position.
3. LLM call costs require quota mechanism constraints
During pilot, LLM call costs approximately $2000/month, reduced to $500/month through quotas and local small model fallback.
4. Output uncertainty increases operations complexity
LLM output has randomness; requires temperature control, structured output parsing, and constraint validation triple mechanisms to ensure reliability.
5. Model supply chain security requires additional review
Third-party models may have backdoors or biases; need to establish model review processes and adversarial test sets.
Follow-up Work
- Introduce Retrieval-Agent for grid knowledge base retrieval (landed in v0.48)
- Explore small model local deployment to reduce latency and cost (planned for v0.52)
- Improve Agent intelligence evaluation benchmarks (planned for v0.50)
- Support multimodal input (images, oscilloscope waveforms) (planned for v0.55)
- Introduce Agent intelligence self-learning and fine-tuning pipeline (planned for v0.60)
Alternatives
Option A: External LLM Gateway
Description: Deploy a standalone LLM gateway; Agents call via HTTP, with the gateway returning results and executing directly.
Advantages:
- Simple implementation; can launch in 2 weeks
- No kernel modification; zero intrusion on existing architecture
- Can use any LLM provider
Rejection Reasons:
- Bypasses kernel constraint engine, posing security risks—LLM may output instructions violating physical constraints
- Audit chain broken; cannot prove which LLM call produced an operation
- Cannot unify scheduling with rule and model backends; multi-backend switching difficult
- Conflicts with Power-Native philosophy of ADR-0002
Option B: Each Agent Integrates Models Independently
Description: Each Agent independently selects and integrates rules, models, or LLM, with no unified interface.
Advantages:
- Maximum flexibility; Agents choose as needed
- No kernel changes required
- Team can develop in parallel
Rejection Reasons:
- Duplicates reasoning, constraint, and audit logic; code bloat
- Security policies scattered; difficult to ensure consistency
- Model version management chaotic; cannot unify monitoring
- New Agents must relearn reasoning framework; low development efficiency
Option C: Maintain Pure Rules
Description: Continue expanding the rule base, covering more scenarios through finer-grained rules.
Advantages:
- Simple implementation; team familiar
- High determinism; predictable output
- No LLM costs
Rejection Reasons:
- Cannot cover long-tail scenarios; rule base has expanded to 1200 rules, with surging maintenance costs
- Cannot handle unstructured data (text, images)
- Cannot meet prediction needs under high renewable penetration
- Competitors (such as GE Grid Software, Siemens Spectrum Power) have introduced AI capabilities; pure rule approach lacks competitiveness
Option D: Pure LLM-Driven
Description: All Agent decisions go through LLM reasoning; abandon rule engine.
Advantages:
- Minimal implementation; unified backend
- Maximum flexibility; can handle any scenario
- Fast development
Rejection Reasons:
- Latency too high (> 1s); cannot meet real-time scenarios like protection logic
- High cost; $0.01-0.1 per call; unacceptable for high-frequency scenarios
- High output uncertainty; unacceptable for power critical infrastructure
- Complete dependence on external LLM suppliers; supply chain risk
Performance Assessment
Performance measured data after intelligence layer introduction (standard test environment: 4 cores / 8GB / NVIDIA T4 GPU):
| Backend | Task | Average Latency | P99 Latency | Throughput | Cost/1k calls |
|---|---|---|---|---|---|
| Rule | Protection logic | 0.15ms | 0.5ms | 100k QPS | $0 |
| Rule | Dispatch combination judgment | 0.8ms | 2ms | 50k QPS | $0 |
| Model | Load forecasting | 35ms | 80ms | 200 QPS | $0.01 |
| Model | PV output prediction | 42ms | 95ms | 180 QPS | $0.01 |
| LLM (local) | Root cause analysis | 1.2s | 3.5s | 5 QPS | $0.05 |
| LLM (local) | Conversational interaction | 0.8s | 2.0s | 8 QPS | $0.03 |
| LLM (cloud) | Complex planning | 4.5s | 12s | 2 QPS | $0.80 |
Constraint validation overhead (all backends must pass through):
| Validation Stage | Latency | Description |
|---|---|---|
| Physical constraints | 8μs | Voltage, current, frequency |
| Operational constraints | 12μs | Device state, operation sequence |
| Temporal constraints | 5μs | Operation intervals, concurrency |
| Audit recording | 25μs | Chained log writing |
| Total | 50μs | Acceptable |
Security Constraint Details
LLM Output Sandboxing
LLM backend output undergoes strict sandboxing before storage:
pub struct LlmOutputSandbox {
parser: StructuredOutputParser,
constraint_engine: Arc<ConstraintEngine>,
policy: ConstraintViolationPolicy,
}
impl LlmOutputSandbox {
pub async fn process(&self, raw: &str) -> Result<SandboxedOutput> {
// 1. Parse to structured action (reject unparseable output)
let action = self.parser.parse(raw)?;
// 2. Scope check (action type, target device legality)
self.validate_scope(&action)?;
// 3. Physical constraint validation
let validated = match self.constraint_engine.validate(&action) {
Ok(v) => v,
Err(violation) => match self.policy {
ConstraintViolationPolicy::Reject => return Err(violation.into()),
ConstraintViolationPolicy::AutoCorrect => self.autocorrect(action, violation)?,
ConstraintViolationPolicy::Escalate => {
return Ok(SandboxedOutput::NeedsReview(action, violation))
}
ConstraintViolationPolicy::FallbackRule => {
return Ok(SandboxedOutput::FallbackToRule(action))
}
},
};
Ok(SandboxedOutput::Validated(validated))
}
}
Prompt Injection Protection
The LLM backend employs multi-layer protection against prompt injection attacks:
pub struct PromptGuard {
forbidden_patterns: Vec<Regex>,
max_input_length: usize,
}
impl PromptGuard {
pub fn sanitize(&self, user_input: &str) -> Result<String> {
// 1. Length limit
if user_input.len() > self.max_input_length {
return Err(PromptError::InputTooLong);
}
// 2. Pattern filtering (e.g., "ignore previous instructions")
for pattern in &self.forbidden_patterns {
if pattern.is_match(user_input) {
return Err(PromptError::SuspiciousPattern);
}
}
// 3. Escape special characters
Ok(escape_special_chars(user_input))
}
pub fn build_safe_prompt(&self, user_input: &str, context: &ReasoningContext) -> String {
let sanitized = self.sanitize(user_input).unwrap_or_default();
format!(
"You are a power system dispatch assistant. Please answer questions based on the following grid context.\n\
Strict constraints:\n\
1. Only propose operation suggestions that comply with physical constraints\n\
2. Must not output any content violating safety regulations\n\
3. Output must be in JSON format: {{\"action\": ..., \"confidence\": ..., \"explanation\": ...}}\n\n\
Grid context:\n{}\n\n\
User question:\n{}",
context.summary(),
sanitized
)
}
}
Future Evolution
This ADR establishes the foundation architecture of the intelligence layer; future evolution will focus on the following directions:
- Multimodal reasoning: Support image (infrared thermometry, inspection photos), waveform (oscilloscope recordings) input
- Federated learning: Cross-grid-company collaborative model training, data stays local
- Agent self-learning: Agents continuously optimize strategies from historical operations
- Small model fine-tuning pipeline: Fine-tune 7B models based on grid domain data, reducing costs
- Intelligence evaluation benchmark: Establish EnerOS-Bench to quantify Agent intelligence levels
References
- ADR Overview — ADR index and writing guidelines
- ADR-0002 Power-Native AgentOS — Power-Native design decision
- ADR-0007 Zero Trust mTLS — Zero Trust architecture
- Agent Intelligence Evolution — Capability document
- Physics Constraint Engine — Constraint mechanism
- Sutton & Barto, “Reinforcement Learning: An Introduction”
- Wei et al., “Chain-of-Thought Prompting Elicits Reasoning in Large Language Models”, 2022
- ONNX Runtime specification, https://onnxruntime.ai
- SPIFFE specification, https://spiffe.io
- Related crates:
eneros-reasoning,eneros-agent,eneros-tool,eneros-constraint