85b92ae9d4
按 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
61 lines
2.7 KiB
Rust
61 lines
2.7 KiB
Rust
//! quick_start —— 30 行最小可运行示例,展示 Agent / BaseTool / Builder / Session 四层抽象。
|
|
//!
|
|
//! 运行:`cargo run --example quick_start`(离线,零配置)
|
|
|
|
use std::sync::Arc;
|
|
use agcore::agent::{Agent, AgentBuilder, AgentSession};
|
|
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, ToolError, ToolRegistry};
|
|
use async_trait::async_trait;
|
|
use serde_json::{Value, json};
|
|
|
|
struct Greeter;
|
|
impl Agent for Greeter {
|
|
fn name(&self) -> &str { "greeter" }
|
|
fn system_prompt(&self) -> Option<&str> { Some("中文助手,先调用 echo 工具,再总结。") }
|
|
}
|
|
|
|
struct EchoTool;
|
|
#[async_trait]
|
|
impl BaseTool for EchoTool {
|
|
fn name(&self) -> &str { "echo" }
|
|
fn description(&self) -> &str { "回显文本" }
|
|
fn parameters(&self) -> Value {
|
|
json!({"type":"object","properties":{"text":{"type":"string"}},"required":["text"]})
|
|
}
|
|
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}")}))
|
|
}
|
|
}
|
|
|
|
fn resp(content: Vec<ContentBlock>, stop: StopReason, u: (u32, u32)) -> MessageResponse {
|
|
MessageResponse { id: String::new(), model: "mock".into(), message: Message::Assistant { content },
|
|
usage: Usage::from_input_output(u.0, u.1), stop_reason: stop, extra: Default::default() }
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
let mut registry = ToolRegistry::new();
|
|
registry.register(Arc::new(EchoTool)).unwrap();
|
|
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: "EchoTool 已收到您的消息并完成回传。".into() }],
|
|
StopReason::Stop, (8, 16)),
|
|
]));
|
|
let bundle = Arc::new(AgentBuilder::new()
|
|
.provider(provider).tool_registry(Arc::new(registry))
|
|
.hook_executor(Arc::new(HookExecutor::new())).build().unwrap());
|
|
let mut session = AgentSession::new(Arc::new(Greeter), "qs", bundle);
|
|
let resp = session.submit_turn("你好").await.unwrap();
|
|
let text = resp.text();
|
|
println!("LLM: {text}");
|
|
assert!(text.contains("收到"), "响应应包含'收到'字样: {text}");
|
|
println!("\n✓ quick_start 完成");
|
|
}
|