Startseite / Artikel

Entwicklung von KI-Agenten in Rust – Teil 8

Teil 8 des Bauens von KI-Agenten in Rust: die nächste Ebene des Agenten mit geordneten Code-Blöcken, Wiederherstellungshinweisen sowie einer Betriebskontrollliste zur Ausführung.

Verlag: REACTAPP.TOP4 Min. Lesezeit883 Wörter

Quellen: 🦀 Building AI Agents in Rust - part 8

Nutzen Sie dies als für Operator bestimmte Neuformulierung der Ideen aus „Building AI Agents in Rust - Teil 8“: klare Phasen, geordnete Codeabschnitte sowie Wiederherstellungshinweise, die auch bei Übergaben erhalten bleiben.

---
name: rust-typing
description: User prefers terse explanations, no hedging.
type: user
---

The user has been writing Rust for years and finds verbose explanations
annoying. Frame technical answers as a peer-to-peer conversation, not as
teaching from a tutorial.
pub async fn read_entrypoint(&self, max_bytes: usize) -> Result<Option<String>, MemoryError> {
    let path = self.dir.join("MEMORY.md");
    match fs::read_to_string(&path).await {
        Ok(text) => {
            let trimmed = text.trim();
            if trimmed.len() <= max_bytes {
                Ok(Some(trimmed.to_string()))
            } else {
                let cut = trimmed[..max_bytes]
                    .rsplit_once('\n')
                    .map(|(a, _)| a)
                    .unwrap_or(&trimmed[..max_bytes]);
                Ok(Some(format!(
                    "{cut}\n\n[memory entrypoint truncated: file exceeds {max_bytes} bytes]"
                )))
            }
        }
        Err(e) if e.kind() == std::io::ErrorKind::NotFound => Ok(None),
        Err(e) => Err(MemoryError::Io(e.to_string())),
    }
}
pub async fn select_relevant_memories<F, Fut>(
    query: &str,
    mem: &FileMemory,
    limit: usize,
    selector: F,
) -> Result<Vec<MemoryEntry>, MemoryError>
where
    F: FnOnce(&str, String) -> Fut,
    Fut: std::future::Future<Output = Result<Vec<String>, MemoryError>>,
{
    let headers = mem.list().await?;
    if headers.is_empty() {
        return Ok(Vec::new());
    }
    let manifest = format_manifest(&headers);
    let picked = selector(query, manifest).await?;
    let mut entries = Vec::with_capacity(limit.min(picked.len()));
    for name in picked.into_iter().take(limit) {
        if let Ok(entry) = mem.load(&name).await {
            entries.push(entry);
        }
    }
    Ok(entries)
}
#[async_trait]
pub trait VectorStore: Send + Sync + 'static {
    async fn upsert(&self, id: &str, embedding: Vec<f32>, payload: Value)
        -> Result<(), MemoryError>;

    async fn search(&self, embedding: &[f32], limit: usize)
        -> Result<Vec<VectorHit>, MemoryError>;

    async fn delete(&self, id: &str) -> Result<(), MemoryError>;
}
#[async_trait]
pub trait Embedder: Send + Sync + 'static {
    async fn embed(&self, text: &str) -> Result<Vec<f32>, MemoryError>;
}
1. embed the user's query → query_vec
2. store.search(&query_vec, 5) → top-5 hits
3. for each hit, return the payload text
let dense_hits = vec_store.search(&q_vec, 20).await?;
let sparse_hits = bm25_index.search(&query, 20).await?;
let fused = rrf(dense_hits, sparse_hits);
let final_hits = reranker.rerank(&query, fused, 5).await?;
fn tool_defs() -> Vec<Tool> {
    vec![
        Tool {
            name: "remember".into(),
            description: "Save a memory for future conversations. Provide a short kebab-case \
                          `name`, a one-line `description`, an optional `type`, and the full `body`.",
            input_schema: serde_json::json!({ /* ... */ }),
        },
        Tool {
            name: "recall".into(),
            description: "Search the memory directory by free-form query. Returns the most \
                          relevant memories (up to 5), routed by a small selector model.",
            input_schema: serde_json::json!({ /* ... */ }),
        },
    ]
}
$ ANTHROPIC_API_KEY=... cargo run -- "remember that I prefer Rust and terse answers"
[memory] /Users/me/.eugene/memory
I've saved that. I'll keep replies short and assume you'd rather see Rust than Python.
$ ANTHROPIC_API_KEY=... cargo run -- "what do you know about me?"
[memory] /Users/me/.eugene/memory
You prefer Rust over Python and want terse answers, no hedging.

Operative Kontrollliste

Die Stufe 0 der Verstärkungsmaßnahmen funktioniert am besten, wenn sie als messbare Größe betrachtet wird. Erfassen Sie vor der Erweiterung des Umfangs ein „goldenes“ Transkript, einen Fehlerfall sowie die Rollback-Hinweise. Ziehen Sie kleine, testbare Einheiten vor umfangreichen Skripten vor. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufverfahren.

Detail der Verstärkungsmaßnahme 0/836: Messen Sie die Ausführungsdauer, die Fehlerklasse sowie den Tokenverbrauch für diese Anmerkung und entscheiden Sie anschließend auf der Grundlage eines festgelegten Fragebogens statt von Einzelbeobachtungen, ob die Änderung beibehalten werden soll.

Für die erste Stufe der Verstärkungsmaßnahmen sollten vor dem Ändern des Codes die Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Neben den funktionalen Ergebnissen sollten Zeiten sowie Kosten für Token oder Abfragen aufgezeichnet werden. Eine frühzeitige Sichtbarkeit der Kosten verhindert überraschende Rechnungen, wenn der Weg von einer Demo-Umgebung in gemeinsam genutzte Umgebungen wechselt.

Detail der Verstärkungsmaßnahmen 1/836: Messen Sie die Gesamtlaufzeit, die Fehlerklasse sowie den Tokenverbrauch für diese Maßnahme und entscheiden Sie anschließend anhand eines festgelegten Fragebogens statt aufgrund von Einzelfallbeobachtungen, ob die Änderung beibehalten werden soll.

Für die Stufe 0 der Verstärkungsmaßnahmen sollten vor dem Ändern des Codes die Eingabedaten, der Verantwortliche für den Schritt sowie die Abbruchkriterien definiert werden. Die Operator sollten in der Lage sein, den Schritt von einem bekannten Checkpoint aus erneut auszuführen, ohne auf versteckte Zustände schließen zu müssen. Die Konfiguration sollte außerhalb des Anwendungscode gespeichert werden. Umgebungsdateien, Geheimdatenspeicher sowie Feature-Flags sollten an einem Ort zusammengefasst sein, den die Operator überprüfen können, ohne den gesamten Codeverlauf durchlesen zu müssen.

Detail der Verstärkungsmaßnahme 0/855: Messen Sie für diese Maßnahme die Ausführungsdauer, die Fehlerklasse sowie den Tokenverbrauch und entscheiden Sie anschließend auf der Grundlage eines festgelegten Fragekatalogs – und nicht aufgrund von Einzelfällen –, ob die Änderung beibehalten werden soll.

Beim Bearbeiten der ersten Stufe der Verstärkungsmaßnahmen sollten Sie zunächst den Vertrag aufschreiben: erforderliche Eingaben, Erfolgszeichen sowie das Vorgehen bei teilweisen Fehlern. Diese Checkliste sorgt dafür, dass spätere Codeänderungen transparent bleiben. Ziehen Sie kleine, testbare Einheiten vor großen Skripten. Wenn ein Schritt fehlschlägt, sollte der Fehler auf eine einzige Verantwortung verweisen und nicht auf ein verworrenes Ablaufschema.

Detail 1/855 zur Verstärkung: Messen Sie die Ausführungszeit, die Fehlerklasse sowie den Tokenverbrauch für diese Maßnahme und entscheiden Sie anschließend anhand eines festgelegten Fragekatalogs statt aufgrund von Einzelfällen, ob die Änderung beibehalten werden soll.