fix(examples): 修复 Phase 8 端到端示例审查发现的 6 项问题
按 PM/SA/Code Reviewer 三方审计报告修复: 🔴 阻塞修复: - CalcTool 除零 panic: `a / b` 改 `a.checked_div(b).ok_or_else(...)`, b=0 时返回 ToolError::InvalidArguments 而非 panic 🟡 警告修复: - end_to_end.rs 持久化验证注释与实际不符: 显式 drop(backend) 让注释 描述与 Arc 释放顺序一致 - quick_start EchoTool 参数验证: 用 args.get("text").and_then().ok_or_else() 替换 as_str().unwrap_or("") 静默降级, 缺失/类型错误时返回显式错误 💭 一致性修复: - end_to_end.rs EchoTool 与 quick_start 一致化 (format!("收到: {text}")) - quick_start mock 响应文本 "已通过 echo 回传" → "EchoTool 已收到并完成回传" - quick_start 断言改为检查 "收到", 与方案字面要求一致 - quick_start 末尾追加 POSIX trailing newline 验收: cargo test 200/0 + clippy 0 警告 + doc 0 warning + 10 示例 exit 0
This commit is contained in:
+14
-5
@@ -47,7 +47,9 @@ impl BaseTool for EchoTool {
|
||||
json!({"type":"object","properties":{"text":{"type":"string"}},"required":["text"]})
|
||||
}
|
||||
async fn execute(&self, args: Value, _: &ToolContext<'_>) -> Result<Value, ToolError> {
|
||||
Ok(json!({"echoed": args["text"]}))
|
||||
let text = args.get("text").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| ToolError::InvalidArguments("text".into(), "需要 string 类型的 text 参数".into()))?;
|
||||
Ok(json!({"echoed": format!("收到: {text}")}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -69,7 +71,12 @@ impl BaseTool for CalcTool {
|
||||
let a: i64 = parts[0].parse().map_err(|_| ToolError::InvalidArguments("expr".into(), format!("无法解析 '{}'", parts[0])))?;
|
||||
let b: i64 = parts[2].parse().map_err(|_| ToolError::InvalidArguments("expr".into(), format!("无法解析 '{}'", parts[2])))?;
|
||||
let result = match parts[1] {
|
||||
"+" => a + b, "-" => a - b, "*" => a * b, "/" => a / b,
|
||||
"+" => a + b,
|
||||
"-" => a - b,
|
||||
"*" => a * b,
|
||||
"/" => a.checked_div(b).ok_or_else(|| {
|
||||
ToolError::InvalidArguments("expr".into(), "除数不能为 0".into())
|
||||
})?,
|
||||
op => return Err(ToolError::InvalidArguments("expr".into(), format!("不支持的运算符: {op}"))),
|
||||
};
|
||||
Ok(json!({"result": result}))
|
||||
@@ -221,9 +228,11 @@ async fn main() {
|
||||
total.prompt_tokens, total.completion_tokens, total.total_tokens);
|
||||
|
||||
println!("\n=== 持久化验证 ===");
|
||||
drop(session); // 释放 session → bundle Arc 计数 -1
|
||||
drop(bundle); // bundle Arc 计数归零 → registry 被 drop → NoteTool 被 drop
|
||||
// → backend 的最后一个 Arc 引用释放 → SqliteStore Connection 自动 close
|
||||
// 显式释放所有对 backend 的 Arc 引用,确保 SqliteStore Connection 真正关闭。
|
||||
// 释放顺序:session → bundle(间接持有 NoteTool → backend clone)→ backend 局部变量。
|
||||
drop(session); // session.bundle Arc 计数 -1
|
||||
drop(bundle); // bundle Arc 计数归零 → registry → NoteTool → backend clone Arc 计数 2→1
|
||||
drop(backend); // backend 局部变量 Arc 计数 1→0 → SqliteStore::drop → Connection 自动 close
|
||||
let backend2: Arc<dyn MemoryStore> =
|
||||
Arc::new(SqliteStore::open(&db_path).expect("重开 SqliteStore 失败"));
|
||||
let filter = MemoryFilter { prefix: Some("note:".into()), ..Default::default() };
|
||||
|
||||
@@ -8,7 +8,7 @@ use agcore::llm::hooks::HookExecutor;
|
||||
use agcore::llm::mock::MockProvider;
|
||||
use agcore::llm::provider::LlmProvider;
|
||||
use agcore::llm::types::{Usage, message::{ContentBlock, Message}, response_v2::{MessageResponse, StopReason}};
|
||||
use agcore::tools::{BaseTool, ToolContext, ToolRegistry};
|
||||
use agcore::tools::{BaseTool, ToolContext, ToolError, ToolRegistry};
|
||||
use async_trait::async_trait;
|
||||
use serde_json::{Value, json};
|
||||
|
||||
@@ -26,8 +26,10 @@ impl BaseTool for EchoTool {
|
||||
fn parameters(&self) -> Value {
|
||||
json!({"type":"object","properties":{"text":{"type":"string"}},"required":["text"]})
|
||||
}
|
||||
async fn execute(&self, args: Value, _: &ToolContext<'_>) -> Result<Value, agcore::tools::ToolError> {
|
||||
Ok(json!({"echoed": format!("收到: {}", args["text"].as_str().unwrap_or(""))}))
|
||||
async fn execute(&self, args: Value, _: &ToolContext<'_>) -> Result<Value, ToolError> {
|
||||
let text = args.get("text").and_then(|v| v.as_str())
|
||||
.ok_or_else(|| ToolError::InvalidArguments("text".into(), "需要 string 类型的 text 参数".into()))?;
|
||||
Ok(json!({"echoed": format!("收到: {text}")}))
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +45,7 @@ async fn main() {
|
||||
let provider: Arc<dyn LlmProvider> = Arc::new(MockProvider::new(vec![
|
||||
resp(vec![ContentBlock::ToolUse { id: "c1".into(), name: "echo".into(),
|
||||
input: json!({"text": "你好"}) }], StopReason::ToolUse, (5, 8)),
|
||||
resp(vec![ContentBlock::Text { text: "已通过 echo 回传您的消息。".into() }],
|
||||
resp(vec![ContentBlock::Text { text: "EchoTool 已收到您的消息并完成回传。".into() }],
|
||||
StopReason::Stop, (8, 16)),
|
||||
]));
|
||||
let bundle = Arc::new(AgentBuilder::new()
|
||||
@@ -53,6 +55,6 @@ async fn main() {
|
||||
let resp = session.submit_turn("你好").await.unwrap();
|
||||
let text = resp.text();
|
||||
println!("LLM: {text}");
|
||||
assert!(text.contains("echo") || text.contains("回传"), "响应应提及工具回传: {text}");
|
||||
assert!(text.contains("收到"), "响应应包含'收到'字样: {text}");
|
||||
println!("\n✓ quick_start 完成");
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user