प्लगिन
adk-plugin क्रेट एजेंट के लिए लाइफ़साइकल हुक सिस्टम प्रदान करता है। प्लगिन एजेंट कोड में बदलाव किए बिना टूल कॉल, मॉडल कॉल और निष्पादन इवेंट को इंटरसेप्ट करते हैं — यह लॉगिंग, गार्डरेल्स, कैशिंग, लागत ट्रैकिंग और कस्टम मिडलवेयर के लिए उपयोगी है।
अवलोकन
प्लगिन सिस्टम EnhancedPlugin trait के आधार पर बनाया गया है। आप केवल उन हुक को लागू करते हैं जिनकी आपको आवश्यकता है:
- before_run / after_run — पूरे एजेंट इनवोकेशन को रैप करें
- before_tool / after_tool — टूल निष्पादन को इंटरसेप्ट करें (आर्ग्युमेंट संशोधित करें, शॉर्ट-सर्किट करें, परिणामों का निरीक्षण करें)
- before_model / after_model — LLM कॉल को इंटरसेप्ट करें (रिक्वेस्ट संशोधित करें, रिस्पॉन्स कैश करें)
- on_event — एजेंट द्वारा उत्सर्जित प्रत्येक इवेंट का निरीक्षण करें
प्लगिन प्राथमिकता-क्रम वाली पाइपलाइन में चलते हैं, जिससे संयोज्य मिडलवेयर स्टैक बनाए जा सकते हैं।
इंस्टॉलेशन
[dependencies]
adk-plugin = "2.1.0"
# Or via umbrella crate (included in standard tier)
adk-rust = { version = "2.1.0", features = ["standard"] }
त्वरित शुरुआत
use adk_plugin::{EnhancedPlugin, PluginContext, ToolCallInfo, ToolResultInfo};
use adk_core::{Content, Result};
use async_trait::async_trait;
use serde_json::Value;
struct LoggingPlugin;
#[async_trait]
impl EnhancedPlugin for LoggingPlugin {
fn name(&self) -> &str { "logging" }
fn priority(&self) -> i32 { 0 }
async fn before_tool(
&self,
ctx: &PluginContext,
tool_call: &mut ToolCallInfo,
) -> Result<Option<Value>> {
tracing::info!(
tool = tool_call.name,
args = %tool_call.args,
"tool call started"
);
Ok(None) // Continue to actual tool execution
}
async fn after_tool(
&self,
ctx: &PluginContext,
tool_result: &mut ToolResultInfo,
) -> Result<()> {
tracing::info!(
tool = tool_result.name,
duration_ms = tool_result.duration_ms,
"tool call completed"
);
Ok(())
}
}
EnhancedPlugin Trait
#[async_trait]
pub trait EnhancedPlugin: Send + Sync {
/// Unique plugin identifier
fn name(&self) -> &str;
/// Execution order (lower = runs first) [default: 0]
fn priority(&self) -> i32 { 0 }
/// Called before agent execution starts
async fn before_run(&self, ctx: &PluginContext) -> Result<()> { Ok(()) }
/// Called after agent execution completes
async fn after_run(&self, ctx: &PluginContext) -> Result<()> { Ok(()) }
/// Called before each tool execution.
/// Return Some(value) to short-circuit (skip tool, return this value).
/// Return None to continue with normal execution.
async fn before_tool(
&self,
ctx: &PluginContext,
tool_call: &mut ToolCallInfo,
) -> Result<Option<Value>> { Ok(None) }
/// Called after each tool execution.
/// Can modify the result before it's returned to the LLM.
async fn after_tool(
&self,
ctx: &PluginContext,
tool_result: &mut ToolResultInfo,
) -> Result<()> { Ok(()) }
/// Called before each model (LLM) call.
/// Can modify the request or short-circuit with a cached response.
async fn before_model(
&self,
ctx: &PluginContext,
request: &mut ModelCallInfo,
) -> Result<Option<Content>> { Ok(None) }
/// Called after each model call.
/// Can modify the response before it's processed.
async fn after_model(
&self,
ctx: &PluginContext,
response: &mut ModelResultInfo,
) -> Result<()> { Ok(()) }
/// Called for every event emitted during execution.
async fn on_event(
&self,
ctx: &PluginContext,
event: &Event,
) -> Result<()> { Ok(()) }
}
टूल-कॉल इंटरसेप्शन
आर्ग्युमेंट संशोधन
निष्पादन से पहले टूल आर्ग्युमेंट संशोधित करें:
async fn before_tool(
&self,
ctx: &PluginContext,
tool_call: &mut ToolCallInfo,
) -> Result<Option<Value>> {
// Inject default values
if tool_call.name == "search" {
if tool_call.args.get("limit").is_none() {
tool_call.args["limit"] = serde_json::json!(10);
}
}
Ok(None) // Continue to tool execution
}
शॉर्ट-सर्किट (टूल निष्पादन छोड़ें)
टूल को कॉल किए बिना सीधे कोई मान लौटाएँ:
async fn before_tool(
&self,
ctx: &PluginContext,
tool_call: &mut ToolCallInfo,
) -> Result<Option<Value>> {
// Check cache
let cache_key = format!("{}:{}", tool_call.name, tool_call.args);
if let Some(cached) = self.cache.get(&cache_key).await {
return Ok(Some(cached)); // Short-circuit: return cached result
}
Ok(None) // Cache miss: proceed with tool execution
}
परिणाम संशोधन
निष्पादन के बाद टूल परिणाम संशोधित करें:
async fn after_tool(
&self,
ctx: &PluginContext,
tool_result: &mut ToolResultInfo,
) -> Result<()> {
// Redact sensitive data from results
if let Some(obj) = tool_result.result.as_object_mut() {
if obj.contains_key("ssn") {
obj.insert("ssn".into(), serde_json::json!("***-**-****"));
}
}
Ok(())
}
मॉडल-कॉल इंटरसेप्शन
रिक्वेस्ट संशोधन
भेजे जाने से पहले LLM रिक्वेस्ट संशोधित करें:
async fn before_model(
&self,
ctx: &PluginContext,
request: &mut ModelCallInfo,
) -> Result<Option<Content>> {
// Add system context to every request
if let Some(ref mut instruction) = request.system_instruction {
instruction.push_str("\nAlways respond in JSON format.");
}
Ok(None)
}
रिस्पॉन्स कैशिंग
async fn before_model(
&self,
ctx: &PluginContext,
request: &mut ModelCallInfo,
) -> Result<Option<Content>> {
let key = self.hash_request(request);
if let Some(cached) = self.cache.get(&key).await {
return Ok(Some(cached)); // Return cached response
}
Ok(None)
}
async fn after_model(
&self,
ctx: &PluginContext,
response: &mut ModelResultInfo,
) -> Result<()> {
// Cache the response for future calls
let key = self.hash_request(&response.original_request);
self.cache.set(&key, response.content.clone()).await;
Ok(())
}
प्राथमिकता-आधारित पाइपलाइन
प्लगिन प्राथमिकता क्रम में निष्पादित होते हैं (कम संख्याएँ पहले चलती हैं):
struct AuthPlugin;
impl EnhancedPlugin for AuthPlugin {
fn name(&self) -> &str { "auth" }
fn priority(&self) -> i32 { -10 } // Runs first
// ...
}
struct LogPlugin;
impl EnhancedPlugin for LogPlugin {
fn name(&self) -> &str { "log" }
fn priority(&self) -> i32 { 0 } // Runs second
// ...
}
struct CachePlugin;
impl EnhancedPlugin for CachePlugin {
fn name(&self) -> &str { "cache" }
fn priority(&self) -> i32 { 10 } // Runs last
// ...
}
before_* हुक के लिए, प्लगिन सबसे कम प्राथमिकता वाले से शुरू होकर चलते हैं। after_* हुक के लिए, वे उलटे क्रम में चलते हैं (सबसे अधिक प्राथमिकता वाला पहले), जिससे नेस्टेड मिडलवेयर पैटर्न बनता है।
PluginContext — साझा स्थिति
PluginContext एक इनवोकेशन के दौरान सभी प्लगिन के लिए उपलब्ध साझा स्थिति प्रदान करता है:
use adk_plugin::PluginContext;
async fn before_tool(
&self,
ctx: &PluginContext,
tool_call: &mut ToolCallInfo,
) -> Result<Option<Value>> {
// Read shared state
let call_count: u64 = ctx.get("tool_call_count").unwrap_or(0);
// Write shared state
ctx.set("tool_call_count", call_count + 1);
// Access invocation metadata
let user_id = ctx.user_id();
let session_id = ctx.session_id();
let agent_name = ctx.agent_name();
Ok(None)
}
एजेंट के साथ प्लगिन रजिस्टर करना
use adk_agent::LlmAgentBuilder;
use std::sync::Arc;
let agent = LlmAgentBuilder::new("my_agent")
.model(model)
.instruction("You are a helpful assistant.")
.tool(Arc::new(my_tool))
.plugin(Arc::new(LoggingPlugin))
.plugin(Arc::new(CachePlugin::new(cache_store)))
.plugin(Arc::new(CostTrackingPlugin::new()))
.build()?;
उदाहरण: लागत ट्रैकिंग प्लगइन
use adk_plugin::{EnhancedPlugin, PluginContext, ModelResultInfo};
use adk_core::Result;
use std::sync::atomic::{AtomicU64, Ordering};
struct CostPlugin {
total_tokens: AtomicU64,
}
#[async_trait]
impl EnhancedPlugin for CostPlugin {
fn name(&self) -> &str { "cost_tracker" }
fn priority(&self) -> i32 { 100 }
async fn after_model(
&self,
ctx: &PluginContext,
response: &mut ModelResultInfo,
) -> Result<()> {
if let Some(usage) = &response.usage {
let tokens = usage.prompt_tokens + usage.completion_tokens;
self.total_tokens.fetch_add(tokens as u64, Ordering::Relaxed);
tracing::info!(
total_tokens = self.total_tokens.load(Ordering::Relaxed),
"token usage updated"
);
}
Ok(())
}
}
संबंधित
- पुनः प्रयास और चिंतन — टूल विफलता से पुनर्प्राप्ति के लिए अंतर्निहित प्लगइन
- गार्डरेल्स — इनपुट/आउटपुट सत्यापन प्लगइन
- टेलीमेट्री — अवलोकनीयता एकीकरण
- कॉलबैक — सामान्य मामलों के लिए सरल हुक प्रणाली
पिछला: ← एक्शन नोड्स | अगला: सत्र →