上一篇文章讨论了 Memory。
Memory 让 AI 应用可以在可控范围内延续上下文。
但当一个 AI 应用真的上线后,仅仅能记住上下文还不够。
它还必须能被排查、被追踪、被复盘。
因为线上问题一定会出现。
模型可能回答异常。
Prompt 可能被错误拼接。
RAG 可能检索不到正确内容。
Tool 可能调用失败。
Workflow 可能卡在某个节点。
Agent 可能重复执行无效步骤。
Token 成本可能突然升高。
用户输入可能触发边界情况。
如果没有日志和可观测性,开发者只能靠猜。
而 AI 应用最怕的就是靠猜排查问题。
所以,从 Demo 走向工程化,日志与可观测性是必须补上的一层能力。
一、AI 应用的问题不只是代码报错
传统后端系统排查问题,通常关注:
- 接口是否报错。
- 数据库是否异常。
- 响应时间是否过长。
- 服务器资源是否不足。
- 异常栈在哪里。
这些在 AI 应用里仍然重要。
但 AI 应用的问题更多。
例如:
模型没有报错,但回答质量很差。
接口返回 200,但用户觉得结果不对。
Tool 调用成功了,但用了错误参数。
RAG 检索返回了内容,但不是用户真正需要的内容。
Agent 执行完了,但路径明显绕远了。
Workflow 没有崩溃,但某个节点输出不合理。
Token 消耗没有报警,但单次请求成本比预期高很多。
这些问题不是普通异常栈能解释的。
它们需要更细的链路记录。
也需要专门面向 AI 应用的观测指标。
二、为什么 print 日志不够
很多 Demo 项目会这样写日志:
python
print("start workflow")
print("call llm")
print("result:", result)
这在本地调试阶段还可以。
但上线后远远不够。
首先,print 没有结构。
你很难按用户、请求、模型、任务、错误类型去检索。
其次,print 很难串联链路。
一次用户请求可能包含多次模型调用、多次 Tool 调用、多次检索和多个 Workflow 节点。
如果没有统一 trace_id,很难知道这些日志属于同一次请求。
第三,print 容易泄露敏感信息。
开发阶段随手打印 Prompt、用户输入、接口参数,线上可能就会把隐私或密钥打进日志。
第四,print 不适合统计。
你很难从一堆文本中稳定统计模型耗时、Token 成本、失败率和工具调用次数。
所以,AI 应用需要结构化日志。
不是多打几行 print。
三、一次 AI 请求应该有 trace_id
AI 应用的调用链通常比较长。
例如一次问答请求可能是:
text
用户请求
↓
解析输入
↓
加载 Memory
↓
RAG 检索
↓
构造 Prompt
↓
调用 LLM
↓
判断是否调用 Tool
↓
执行 Tool
↓
再次调用 LLM
↓
返回结果
如果每一步日志没有关联,排查问题会非常痛苦。
所以每次请求都应该生成一个 trace_id。
例如:
text
trace_id = "req_20260711_100001_xxxx"
后续所有日志都带上这个 trace_id:
json
{
"trace_id": "req_20260711_100001_xxxx",
"event": "llm_call_start",
"model": "deepseek-chat",
"workflow": "rag_workflow"
}
这样排查问题时,就能把一次请求下的所有事件串起来。
trace_id 是可观测性的基础。
没有 trace_id,日志只是散落的碎片。
四、请求日志应该记录什么
请求日志是入口层日志。
它回答的是:
谁在什么时候发起了什么类型的请求,最终结果如何。
请求日志可以记录:
text
trace_id:请求链路 ID。
user_id:用户 ID 或匿名 ID。
endpoint:接口路径或入口名称。
method:请求方法。
request_type:请求类型。
status:成功或失败。
latency_ms:总耗时。
created_at:请求时间。
error_code:错误码。
例如:
json
{
"trace_id": "req_001",
"event": "request_finished",
"endpoint": "/chat",
"status": "success",
"latency_ms": 3420
}
请求日志不一定要记录完整用户输入。
很多时候只需要记录输入长度、类型、是否命中某类规则。
如果必须记录输入,也要做脱敏和权限控制。
五、模型调用日志应该记录什么
模型调用是 AI 应用最核心、也最容易出问题的地方。
模型调用日志应该至少记录:
text
trace_id:请求链路 ID。
provider:模型供应商。
model:模型名称。
prompt_version:Prompt 版本。
input_tokens:输入 token。
output_tokens:输出 token。
total_tokens:总 token。
latency_ms:模型耗时。
status:成功或失败。
error_type:错误类型。
retry_count:重试次数。
例如:
json
{
"trace_id": "req_001",
"event": "llm_call_finished",
"provider": "openai",
"model": "gpt-4.1",
"prompt_version": "qa_v3",
"input_tokens": 1200,
"output_tokens": 360,
"latency_ms": 2100,
"status": "success"
}
这里有一个重点:
不要默认把完整 Prompt 和完整 Response 全部写进普通日志。
因为里面可能包含用户隐私、业务数据或敏感上下文。
更合理的做法是:
- 默认记录结构化元数据。
- 必要时记录脱敏后的摘要。
- 调试环境可以打开详细日志。
- 生产环境详细日志需要权限和保留周期。
六、Token 成本必须可观测
AI 应用和传统后端最大的不同之一,是模型调用有直接成本。
一次请求消耗多少 token,可能直接影响账单。
如果没有 Token 统计,很难发现成本异常。
需要观察的指标包括:
- 单次请求平均 token。
- 单个用户 token 消耗。
- 单个 Workflow token 消耗。
- 单个模型 token 消耗。
- 每日总 token。
- 输入 token 和输出 token 比例。
- 重试导致的额外 token。
例如某个 Workflow 改版后,平均输入 token 从 1000 增加到 8000。
接口仍然正常。
用户也可能没有马上反馈。
但成本已经明显异常。
所以 Token 不是附属信息。
它应该是 AI 应用的核心指标。
七、Tool 调用日志应该记录什么
Tool 调用让 Agent 具备行动能力。
但 Tool 也是风险点。
每一次 Tool 调用都应该可追踪。
Tool 调用日志可以记录:
text
trace_id:请求链路 ID。
tool_name:工具名称。
tool_version:工具版本。
permission_level:权限级别。
arguments_schema:参数结构。
latency_ms:执行耗时。
status:成功或失败。
error_type:错误类型。
result_size:结果大小。
例如:
json
{
"trace_id": "req_001",
"event": "tool_call_finished",
"tool_name": "search_docs",
"permission_level": "read_only",
"latency_ms": 320,
"status": "success"
}
注意,不要随意记录完整参数。
比如文件路径、用户输入、业务 ID、接口返回,都可能包含敏感信息。
更稳妥的做法是记录参数结构和脱敏后的关键字段。
Tool 日志的目标不是把所有内容打出来。
而是让开发者能知道工具有没有被调用、调用是否成功、耗时多少、失败在哪里。
八、RAG 检索也需要日志
RAG 系统里,模型回答不好,问题不一定在模型。
很可能是检索阶段出了问题。
例如:
- 没有检索到内容。
- 检索到了错误文档。
- top_k 设置不合理。
- 相似度分数太低。
- 文档切片质量差。
- 过滤条件写错。
所以 RAG 检索也需要日志。
可以记录:
text
trace_id:请求链路 ID。
query_length:查询长度。
collection:知识库名称。
top_k:召回数量。
hit_count:命中数量。
score_range:相似度范围。
latency_ms:检索耗时。
rerank_used:是否使用重排。
例如:
json
{
"trace_id": "req_001",
"event": "retrieval_finished",
"collection": "product_docs",
"top_k": 5,
"hit_count": 5,
"latency_ms": 180
}
如果需要记录命中文档,也建议记录文档 ID、chunk ID 和分数。
不要默认记录完整原文内容。
九、Workflow 节点状态要能追踪
复杂 AI 应用通常不是一次模型调用。
它可能包含多个 Workflow 节点。
例如:
text
load_memory
retrieve_docs
build_prompt
call_llm
parse_answer
call_tool
finalize_response
每个节点都应该有开始、结束、失败日志。
例如:
json
{
"trace_id": "req_001",
"event": "workflow_node_finished",
"workflow": "rag_workflow",
"node": "retrieve_docs",
"status": "success",
"latency_ms": 210
}
这样当用户说结果慢时,可以看到慢在检索、模型、Tool,还是最终处理。
当结果错误时,也可以看是哪个节点开始偏离。
Workflow 可观测性的关键,是把黑盒流程拆成可追踪节点。
十、Agent step 也要能复盘
Agent 的问题往往更难排查。
因为 Agent 不是固定流程。
它可能根据上下文动态决定下一步。
所以 Agent step 需要记录。
可以记录:
text
trace_id:请求链路 ID。
agent_name:Agent 名称。
step_index:步骤序号。
thought_type:步骤类型。
selected_tool:选择的工具。
decision_status:决策状态。
latency_ms:步骤耗时。
例如:
json
{
"trace_id": "req_001",
"event": "agent_step",
"agent_name": "research_agent",
"step_index": 3,
"selected_tool": "search_docs",
"decision_status": "tool_call"
}
这里要注意:
不一定要记录完整推理过程。
很多场景只需要记录可审计的决策摘要。
例如选择了哪个 Tool、为什么进入某个分支、当前步骤是否成功。
这样既能复盘,又能降低敏感信息和冗余日志风险。
十一、错误日志要分层
AI 应用错误不能只分成成功和失败。
更合理的做法是分层。
例如:
text
config_error:配置错误。
llm_error:模型调用错误。
retrieval_error:检索错误。
tool_error:工具调用错误。
workflow_error:流程错误。
validation_error:参数校验错误。
permission_error:权限错误。
rate_limit_error:限流错误。
timeout_error:超时错误。
这样做的好处是:
报警更准确。
统计更清楚。
排查路径更短。
例如都是失败,但模型限流和 Tool 参数错误完全是两类问题。
前者可能要调整重试、降级或额度。
后者可能要修 schema、Prompt 或参数校验。
错误日志越清晰,线上恢复越快。
十二、日志必须脱敏
日志不是越详细越好。
尤其是 AI 应用,输入输出里很容易包含敏感信息。
不能直接记录的内容包括:
- API Key。
- Token。
- 密码。
- 验证码。
- 身份证号。
- 手机号。
- 邮箱。
- 支付信息。
- 用户隐私内容。
- 原始大文件全文。
所以需要 Redactor,也就是脱敏模块。
例如:
python
class Redactor:
def redact(self, text: str) -> str:
text = self.mask_api_keys(text)
text = self.mask_phone_numbers(text)
text = self.mask_emails(text)
return text
在写日志之前先脱敏。
在保存 Prompt 或 Response 之前也要脱敏。
日志系统不能成为隐私泄露的入口。
十三、AI Scaffold 里的可观测性模块
在 AI Scaffold 中,可以把可观测性作为独立模块。
项目结构可以这样设计:
text
app/
├── observability/
│ ├── __init__.py
│ ├── logger.py
│ ├── tracing.py
│ ├── metrics.py
│ ├── events.py
│ ├── redactor.py
│ └── exporters.py
├── llm/
│ └── client.py
├── tools/
│ └── registry.py
├── workflows/
│ └── rag_workflow.py
├── agents/
│ └── assistant_agent.py
└── config/
└── settings.py
其中:
logger.py 负责结构化日志。
tracing.py 负责 trace_id 和链路追踪。
metrics.py 负责统计指标。
events.py 定义 AI 应用里的事件类型。
redactor.py 负责敏感信息脱敏。
exporters.py 负责对接外部日志平台或监控系统。
LLM、Tool、Workflow、Agent 都不应该各写一套日志逻辑。
它们应该统一发事件。
由 observability 模块负责记录、脱敏和导出。
十四、总结
AI 应用上线后,排查问题不能只靠 print。
因为 AI 应用的问题可能来自模型、Prompt、RAG、Tool、Workflow、Agent、Memory、配置、权限和用户输入。
一个合理的日志与可观测性设计,至少应该做到:
- 每次请求有 trace_id。
- 请求日志结构化。
- 模型调用记录 provider、model、耗时和 token。
- Token 成本可统计。
- Tool 调用可追踪。
- RAG 检索可复盘。
- Workflow 节点状态可观察。
- Agent step 可以审计。
- 错误类型要分层。
- 日志写入前必须脱敏。
- 生产环境不要默认记录完整 Prompt 和 Response。
- 可观测性模块要统一,而不是散落在各个业务模块里。
对于 AI Scaffold 来说,可观测性不是上线后的附加功能。
它应该从项目结构阶段就被考虑进去。
因为 AI 应用越复杂,越需要知道每一次结果是怎么来的。
能跑只是第一步。
能解释、能排查、能复盘,才是工程化系统真正可维护的开始。
下一篇文章可以继续讨论安全治理:
当 Agent 能调用 Tool、读取 Memory、访问外部系统时,如何设计权限边界、审计机制和 Prompt Injection 防护。