如果你在 Hermes Agent、Docker、Chromebook、macOS 终端或精简 Linux 环境里看到类似错误:
UnicodeEncodeError: 'ascii' codec can't encode character
尤其日志里还出现 LANG=C、ascii、Authorization: Bearer ...、api_messages、reasoning_content、System encoding is ASCII 之类线索,那这通常不是模型突然不可用,也不一定是 API 服务商挂了,而是运行环境或请求载荷里混入了非 ASCII 字符,但当前进程按 ASCII 编码发送/写入/清洗数据。
这篇文章基于 NousResearch/hermes-agent 的 closed/completed issue #6843 以及后续 4 个已合并 PR:#9011、#9946、#10090、#10537,整理一份面向搜索和实操的排错指南。
> 适合搜索关键词:Hermes Agent UnicodeEncodeError ascii codec can't encode character、Hermes Agent LANG=C Unicode error、Hermes API key non ASCII、reasoning_content UnicodeEncodeError、OpenAI SDK Authorization header ascii error。
先给结论:这类 UnicodeEncodeError 常见有 4 个来源
| 现象 | 常见根因 | 应对方向 |
ascii codec can't encode character | 系统 locale 是 C / ASCII | 改成 UTF-8 locale |
报错位置落在 Authorization header 附近 | API Key 里混入 Unicode 近似字符 | 重新复制/轮换 API Key,排除 ʋ 这类字符 |
| 多轮对话、工具调用后才炸 | api_messages / reasoning_content 带非 ASCII | 升级到包含 #10537 的版本 |
| 日志、system prompt、tool schema 里有中文/emoji | 全请求载荷未被清洗 | 升级到包含 #9011 的版本,并检查环境编码 |
最容易踩坑的一点是:你看到的是 UnicodeEncodeError,但真正污染源可能不在用户输入里。它可能在 API Key、工具描述、系统提示词、HTTP header、模型 reasoning 字段,甚至 OpenAI SDK 内部持有的 client.api_key 副本里。
典型报错长什么样?
这类问题常见错误包括:
UnicodeEncodeError: 'ascii' codec can't encode character '\u028b'
或更泛化的:
'ascii' codec can't encode character
在 issue #6843 的后续修复链里,维护者把问题拆成了几类:
- ASCII-only 系统环境,例如
LANG=C、精简容器、某些 Chromebook/macOS shell; - 工具 schema、system prompt、prefill message、默认 HTTP header 中包含非 ASCII;
- API Key 中混入 Unicode lookalike,例如
ʋ(U+028B)看起来像普通v; - 运行时虽然清洗了
self.api_key,但 OpenAI SDK 的self.client.api_key仍保留旧值; - 多轮会话里
messages[i]['reasoning']被复制到api_messages[i]['reasoning_content'],但早期清洗逻辑没有覆盖这些额外字段。
所以,如果你只盯着“我刚才输入了什么”,很可能找错方向。
根因 1:系统 locale 是 ASCII,而不是 UTF-8
在容器、CI、远程服务器、Chromebook 或极简 Linux 镜像里,进程可能运行在 ASCII locale:
locale
如果你看到类似:
LANG=C
LC_ALL=
或 Python 里:
python - <<'PY'
import locale, sys
print(locale.getpreferredencoding(False))
print(sys.getfilesystemencoding())
PY
输出接近 ANSI_X3.4-1968 / ascii,那就很可疑。
立即可用的修复
Linux/macOS shell 可以先临时设为 UTF-8:
export LANG=C.UTF-8
export LC_ALL=C.UTF-8
如果系统没有 C.UTF-8,可改用:
export LANG=en_US.UTF-8
export LC_ALL=en_US.UTF-8
Dockerfile 里建议显式写:
ENV LANG=C.UTF-8
ENV LC_ALL=C.UTF-8
ENV PYTHONUTF8=1
这不是“魔法修复”,而是让 Python、日志、终端和 HTTP 客户端都尽量在 UTF-8 前提下工作。
根因 2:API Key 里混入了 Unicode 近似字符
PR #9946 里提到一个很典型的场景:API Key 里混入 ʋ(U+028B,Latin small letter v with hook),它看起来像普通 v,但不是 ASCII。
当 OpenAI SDK / httpx 组装:
Authorization: Bearer <key>
HTTP header 按规范需要可编码为 ASCII,于是直接触发:
UnicodeEncodeError: 'ascii' codec can't encode character '\u028b'
怎么检查 API Key 是否混入非 ASCII?
不要把真实 key 发给别人。你可以本地运行:
python - <<'PY'
import os
for name in ["OPENAI_API_KEY", "DEEPAI_API_KEY", "ANTHROPIC_API_KEY"]:
v = os.getenv(name)
if not v:
continue
bad = [(i, ch, hex(ord(ch))) for i, ch in enumerate(v) if ord(ch) > 127]
print(name, "non-ascii:", bad)
PY
如果输出有内容,说明 key 或 token 中存在非 ASCII 字符。最稳妥的处理不是手动删字符,而是:
1. 到服务商后台重新复制 key,确保从纯文本区域复制; 2. 避免从 PDF、富文本、聊天软件气泡中二次复制; 3. 必要时轮换/重置 API Key; 4. 保存到 .env 后再次用上面的脚本检查。
如果你使用 DeepAI API 中转站统一管理模型,也同样建议把 Base URL 和 API Key 写入纯文本 .env,避免从富文本页面带入不可见字符。
根因 3:只清洗 messages 不够,tool schema / system prompt / headers 也可能炸
PR #9011 的重点是:早期恢复逻辑只处理 messages,但真实 API 请求远不止 messages。
可能包含非 ASCII 的位置有:
prefill_messages- tool schema / tool description
- system prompt
- ephemeral system prompt
- default HTTP headers
- nested
api_kwargs - 用户输入、工具返回、日志上下文
这解释了一个常见困惑:我明明没有在当前消息里输入中文或 emoji,为什么还是 UnicodeEncodeError?
因为非 ASCII 可能来自工具描述、历史消息、系统提示词、环境变量,或者某个 provider adapter 组装出的额外字段。
排查方法
如果你维护自己的 Hermes Agent 配置,可以按这个顺序查:
# 1. 检查 locale
locale
# 2. 检查 env 里的 key/token 是否有非 ASCII
python check-non-ascii-env.py
# 3. 临时移除自定义 tool schema / MCP 工具 / system prompt
# 4. 用最小模型配置复现
# 5. 开 debug 日志,看错误发生在 headers、messages 还是 tool payload
如果“移除工具后正常,加工具后报错”,优先检查 tool name、description、JSON schema 中是否有中文标点、emoji、不可见字符。
根因 4:reasoning_content / api_messages 没有被完整清洗
PR #10537 是这条修复链里非常关键的一环。
维护者提到:在某个路径里,agent 会把:
messages[i]['reasoning']
复制到:
api_messages[i]['reasoning_content']
但早期 _sanitize_messages_non_ascii() 只检查 content、name、tool_calls,没有检查 reasoning 这类额外顶层字段。结果是:
1. 系统检测到 ASCII codec 问题; 2. recovery 设置 _force_ascii_payload = True; 3. 清洗 canonical messages 时没发现问题; 4. 非 ASCII 实际藏在 api_messages.reasoning_content; 5. retry 仍失败,甚至白白消耗重试次数。
这和我们前面写过的 DeepSeek reasoning_content must be passed back、Gemini thought_signature 有相似点:都说明现代 LLM provider 的“消息历史”不只是 role + content。但这里的主题不是“隐藏字段必须回传”,而是这些额外字段也必须参与编码清洗和重试路径。
推荐升级路径:确认是否包含 4 个相关 PR
issue #6843 后续至少关联了这些已合并修复:
| PR | 作用 | 状态 |
#9011 | 扩展 ASCII-locale recovery 到完整 request payload | merged |
#9946 | 检测并剥离 API Key 中的非 ASCII 字符 | merged |
#10090 | 清洗后同步 client.api_key,避免 OpenAI SDK 仍用旧 key | merged |
#10537 | 补齐 api_messages / reasoning_content recovery 缺口 | merged |
如果你在旧版本 Hermes Agent 上遇到 UnicodeEncodeError,先做两件事:
hermes --version
然后对照你的安装来源是否包含上述 PR。若是源码安装,直接更新到较新 commit;若是包管理器安装,确认对应 release 是否已经包含这些修复。
DeepAI API 中转站在这里能帮什么,不能帮什么?
这里要说清边界,避免误导。
DeepAI API 中转站能帮你的是:
- 统一 OpenAI-compatible Base URL;
- 统一 API Key 和模型接入;
- 降低 Cherry Studio、Cline、Dify、Open WebUI 等工具切换不同模型供应商的成本;
- 让你更容易把 provider 侧问题和客户端配置问题分开排查。
但它不能自动修复:
- 你本地 shell 的
LANG=C/ ASCII locale; - 你复制进
.env的 Unicode lookalike API Key; - Hermes Agent 旧版本里未覆盖
api_messages/reasoning_content的 recovery bug; - 客户端工具 schema 或 system prompt 中混入的不可见字符。
所以,建议把 DeepAI API 中转站当作“模型接入和配置收敛层”,而不是把所有客户端编码问题都甩给 API 网关。
一份实用排错清单
按优先级执行:
1. 运行 locale,确认不是 LANG=C / ASCII-only; 2. 设置 LANG=C.UTF-8、LC_ALL=C.UTF-8、PYTHONUTF8=1; 3. 检查 .env 里的 API Key / Token 是否存在 ord(ch) > 127; 4. 如果 API Key 污染,重新复制或轮换 key; 5. 暂时移除自定义工具、MCP、中文 system prompt,做最小复现; 6. 升级 Hermes Agent,确认包含 #9011、#9946、#10090、#10537; 7. 如果仍复现,记录完整错误位置:header、message、tool schema、api_messages 还是 reasoning_content; 8. 提 issue 时附上环境:OS、Python 版本、locale、Hermes commit、provider、是否工具调用、是否 reasoning 模型。
FAQ
这是 OpenAI、DeepSeek、Gemini 或 DeepAI API 的问题吗?
不一定。UnicodeEncodeError: 'ascii' codec can't encode character 很多时候发生在客户端进程准备 HTTP 请求、写日志或编码 header 的阶段,请求甚至可能还没真正到达 API 服务商。
为什么 API Key 里会有非 ASCII 字符?
最常见是从 PDF、网页、聊天软件或富文本编辑器复制时,普通 ASCII 字符被替换成 Unicode lookalike。例如 v 看起来像 ʋ,但后者不是 ASCII。
为什么手动运行正常,webhook 或后台任务会失败?
因为两者可能使用不同环境变量来源、不同 locale、不同 shell 启动方式。PR #10090 里就提到 webhook-triggered routines 和 manual runs 表现不一致的情况。
直接把所有非 ASCII 都删掉安全吗?
对 API Key 这类凭据,最安全的做法是重新复制或轮换,而不是随手删字符。对 messages/tool schema,清洗可以作为 recovery,但长期还是建议运行在 UTF-8 环境。
reasoning_content 和这次 UnicodeEncodeError 有什么关系?
reasoning_content 可能是 API 请求中的额外字段。如果里面包含非 ASCII,而客户端旧版本清洗逻辑没覆盖它,就可能在 ASCII locale 下继续失败。PR #10537 正是补齐这个缺口。
总结
Hermes Agent 的 UnicodeEncodeError: 'ascii' codec can't encode character 不应该只按“终端乱码”处理。它可能同时涉及:
- 系统 locale;
- API Key 复制污染;
- HTTP header 编码;
- tool schema / system prompt;
api_messages;reasoning_content;- OpenAI SDK client 内部状态同步。
如果你正在把多个模型、多个工具和多个供应商接到一个 Agent 工作流里,建议一边升级 Hermes Agent,一边把 API 接入收敛到稳定的 OpenAI-compatible 配置层。DeepAI API 中转站适合承担这个“统一入口”的角色;而客户端侧的 locale、凭据字符集和 Hermes 版本,仍然要按本文清单逐项排查。