登录
推荐 文章 Go 技术 课程 下载 专题 AI
首页 >  科技周边 >  人工智能

安全过滤把拒答与业务失败分开记录的实现方法

来源:17golang原创

时间:2026-09-20 04:21:42 310浏览 收藏

接入安全过滤后,最容易出现的误判是:用户没有拿到正常答案,系统就把这次请求记成“业务失败”。实际情况可能完全不同。安全策略拒答代表请求被策略分支拦截;业务失败代表输入或业务状态不满足条件;超时、限流和网络错误则属于上游调用失败。三者的处理动作不同,日志却经常只剩一个 failed

我更建议在应用层把它们拆成稳定的结果类型,再分别决定用户提示、重试策略和告警级别。示例会参考安全过滤与审核的常见做法,官方资料可从 https://platform.openai.com/docs/guides/safety-best-practiceshttps://platform.openai.com/docs/api-reference/moderations 查看。

要点速览
  • 拒答是策略结果,不等于程序故障,也不应默认自动重试。
  • 业务失败要保留可定位的业务码;传输失败才进入有限重试。
  • 日志事件同时服务用户提示、指标聚合和后续人工复核,字段应稳定而克制。

先把“没有答案”拆成三条应用层路径

分类时不要从展示文案反推原因,而要从调用链上游的结构化结果开始。安全过滤通常会给出是否命中和类别信息;业务校验有自己的错误码;网络或服务端问题则表现为调用错误、状态码或超时。应用层只负责把这些事实归一化,不要把“拒答”改写成“服务异常”。

结果类型用户提示是否重试主要指标
policy_refusal换一个合规请求默认不重试策略命中率
business_rejected补充或修正业务输入按业务规则决定业务失败率
upstream_error稍后再试限次、退避超时/限流/5xx
AI 安全过滤应用层把策略拒答、业务失败和上游错误分开的边界说明图
图1:响应分类说明图,展示三类结果的边界与后续动作,不是运行截图。

安全过滤分支要先于业务重试判断

如果代码先进入通用重试器,再判断安全结果,拒答可能被重复发送,既浪费调用额度,也让日志看起来像服务不稳定。正确顺序是先完成安全结果归类;命中策略时只返回安全的用户提示,记录必要的类别和关联 ID,不把原始敏感内容写进普通业务日志。

// Result 表示应用层对一次 AI 请求的稳定归类。
type Result struct {
    Kind      string // policy_refusal、business_rejected 或 upstream_error
    Code      string // 给指标和前端使用的稳定错误码
    Retryable bool   // 只有明确允许时才进入重试器
    Message   string // 面向用户的安全提示,不放内部策略细节
}

func classify(filter FilterResult, callErr error, inputErr error) Result {
    // 策略命中优先返回,避免拒答被误送进通用重试。
    if filter.Refused {
        return Result{Kind: "policy_refusal", Code: "SAFETY_REFUSED", Message: "请调整请求内容后再试"}
    }
    // 业务错误由调用方修正,不能用网络重试解决。
    if inputErr != nil {
        return Result{Kind: "business_rejected", Code: "INPUT_INVALID", Message: "请检查输入条件"}
    }
    // 只有上游暂时性错误才标记可重试,次数和退避仍由外层控制。
    if callErr != nil {
        return Result{Kind: "upstream_error", Code: "MODEL_UNAVAILABLE", Retryable: true, Message: "服务暂时不可用"}
    }
    return Result{Kind: "ok", Code: "OK", Message: "处理完成"}
}

这里的关键不是字段数量,而是优先级:安全结果已经明确时,不再用后面的通用错误覆盖它。实际项目还应把过滤服务本身的超时和服务端错误单独归入 upstream_error,不要因为它发生在过滤阶段就误标成拒答。

日志字段要同时服务排障与隐私边界

统一事件建议至少包含 request_idkindcoderetryablelatency_mssourcesource 可以区分 input-filter、output-filter、business 或 model-call,便于看出失败发生在哪一段。策略类别可按需要做低基数枚举;原始提示词、完整模型输出和敏感分类细节不应无条件进入普通日志。

AI 响应事件日志字段与用户提示、指标和重试决策的关系结构图
图2:事件字段关系说明图,展示同一归类如何分别驱动日志、指标和重试决策。

告警也要按类型分开:policy_refusal 更适合做趋势和抽样复核,突然升高才触发策略排查;upstream_error 才是服务可用性告警的直接输入;business_rejected 则应该反馈给产品或业务规则负责人。三种数量混在一张“失败率”图上,会让值班人员做出错误动作。

哪些情况可以重试,哪些情况必须停止

可以把重试决策固定成小表,避免不同接口各写一套判断:

场景处理原因
策略拒答停止自动重试,给出合规提示重复相同输入不会改变策略结果
参数或权限不满足返回业务码,要求修正输入重试不会修复业务条件
短暂超时、限流、5xx指数退避并限制次数可能是暂时性上游故障
响应解析失败记录原始错误摘要并停止先确认协议或供应商响应是否变化

上线前可以用四条检查清单收尾:拒答是否有独立的 kind;业务失败是否能定位到稳定的 code;重试器是否只接受 Retryable=true;普通日志是否避开原始敏感内容。只要这四点成立,安全过滤就不会再被当成模糊的“调用失败”。

常见问题

拒答是否应该返回 HTTP 4xx?

不必一概而论。对外 API 可以使用稳定的业务响应码,并在内部用 policy_refusal 区分;是否映射为 4xx 要看客户端契约,不能让网关把它误记成服务故障。

过滤服务超时算拒答吗?

不算。没有拿到明确的策略结果时,应记录为过滤链路的 upstream_error,并按超时策略处理。

日志里要保存完整拒答文本吗?

通常不需要。保存稳定的类型、类别摘要和请求关联 ID 就够排障;完整内容应遵循最小化和访问控制原则。

声明:本文转载于:17golang原创 如有侵犯,请联系study_golang@163.com删除
相关阅读
更多>
最新阅读
更多>
课程推荐
更多>