在复杂的AI Agent应用中,如多步骤决策、工具调用和长链推理,一个核心挑战是缺乏透明度(即“黑箱”问题)。当Agent的最终输出不符合预期时,我们很难知道它在哪一步做出了错误的决策、调用了错误的工具,或是接收到了不正确的中间输入。为了解决这个问题,我们需要在基础设施层面建立强大的可审计性(Auditability)。
本文将聚焦于如何通过结构化日志(Structured Logging)和事件溯源(Event Sourcing)的理念,确保Agent在执行复杂任务链的每一步行动都清晰、可追踪、可回溯。
一、为什么Agent需要严格的审计?
- 调试与可解释性: 审计日志是理解Agent内部决策逻辑的唯一途径。它们帮助开发者定位失败的步骤、评估工具调用的效果。
- 合规性与治理(Governance): 在金融、医疗等受监管行业,Agent必须证明其决策过程符合特定规范。完整的审计路径是满足合规要求的基石。
- 性能优化: 通过分析不同步骤的耗时,可以识别任务链中的瓶颈,从而针对性地优化LLM调用或外部工具接口。
二、核心技术方案:审计日志的结构化设计
传统的纯文本日志难以查询和分析。为了实现高效审计,我们必须使用JSON或其他结构化格式,并确保每个日志事件(Audit Event)包含以下关键字段:
| 字段名 | 描述 | 示例值 |
|---|---|---|
| timestamp | 事件发生时间 | 2024-05-30T10:00:00.123Z |
| trace_id | 贯穿整个任务链的唯一ID | a1b2c3d4e5f6 |
| step_id | 当前步骤的唯一ID | step_003 |
| agent_name | 执行动作的Agent或模块名称 | PlanningAgent |
| action_type | 动作类型(Plan/ToolCall/LLMInference/FinalAnswer) | ToolCall |
| tool_name | 如果是工具调用,工具的名称 | FileSearchTool |
| inputs | 当前步骤接收到的输入(或Prompt) | {“query”: “最新的市场报告”} |
| outputs | 当前步骤产生的输出或结果 | {“status”: “success”, “result_len”: 1024} |
| status | 步骤执行状态 | SUCCESS / FAILURE |
三、实操示例:构建可审计的Agent执行器
我们将使用Python的logging模块,并集成一个自定义的JSON Formatter来确保输出的结构化。
步骤 1: 定义结构化日志格式器
为了将Python的logging输出为标准的JSON格式,我们定义一个JsonAuditFormatter。
import logging
import json
import time
import uuid
class JsonAuditFormatter(logging.Formatter):
def format(self, record):
# 基础结构
log_record = {
"timestamp": time.strftime("%Y-%m-%dT%H:%M:%S.000Z", time.gmtime(record.created)),
"level": record.levelname,
"message": record.getMessage(),
"service": "AgentExecutor",
# 附加的审计数据(来自extra参数)
}
# 合并通过 extra={...} 传递的自定义字段
if hasattr(record, 'audit_data'):
log_record.update(record.audit_data)
return json.dumps(log_record, ensure_ascii=False)
# 配置Logger
def setup_audit_logger(name='AgentAuditor'):
logger = logging.getLogger(name)
logger.setLevel(logging.INFO)
# 避免重复添加Handler
if not logger.handlers:
handler = logging.StreamHandler()
handler.setFormatter(JsonAuditFormatter())
logger.addHandler(handler)
return logger
AUDIT_LOGGER = setup_audit_logger()
步骤 2: 实现可审计的Agent步骤
我们定义一个简化的Agent类,它在执行任何核心逻辑(如规划或工具调用)之前和之后,都会调用审计日志。
class AuditableAgent:
def __init__(self, name, trace_id):
self.name = name
self.trace_id = trace_id
def execute_planning(self, initial_query):
step_id = str(uuid.uuid4())[:8]
# --- 审计点 A: 步骤开始 ---
AUDIT_LOGGER.info(
"Planning step started.",
extra={
'audit_data': {
'trace_id': self.trace_id,
'step_id': step_id,
'agent_name': self.name,
'action_type': 'Planning',
'inputs': {'query': initial_query},
'status': 'RUNNING'
}
}
)
# 模拟LLM推理和规划逻辑
time.sleep(0.1)
plan_result = f"Plan generated for query: {initial_query}. Requires search tool."
# --- 审计点 B: 步骤结束 ---
AUDIT_LOGGER.info(
"Planning step finished successfully.",
extra={
'audit_data': {
'trace_id': self.trace_id,
'step_id': step_id,
'agent_name': self.name,
'action_type': 'Planning',
'outputs': {'plan': plan_result},
'status': 'SUCCESS'
}
}
)
return plan_result
def execute_tool_call(self, plan_output, tool_name='SearchTool'):
step_id = str(uuid.uuid4())[:8]
# --- 审计点 C: 工具调用开始 ---
AUDIT_LOGGER.info(
"Tool call initiated.",
extra={
'audit_data': {
'trace_id': self.trace_id,
'step_id': step_id,
'agent_name': self.name,
'action_type': 'ToolCall',
'tool_name': tool_name,
'inputs': {'plan': plan_output},
'status': 'RUNNING'
}
}
)
# 模拟工具执行
time.sleep(0.2)
tool_result = "Found 5 relevant documents about the market."
# --- 审计点 D: 工具调用结束 ---
AUDIT_LOGGER.info(
"Tool call completed.",
extra={
'audit_data': {
'trace_id': self.trace_id,
'step_id': step_id,
'agent_name': self.name,
'action_type': 'ToolCall',
'tool_name': tool_name,
'outputs': {'result': tool_result},
'status': 'SUCCESS'
}
}
)
return tool_result
# 任务执行
def run_task(query):
trace_id = str(uuid.uuid4())
print(f"\n--- Starting Task Chain (Trace ID: {trace_id}) ---")
agent = AuditableAgent("FinancialAgent", trace_id)
plan = agent.execute_planning(query)
result = agent.execute_tool_call(plan)
print(f"\n--- Task Completed. Final Output: {result} ---")
# 运行示例
run_task("分析最新的全球经济走势")
步骤 3: 分析日志输出
运行上述代码后,控制台将输出一系列JSON格式的审计事件。这些事件包含了完整的上下文信息。例如,一个工具调用成功的记录可能如下所示(为清晰起见,此处已格式化):
{
"timestamp": "2024-05-30T10:05:30.456Z",
"level": "INFO",
"message": "Tool call completed.",
"service": "AgentExecutor",
"trace_id": "3a1f2b4c-9d0e-4f7a-8b3c-5e6d7a8f9b0c",
"step_id": "b8e2a3f9",
"agent_name": "FinancialAgent",
"action_type": "ToolCall",
"tool_name": "SearchTool",
"outputs": {
"result": "Found 5 relevant documents about the market."
},
"status": "SUCCESS"
}
通过数据库或日志聚合系统(如Elasticsearch, Loki)存储这些日志,我们就可以:
- 根据trace_id重构整个任务链: 轻松查看Agent从开始到结束的所有中间决策。
- 根据status筛选失败步骤: 快速定位导致任务中断或结果异常的具体环节。
- 分析inputs和outputs: 精确了解信息在Agent各步骤之间的流转和转换。
四、进阶:集成OpenTelemetry追踪
对于更复杂的微服务架构或需要跨服务追踪的情况,可以考虑将上述结构化审计事件映射为OpenTelemetry (OTel) 的 Span 事件。每个Agent步骤(如Planning, ToolCall)可以被视为一个Span。OTel提供了标准化的协议和采集器,使其能够无缝接入到 Jaeger 或 Zipkin 等分布式追踪系统,实现更强大的可视化和性能分析能力。
汤不热吧