Accueil / Articles

Création d’agents IA en Rust – partie 8

La partie 8 de la création d’agents IA en Rust : la couche suivante de l’agent, avec des emplacements de code ordonnés, des notes de récupération et une liste de contrôle opérationnelle pour son exécution.

Éditeur : REACTAPP.TOP4 min de lecture883 mots

Sources: 🦀 Building AI Agents in Rust - part 8

Utilisez ceci comme une version révisée destinée aux opérateurs des idées présentées dans « Construire des agents IA en Rust - partie 8 » : étapes claires, emplacements de code ordonnés et notes de récupération qui survivent au transfert de responsabilités.

---
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.

Checklist opérationnel

La phase 0 des notes de renforcement fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé.

Détail de renforcement 0/836 : mesurez le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décidez si vous conservez le changement en vous basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Pour la première étape de l’amélioration de sécurité, définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts permet d’éviter des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés.

Détail 1/836 de l’amélioration de sécurité : mesurez le temps d’exécution réel, la catégorie de l’erreur et la consommation de jetons pour cette étape, puis décidez s’il convient de conserver la modification en vous basant sur un ensemble de critères prédéfinis plutôt que sur des observations subjectives.

Pour l’étape 0 des notes de renforcement de sécurité, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.

Détail de renforcement 0/855 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez s’il convient de conserver la modification en vous basant sur un ensemble fixe de critères plutôt que sur des observations subjectives.

Lors de la réalisation de la première étape des notes de renforcement de sécurité, notez d’abord les contrats : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.

Détail de renforcement 1/855 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.