9.1. 日志解读能力是定位问题的首要技能

日志解读能力是定位问题的首要技能

场景还原
时间:周三下午 3:10,你的 Slack 被一条告警打穿——“Agent 不回消息了,客户在等。”你打开终端,tail -f hermes.log 刷出一屏又一屏的混合日志,第一时间却不知道该盯哪一行。十分钟后你才意识到:问题不在代码,而在你从未真正“看懂”过这些日志。

在 Agent 研发的日常里,日志是唯一能从外部观测内部决策过程的窗口。没有日志解读能力,每一次故障都是猜谜。本章将带你拆解 Hermes Agent 的日志格式与关键字段,让你能在 30 分钟内从“日志恐惧”过渡到“定位问题只需 3 行日志”。


你需要什么

资源 说明
一个运行中的 Hermes Agent 实例 版本 ≥ v0.12,启用调试日志
终端工具 lessgrepjq(可选,方便结构化过滤)
预计时间 约 30 分钟

请在启动 Agent 时设置环境变量 LOG_LEVEL=DEBUG,或者直接在 config.toml 中启用详细日志:

[logging]
level = "DEBUG"
output = "file"
file_path = "logs/hermes-agent.log"

重新启动 Agent 后,确认日志文件已经开始写入。本章所有操作均在此环境下完成。


最终成果

学完本章后,你将能够:

  1. 看懂任意一条 Herman Agent 日志的五个组成要素
  2. 追踪一次完整对话的执行循环,定位任一环节的耗时与卡点
  3. 从异常堆栈中快速判断错误类型,并知道下一步排查方向

为什么必须掌握这门技能?因为 Hermes Agent 的定位是“可自进化的 Agent 操作系统”,其内部并发管线、工具调度、记忆读写全部异步化。一旦出问题,表面症状与实际故障点往往隔着三层调用栈。只有通过日志还原执行路径,才能一击命中。


步骤一:拆解日志格式——五个维度看懂一条日志

1.1 Hermes Agent 日志的统一格式

Hermes Agent 的日志输出遵循固定的结构,无论来自哪个子系统,都用同一套格式。打开日志文件,你会看到这样的行:

2026-06-14T09:12:35.421Z  INFO  app.core.loop  [req_8f3a]  Outer loop iteration 3: dispatching user message

每一项的含义如下:

位置 字段 说明 示例
1 时间戳 ISO 8601 格式,精确到毫秒 2026-06-14T09:12:35.421Z
2 日志级别 DEBUG / INFO / WARNING / ERROR INFO
3 模块标识 用点分隔的层级命名,精确定位代码来源 app.core.loop
4 请求 ID 方括号包裹,唯一标识一次用户交互或任务 [req_8f3a]
5 消息内容 人类可读的描述或结构化数据 Outer loop iteration 3...

⚠️ 注意
如果你的日志中没有出现 [req_xxx] 这样的请求 ID,说明你当前使用的是简化模式(LOG_FORMAT=simple)。务必切换为 detailed 格式,否则后续所有步骤都无法完成跨行追踪。

1.2 常见模块标识速查表

日志里的模块前缀直接告诉你“这段逻辑是谁打的”。以下是在日常调试中出现频率最高的模块:

模块前缀 职责 出现时的含义
app.core.loop Agent 主循环 每一轮决策、工具调用、反思的调度入口
tool.dispatch 工具注册与分发 正在查找或执行某个工具
prompt.build 提示词组装 正在将上下文、记忆、工具描述拼成最终 Prompt
memory.store 记忆持久化 写入或读取短期/长期记忆
gateway.http HTTP 网关 接收用户请求或向上游 API 发起调用
llm.invoke LLM 调用 实际向模型发起的请求与响应
skill.exec Skill 执行 代码技能的执行入口

现在我们来做第一项练习:用 grep 筛选出过去 5 分钟内所有 ERROR 日志,并观察模块分布。

grep "ERROR" logs/hermes-agent.log | tail -20

预期结果:你应该能看到错误来自哪些模块——如果 80% 的 ERROR 集中在 tool.dispatch,那问题大概率是工具注册缺失或名称拼写错误。


步骤二:追踪一条完整的执行循环

2.1 从用户消息到最终回复的日志链

现在我们追踪一条真实请求的完整日志。假设用户在 9:12:35 发送了消息“帮我查一下上海明天天气”。我们提取它的执行路径。

2026-06-14T09:12:35.410Z  INFO  gateway.http  [req_8f3a]  Received POST /chat {"message":"帮我查一下上海明天天气"}
2026-06-14T09:12:35.412Z  DEBUG app.core.loop  [req_8f3a]  Entering outer loop, iteration=0
2026-06-14T09:12:35.415Z  DEBUG prompt.build  [req_8f3a]  Assembling system prompt with 3 memories, 5 tools
2026-06-14T09:12:35.420Z  DEBUG tool.dispatch  [req_8f3a]  Intent classified as: weather_query (confidence=0.97)
2026-06-14T09:12:35.421Z  INFO  app.core.loop  [req_8f3a]  Outer loop iteration 1: dispatching user message
2026-06-14T09:12:35.425Z  DEBUG tool.dispatch  [req_8f3a]  Tool matched: get_weather (source=skill_registry)
2026-06-14T09:12:35.430Z  DEBUG skill.exec  [req_8f3a]  Executing skill 'get_weather' with params {"city":"上海","date":"2026-06-15"}
2026-06-14T09:12:35.480Z  INFO  llm.invoke  [req_8f3a]  LLM request sent (model=gpt-4o, tokens=1234)
2026-06-14T09:12:37.112Z  INFO  llm.invoke  [req_8f3a]  LLM response received (tokens=89, latency=1632ms)
2026-06-14T09:12:37.120Z  DEBUG memory.store  [req_8f3a]  Storing conversation turn (short-term, ttl=3600)
2026-06-14T09:12:37.125Z  INFO  gateway.http  [req_8f3a]  Response sent (status=200, total_latency=1715ms)

逐行解读:

  • gateway.http 收到请求,分配 ID req_8f3a。后续所有日志都带着这个 ID,像一条金线串起整个处理流程。
  • app.core.loop 进入主循环,iteration=0 表示这是一次全新对话的第一轮。
  • prompt.build 组装系统提示词,此时已将 3 条记忆和 5 个可用工具注入。
  • tool.dispatch 先做意图分类,识别为 weather_query,置信度 0.97。
  • 主循环第二次迭代 (iteration=1) 开始实际分发:工具 get_weather 被匹配成功。
  • skill.exec 执行技能,传入城市和日期参数。
  • llm.invoke 发出模型请求,耗时 1632ms。
  • memory.store 将本轮对话写入短期记忆 (TTL 1 小时)。
  • gateway.http 返回 200 响应,总延迟 1715ms。

2.2 提取性能瓶颈

使用 grep + awk 快速统计每个模块的平均耗时。以上面日志为例,llm.invoke 耗时 1632ms,占整个处理时间的 95% 以上——这是典型情况,属于正常范围。如果某天这个值突然飙升到 10 秒,你就应该去检查模型 API 的可用区或限流策略。

grep "req_8f3a" logs/hermes-agent.log | grep "latency"

预期结果:能直接看到每一步的耗时,并绘制出简单的时序图(脑子里的即可)。


步骤三:异常堆栈分析——一眼看穿错误类型

Hermes Agent 的异常日志具有极强的特征性,熟悉三类高频错误,可以让你在故障前 10 秒内就打开正确的文档。

3.1 错误类型一:ToolNotFound

2026-06-14T09:15:01.033Z  ERROR tool.dispatch  [req_9b2c]  Tool 'get_stock_price' not found in local or remote registry.
Traceback (most recent call last):
  File "hermes/tool/dispatcher.py", line 142, in resolve_tool
    tool = self.registry[tool_name]
KeyError: 'get_stock_price'

含义:Agent 尝试调用一个不存在的工具 get_stock_price。原因通常是 Skill 文件未部署、工具名拼写错误,或者注册了但未重启。

应对策略

  1. 检查 skills/ 目录下是否有对应 Python 文件。
  2. 确认 skills/manifest.toml 中已注册该工具名。
  3. 重启 Agent 使注册生效。

3.2 错误类型二:MemoryFull

2026-06-14T10:22:18.771Z  WARNING memory.store  [req_d7e4]  Short-term memory buffer full (200/200 entries). Evicting oldest entry.
2026-06-14T10:22:18.772Z  ERROR memory.store  [req_d7e4]  Failed to store new memory: MemoryStoreQuotaExceeded("quota 'short_term' reached, eviction policy disabled")

含义:短期记忆缓冲区已满(默认 200 条),且未启用自动淘汰策略,导致写入失败。

应对策略

  1. 检查 memory.short_term.max_entries 配置项。
  2. 启用淘汰策略:memory.short_term.eviction_policy = "LRU"
  3. 或者增加配额至更大值(如 1000),但注意内存占用。

3.3 错误类型三:API 超时或限流

2026-06-14T11:05:44.002Z  ERROR llm.invoke  [req_f12a]  Request to llm.openai failed after 3 retries.
Traceback (most recent call last):
  File "hermes/llm/http_client.py", line 89, in _invoke
    response = await session.post(url, json=payload, timeout=30)
asyncio.TimeoutError

含义:调用 LLM API 时连续三次重试均超时或返回 429/503。

应对策略

  1. 使用 curl 单独测试 API 端点是否可达。
  2. 检查 API 密钥配额是否耗尽。
  3. 降低请求并发度,或在网关层增加退避逻辑。

⚠️ 踩坑经验
很多开发者在遇到 TimeoutError 后会立刻翻代码,但实际上日志里已经提示了 after 3 retries——这说明完全是网络侧的问题,和你的业务逻辑无关。直接查网络/代理/API 状态页,不要浪费时间在代码中找 bug。


回顾

在这次 30 分钟的实战中,你完成以下动作:

  1. 拆解了 Hermes Agent 日志的五个关键字段,记住了主要模块前缀的含义。
  2. 追踪了一条从“用户输入”到“最终回复”的完整日志链,并学会了如何定位性能瓶颈。
  3. 分析了三种最常见的异常日志(ToolNotFound、MemoryFull、API 超时),知道了它们的直接含义和排查方向。

现在,你在看到任何一条日志时,都能迅速提取出“谁在什么时间、哪个模块、执行什么操作、结果如何”这一完整信息。这是调试 Hermes Agent 的元技能。


行动清单

  • [ ] 将 LOG_LEVEL=DEBUGLOG_FORMAT=detailed 设置为开发环境的默认配置
  • [ ] 为你的项目创建一个“日志模块速查卡片”,列出团队定义的 5 个核心模块及其含义
  • [ ] 用 grep 提取最近 10 次请求的日志,画出其中一条的时序图(纸笔即可)
  • [ ] 针对 ToolNotFoundMemoryFullTimeoutError 三种错误,建立团队的内部处理 SOP
  • [ ] 将本章用到的 grep/awk/jq 命令保存为脚本 hermes-log-analyzer.sh,加入项目根目录

一旦你学会了用日志“透视” Agent 内部,你很快会发现一些更深层的问题:为什么 Agent 会反复使用过期的用户偏好?为什么对话历史越来越长,响应反而越来越离谱?这正是下一章的主题——记忆过期与污染是持久化 Agent 的常见顽疾,我们将直接从日志中的 memory.store 异常切入,教你诊断与清理被污染的记忆。

本文章首发在 LearnKu.com 网站上。

上一篇 下一篇
讨论数量: 0
发起讨论 只看当前版本


暂无话题~