快速结论:该问题出现在 LiteLLM 使用 Anthropic 原生 API(非 OpenAI 兼容路径)并开启 Presidio guardrail 与 output_parse_pii: true 时:输入侧 PII 会被遮蔽,但响应侧不会还原;请求中携带工具时还会触发 400 错误。优先排查是否走了 anthropic/ 原生路径,以及 guardrail 是否导致工具定义被改写。
适用环境:Issue 已确认的场景为 LiteLLM + Presidio guardrail + output_parse_pii: true + Anthropic 原生 provider(anthropic/claude-sonnet-4-5,非 openai-compat);正文只给出 YAML 配置,未提供 OS、Python、CUDA、显卡或依赖版本信息。
最快修复方案:暂无确认的一步修复方案。Issue 中原先的 PR(#22879–#22882、#23037)已被关闭并由 #24290、#24291 取代,但讨论中未确认这些 PR 已合并发布。
注意事项:Issue 中提到的“升级到包含修复的 LiteLLM 版本”在评论里没有得到合并或发布确认;流式响应路径、工具调用、非 ASCII 字符以及 byte/char offset 等问题在评论中仍有讨论,修复后也需要针对这些场景回归验证。
问题场景
用户在 LiteLLM 中以 anthropic/ 原生路径调用 Claude(例如 model: anthropic/claude-sonnet-4-5),同时启用 Presidio guardrail、default_on: true 和 output_parse_pii: true。典型触发场景包括:普通非流式对话(输出中出现 <PERSON> 占位符而没有被还原)、流式对话(原生路径返回 SSE bytes,未执行还原),以及携带工具调用的请求(例如通过 Claude Code 使用 bash_20250124、text_editor_20250124 等原生工具)。
报错原文
Input tag 'function' found using 'type' does not match expected tags: 'bash_20250124', 'text_editor_20250124', ...
原因分析
根据 Issue 作者的分析,问题由多个原因叠加造成:
- 工具键污染:
apply_guardrail()返回了整个被修改过的inputsdict,其中包含process_input_messages()添加的 OpenAI 转换键(tools、structured_messages、model、images)。在 Anthropic 原生路径上,这些键会用 OpenAI 的type:"function"格式覆盖原生工具定义,从而触发 400。 - 缺少还原路径:
apply_guardrail()只有遮蔽路径;当以input_type="response"和output_parse_pii=True调用时,PII token 从未被恢复。 - 抽象接口不同步:
BaseGuardrailTranslation未声明request_data参数,而各process_output_response()实现已接收该参数,导致类型/mypy 不匹配。 - 流式钩子只处理
ModelResponseStream:async_post_call_streaming_iterator_hook的还原逻辑期望收到ModelResponseStream对象,但 Anthropic 原生路径返回的是原始bytes(SSE 格式),因此直接被透传,还原逻辑没有执行。此外AnthropicMessagesHandler.process_output_response()新建了request_datadict,丢弃了输入遮蔽阶段保存的pii_tokens。
评论中还提到一个可能相关的历史问题:presidio.py 中 pii_tokens 的偏移 bug——从左到右替换而不重新计算索引,会破坏字符串;PR #5068 曾提出从右到左的修复,但以 stale 状态关闭、未合并。
环境排查
- 确认
model是否为anthropic/...原生 provider,而不是 OpenAI 兼容路径。 - 确认 guardrail 配置中包含
guardrail: presidio与output_parse_pii: true。 - 确认请求中是否携带工具(MCP、Claude Code 等),并检查工具
type是否被改为function。 - 区分非流式与流式响应,两者在 Issue 中属于不同代码路径。
- 如果返回内容包含非 ASCII 文本,注意评论中提到的 byte/char offset 不一致风险。
- Issue 未提供 OS、Python、CUDA、显卡及依赖版本,这些项无法从原文确认。
解决步骤
- 先确认触发路径:将
model固定为anthropic/原生路径,并复现一次带工具的请求,确认 400 报错与 PII 未还原是否为同一配置下出现。 - 关注 LiteLLM 对 #24290 与 #24291 的合并与发布状态。Issue 中原先的四个 PR(#22879–#22882、#23037)已关闭并被这两个 PR 取代;在确认合并版本之前,不要假定升级即可修复。
- 若暂时无法升级,可优先尝试评论中提到的临时方案:不要直接暴露 Presidio guardrail,而是把它包成 custom-guardrail,从而绕开 LiteLLM 内置的 Presidio 处理路径。该方案在评论中仅被描述为个人做法,未经验证,且会关闭内置还原能力,需要自行实现还原逻辑。
- 如条件允许,可在 LiteLLM 上游部署独立的 PII 代理,在请求到达 provider 之前完成遮蔽与还原,从而避开 Anthropic 与 OpenAI 之间的 API 差异。评论中提到的
PrivAiTe属于外部项目,不是 Issue 验证过的官方方案。 - 不要仅凭
output_parse_pii: true就认为流式响应会被还原;原生 SSE 字节需要专门的解包逻辑,#24291 声称处理了这一点,但以实际发布版本为准。
验证方法
在 Anthropic 原生路径下分别测试:1)包含人名等 PII 的非流式请求,确认响应中不残留 <PERSON> 等占位符,而是还原为原始值;2)流式请求,确认 SSE 输出在保持实时流式的同时完成还原;3)携带工具的请求,确认不再出现 Input tag 'function' ... 的 400,且工具 type 保持原生格式(如 bash_20250124)。同时检查 pii_tokens 在输出阶段是否被正确传入,而没有被新 dict 丢弃。
参考来源
AI 工具推荐
想把多个 AI 模型放在一个入口?
GamsGo AI 集成 ChatGPT、DeepSeek、Gemini、Claude、Midjourney、Veo 等常用模型,适合写作、绘图、视频和日常 AI 工作流。
推广链接:通过此链接购买,我可能获得佣金,不影响你的价格。
这个方案解决了吗?
可以继续搜索完整报错,或查看同一工具的其他排查指南。
![[Bug]: Cloudflare decisions provider uses the /ai/run path the chat provider calls 'legacy' — inconsistent and undocumented](https://www.chat-gpts.plus/wp-content/uploads/2026/10/47965-5a0857ed-768x403.jpg)

