symbi 1.8.0

AI-native agent framework for building autonomous, policy-aware agents that can safely collaborate with humans, other agents, and large language models
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
---
layout: default
title: Leitfaden zur Reasoning-Schleife
nav_exclude: true
description: "Leitfaden zum agentischen Reasoning-Loop-System von Symbiont"
---

# Leitfaden zur Reasoning-Schleife
{: .no_toc }

## Andere Sprachen
{: .no_toc}

[English]reasoning-loop.md | [中文简体]reasoning-loop.zh-cn.md | [Español]reasoning-loop.es.md | [Português]reasoning-loop.pt.md | [日本語]reasoning-loop.ja.md | **Deutsch**

---

Vollstaendiger Leitfaden zur agentischen Reasoning-Schleife von Symbiont: ein typestate-erzwungener Observe-Reason-Gate-Act (ORGA)-Zyklus fuer autonomes Agentenverhalten.
{: .fs-6 .fw-300 }

## Inhaltsverzeichnis
{: .no_toc .text-delta }

1. TOC
{:toc}

---

## Ueberblick

Die Reasoning-Schleife ist die zentrale Ausfuehrungsmaschine fuer autonome Agenten in Symbiont. Sie treibt eine mehrrundige Konversation zwischen einem LLM, einem Policy-Gate und externen Tools durch einen strukturierten Zyklus:

1. **Observe** -- Ergebnisse vorheriger Tool-Ausfuehrungen sammeln
2. **Reason** -- LLM erzeugt vorgeschlagene Aktionen (Tool-Aufrufe oder Textantworten)
3. **Gate** -- Policy-Engine bewertet jede vorgeschlagene Aktion
4. **Act** -- Genehmigte Aktionen werden an Tool-Executors dispatcht

Die Schleife laeuft weiter, bis das LLM eine finale Textantwort erzeugt, Iterations-/Token-Limits erreicht oder ein Timeout eintritt.

### Designprinzipien

- **Compile-Time-Sicherheit**: Ungueltige Phasenwechsel werden zur Kompilierzeit durch Rusts Typsystem erkannt
- **Opt-in-Komplexitaet**: Die Schleife funktioniert nur mit einem Provider und Policy-Gate; Knowledge-Bridge, Cedar-Policies und Human-in-the-Loop sind alle optional
- **Rueckwaertskompatibel**: Das Hinzufuegen neuer Features (wie der Knowledge-Bridge) bricht niemals bestehenden Code
- **Beobachtbar**: Jede Phase emittiert Journal-Events und Tracing-Spans

---

## Schnellstart

### Minimales Beispiel

```rust
use std::sync::Arc;
use symbi_runtime::reasoning::circuit_breaker::CircuitBreakerRegistry;
use symbi_runtime::reasoning::context_manager::DefaultContextManager;
use symbi_runtime::reasoning::conversation::{Conversation, ConversationMessage};
use symbi_runtime::reasoning::executor::DefaultActionExecutor;
use symbi_runtime::reasoning::loop_types::{BufferedJournal, LoopConfig};
use symbi_runtime::reasoning::policy_bridge::DefaultPolicyGate;
use symbi_runtime::reasoning::reasoning_loop::ReasoningLoopRunner;
use symbi_runtime::types::AgentId;

// Set up the runner with default components
let runner = ReasoningLoopRunner {
    provider: Arc::new(my_inference_provider),
    policy_gate: Arc::new(DefaultPolicyGate::permissive()),
    executor: Arc::new(DefaultActionExecutor::default()),
    context_manager: Arc::new(DefaultContextManager::default()),
    circuit_breakers: Arc::new(CircuitBreakerRegistry::default()),
    journal: Arc::new(BufferedJournal::new(1000)),
    knowledge_bridge: None,
};

// Build a conversation
let mut conv = Conversation::with_system("You are a helpful assistant.");
conv.push(ConversationMessage::user("What is 6 * 7?"));

// Run the loop
let result = runner.run(AgentId::new(), conv, LoopConfig::default()).await;

println!("Output: {}", result.output);
println!("Iterations: {}", result.iterations);
println!("Tokens used: {}", result.total_usage.total_tokens);
```

### Mit Tool-Definitionen

```rust
use symbi_runtime::reasoning::inference::ToolDefinition;

let config = LoopConfig {
    max_iterations: 10,
    tool_definitions: vec![
        ToolDefinition {
            name: "web_search".into(),
            description: "Search the web for information".into(),
            parameters: serde_json::json!({
                "type": "object",
                "properties": {
                    "query": { "type": "string" }
                },
                "required": ["query"]
            }),
        },
    ],
    ..Default::default()
};

let result = runner.run(agent_id, conv, config).await;
```

---

## Phasensystem

### Typestate-Muster

Die Schleife nutzt Rusts Typsystem, um gueltige Phasenwechsel zur Kompilierzeit zu erzwingen. Jede Phase ist ein Zero-Sized-Type-Marker:

```rust
pub struct Reasoning;      // LLM produces proposed actions
pub struct PolicyCheck;    // Each action evaluated by the gate
pub struct ToolDispatching; // Approved actions executed
pub struct Observing;      // Results collected for next iteration
```

Die `AgentLoop<Phase>`-Struktur traegt den Schleifenstatus und kann nur Methoden aufrufen, die fuer ihre aktuelle Phase geeignet sind. Zum Beispiel stellt `AgentLoop<Reasoning>` nur `produce_output()` bereit, das self konsumiert und `AgentLoop<PolicyCheck>` zurueckgibt.

Das bedeutet, die folgenden Fehler sind **Kompilierfehler**, keine Laufzeitbugs:
- Policy-Check ueberspringen
- Tools dispatchen, ohne vorher zu schlussfolgern
- Ergebnisse beobachten, ohne zu dispatchen

### Phasenablauf

```mermaid
graph TD
    R["AgentLoop&lt;Reasoning&gt;<br/><code>produce_output()</code>"]
    P["AgentLoop&lt;PolicyCheck&gt;<br/><code>check_policy()</code>"]
    T["AgentLoop&lt;ToolDispatching&gt;<br/><code>dispatch_tools()</code>"]
    O["AgentLoop&lt;Observing&gt;<br/><code>observe_results()</code>"]
    LR["LoopResult"]

    R --> P
    P --> T
    T --> O
    O -->|Continue| R
    O -->|Complete| LR
```

---

## Inference-Provider

Das `InferenceProvider`-Trait abstrahiert ueber LLM-Backends:

```rust
#[async_trait]
pub trait InferenceProvider: Send + Sync {
    async fn complete(
        &self,
        conversation: &Conversation,
        options: &InferenceOptions,
    ) -> Result<InferenceResponse, InferenceError>;

    fn provider_name(&self) -> &str;
    fn default_model(&self) -> &str;
    fn supports_native_tools(&self) -> bool;
    fn supports_structured_output(&self) -> bool;
}
```

### Cloud-Provider (OpenRouter)

Der `CloudInferenceProvider` verbindet sich mit OpenRouter (oder jedem OpenAI-kompatiblen Endpunkt):

```bash
export OPENROUTER_API_KEY="sk-or-..."
export OPENROUTER_MODEL="google/gemini-2.0-flash-001"  # optional
```

```rust
use symbi_runtime::reasoning::providers::cloud::CloudInferenceProvider;

let provider = CloudInferenceProvider::from_env()
    .expect("OPENROUTER_API_KEY must be set");
```

---

## Policy-Gate

Jede vorgeschlagene Aktion durchlaeuft das Policy-Gate vor der Ausfuehrung:

```rust
#[async_trait]
pub trait ReasoningPolicyGate: Send + Sync {
    async fn evaluate_action(
        &self,
        agent_id: &AgentId,
        action: &ProposedAction,
        state: &LoopState,
    ) -> LoopDecision;
}

pub enum LoopDecision {
    Allow,
    Deny { reason: String },
    Modify { modified_action: Box<ProposedAction>, reason: String },
}
```

### Integrierte Gates

- **`DefaultPolicyGate::permissive()`** -- Erlaubt alle Aktionen (Entwicklung/Tests)
- **`DefaultPolicyGate::new()`** -- Standard-Policy-Regeln
- **`OpaPolicyGateBridge`** -- Bruecke zur OPA-basierten Policy-Engine
- **`CedarGate`** -- Cedar Policy Language Integration

### Policy-Ablehnungs-Feedback

Wenn eine Aktion abgelehnt wird, wird der Ablehnungsgrund als Policy-Feedback-Observation an das LLM zurueckgegeben, damit es seinen Ansatz in der naechsten Iteration anpassen kann.

---

## Aktionsausfuehrung

### ActionExecutor-Trait

```rust
#[async_trait]
pub trait ActionExecutor: Send + Sync {
    async fn execute_actions(
        &self,
        actions: &[ProposedAction],
        config: &LoopConfig,
        circuit_breakers: &CircuitBreakerRegistry,
    ) -> Vec<Observation>;
}
```

### Integrierte Executors

| Executor | Beschreibung |
|----------|-------------|
| `DefaultActionExecutor` | Paralleles Dispatching mit tool-spezifischen Timeouts |
| `EnforcedActionExecutor` | Delegiert durch `ToolInvocationEnforcer` an die MCP-Pipeline |
| `KnowledgeAwareExecutor` | Faengt Wissens-Tools ab, delegiert den Rest an den inneren Executor |

### Circuit Breaker

Jedes Tool hat einen zugeordneten Circuit Breaker, der Fehler verfolgt:

- **Closed** (normal): Tool-Aufrufe werden normal ausgefuehrt
- **Open** (ausgeloest): Zu viele aufeinanderfolgende Fehler; Aufrufe werden sofort abgelehnt
- **Half-open** (probing): Begrenzte Aufrufe erlaubt, um Wiederherstellung zu testen

```rust
let circuit_breakers = CircuitBreakerRegistry::new(CircuitBreakerConfig {
    failure_threshold: 3,
    recovery_timeout: Duration::from_secs(60),
    half_open_max_calls: 1,
});
```

---

## Wissens-Reasoning-Bruecke

Die `KnowledgeBridge` verbindet den Wissensspeicher des Agenten (hierarchischer Speicher, Wissensbasis, Vektorsuche) mit der Reasoning-Schleife.

### Einrichtung

```rust
use symbi_runtime::reasoning::knowledge_bridge::{KnowledgeBridge, KnowledgeConfig};

let bridge = Arc::new(KnowledgeBridge::new(
    context_manager.clone(),  // Arc<dyn context::ContextManager>
    KnowledgeConfig {
        max_context_items: 5,
        relevance_threshold: 0.3,
        auto_persist: true,
    },
));

let runner = ReasoningLoopRunner {
    // ... other fields ...
    knowledge_bridge: Some(bridge),
};
```

### Funktionsweise

**Vor jedem Reasoning-Schritt:**
1. Suchbegriffe werden aus aktuellen Benutzer-/Tool-Nachrichten extrahiert
2. `query_context()` und `search_knowledge()` rufen relevante Elemente ab
3. Ergebnisse werden formatiert und als Systemnachricht injiziert (ersetzen die vorherige Injektion)

**Waehrend des Tool-Dispatchings:**
Der `KnowledgeAwareExecutor` faengt zwei spezielle Tools ab:

- **`recall_knowledge`** -- Durchsucht die Wissensbasis und gibt formatierte Ergebnisse zurueck
  ```json
  { "query": "capital of France", "limit": 5 }
  ```

- **`store_knowledge`** -- Speichert ein neues Faktum als Subjekt-Praedikat-Objekt-Tripel
  ```json
  { "subject": "Earth", "predicate": "has", "object": "one moon", "confidence": 0.95 }
  ```

Alle anderen Tool-Aufrufe werden unveraendert an den inneren Executor delegiert.

**Nach Abschluss der Schleife:**
Wenn `auto_persist` aktiviert ist, extrahiert die Bridge Assistenten-Antworten und speichert sie als Arbeitsspeicher fuer zukuenftige Konversationen.

### Rueckwaertskompatibilitaet

Das Setzen von `knowledge_bridge: None` laesst den Runner sich identisch wie zuvor verhalten -- keine Kontextinjektion, keine Wissens-Tools, keine Persistenz.

---

## Konversationsverwaltung

### Konversationstyp

`Conversation` verwaltet eine geordnete Sequenz von Nachrichten mit Serialisierung in OpenAI- und Anthropic-API-Formate:

```rust
let mut conv = Conversation::with_system("You are a helpful assistant.");
conv.push(ConversationMessage::user("Hello"));
conv.push(ConversationMessage::assistant("Hi there!"));

// Serialize for API calls
let openai_msgs = conv.to_openai_messages();
let (system, anthropic_msgs) = conv.to_anthropic_messages();
```

### Token-Budget-Durchsetzung

Der In-Loop-`ContextManager` (nicht zu verwechseln mit dem Wissens-`ContextManager`) verwaltet das Konversations-Token-Budget:

- **Sliding Window**: Aelteste Nachrichten zuerst entfernen
- **Observation Masking**: Ausfuehrliche Tool-Ergebnisse ausblenden
- **Anchored Summary**: Systemnachricht + N aktuelle Nachrichten beibehalten

---

## Dauerhaftes Journal

Jeder Phasenwechsel emittiert einen `JournalEntry` an den konfigurierten `JournalWriter`:

```rust
pub struct JournalEntry {
    pub sequence: u64,
    pub timestamp: DateTime<Utc>,
    pub agent_id: AgentId,
    pub iteration: u32,
    pub event: LoopEvent,
}

pub enum LoopEvent {
    Started { agent_id, config },
    ReasoningComplete { iteration, actions, usage },
    PolicyEvaluated { iteration, action_count, denied_count },
    ToolsDispatched { iteration, tool_count, duration },
    ObservationsCollected { iteration, observation_count },
    Terminated { reason, iterations, total_usage, duration },
    RecoveryTriggered { iteration, tool_name, strategy, error },
}
```

Das Standard-`BufferedJournal` speichert Eintraege im Speicher. Produktionsumgebungen koennen `JournalWriter` fuer persistente Speicherung implementieren.

---

## Konfiguration

### LoopConfig

```rust
pub struct LoopConfig {
    pub max_iterations: u32,        // Default: 25
    pub max_total_tokens: u32,      // Default: 100,000
    pub timeout: Duration,          // Default: 5 minutes
    pub default_recovery: RecoveryStrategy,
    pub tool_timeout: Duration,     // Default: 30 seconds
    pub max_concurrent_tools: usize, // Default: 5
    pub context_token_budget: usize, // Default: 32,000
    pub tool_definitions: Vec<ToolDefinition>,
}
```

### Wiederherstellungsstrategien

Wenn die Tool-Ausfuehrung fehlschlaegt, kann die Schleife verschiedene Wiederherstellungsstrategien anwenden:

| Strategie | Beschreibung |
|-----------|-------------|
| `Retry` | Wiederholung mit exponentiellem Backoff |
| `Fallback` | Alternative Tools ausprobieren |
| `CachedResult` | Gecachtes Ergebnis verwenden, falls aktuell genug |
| `LlmRecovery` | LLM nach einem alternativen Ansatz fragen |
| `Escalate` | An eine menschliche Operator-Warteschlange weiterleiten |
| `DeadLetter` | Aufgeben und den Fehler protokollieren |

---

## Testen

### Unit-Tests (kein API-Key erforderlich)

```bash
cargo test -j2 -p symbi-runtime --lib -- reasoning::knowledge
```

### Integrationstests mit Mock-Provider

```bash
cargo test -j2 -p symbi-runtime --test knowledge_reasoning_tests
```

### Live-Tests mit echtem LLM

```bash
OPENROUTER_API_KEY="sk-or-..." OPENROUTER_MODEL="google/gemini-2.0-flash-001" \
  cargo test -j2 -p symbi-runtime --features http-input --test reasoning_live_tests -- --nocapture
```

---

## Implementierungsphasen

Die Reasoning-Schleife wurde in fuenf Phasen aufgebaut, die jeweils Faehigkeiten hinzufuegen:

| Phase | Fokus | Kernkomponenten |
|-------|-------|----------------|
| **1** | Kern-Schleife | `conversation`, `inference`, `phases`, `reasoning_loop` |
| **2** | Resilienz | `circuit_breaker`, `executor`, `context_manager`, `policy_bridge` |
| **3** | DSL-Integration | `human_critic`, `pipeline_config`, REPL-Builtins |
| **4** | Multi-Agent | `agent_registry`, `critic_audit`, `saga` |
| **5** | Beobachtbarkeit | `cedar_gate`, `journal`, `metrics`, `scheduler`, `tracing_spans` |
| **Bridge** | Wissen | `knowledge_bridge`, `knowledge_executor` |
| **orga-adaptive** | Erweitert | `tool_profile`, `progress_tracker`, `pre_hydrate`, erweitertes `knowledge_bridge` |

---

## Erweiterte Primitiven (orga-adaptive)

Das `orga-adaptive` Feature Gate fuegt vier erweiterte Faehigkeiten hinzu. Siehe den [vollstaendigen Leitfaden](orga-adaptive.md) fuer Details.

| Primitive | Zweck |
|-----------|---------|
| **Tool Profile** | Glob-basierte Filterung der fuer das LLM sichtbaren Tools |
| **Progress Tracker** | Schrittweise Wiederholungslimits mit Stuck-Loop-Erkennung |
| **Pre-Hydration** | Deterministischer Kontext-Pre-Fetch aus Aufgabeneingabe-Referenzen |
| **Scoped Conventions** | Verzeichnisspezifischer Konventionsabruf ueber `recall_knowledge` |

```rust
let config = LoopConfig {
    tool_profile: Some(ToolProfile::include_only(&["search_*", "file_*"])),
    pre_hydration: Some(PreHydrationConfig::default()),
    ..Default::default()
};
```

---

## Naechste Schritte

- **[Laufzeit-Architektur]runtime-architecture.md** -- Vollstaendige Systemarchitektur-Uebersicht
- **[Sicherheitsmodell]security-model.md** -- Policy-Durchsetzung und Audit-Trails
- **[DSL-Leitfaden]dsl-guide.md** -- Agenten-Definitionssprache
- **[API-Referenz]api-reference.md** -- Vollstaendige API-Dokumentation