[TOOL_CALL]
{tool => "terminal", args => {
--command "tasklist | findstr python"
--timeout 5
}}
[/TOOL_CALL]
[TOOL_CALL],调用 get_parser("bracket") → BracketToolCallParser.parse()if self.START_TOKEN not in text —— 这里是 bug 所在START_TOKEN = "***"(三颗星),而实际内容是 [TOOL_CALL](方括号),检查失败 → 直接返回原始文本,不提取工具调用[TOOL_CALL]... 标签直接显示在飞书消息里,用户看到的就是原始代码块文件:D:\Hermes Agent\hermes-agent-main\environments\tool_call_parsers\bracket_parser.py
class BracketToolCallParser(ToolCallParser):
START_TOKEN = "***" ← 这里是BUG!配置成了***而不是[TOOL_CALL]
END_TOKEN = "***" 同上
def parse(self, text: str) -> ParseResult:
if self.START_TOKEN not in text: ← 检查的是"***",实际内容是"[TOOL_CALL]" → 检查失败!
return text, None ← 直接返回原始内容,不解析
tool_calls: List[ChatCompletionMessageToolCall] = []
pattern = re.compile(r'\[TOOL_CALL\](.*?)\[/TOOL_CALL\]', re.DOTALL) ← 正则本身是对的
matches = pattern.findall(text)
...
| 步骤 | 代码逻辑 | 实际值 | 结果 |
|---|---|---|---|
| 1. 正则匹配 | pattern.findall(text) | 能找到 [TOOL_CALL]...[/TOOL_CALL] | ✅ 能匹配到 |
| 2. START_TOKEN检查 | if self.START_TOKEN not in text | START_TOKEN = "***",文本里没有 *** | ❌ 条件为True,直接return,不执行到正则 |
# Parse [TOOL_CALL] format from assistant message
content = assistant_message.content or ""
if not assistant_message.tool_calls and content:
try:
from environments.tool_call_parsers import get_parser
# Try hermes parser first (XML format: <tool_call>)
if "<tool_call>" in content:
fallback_parser = get_parser("hermes")
...
# Try bracket parser (bracket format: [TOOL_CALL])
elif "[TOOL_CALL]" in content: ← conversation_loop 检查的是"[TOOL_CALL]"字符串 ✓
fallback_parser = get_parser("bracket") ← 调用了 bracket 解析器
parsed_content, parsed_calls = fallback_parser.parse(content)
if parsed_calls:
assistant_message.tool_calls = parsed_calls ← 但 parsed_calls 是 None!解析失败了
if parsed_content is not None:
assistant_message.content = parsed_content
START_TOKEN = "[TOOL_CALL]"
START_TOKEN = "***"
BracketToolCallParser 的 START_TOKEN 和 END_TOKEN 配置成了 "***"(三颗星),而不是实际的 "[TOOL_CALL]" 和 "[/TOOL_CALL]"。
解析器的 parse() 方法第一行就检查 if self.START_TOKEN not in text,由于文本里根本没有 ***,这个条件为 True,函数直接 return text, None,根本不执行后面的正则解析逻辑。
结果:原始文本(含 [TOOL_CALL] 标签)原封不动返回,被当成普通消息发给了用户。
修改 bracket_parser.py 第 88-89 行:
class BracketToolCallParser(ToolCallParser):
START_TOKEN = "***"
END_TOKEN = "***"
class BracketToolCallParser(ToolCallParser):
START_TOKEN = "[TOOL_CALL]"
END_TOKEN = "[/TOOL_CALL]"
修复后,parse() 方法里的 if self.START_TOKEN not in text 检查就会正确命中 [TOOL_CALL],继续执行正则解析,提取出工具名称和参数,正常执行工具调用。
| 文件 | 作用 |
|---|---|
D:\Hermes Agent\hermes-agent-main\agent\conversation_loop.py:2984-3014 | 检测 [TOOL_CALL] 并调用 bracket_parser |
D:\Hermes Agent\hermes-agent-main\environments\tool_call_parsers\bracket_parser.py | bracket 格式解析器(有 bug) |
D:\Hermes Agent\hermes-agent-main\environments\tool_call_parsers\__init__.py | 解析器注册表(bracket 已注册) |
D:\Hermes Agent\hermes-agent-main\.hermes\config.yaml | Hermes 配置(provider: minimax-cn) |
模型生成的命令用了 Linux 语法(如 grep、/dev/null),但 Hermes 跑在 Windows 上,这些命令不存在。
tasklist | grep -i python ← grep 是 Linux 命令,Windows 应用 findstr
2>/dev/null ← /dev/null 是 Linux,Windows 应用 2>NUL
系统提示词可能没有明确告知模型当前运行环境是 Windows,模型默认生成了 Linux 风格的命令。建议在系统提示词里加一句:
当前运行环境:Windows PowerShell。请生成 Windows 兼容的命令,不要使用 Linux/Unix 特有命令(如 grep、head、tail、/dev/null 等)。
bracket_parser.py 的 START_TOKEN 配置为 "***" 而非 "[TOOL_CALL]",导致解析器直接跳过所有解析逻辑