1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
//! 审计日志核心 trait 和事件类型
use crate::error::Result;
use crate::guard::GuardDirection;
use crate::tools::permission::ToolPermission;
use chrono::{DateTime, Utc};
use futures::future::BoxFuture;
use serde::{Deserialize, Serialize};
use serde_json::Value;
/// 审计事件
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AuditEvent {
/// Time when the event was recorded.
pub timestamp: DateTime<Utc>,
/// Optional conversation or session identifier.
pub session_id: Option<String>,
/// Agent name associated with the event.
pub agent_name: String,
/// Structured payload describing the event kind.
pub event_type: AuditEventType,
/// Optional distributed tracing identifier.
#[serde(skip_serializing_if = "Option::is_none")]
pub trace_id: Option<String>,
}
impl AuditEvent {
/// Construct a new event stamped with the current UTC time.
pub fn now(session_id: Option<String>, agent_name: String, event_type: AuditEventType) -> Self {
Self {
timestamp: Utc::now(),
session_id,
agent_name,
event_type,
trace_id: None,
}
}
}
/// 审计事件类型
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(tag = "type", rename_all = "snake_case")]
pub enum AuditEventType {
/// Raw user input captured before the agent starts processing.
UserInput {
/// User-provided content.
content: String,
},
/// A model invocation with optional token accounting.
LlmCall {
/// Model name used for the request.
model: String,
#[serde(skip_serializing_if = "Option::is_none")]
/// Prompt token count if the provider reported it.
prompt_tokens: Option<u64>,
#[serde(skip_serializing_if = "Option::is_none")]
/// Completion token count if the provider reported it.
completion_tokens: Option<u64>,
},
/// A tool invocation and its observed outcome.
ToolCall {
/// Tool identifier.
tool: String,
/// Serialized tool input.
input: Value,
/// Tool output text.
output: String,
/// Whether the tool succeeded.
success: bool,
/// Execution duration in milliseconds.
duration_ms: u64,
},
/// A guard rejected input or output.
GuardBlock {
/// Guard name.
guard: String,
/// Whether the guard acted on input or output.
direction: GuardDirection,
/// Human-readable reason.
reason: String,
},
/// A permission policy denied a tool invocation.
PermissionDenied {
/// Tool identifier.
tool: String,
/// Permissions that triggered the denial.
required: Vec<ToolPermission>,
/// Human-readable reason.
reason: String,
},
/// Final answer returned to the caller.
FinalAnswer {
/// Final response content.
content: String,
},
/// 审批请求已发起
ApprovalRequested {
/// 工具名称
tool: String,
/// 参数哈希(用于匹配缓存)
args_hash: String,
/// 风险等级
risk_level: String,
},
/// 审批决策已返回
ApprovalCompleted {
/// 工具名称
tool: String,
/// 决策结果
decision: String,
/// 审批范围
scope: String,
/// 拒绝原因(如果有)
reason: Option<String>,
/// 从请求到决策的耗时(毫秒)
duration_ms: u64,
},
}
/// 审计查询过滤器
#[derive(Debug, Default, Clone)]
pub struct AuditFilter {
/// Restrict results to a specific session.
pub session_id: Option<String>,
/// Restrict results to a specific agent name.
pub agent_name: Option<String>,
/// Inclusive lower timestamp bound.
pub from: Option<DateTime<Utc>>,
/// Inclusive upper timestamp bound.
pub to: Option<DateTime<Utc>>,
/// Maximum number of results to return.
pub limit: Option<usize>,
}
/// 审计日志记录器 trait
pub trait AuditLogger: Send + Sync {
/// Persist one audit event.
fn log<'a>(&'a self, event: AuditEvent) -> BoxFuture<'a, Result<()>>;
/// Query stored events with a filter.
fn query<'a>(&'a self, filter: AuditFilter) -> BoxFuture<'a, Result<Vec<AuditEvent>>>;
}