核心类型
来自 adk-core 的基础类型和 trait,它们构成了 ADK-Rust 的基础。
Content 和 Part
ADK 中的每条消息都流经 Content 和 Part。理解这些类型对于使用 Agent、Tool 和回调至关重要。
Content
Content 表示对话中的一条单一消息。它包含一个 role(发送者)和一个或多个 parts(实际内容)。
角色:
"user"- 来自用户的消息"model"- 来自 AI 模型的响应"tool"- Tool 执行的结果
use adk_core::Content;
// Simple text message from user
let user_msg = Content::new("user")
.with_text("What's the weather like?");
// Model response
let model_msg = Content::new("model")
.with_text("The weather is sunny and 72°F.");
// Multiple text parts in one message
let detailed_msg = Content::new("user")
.with_text("Here's my question:")
.with_text("What is the capital of France?");
多模态 Content:
Content 可以包含图像、音频、PDF 以及其他二进制数据和文本:
// Image from bytes (e.g., read from file)
let image_bytes = std::fs::read("photo.jpg")?;
let content = Content::new("user")
.with_text("What's in this image?")
.with_inline_data("image/jpeg", image_bytes);
// Image from URL (model fetches it)
let content = Content::new("user")
.with_text("Describe this image")
.with_file_uri("image/png", "https://example.com/chart.png");
// PDF document
let pdf_bytes = std::fs::read("report.pdf")?;
let content = Content::new("user")
.with_text("Summarize this document")
.with_inline_data("application/pdf", pdf_bytes);
Part
Part 是一个 enum,表示消息中不同类型的内容:
pub enum Part {
// Plain text
Text { text: String },
// Binary data embedded in the message
InlineData { mime_type: String, data: Vec<u8> },
// Reference to external file (URL or cloud storage)
FileData { mime_type: String, file_uri: String },
// Model requesting a tool call
FunctionCall { name: String, args: Value, id: Option<String> },
// Result of a tool execution
FunctionResponse { function_response: FunctionResponseData, id: Option<String> },
}
直接创建 Part:
use adk_core::Part;
// Text part
let text = Part::text_part("Hello, world!");
// Image from bytes
let image = Part::inline_data("image/png", png_bytes);
// Image from URL
let remote_image = Part::file_data("image/jpeg", "https://example.com/photo.jpg");
检查 Part:
// Get text content (returns None for non-text parts)
if let Some(text) = part.text() {
println!("Text: {}", text);
}
// Get MIME type (for InlineData and FileData)
if let Some(mime) = part.mime_type() {
println!("MIME type: {}", mime);
}
// Get file URI (for FileData only)
if let Some(uri) = part.file_uri() {
println!("File URI: {}", uri);
}
// Check if part contains media (image, audio, video, etc.)
if part.is_media() {
println!("This part contains binary media");
}
迭代 Part:
for part in &content.parts {
match part {
Part::Text { text } => println!("Text: {}", text),
Part::InlineData { mime_type, data } => {
println!("Binary data: {} ({} bytes)", mime_type, data.len());
}
Part::FileData { mime_type, file_uri } => {
println!("File: {} at {}", mime_type, file_uri);
}
Part::FunctionCall { name, args, .. } => {
println!("Tool call: {}({})", name, args);
}
Part::FunctionResponse { function_response, .. } => {
println!("Tool result: {} -> {}",
function_response.name,
function_response.response
);
}
}
}
Agent Trait
Agent trait 是 ADK 中所有 Agent 的核心抽象。每种 Agent 类型——LLM Agent、工作流 Agent、自定义 Agent——都实现了这个 trait。
#[async_trait]
pub trait Agent: Send + Sync {
/// 此 Agent 的唯一标识符
fn name(&self) -> &str;
/// 此 Agent 功能的人类可读描述
fn description(&self) -> &str;
/// 子 Agent(用于 SequentialAgent, ParallelAgent 等工作流 Agent)
fn sub_agents(&self) -> &[Arc<dyn Agent>];
/// 执行 Agent 并返回 EventStream
async fn run(&self, ctx: Arc<dyn InvocationContext>) -> Result<EventStream>;
}
要点:
name(): 用于日志记录、Agent 间转移和身份识别。在多 Agent 系统中必须是唯一的。description(): 当 Agent 用作 FunctionTool 或用于路由决策时,会显示给 LLMs。sub_agents(): 返回子 Agent。对于叶子 Agent (LlmAgent) 为空,对于容器 (SequentialAgent, ParallelAgent) 则会填充。run(): 主要执行方法。接收上下文并返回 EventStream。
为何使用 EventStream?
Agent 返回 EventStream(一个 Result<Event> 流),而不是单个响应,原因如下:
- 流式传输:响应可以按 token 流式传输,以提供更好的用户体验
- Tool 调用:执行期间会发生多次 Tool 调用和响应
- 状态变更:状态更新会作为事件发出
- 转移:Agent 转移通过事件发出信号
工具 Trait
工具将 agent 的能力扩展到对话之外。它们允许 agent 与 API、数据库、文件系统交互,或执行计算。
#[async_trait]
pub trait Tool: Send + Sync {
/// Tool name (used in function calls from the model)
fn name(&self) -> &str;
/// Description shown to the LLM to help it decide when to use this tool
fn description(&self) -> &str;
/// JSON Schema defining the expected parameters
fn parameters_schema(&self) -> Option<Value> { None }
/// JSON Schema defining the response format
fn response_schema(&self) -> Option<Value> { None }
/// Whether this tool runs asynchronously (returns task ID immediately)
fn is_long_running(&self) -> bool { false }
/// Execute the tool with given arguments
async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value>;
}
要点:
name(): 模型用于调用此工具的函数名称。保持简短和描述性(例如,get_weather,search_database)。description(): 对于模型理解何时使用此工具至关重要。具体说明它做什么以及何时使用它。parameters_schema(): 告知模型应提供哪些参数的 JSON Schema。如果没有此项,模型将进行猜测。execute(): 接收解析后的参数作为serde_json::Value。将结果作为 JSON 返回。
实现自定义工具:
use adk_core::{Tool, ToolContext, Result};
use async_trait::async_trait;
use serde_json::{json, Value};
use std::sync::Arc;
struct WeatherTool {
api_key: String,
}
#[async_trait]
impl Tool for WeatherTool {
fn name(&self) -> &str {
"get_weather"
}
fn description(&self) -> &str {
"Get current weather for a city. Use this when the user asks about weather conditions."
}
fn parameters_schema(&self) -> Option<Value> {
Some(json!({
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "City name (e.g., 'London', 'New York')"
},
"units": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "Temperature units"
}
},
"required": ["city"]
}))
}
async fn execute(&self, _ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
let city = args["city"].as_str().unwrap_or("Unknown");
let units = args["units"].as_str().unwrap_or("celsius");
// Call weather API here...
Ok(json!({
"city": city,
"temperature": 22,
"units": units,
"condition": "sunny"
}))
}
}
长时间运行的工具:
对于需要长时间运行的操作(文件处理、外部 API 调用),将工具标记为长时间运行:
fn is_long_running(&self) -> bool { true }
长时间运行的工具会立即返回一个任务 ID。模型被指示在任务待处理期间不要再次调用该工具。
Toolset 特性
Toolset 根据上下文动态提供工具。在以下情况使用此特性:
- 工具依赖于用户权限
- 工具从外部源(MCP servers)加载
- 工具可用性在执行期间发生变化
#[async_trait]
pub trait Toolset: Send + Sync {
/// Toolset 标识符
fn name(&self) -> &str;
/// 返回当前上下文可用的工具
async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>>;
}
示例:基于权限的 Toolset:
struct AdminToolset {
admin_tools: Vec<Arc<dyn Tool>>,
user_tools: Vec<Arc<dyn Tool>>,
}
#[async_trait]
impl Toolset for AdminToolset {
fn name(&self) -> &str { "admin_toolset" }
async fn tools(&self, ctx: Arc<dyn ReadonlyContext>) -> Result<Vec<Arc<dyn Tool>>> {
if ctx.user_id().starts_with("admin_") {
Ok(self.admin_tools.clone())
} else {
Ok(self.user_tools.clone())
}
}
}
Context 特性
Contexts 在执行期间向 agents 和 tools 提供信息和服务。Context 特性存在一个层级结构,每个层级都增加了更多功能。
ReadonlyContext
随处可用的基本信息:
pub trait ReadonlyContext: Send + Sync {
/// 此调用的唯一 ID
fn invocation_id(&self) -> &str;
/// 当前正在执行的 agent 的名称
fn agent_name(&self) -> &str;
/// 用户标识符
fn user_id(&self) -> &str;
/// 应用程序名称
fn app_name(&self) -> &str;
/// Session 标识符
fn session_id(&self) -> &str;
/// 触发此调用的用户输入
fn user_content(&self) -> &Content;
}
CallbackContext
添加 artifact 访问(扩展 ReadonlyContext):
pub trait CallbackContext: ReadonlyContext {
/// 访问 artifact 存储(如果已配置)
fn artifacts(&self) -> Option<Arc<dyn Artifacts>>;
}
ToolContext
用于 tool 执行(扩展 CallbackContext):
pub trait ToolContext: CallbackContext {
/// 触发此 tool 的 function call ID
fn function_call_id(&self) -> &str;
/// 获取当前事件 actions(transfer、escalate 等)
fn actions(&self) -> EventActions;
/// 设置事件 actions(例如,触发 transfer)
fn set_actions(&self, actions: EventActions);
/// 搜索长期 memory
async fn search_memory(&self, query: &str) -> Result<Vec<MemoryEntry>>;
}
InvocationContext
agent 执行的完整上下文(扩展 CallbackContext):
pub trait InvocationContext: CallbackContext {
/// 正在执行的 agent
fn agent(&self) -> Arc<dyn Agent>;
/// Memory 服务(如果已配置)
fn memory(&self) -> Option<Arc<dyn Memory>>;
/// 具有状态和历史记录的当前 Session
fn session(&self) -> &dyn Session;
/// 执行配置
fn run_config(&self) -> &RunConfig;
/// 标志此调用应结束
fn end_invocation(&self);
/// 检查调用是否已结束
fn ended(&self) -> bool;
}
会话和状态
会话跟踪对话。状态在会话中存储数据。
会话
pub trait Session: Send + Sync {
/// 唯一会话标识符
fn id(&self) -> &str;
/// 此会话所属的应用程序
fn app_name(&self) -> &str;
/// 拥有此会话的用户
fn user_id(&self) -> &str;
/// 可变状态存储
fn state(&self) -> &dyn State;
/// 此对话中的先前消息
fn conversation_history(&self) -> Vec<Content>;
}
状态
具有作用域前缀的键值存储:
pub trait State: Send + Sync {
/// 通过键获取值
fn get(&self, key: &str) -> Option<Value>;
/// 设置值
fn set(&mut self, key: String, value: Value);
/// 获取所有键值对
fn all(&self) -> HashMap<String, Value>;
}
状态前缀:
状态键使用前缀来控制作用域和持久性:
| 前缀 | 作用域 | 持久性 | 用例 |
|---|---|---|---|
user: | 用户级别 | 跨所有会话 | 用户偏好设置 |
app: | 应用程序级别 | 应用程序范围 | 共享配置 |
temp: | 回合级别 | 每回合清除 | 临时计算数据 |
| (无) | 会话级别 | 仅此会话 | 对话上下文 |
// 在回调或工具中
let state = ctx.session().state();
// 用户偏好设置 (跨会话持久化)
state.set("user:theme".into(), json!("dark"));
// 会话特定数据
state.set("current_topic".into(), json!("weather"));
// 临时数据 (此回合后清除)
state.set("temp:step_count".into(), json!(1));
// 读取值
if let Some(theme) = state.get("user:theme") {
println!("Theme: {}", theme);
}
错误处理
ADK 为所有操作使用统一的错误类型:
pub enum AdkError {
Agent(String), // Agent 执行错误
Tool(String), // Tool 执行错误
Model(String), // LLM API 错误
Session(String), // Session 存储错误
Artifact(String), // Artifact 存储错误
Config(String), // 配置错误
Io(std::io::Error), // 文件/网络 I/O 错误
Json(serde_json::Error), // JSON 解析错误
}
pub type Result<T> = std::result::Result<T, AdkError>;
工具中的错误处理:
async fn execute(&self, ctx: Arc<dyn ToolContext>, args: Value) -> Result<Value> {
let city = args["city"]
.as_str()
.ok_or_else(|| AdkError::Tool("Missing 'city' parameter".into()))?;
let response = reqwest::get(&format!("https://api.weather.com/{}", city))
.await
.map_err(|e| AdkError::Tool(format!("API error: {}", e)))?;
Ok(json!({ "weather": "sunny" }))
}
EventStream
Agent 返回一个事件流而不是单一响应:
pub type EventStream = Pin<Box<dyn Stream<Item = Result<Event>> + Send>>;
处理事件:
use futures::StreamExt;
let mut stream = agent.run(ctx).await?;
while let Some(result) = stream.next().await {
match result {
Ok(event) => {
// 检查文本内容
if let Some(content) = event.content() {
for part in &content.parts {
if let Some(text) = part.text() {
print!("{}", text);
}
}
}
// 检查状态变化
for (key, value) in event.state_delta() {
println!("State changed: {} = {}", key, value);
}
// 检查这是否是最终响应
if event.is_final_response() {
println!("\n[Done]");
}
}
Err(e) => {
eprintln!("Error: {}", e);
break;
}
}
}
Llm 特性
所有 LLM 提供商都实现的特性:
#[async_trait]
pub trait Llm: Send + Sync {
/// 模型标识符(例如:"gemini-2.0-flash", "gpt-4o")
fn name(&self) -> &str;
/// 生成内容(流式或非流式)
async fn generate_content(
&self,
request: LlmRequest,
stream: bool,
) -> Result<LlmResponseStream>;
}
LlmRequest:
pub struct LlmRequest {
pub contents: Vec<Content>, // 对话历史
pub tools: Vec<ToolDeclaration>, // 可用工具
pub system_instruction: Option<String>, // 系统提示
pub config: GenerateContentConfig, // 温度、最大 tokens 等
}
LlmResponse:
pub struct LlmResponse {
pub content: Option<Content>, // 生成的内容
pub finish_reason: Option<FinishReason>, // 生成停止的原因
pub usage: Option<UsageMetadata>, // Token 计数
pub partial: bool, // 这是否是流式数据块?
pub turn_complete: bool, // 本轮是否完成?
}
所有提供商(Gemini、OpenAI、Anthropic、Ollama 等)都实现了此特性,使得它们可以互换使用:
// 通过更改一行代码来切换提供商
let model: Arc<dyn Llm> = Arc::new(GeminiModel::new(&key, "gemini-2.0-flash")?);
// let model: Arc<dyn Llm> = Arc::new(OpenAIClient::new(config)?);
// let model: Arc<dyn Llm> = Arc::new(AnthropicClient::new(config)?);
let agent = LlmAgentBuilder::new("assistant")
.model(model)
.build()?;
上一页: ← Introduction | 下一页: Runner →