用 Rust 构建 AI 智能体——第8部分
用Rust构建AI智能体之第八部分:智能体的进阶层级,包含有序的代码模块、恢复指南以及运行时的操作检查表。
可将此内容视为《用 Rust 构建 AI 智能体——第 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:需测量该步骤的运行时间、错误类型以及令牌消耗情况,然后根据固定的评估标准而非主观判断来决定是否保留该变更。