создание AI-агентов на Rust — часть 8
Часть 8 курса по созданию ИИ-агентов на Rust: следующий уровень агента с упорядоченными слотами для кода, записями о восстановлении и чек-листом для его запуска.
Источники: 🦀 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: измерьте время выполнения, класс ошибки и расход токенов для данного пункта, затем решите, следует ли сохранять изменение на основе фиксированного набора критериев, а не на основе устных замечаний.