Creación de agentes de IA en Rust - parte 8
Parte 8 de la creación de agentes de IA en Rust: la capa siguiente del agente, con espacios ordenados para el código, notas de recuperación y una lista de verificación operativa para ejecutarlo.
Úselo como una versión reestructurada dirigida a los operadores de las ideas presentadas en “Building AI Agents in Rust - part 8”: etapas claras, espacios ordenados para el código y notas de recuperación que perduran tras una transferencia de tareas.
---
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.
Lista de verificación operativa
La etapa 0 de las notas de fortalecimiento funciona mejor cuando se trata como una superficie medible. Capture una transcripción de referencia, un caso de fallo y la nota de reversión antes de ampliar el alcance. Prefiera unidades pequeñas y verificables en lugar de scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado.
Detalle de fortalecimiento 0/836: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de anécdotas.
Para la fase 1 de las notas de fortalecimiento, defina los insumos, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Registre los tiempos de ejecución y el costo en tokens o consultas junto con los resultados funcionales. La visibilidad temprana de los costos evita facturas inesperadas cuando el proceso pasa de entornos de demostración a entornos compartidos.
Detalle de fortalecimiento 1/836: mida el tiempo de ejecución, la clase del error y el gasto en tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de en observaciones anecdóticas.
Para la fase 0 de las notas de fortalecimiento, defina las entradas, el responsable del paso y los criterios de finalización antes de modificar el código. Los operadores deben poder volver a ejecutar el paso a partir de un punto de control conocido sin tener que adivinar el estado oculto. Guarde la configuración fuera del código de la aplicación. Los archivos de entorno, los almacenes de secretos y las banderas de funcionalidad deben encontrarse en un lugar que los operadores puedan auditar sin necesidad de leer todo el sistema.
Detalle de fortalecimiento 0/855: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de en observaciones anecdóticas.
Al trabajar en la fase 1 de las notas de fortalecimiento, anote primero el contrato: los datos requeridos, la señal de éxito y qué ocurre en caso de fallo parcial. Esa lista de verificación mantiene honestas las futuras modificaciones del código. Prefiera unidades pequeñas y verificables a scripts extensos. Cuando un paso falla, el fallo debe apuntar a una única responsabilidad y no a un proceso complicado.
Detalle de fortalecimiento 1/855: mida el tiempo de ejecución, la clase del error y el consumo de tokens para esta nota, y luego decida si mantener el cambio basándose en un conjunto fijo de preguntas en lugar de anécdotas.