Prhub

#7369 [BugFix] fix tool call parser

PaddlePaddle/FastDeploy · 作者 luukunn · 合并时间 2026-04-15 16:21

分析状态 已生成
文件变更 2提交数 4 · 评论 13
代码增减 +461 / -345
bugfix APIServer DataProcessor

执行摘要

修复 ErnieX1 工具调用解析器在流式场景中的空参数判断和结束标记处理问题。

修复 extract_tool_callstools_calledtool_calls 为空时仍返回 True 的问题;修复空字典 {} 被误判为无参数的问题;修复流式输出结束处理中 '"}' 检查过于严格导致数字/布尔值结尾的参数无法正确流式传输的问题。

建议精读此 PR,关注其如何通过精确的类型判断(None vs. 真值)和流式结束处理(使用 rindex 而非固定字符串)解决边界条件问题,可作为处理类似解析场景的参考设计。

讨论亮点

review 中,Copilot 指出了变量 diff 复用导致类型语义混用,可能降低可读性(设计问题),但作者未直接回应;fastdeploy-bot 建议将 rindex('}') 包裹在 try-except 中以避免 ValueError 异常,但作者回复“并不会出现这个问题”,暗示逻辑已确保安全;此外,bot 提醒了 PR 标题和描述规范,后续得到完善。决策结论是 PR 被批准合并,未解决疑虑包括 Copilot 提到的内容丢失可能性。

实现拆解

  1. 修改核心解析逻辑:在 fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py 中,将 extract_tool_calls 方法的 tools_called=True 改为 tools_called=len(tool_calls) > 0,确保仅在实际提取到工具调用时返回 True。
  2. 修复流式参数判断:在同一文件的 extract_tool_calls_streaming 方法中,将 if diff: 改为 if diff is not None: 以正确处理空字典;将 if '"}' not in delta_text: 改为 if "}" not in delta_text: 并使用 rindex("}") 定位结束位置,避免对数字/布尔值结尾参数的误判。
  3. 调整参数分支逻辑:将所有 if not cur_arguments 改为 if cur_arguments is None,防止空字典被错误视为无参数。
  4. 新增回归测试:在 tests/entrypoints/openai/tool_parsers/test_ernie_x1_tool_parser.py 中,添加 _simulate_streaming 辅助方法和多个测试用例,覆盖空参数、数字/布尔值结尾、嵌套对象等边界场景,确保修复的正确性。
文件 模块 状态 重要度
fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py 入口点 modified 5.33
tests/entrypoints/openai/tool_parsers/test_ernie_x1_tool_parser.py 工具解析器 modified 6.92
fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py core-logic

核心解析器源码,直接修复工具调用解析逻辑,包括 tools_called 判断、空参数误判和流式结束标记处理。

def extract_tool_calls(self, model_output: str, request: ChatCompletionRequest):
    # ... 解析工具调用 JSON 的逻辑 ...
    tool_calls = [] # 提取到的工具调用列表
    # 使用正则匹配获取 tool_call_json_list
    if not tool_calls:
        # 当未匹配到任何工具调用时,返回 tools_called=False
        return ExtractedToolCallInformation(
            tools_called=False, # 修复前为 True,现改为 len(tool_calls) > 0
            tool_calls=[]
        )
    else:
        return ExtractedToolCallInformation(
            tools_called=len(tool_calls) > 0, # 关键修改:确保仅在列表非空时返回 True
            tool_calls=tool_calls
        )

关键符号

extract_tool_calls extract_tool_calls_streaming _simulate_streaming

评论区精华

变量复用可读性问题 设计

Copilot 指出 diff 变量在 close 分支中类型语义混用(从 dict/ 其他类型复用为 str),可能降低代码可读性。

结论:作者未直接回应此建议,PR 被批准合并,变量复用逻辑维持不变。 · 未解决

rindex 异常处理建议 正确性

fastdeploy-bot 建议将 rindex('}') 包裹在 try-except 中,以防 delta_text 不包含 '}' 时抛出 ValueError。

结论:作者回复“并不会出现这个问题”,暗示逻辑已确保安全,建议未被采纳。 · 已解决

PR 规范检查 documentation

fastdeploy-bot 提醒 PR 标题需遵循 [TAG]Title 格式并补全描述,后续 PR 得到完善。

结论:PR 标题和描述已更新符合规范,问题解决。 · 已解决

风险与影响

技术风险较低,因为修改针对具体 bug 并添加了全面测试。潜在风险:类型判断从真值改为 None 可能影响极端边缘场景,但测试覆盖了常见边界;流式结束标记调整使用 rindex,需确保 delta_text 总包含 '}',否则有异常风险,尽管作者声称逻辑安全。

用户影响:改善工具调用的正确性,特别是处理空参数和流式传输时,提升用户体验;系统影响:仅限于 APIServer 下的 ErnieX1 工具解析器模块,不影响其他核心功能;团队影响:变更被 cherry-pick 到多个 release 分支(2.4、2.5、2.6),需确保同步部署和维护一致性。

空参数处理 流式传输兼容性 类型判断变更

关联 Issue

未识别关联 Issue

当前没有检测到明确关联的 Issue 链接,后续同步到相关引用后会出现在这里。

完整报告

执行摘要

  • 一句话:修复 ErnieX1 工具调用解析器在流式场景中的空参数判断和结束标记处理问题。
  • 推荐动作:建议精读此 PR,关注其如何通过精确的类型判断(None vs. 真值)和流式结束处理(使用 rindex 而非固定字符串)解决边界条件问题,可作为处理类似解析场景的参考设计。

功能与动机

修复 extract_tool_callstools_calledtool_calls 为空时仍返回 True 的问题;修复空字典 {} 被误判为无参数的问题;修复流式输出结束处理中 '"}' 检查过于严格导致数字/布尔值结尾的参数无法正确流式传输的问题。

实现拆解

  1. 修改核心解析逻辑:在 fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py 中,将 extract_tool_calls 方法的 tools_called=True 改为 tools_called=len(tool_calls) > 0,确保仅在实际提取到工具调用时返回 True。
  2. 修复流式参数判断:在同一文件的 extract_tool_calls_streaming 方法中,将 if diff: 改为 if diff is not None: 以正确处理空字典;将 if '"}' not in delta_text: 改为 if "}" not in delta_text: 并使用 rindex("}") 定位结束位置,避免对数字/布尔值结尾参数的误判。
  3. 调整参数分支逻辑:将所有 if not cur_arguments 改为 if cur_arguments is None,防止空字典被错误视为无参数。
  4. 新增回归测试:在 tests/entrypoints/openai/tool_parsers/test_ernie_x1_tool_parser.py 中,添加 _simulate_streaming 辅助方法和多个测试用例,覆盖空参数、数字/布尔值结尾、嵌套对象等边界场景,确保修复的正确性。

关键文件:

  • fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py(模块 入口点;类别 source;类型 core-logic;符号 extract_tool_calls, extract_tool_calls_streaming): 核心解析器源码,直接修复工具调用解析逻辑,包括 tools_called 判断、空参数误判和流式结束标记处理。
  • tests/entrypoints/openai/tool_parsers/test_ernie_x1_tool_parser.py(模块 工具解析器;类别 test;类型 test-coverage;符号 _simulate_streaming, test_extract_tool_calls_empty_arguments, test_streaming_close_with_number_ending_arguments, test_streaming_close_with_boolean_ending_arguments): 新增回归测试,覆盖修复的边界条件(如空参数、数字/布尔值结尾),确保代码质量和修复正确性。

关键符号:extract_tool_calls, extract_tool_calls_streaming, _simulate_streaming

关键源码片段

fastdeploy/entrypoints/openai/tool_parsers/ernie_x1_tool_parser.py

核心解析器源码,直接修复工具调用解析逻辑,包括 tools_called 判断、空参数误判和流式结束标记处理。

def extract_tool_calls(self, model_output: str, request: ChatCompletionRequest):
    # ... 解析工具调用 JSON 的逻辑 ...
    tool_calls = [] # 提取到的工具调用列表
    # 使用正则匹配获取 tool_call_json_list
    if not tool_calls:
        # 当未匹配到任何工具调用时,返回 tools_called=False
        return ExtractedToolCallInformation(
            tools_called=False, # 修复前为 True,现改为 len(tool_calls) > 0
            tool_calls=[]
        )
    else:
        return ExtractedToolCallInformation(
            tools_called=len(tool_calls) > 0, # 关键修改:确保仅在列表非空时返回 True
            tool_calls=tool_calls
        )

评论区精华

review 中,Copilot 指出了变量 diff 复用导致类型语义混用,可能降低可读性(设计问题),但作者未直接回应;fastdeploy-bot 建议将 rindex('}') 包裹在 try-except 中以避免 ValueError 异常,但作者回复“并不会出现这个问题”,暗示逻辑已确保安全;此外,bot 提醒了 PR 标题和描述规范,后续得到完善。决策结论是 PR 被批准合并,未解决疑虑包括 Copilot 提到的内容丢失可能性。

  • 变量复用可读性问题 (design): 作者未直接回应此建议,PR 被批准合并,变量复用逻辑维持不变。
  • rindex 异常处理建议 (correctness): 作者回复“并不会出现这个问题”,暗示逻辑已确保安全,建议未被采纳。
  • PR 规范检查 (documentation): PR 标题和描述已更新符合规范,问题解决。

风险与影响

  • 风险:技术风险较低,因为修改针对具体 bug 并添加了全面测试。潜在风险:类型判断从真值改为 None 可能影响极端边缘场景,但测试覆盖了常见边界;流式结束标记调整使用 rindex,需确保 delta_text 总包含 '}',否则有异常风险,尽管作者声称逻辑安全。
  • 影响:用户影响:改善工具调用的正确性,特别是处理空参数和流式传输时,提升用户体验;系统影响:仅限于 APIServer 下的 ErnieX1 工具解析器模块,不影响其他核心功能;团队影响:变更被 cherry-pick 到多个 release 分支(2.4、2.5、2.6),需确保同步部署和维护一致性。
  • 风险标记:空参数处理, 流式传输兼容性, 类型判断变更

关联脉络

  • PR #7307 [DataProcessor] add strict: 同属 APIServer 和 DataProcessor 模块,涉及工具调用协议处理(添加 FunctionDefinition strict 字段),功能上相关。
  • PR #7298 [DataProcessor] Refactor multimodal processor: extract encoding strategies and unify MM processing pipeline: 涉及 DataProcessor 模块重构,展示了类似边界条件处理的设计模式,可作为参考。

参与讨论