先看一个真实事故:Agent 在凌晨三点发疯
我昨天凌晨被手机震醒。Hermes 跑的一个定时任务,本该在凌晨两点把当天的销售数据汇总成表格发到邮箱,结果它发了一封标题全是乱码、正文是一堆 JSON 报错的邮件,还在日志里刷了三十多条 warning。
爬起来打开终端,先看 Hermes 的日志文件。默认路径在 ~/.hermes/logs/,按天滚动,昨天的日志叫 agent-2025-04-14.log。用 tail 看了最后一百行,发现它在调用一个技能的时候,参数里的日期格式传成了 2025/04/14,但技能内部用的是 datetime.strptime(),默认格式是 %Y-%m-%d,直接抛了 ValueError。这个异常被 Hermes 的兜底逻辑接住,返回了一个空结果,Agent 把这个空结果当成正常数据,继续跑完流程,然后给你发了一封内容为空的邮件。
问题链条很清楚:日志里只记录了异常信息,但没记录 Agent 当时的推理过程。你只知道它错了,不知道它为什么这么传。这时候需要把 Hermes 的日志级别调低,看更细的调试输出。
配置文件在 ~/.hermes/config.yaml,打开找到 logging 段,默认是 level: INFO,改成 DEBUG,同时把 format 加上调用链信息。改完后重启 Hermes 服务,再手动触发一次那个定时任务,看完整输出。
DEBUG 级别的日志会记录每一步 tool call 的输入输出、Agent 在每个决策点的思考摘要、以及 token 消耗。这次能看到它把日期格式搞错的真正原因:它在记忆里找到了一条两周前的旧数据,那条数据的日期格式是 2025/04/01,它直接把这个格式套用到了今天的日期上。典型的记忆污染问题。
排查到这里,问题定位完成。修复方式有两种,一种是在技能里加日期格式校验,另一种是清掉那条旧记忆。我选了前者,因为后者治标不治本。
日志文件怎么读:别只看报错,要看上下文
很多人在排查 Hermes 问题的时候,习惯性 grep "error" 或者 "exception",把报错行揪出来看一眼就完事。这样不行。Hermes 的日志设计里,真正有用的信息往往在报错之前的三五行。
举个例子,你看这段日志:
2025-04-14 14:23:01 INFO skill_executor.py:87 | executing skill: fetch_weather
2025-04-14 14:23:02 DEBUG http_client.py:34 | GET https://api.weather.com/v1/current?city=shenzhen
2025-04-14 14:23:03 WARNING retry_handler.py:12 | request failed: timeout, retry 1/3
2025-04-14 14:23:06 ERROR skill_executor.py:102 | skill fetch_weather failed: Max retries exceeded
报错是 "Max retries exceeded",但真正的原因在第二行——请求参数里 city=shenzhen 没有做 URL 编码,而天气 API 要求城市名用拼音且带地区后缀。你光看 ERROR 行,只能看到重试耗尽,看不到请求参数的问题。
所以读日志的正确姿势是:找到 ERROR 后,往上翻十行,看这个错误发生之前的完整调用链。Hermes 的日志格式默认是 时间 级别 模块:行号 | 消息,模块和行号能帮你快速定位到具体代码位置。比如 skill_executor.py:87 说明是技能执行器在跑技能前的准备阶段出了问题。
还有一个实用技巧:用 hermes logs --tail -f 实时跟踪日志。在调试交互式对话的时候,开一个终端窗口挂着这个命令,你能看到 Agent 每一步的真实反应,比事后翻文件直观得多。
如果你觉得默认的日志格式不够用,可以在 config.yaml 里自己定义格式。我现在的配置是这样的:
logging:
level: DEBUG
format: "%(asctime)s %(levelname)s %(name)s:%(lineno)d | %(message)s"
file: ~/.hermes/logs/agent.log
max_size: 10MB
backup_count: 5
这个配置会把日志写到指定文件,单个文件超过 10MB 就轮转,保留最近 5 份。日志文件太大不是好事,查起来费劲。
技能调用失败:先查技能目录,再查参数传递
Hermes 的技能放在 ~/.hermes/skills/ 目录下,每个技能一个文件夹,里面有一个 SKILL.md 描述文件和一个或多个 Python 脚本。技能调用失败的时候,最常见的两个原因:路径写错、参数对不上。
路径问题很好查。在 Hermes 的交互界面里输入 skills list,它会列出所有已加载的技能和对应的路径。如果你发现某个技能没出现在列表里,说明加载失败了,去看日志里的 skill_loader 相关记录,多半是 SKILL.md 里的 YAML front matter 写错了,比如缺少 name 字段或者描述太长。
参数问题稍微隐蔽一些。Hermes 在调用技能的时候,会按照 SKILL.md 里的参数定义来校验传入值。比如你定义了一个技能需要 start_date 和 end_date 两个参数,类型都是字符串,格式是 YYYY-MM-DD。Agent 在调用时如果传了 2025/04/14,技能内部不会报错,但你的代码里可能用了 datetime.fromisoformat(),这个函数只认 ISO 格式,直接抛异常。
这种问题排查起来有个捷径:在技能脚本里加一个入口,支持直接从命令行传参调试。比如在 fetch_weather.py 里加一段:
if __name__ == "__main__":
import sys
print(fetch_weather(city=sys.argv[1], date=sys.argv[2]))
然后手动跑 python fetch_weather.py shenzhen 2025-04-14,看是不是能复现问题。能复现,问题就在技能内部;不能复现,问题出在 Agent 的参数生成环节。
还有一种情况:Agent 生成了错误的参数名。比如技能定义里参数叫 api_key,但 Agent 传了一个 apikey,Hermes 不会自动做映射,直接报 missing required argument: api_key。这种情况我遇到过好几次,尤其是在 Agent 参考了别的技能写法的时候。解决办法是在 SKILL.md 里把参数名写得更口语化,并且在描述里加一句“参数名必须严格使用以下名称”,Agent 在生成调用时会更谨慎。
如果你用的是 pip install hermes-agent 装的版本,可以通过 pip show hermes-agent 查看版本号。版本不同,技能加载逻辑可能有差异,排查问题前先确认版本,能避免很多误会。
记忆出问题:用 memory 命令查看和清理
前文提到日期格式被旧记忆污染,这类问题在 Hermes 里很常见。记忆模块会在对话中自动提取关键信息存下来,下次遇到类似场景时自动调取。这个机制好用,但也会存错东西。
排查记忆问题,用 memory 命令。在 Hermes 交互界面输入 memory list,能看到所有已存储的记忆条目,每条有一个 ID 和内容摘要。如果你怀疑某条记忆影响了 Agent 的行为,可以用 memory get <id> 查看完整内容,确认无误后 memory delete <id> 删掉。
有一次我做多轮对话测试,Agent 在前一轮把用户的临时需求“查询上海天气”记成了长期偏好“用户常住上海”,后一轮问它“帮我推荐今天的穿搭”,它直接按上海天气生成了建议,完全没考虑用户可能不在上海。查记忆的时候发现这条错误记录,删掉之后重新问,行为恢复正常。
除了手动清理,你还可以在 config.yaml 里设置记忆的保存策略。比如限制单条记忆的最大长度、设置记忆的过期时间、或者要求关键记忆必须经过用户确认才能写入。我的配置是这样的:
memory:
enabled: true
max_items: 500
ttl_days: 30
require_confirmation: true
require_confirmation: true 的意思是,Agent 想写入记忆的时候,会先问你一句“需要我记住这个吗”。虽然多了一次交互,但能避免不少脏数据。
还有一种更隐蔽的情况:记忆内容本身没错,但 Agent 在检索时匹配到了不相关的条目。比如你存了一条“用户喜欢喝美式咖啡”,结果 Agent 在回答“推荐一款适合下午喝的饮料”时把它调了出来,推荐的还是美式。这种问题靠调参数解决不了,需要在记忆里加标签或者分类。目前 Hermes 的记忆模块还不支持自定义标签,但你可以通过修改记忆内容的描述方式,让它更容易被精准匹配。比如把“用户喜欢喝美式咖啡”改成“用户偏好(咖啡):美式,日常下午饮用”,检索效果会好一些。
定时任务出错:hermes cron create 后的排查流程
定时任务出问题,比交互式对话更麻烦,因为没人盯着看。我的销售数据汇总任务就是在凌晨两点跑的,出错后直到我早上起来才发现。
Hermes 的定时任务用 hermes cron create 创建,配置存储在 ~/.hermes/cron/ 目录下。排查定时任务问题时,按这个顺序来:
先确认任务有没有被触发。用 hermes cron list 查看任务列表,注意看 next_run 字段。如果显示的时间和你预期的不一样,说明 cron 表达式写错了。比如你想每天凌晨两点跑,应该写 0 2 * * *,但写成了 0 0 * * *,那就是零点跑,不是两点。
再确认任务的执行状态。用 hermes cron logs <task_id> 查看这个任务历次执行的日志摘要。如果任务根本没执行,日志里不会有记录;如果执行了但失败,日志里会有 error 信息。
我的那个汇总任务,问题出在任务执行时的工作目录不对。Hermes 在跑定时任务时,默认工作目录是 ~/.hermes/,但我的技能脚本里用了相对路径 ./data/sales.csv,实际指向了 ~/.hermes/data/sales.csv,而文件在 ~/workspace/data/sales.csv。文件找不到,任务自然失败。
解决办法是在技能脚本里改用绝对路径,或者在 cron 任务定义里指定 cwd 参数。我推荐前者,因为脚本的可移植性更好。
另外注意一个坑:定时任务执行时,环境变量可能和你在终端里手动跑不一样。比如你在 ~/.bashrc 里设置了 PYTHONPATH,但 cron 任务不会读取 .bashrc,导致技能里的 import 直接失败。遇到这种情况,在 config.yaml 的 cron 段里手动设置环境变量:
cron:
env:
PYTHONPATH: /home/user/workspace
TZ: Asia/Shanghai
还有个容易忽略的点:定时任务里跑的技能如果涉及网络请求,要考虑超时设置。cron 任务默认超时时间是 300 秒,如果你的技能调用的 API 响应慢,加上重试逻辑,可能超时。在 config.yaml 里可以调大:
cron:
timeout: 600
排查完这些问题,我的汇总任务恢复正常。现在我会在创建定时任务后,先手动触发一次 hermes cron run <task_id>,确认没问题再让它自动跑。
调试的终极手段:给 Agent 加临时技能
有些问题反复排查找不到根源,这时候我有个笨办法:写一个临时技能,专门用来输出诊断信息。
比如有一次 Agent 在对话中频繁答非所问,日志里看不出明显错误,记忆也清理过了,参数传递也正常。我写了一个技能叫 debug_context,功能是打印当前对话的完整上下文摘要,包括用户最近五条消息、Agent 最近三条回复、当前命中的记忆条目、以及上次技能调用的返回结果。
技能代码很简单,核心就十几行:
def debug_context():
ctx = get_current_context()
print("--- user messages ---")
for msg in ctx.user_messages[-5:]:
print(f"{msg['role']}: {msg['content'][:100]}")
print("--- agent responses ---")
for msg in ctx.agent_responses[-3:]:
print(f"agent: {msg['content'][:100]}")
print("--- memory hits ---")
for item in ctx.memory_hits:
print(f"memory[{item['id']}]: {item['content'][:80]}")
return "debug output printed"
在对话中让 Agent 调用这个技能,它会把当前的上下文状态完整打出来。我一看,发现 Agent 在回复之前,把用户的一条历史消息当成了自己的输出,导致后续推理全部建立在错误的基础上。这是上下文管理模块的一个 bug,和技能、记忆、参数都没关系。
这种临时技能用完就删,不用留着占地方。在 ~/.hermes/skills/ 目录下删掉对应文件夹,然后重启 Hermes 就能生效。
调试 Hermes 的过程,本质上是在看一个黑盒子里发生了什么。日志给你第一层信息,DEBUG 模式给你第二层,记忆排查给你第三层,临时技能给你第四层。大部分问题到第二层就能解决,少数顽固问题需要用到第三层,需要用到第四层的情况我遇到过两次。
最后说一句,排查问题的时候保持耐心。Agent 出错往往不是单一原因,而是多个因素叠加。你把它当成一个同事,出了问题先问清楚,别急着甩锅给代码。
💬 你用过哪些AI工具?
写到这里,想起最近在 AI House 排行榜(aibunkhouse.com/rankings/)上看到不少有意思的 AI 工具和模型。有做代码生成的,有做数据分析的,还有专门做 Agent 编排的,各有各的玩法。我平时调试 Hermes 卡住的时候,也会去上面逛逛,看看别人在用什么工具解决类似问题。
你在实际项目中用过哪些 AI 工具?有没有遇到过特别难排查的 Agent 问题?评论区聊聊,说不定你踩过的坑,正好是别人在找的答案。如果你有觉得不错的模型或工具,也可以去排行榜上给它投一票,帮后来的人做参考。
常见问题 / FAQ
先看一个真实事故:Agent 在凌晨三点发疯
我昨天凌晨被手机震醒。Hermes 跑的一个定时任务,本该在凌晨两点把当天的销售数据汇总成表格发到邮箱,结果它发了一封标题全是乱码、正文是一堆 JSON 报错的邮件,还在日志里刷了三十多条 warning。 爬起来打开终端,先看 Hermes 的日志文件。默认路径在 ~/.hermes/logs/,按天滚动,昨天的日志叫 agent-2025-04-14.log。用 tail 看了最后一百行,发...
日志文件怎么读:别只看报错,要看上下文
很多人在排查 Hermes 问题的时候,习惯性 grep "error" 或者 "exception",把报错行揪出来看一眼就完事。这样不行。Hermes 的日志设计里,真正有用的信息往往在报错之前的三五行。 举个例子,你看这段日志: 2025-04-14 14:23:01 INFO skill_executor.py:87 | executing skill: fetch_weather 2...
技能调用失败:先查技能目录,再查参数传递
Hermes 的技能放在 ~/.hermes/skills/ 目录下,每个技能一个文件夹,里面有一个 SKILL.md 描述文件和一个或多个 Python 脚本。技能调用失败的时候,最常见的两个原因:路径写错、参数对不上。 路径问题很好查。在 Hermes 的交互界面里输入 skills list,它会列出所有已加载的技能和对应的路径。如果你发现某个技能没出现在列表里,说明加载失败了,去看日志里...
记忆出问题:用 memory 命令查看和清理
前文提到日期格式被旧记忆污染,这类问题在 Hermes 里很常见。记忆模块会在对话中自动提取关键信息存下来,下次遇到类似场景时自动调取。这个机制好用,但也会存错东西。 排查记忆问题,用 memory 命令。在 Hermes 交互界面输入 memory list,能看到所有已存储的记忆条目,每条有一个 ID 和内容摘要。如果你怀疑某条记忆影响了 Agent 的行为,可以用 memory get &...
定时任务出错:hermes cron create 后的排查流程
定时任务出问题,比交互式对话更麻烦,因为没人盯着看。我的销售数据汇总任务就是在凌晨两点跑的,出错后直到我早上起来才发现。 Hermes 的定时任务用 hermes cron create 创建,配置存储在 ~/.hermes/cron/ 目录下。排查定时任务问题时,按这个顺序来: 先确认任务有没有被触发。用 hermes cron list 查看任务列表,注意看 next_run 字段。如果显示...