이벤트
이벤트는 adk-rust에서 대화 기록의 근본적인 구성 요소입니다. 사용자의 메시지, Agent 응답, Tool 실행 등 Agent와의 모든 상호작용은 이벤트로 기록됩니다. 이벤트는 Agent Session의 완전한 실행 추적을 캡처하는 불변 로그를 형성합니다.
개요
이벤트 시스템은 몇 가지 중요한 목적을 수행합니다:
- 대화 기록: 이벤트는 Session 내 모든 상호작용의 시간 순서 기록을 형성합니다.
- 상태 관리: 이벤트는
state_delta필드를 통해 상태 변경을 전달합니다. - 아티팩트 추적: 이벤트는
artifact_delta필드를 통해 아티팩트 작업을 기록합니다. - Agent 조정: 이벤트는 Agent 전송 및 에스컬레이션을 가능하게 합니다.
- 디버깅 및 관찰 가능성: 이벤트는 Agent 동작에 대한 완전한 감사 추적을 제공합니다.
이벤트 구조
Event는 대화에서 단일 상호작용을 나타냅니다. adk-rust는 ADK-Go에서 사용된 디자인 패턴과 일치하도록 LlmResponse를 임베드하는 통합 Event 타입을 사용합니다:
pub struct Event {
pub id: String, // Unique event identifier (UUID)
pub timestamp: DateTime<Utc>, // When the event occurred
pub invocation_id: String, // Links related events in a single invocation
pub branch: String, // For future branching support
pub author: String, // Who created this event (user, agent name, tool name)
pub llm_response: LlmResponse, // Contains content and LLM metadata
pub actions: EventActions, // Side effects and metadata
pub long_running_tool_ids: Vec<String>, // IDs of long-running tools
}
LlmResponse struct는 다음을 포함합니다:
pub struct LlmResponse {
pub content: Option<Content>, // The message content (text, parts, etc.)
pub usage_metadata: Option<UsageMetadata>,
pub finish_reason: Option<FinishReason>,
pub partial: bool, // True for streaming partial responses
pub turn_complete: bool, // True when the turn is complete
pub interrupted: bool, // True if generation was interrupted
pub error_code: Option<String>,
pub error_message: Option<String>,
}
Content는 event.llm_response.content를 통해 일관되게 접근됩니다:
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::Text { text } = part {
println!("{}", text);
}
}
}
주요 필드
-
id: 이 특정 이벤트를 식별하는 고유한 UUID입니다. 이벤트 검색 및 순서 지정에 사용됩니다.
-
timestamp: 이벤트가 생성된 UTC 타임스탬프입니다. 이벤트는 Session에서 시간순으로 정렬됩니다.
-
invocation_id: 동일한 Agent 호출에 속하는 이벤트를 그룹화합니다. Agent가 메시지를 처리할 때 생성된 모든 이벤트(Agent 응답, Tool 호출, 하위-Agent 호출)는 동일한
invocation_id를 공유합니다. -
branch: 향후 브랜칭 기능을 위해 예약되어 있습니다. 현재는 사용되지 않지만, 향후 버전에서 대화 브랜칭을 허용합니다.
-
author: 이벤트를 생성한 주체를 식별합니다:
- 사용자 메시지: 일반적으로 "user" 또는 사용자 식별자
- Agent 응답: Agent의 이름
- Tool 실행: Tool의 이름
- 시스템 이벤트: "system"
-
llm_response: 메시지
Content및 LLM 메타데이터를 포함합니다.event.llm_response.content를 통해Content에 접근합니다.Content타입은 텍스트, 멀티모달 Part(이미지, 오디오) 또는 구조화된 데이터를 포함할 수 있습니다. 일부 이벤트(순수한 상태 업데이트와 같은)는content: None일 수 있습니다.
EventActions
EventActions 구조체는 이벤트와 관련된 메타데이터 및 부수 효과를 포함합니다:
pub struct EventActions {
pub state_delta: HashMap<String, Value>, // State changes to apply
pub artifact_delta: HashMap<String, i64>, // Artifact version changes
pub skip_summarization: bool, // Skip this event in summaries
pub transfer_to_agent: Option<String>, // Transfer control to another agent
pub escalate: bool, // Escalate to human or supervisor
}
state_delta
state_delta 필드는 세션 상태에 대한 변경 사항을 나타내는 키-값 쌍을 포함합니다. 이벤트가 세션에 추가되면, 이러한 변경 사항은 세션의 상태에 병합됩니다.
상태 키는 범위를 제어하기 위해 접두사를 사용할 수 있습니다:
app:key- 애플리케이션 범위 상태 (모든 사용자에게 공유됨)user:key- 사용자 범위 상태 (사용자의 모든 세션에 공유됨)temp:key- 임시 상태 (호출 사이에 지워짐)- 접두사 없음 - 세션 범위 상태 (기본값)
예시:
let mut actions = EventActions::default();
actions.state_delta.insert("user_name".to_string(), json!("Alice"));
actions.state_delta.insert("temp:current_step".to_string(), json!(3));
artifact_delta
artifact_delta 필드는 artifact의 변경 사항을 추적합니다. 키는 artifact 이름이고, 값은 버전 번호입니다. 이를 통해 시스템은 이벤트 동안 어떤 artifact가 생성되거나 수정되었는지 추적할 수 있습니다.
예시:
actions.artifact_delta.insert("report.pdf".to_string(), 1);
actions.artifact_delta.insert("chart.png".to_string(), 2);
skip_summarization
true일 경우, 이 이벤트는 대화 요약에서 제외됩니다. 내부 이벤트, 디버깅 정보 또는 주요 대화 흐름의 일부가 되어서는 안 되는 장황한 Tool 출력에 유용합니다.
transfer_to_agent
agent 이름으로 설정되면, 제어는 해당 agent로 이전됩니다. 이는 한 agent가 다른 agent로 작업을 넘길 수 있는 다중 agent 워크플로우를 가능하게 합니다. 대상 agent는 하위 agent로 구성되어야 합니다.
예시:
actions.transfer_to_agent = Some("specialist_agent".to_string());
escalate
true일 경우, 대화가 사람 운영자 또는 감독 agent에게 에스컬레이션되어야 함을 알립니다. 특정 에스컬레이션 동작은 애플리케이션의 구현에 따라 달라집니다.
대화 기록 형성
이벤트는 세션 내에서 시간순으로 축적되어 대화 기록을 형성합니다. agent가 요청을 처리할 때:
- 사용자 메시지 이벤트: 사용자의 입력으로 새 이벤트가 생성됩니다.
- Agent 처리: agent는 대화 기록(모든 이전 이벤트)을 받습니다.
- Agent 응답 이벤트: agent의 응답이 새 이벤트로 기록됩니다.
- Tool 실행 이벤트: 각 Tool 호출은 추가 이벤트를 생성할 수 있습니다.
- 상태 업데이트: 모든 이벤트의 상태 델타가 세션 상태에 병합됩니다.
대화 기록은 다음을 통해 구성됩니다:
- 세션에서 모든 이벤트를 시간순으로 검색
- 각 이벤트의 Content를 LLM에 적합한 형식으로 변환
- 축적된 상태 델타의 상태 정보 포함
- 적절할 때
skip_summarization으로 표시된 이벤트 필터링
이벤트 흐름 예시
Session Start
↓
[Event 1] User: "What's the weather in Tokyo?"
↓
[Event 2] Agent: "Let me check that for you."
↓
[Event 3] Tool (weather_api): {"temp": 22, "condition": "sunny"}
↓
[Event 4] Agent: "It's 22°C and sunny in Tokyo."
↓
Session State Updated
각 이벤트는 이전 이벤트들을 기반으로 구축되어, 대화의 완전한 감사 추적을 생성합니다.
이벤트 작업
세션에서 이벤트 접근하기
use adk_rust::session::{SessionService, GetRequest};
// 이벤트와 함께 세션을 검색합니다.
let session = session_service.get(GetRequest {
app_name: "my_app".to_string(),
user_id: "user_123".to_string(),
session_id: session_id.clone(),
num_recent_events: None, // 모든 이벤트 가져오기
after: None,
}).await?;
// 이벤트에 접근합니다.
let events = session.events();
println!("Total events: {}", events.len());
// 이벤트를 반복 처리합니다 (참고: 세션 이벤트는 llm_response.content를 사용합니다).
for i in 0..events.len() {
if let Some(event) = events.at(i) {
println!("Event {}: {} by {} at {}",
event.id,
event.llm_response.content.as_ref().map(|_| "has content").unwrap_or("no content"),
event.author,
event.timestamp
);
}
}
이벤트 세부 정보 검사하기
// 세션에서 특정 이벤트를 가져옵니다 (llm_response.content 사용).
if let Some(event) = events.at(0) {
// 작성자를 확인합니다.
println!("Author: {}", event.author);
// 콘텐츠를 확인합니다 (세션 이벤트는 llm_response.content를 사용합니다).
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::Text { text } = part {
println!("Text: {}", text);
}
}
}
// 상태 변경 사항을 확인합니다.
if !event.actions.state_delta.is_empty() {
println!("State changes:");
for (key, value) in &event.actions.state_delta {
println!(" {} = {}", key, value);
}
}
// Agent 전송을 확인합니다.
if let Some(target) = &event.actions.transfer_to_agent {
println!("Transfers to: {}", target);
}
// 아티팩트를 확인합니다.
if !event.actions.artifact_delta.is_empty() {
println!("Artifacts modified:");
for (name, version) in &event.actions.artifact_delta {
println!(" {} (v{})", name, version);
}
}
}
긴 대화의 경우, 최근 이벤트만 검색하고 싶을 수 있습니다:
이벤트 기록 제한하기
// 마지막 10개 이벤트만 가져옵니다.
let session = session_service.get(GetRequest {
app_name: "my_app".to_string(),
user_id: "user_123".to_string(),
session_id: session_id.clone(),
num_recent_events: Some(10),
after: None,
}).await?;
이벤트 흐름: 생성 및 처리
이벤트가 생성되고 처리되는 방식을 이해하면 프레임워크가 액션과 기록을 관리하는 방법을 명확히 할 수 있습니다.
생성 소스
이벤트는 에이전트 실행 수명 주기의 다양한 지점에서 생성됩니다.
- 사용자 입력: Runner는 사용자 메시지를
author = "user"인 Event로 래핑합니다. - 에이전트 응답: Agent는 응답을 전달하기 위해 Event 객체(
author = agent.name())를 반환합니다. - LLM 출력: 모델 통합 계층은 LLM 출력(텍스트, 함수 호출)을 Event 객체로 변환합니다.
- Tool 결과: Tool 실행 후 프레임워크는 Tool 응답을 포함하는 Event를 생성합니다.
처리 흐름
이벤트가 생성되면 다음 처리 경로를 따릅니다.
- 생성: 이벤트는 소스(agent, tool, 또는 사용자 입력 핸들러)에 의해 생성되고 반환됩니다.
- Runner 수신: agent를 실행하는 Runner가 이벤트를 수신합니다.
- SessionService 처리: Runner는 이벤트를 SessionService로 보내며, SessionService는 다음을 수행합니다.
- 델타 적용:
state_delta를 세션 상태에 병합하고 artifact 기록을 업데이트합니다. - 메타데이터 확정: 고유
id가 없으면 할당하고timestamp를 설정합니다. - 기록에 영구 저장: 이벤트를
session.events에 추가합니다.
- 델타 적용:
- 스트림 출력: Runner는 처리된 이벤트를 호출 애플리케이션에 반환합니다.
이 흐름은 상태 변경 및 기록이 통신 내용과 함께 일관되게 기록되도록 보장합니다.
// Conceptual flow
User Input → Runner → Agent → LLM → Event Generated
↓
SessionService
- Apply state_delta
- Record in history
↓
Event Stream → Application
이벤트 유형 식별
Runner에서 이벤트를 처리할 때 어떤 유형의 이벤트를 다루고 있는지 식별해야 합니다.
Author별
author 필드는 누가 이벤트를 생성했는지 알려줍니다.
match event.author.as_str() {
"user" => println!("User input"),
agent_name => println!("Response from agent: {}", agent_name),
}
Content 유형별
페이로드 유형을 결정하려면 llm_response.content 필드를 확인하세요.
if let Some(content) = &event.llm_response.content {
// Check for text content
let has_text = content.parts.iter().any(|part| {
matches!(part, Part::Text { .. })
});
// Check for function calls (tool requests)
let has_function_call = content.parts.iter().any(|part| {
matches!(part, Part::FunctionCall { .. })
});
// Check for function responses (tool results)
let has_function_response = content.parts.iter().any(|part| {
matches!(part, Part::FunctionResponse { .. })
});
if has_text {
println!("Text message");
} else if has_function_call {
println!("Tool call request");
} else if has_function_response {
println!("Tool result");
}
}
Actions별
제어 신호 및 부작용에 대한 actions 필드를 확인하세요.
// State changes
if !event.actions.state_delta.is_empty() {
println!("Event contains state changes");
}
// Agent transfer
if let Some(target) = &event.actions.transfer_to_agent {
println!("Transfer to agent: {}", target);
}
// Escalation signal
if event.actions.escalate {
println!("Escalation requested");
}
// Skip summarization
if event.actions.skip_summarization {
println!("Skip this event in summaries");
}
이벤트 스트림 작업
Agent를 실행하면 이벤트 스트림을 받게 됩니다. 이를 효과적으로 처리하는 방법은 다음과 같습니다.
Runner에서 이벤트 처리
use futures::StreamExt;
let mut stream = runner.run(
"user_123".to_string(),
"session_id".to_string(),
user_input,
).await?;
while let Some(event_result) = stream.next().await {
match event_result {
Ok(event) => {
// 이벤트를 처리합니다
println!("Event from: {}", event.author);
// 텍스트 content 추출
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::Text { text } = part {
print!("{}", text);
}
}
}
// 상태 변경 확인
if !event.actions.state_delta.is_empty() {
println!("\nState updated: {:?}", event.actions.state_delta);
}
}
Err(e) => {
eprintln!("Error: {}", e);
break;
}
}
}
함수 호출 추출
LLM이 tool을 요청하면, 이벤트에 함수 호출 정보가 포함됩니다.
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::FunctionCall { name, args } = part {
println!("Tool requested: {}", name);
println!("Arguments: {}", args);
// 애플리케이션에서 tool 실행을 여기서 디스패치할 수 있습니다
// tool name과 arguments에 따라
}
}
}
함수 응답 추출
tool이 실행된 후, 결과는 함수 응답으로 반환됩니다.
if let Some(content) = &event.llm_response.content {
for part in &content.parts {
if let Part::FunctionResponse { function_response, .. } = part {
println!("Tool result from: {}", function_response.name);
println!("Response: {}", function_response.response);
// tool 결과를 처리합니다
// LLM은 이를 사용하여 대화를 계속합니다
}
}
}
일반적인 이벤트 패턴
다음은 일반적으로 접하게 될 이벤트 시퀀스입니다.
간단한 텍스트 교환
[Event 1] author="user", content=Text("Hello")
[Event 2] author="assistant", content=Text("Hi! How can I help?")
Tool 사용 흐름
[Event 1] author="user", content=Text("What's the weather?")
[Event 2] author="assistant", content=FunctionCall(name="get_weather", args={...})
[Event 3] author="assistant", content=FunctionResponse(name="get_weather", response={...})
[Event 4] author="assistant", content=Text("It's sunny and 72°F")
상태 업데이트
[Event 1] author="assistant", content=Text("I've saved your preference")
actions.state_delta={"user_theme": "dark"}
Agent 전송
[Event 1] author="router", content=Text("Transferring to specialist")
actions.transfer_to_agent=Some("specialist_agent")
[Event 2] author="specialist_agent", content=Text("I can help with that")
이벤트 메타데이터 및 식별자
이벤트 ID
각 이벤트는 정확한 식별을 위한 고유한 id (UUID)를 가집니다.
println!("Event ID: {}", event.id);
Invocation ID
invocation_id는 단일 사용자 요청부터 최종 응답까지의 모든 이벤트를 그룹화합니다.
// 하나의 상호작용 내 모든 이벤트는 동일한 invocation_id를 공유합니다
println!("Invocation: {}", event.invocation_id);
// 로깅 및 트레이싱에 이를 사용합니다
log::info!("Processing event {} in invocation {}", event.id, event.invocation_id);
타임스탬프
이벤트는 시간 순서 정렬을 위해 타임스탬프가 지정됩니다.
println!("Event occurred at: {}", event.timestamp.format("%Y-%m-%d %H:%M:%S"));
모범 사례
-
이벤트 불변성: 이벤트는 생성 후 절대 수정해서는 안 됩니다. 이벤트는 불변하는 감사 로그를 형성합니다.
-
상태 관리: 상태를 직접 수정하는 대신 모든 상태 변경에
state_delta를 사용하세요. 이렇게 하면 변경 사항이 이벤트 로그에 추적됩니다. -
의미 있는 작성자: 이벤트 로그를 더 쉽게 이해할 수 있도록 명확하고 설명적인 작성자 이름을 설정하세요.
-
선택적 요약: 대화 기록을 복잡하게 만들 수 있는 장황하거나 내부적인 이벤트에
skip_summarization을 사용하세요. -
호출 그룹화: 단일 Agent 호출 중에 생성된 모든 이벤트에 동일한
invocation_id를 사용하여 논리적 그룹화를 유지하세요. -
Artifact 추적: Artifact를 생성하거나 수정할 때 일관성을 유지하기 위해 항상
artifact_delta를 업데이트하세요. -
스트림 처리: 이벤트 스트림을 처리할 때 항상 오류를 처리하세요. 이벤트는 LLM 오류, Tool 실패 또는 네트워크 문제로 인해 실패할 수 있습니다.
-
Content 확인: Part에 접근하기 전에 항상
llm_response.content가Some인지 확인하세요. 일부 이벤트(순수 상태 업데이트와 같은)에는 content가 없을 수 있습니다. -
패턴 매칭: Rust의 패턴 매칭을 사용하여 다양한 이벤트 유형과 content Part를 우아하게 처리하세요.
-
로깅: 디버깅 및 가시성을 위해 단일 사용자 상호작용 내의 모든 이벤트를 연관시키기 위해
invocation_id를 사용하세요.
관련 문서
- Sessions - Session 관리 및 수명 주기
- State Management - Session 상태 작업
- Artifacts - 이진 데이터 관리
- Multi-Agent Systems - Agent 전송 및 조정
- Callbacks - 이벤트 가로채기 및 수정
이전: ← Artifacts | 다음: Telemetry →