Главная / Статьи

создание AI-агентов на Rust — часть 8

Часть 8 курса по созданию ИИ-агентов на Rust: следующий уровень агента с упорядоченными слотами для кода, записями о восстановлении и чек-листом для его запуска.

Издатель: REACTAPP.TOP4 мин чтения883 слов

Источники: 🦀 Building AI Agents in Rust - part 8

Используйте это как переработанную версию идей из статьи «Создание AI-агентов на Rust — часть 8», предназначенную для операторов: четкие этапы, упорядоченные блоки кода и записи о восстановлении, сохраняющиеся при передаче задач.

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

Чек-лист операций

Этап 0 по укреплению безопасности работает наилучшим образом, если рассматриваться как измеримая характеристика. Соберите один эталонный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ. Предпочитайте небольшие, тестируемые единицы кода вместо обширных скриптов. При сбое какого-либо шага причина должна быть связана с конкретной функцией, а не с запутанной цепочкой операций.

Подробность укрепления 0/836: измерьте время выполнения, тип ошибки и количество потраченных токенов для этой записи, затем решите оставить изменения на основе определенного набора критериев, а не на основе устных замечаний.

Для первого этапа усиления безопасности необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения, затраты на токены или запросы. Отображение затрат заранее предотвращает неожиданные счета при переходе с демо-среды в общедоступные среды.

Подробности усиления безопасности 1/836: измеряйте время выполнения, класс ошибок и расход токенов для данного этапа, затем принимайте решение о сохранении изменений на основе фиксированного набора критериев, а не на основе устных оценок.

Для этапа 0 записки по укреплению безопасности необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф.

Подробности укрепления 0/855: измеряйте время выполнения, класс ошибок и расход токенов для данной записки, затем принимайте решение о сохранении изменений на основе фиксированного набора критериев, а не на основе устных замечаний.

При работе над первым этапом усиления безопасности сначала запишите контракт: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Лучше использовать небольшие, тестируемые модули вместо обширных скриптов. Когда какой-то шаг терпит неудачу, причина должна указывать на конкретную ответственность, а не на запутанную цепочку операций.

Подробности усиления безопасности 1/855: измерьте время выполнения, класс ошибки и расход токенов для данного пункта, затем решите, следует ли сохранять изменение на основе фиксированного набора критериев, а не на основе устных замечаний.