将通用Chat模型作为策略分类器,通过版本化JSON Schema规范输出决策,实现无专用审核端点下的结构化安全审查。
Short answer: 把聊天模型当作策略分类器来用,而不是把它当作策略执行系统。在模型和你的金融科技审核工作流之间放一个小型、带版本号的 JSON Schema,返回 allow、review 或 block,并在它能影响代码变更队列之前,用带标签的文本和图像 fixture 来衡量每个决策。
这个设计在没有专用审核端点、真实需求是结构化安全审查时同样适用。它还能让成本高昂的部分清晰可见:每租户用量、prompt token、重试次数和人工审核量,这些都应该进入你的应用遥测,而不是放在一个模糊的模型仪表板里。
一个 Node.js 内容审核 API 应该对文本、图像和 JSON Schema 做些什么?
API 边界应该保持无聊。接收租户 ID、条目 ID、内容和策略版本。返回一个决策、一个分类、一条简短理由,以及用于生成决策的版本。审核服务不应该合并代码、发布版本或关闭审核工单。它只做分类;执行由独立的策略层负责。
对于审核代码变更的金融科技工具来说,条目可能是 PR 描述、自动审查员的评论,或者附在变更请求上的截图。文本和图像输入可以共享一个结果契约,即使它们的模型消息有不同的内容形态。这种通用契约让 notebook 原型更容易迁移到队列中,但它不会让图像用例等同于文本用例。
我使用三种结果,因为二元标签会隐藏运营工作。allow 继续工作流。block 在明确策略下停止它。review 将模糊用例发送给人工。分类应该来自有限的策略词汇表,如 credential_exposure、harassment 或 regulated_advice;具体列表属于产品团队,应该被视为带版本号的策略,而不是模型的人格。
这个 schema 是一个护栏,而不是准确性测试。响应可以是有效的 JSON,但仍然错误地对银行账户截图进行分类。先验证结构,然后用带有预期决策的 fixture 来评估行为。
执行代码之前的小型分类器契约
下面的 Python 示例使用通用 HTTP chat-completions 接口和 JSON Schema 响应契约。它有意将模型和基础 URL 留在配置中。这样可以让示例在不同的运行时之间移植,并防止模型名称悄然成为应用策略的一部分。
import json
import os
from typing import Any
import httpx
RESULT_SCHEMA = {
"name": "safety_review_result",
"strict": True,
"schema": {
"type": "object",
"additionalProperties": False,
"properties": {
"decision": {
"type": "string",
"enum": ["allow", "review", "block"],
},
"category": {"type": "string"},
"reason": {"type": "string"},
},
"required": ["decision", "category", "reason"],
},
}
def review_text(text: str, tenant_id: str, item_id: str) -> dict[str, Any]:
payload = {
"model": os.environ["REVIEW_MODEL"],
"messages": [
{
"role": "system",
"content": (
"Classify this item under the supplied application policy. "
"Return exactly one decision: allow, review, or block. "
"Choose one policy category and give a brief reason."
),
},
{"role": "user", "content": text},
],
"response_format": {
"type": "json_schema",
"json_schema": RESULT_SCHEMA,
},
}
response = httpx.post(
os.environ["CHAT_COMPLETIONS_URL"],
headers={"Authorization": f"Bearer {os.environ['REVIEW_API_KEY']}"},
json=payload,
timeout=30.0,
)
response.raise_for_status()
result = json.loads(response.json()["choices"][0]["message"]["content"])
result["tenant_id"] = tenant_id
result["item_id"] = item_id
return result
print(review_text("Example change description", "tenant-a", "change-42"))
路由和响应信封有意保留在配置后面。在生产环境的 Node.js 服务中,可以用运行时的 HTTP 客户端和 JSON Schema 验证器实现相同的边界;契约比 SDK 重要。周围的服务应该拒绝缺失字段,而不是将其转换为 allow,并且应该在结果中记录策略版本。
不要无差别地重试每个失败。速率限制响应可以使用有限延迟和在有可用时使用服务器提供的重试间隔。超时需要请求预算和明确的队列状态。如果分类器在写入之前被调用,写入上的幂等键可以防止后续重试重复创建审核记录。这个区别在 notebook 中很容易被忽略,但在上线后修复会很痛苦。
如何让内容审核决策可以按租户测试?
每租户成本可见性始于身份传播。每个请求都应该携带租户 ID、策略版本、模型配置 ID、输入类型、可用时的 token 计数、延迟、重试次数和最终决策。保持原始内容访问控制;成本和结果记录不需要向每个操作员暴露敏感文本或图像。在实践中,我会在请求被接受的边界处写入该记录,在分类器运行之前,然后用响应结果更新它,而不是稍后尝试从应用日志重建用量。例如,提交长 pull request 描述的租户可能与提交短评论加频繁截图的租户有不同的成本配置;除非输入类型和 token 计数与请求一起传递,否则两者在每日汇总中可能看起来相同。记录还应该保留做出决策的策略版本,因为后续的策略编辑不能重写对早期 block 的解释。这是一个小型数据模型,但它是让工程师能够在不读取租户私有内容的情况下回答租户成本问题的关键部分。
这个边界很重要。
然后构建一个 fixture 集。包括明确的 allow、明确的 block、应该进入 review 的模糊条目,以及每个策略类别至少一个示例。对于代码审核工作流,添加 diff 中 secrets 的 fixture、生成评论中不安全的金融声明、issue 线程中的恶意语言,以及包含账户详情截图的 fixture。这些是测试类,而不是关于任何特定模型性能的声明。
预期值应该是结构化对象或故意部分的断言。评估可以检查 decision 是否在枚举中、category 是否允许用于该策略版本,以及已知 fixture 是否到达预期分支。它还应该捕获输出解析正确但选择了错误执行路径的情况。
队列就是产品。
我分别为 false allow 和 false block 保留指标。false allow 可能会暴露客户或允许不安全的自动评论。false block 可能会延迟合法的代码变更,并训练工程师绕过队列。总体准确率或平均延迟都无法解释这种权衡。
Prompt 成本值得自己的测试。计算策略指令的数量,并在编辑后比较数量;不断增长的 prompt 会在每个请求上收费,在繁忙的评论流中可能成为一笔可观的开支。只有在重新运行相同 fixture 后才删除重复。更短的 prompt 如果改变了审核分布就不是优化。
图像审核需要自己的 fixture 和保留规则。共享的结果模式减少了下游分支,但文本示例无法为截图、图表或附加证据建立覆盖率。随着内容组合的变化,效果可能会有所不同,所以只有在新的评估报告后才发布策略变更。
金融科技审核队列中重要的失败模式
第一个失败是把有效的语法当作安全的决策。JSON Schema 捕获形状错误;它无法决定分类定义是否完整,或者边缘条目是否需要人工。保持执行在模型响应之外,并使回退显式化。对于敏感路径,不可用的或未验证的结果应该进入受控审核状态,而不是静默通过。
第二个是在可观测性中丢失租户边界。全局平均可能让一个嘈杂的租户掩盖另一个租户上升的审核量。按租户、策略版本和输入类型聚合,然后设置与材料敏感性相匹配的保留策略。成本可见性在这里是一项设计要求,而不是最后添加的报告。
第三个是将分类与授权混合。模型可能识别出 diff 中暴露的凭证,但应用仍然决定是否隔离变更、通知所有者或要求第二审查员。保持这些操作确定性和可审计。
第四个是假设一个 prompt 适合每个产物。pull request 描述、支付屏幕的图像和生成的代码审查评论有不同的上下文和隐私风险。在有帮助的地方使用一个结果契约,但保持策略 fixture 和输入处理特定。
最后一个陷阱是在编写评估工具之前选择集成层。抽象可以减少特定提供商的代码,但当结构化输出、多模态输入、重试或用量核算表现不同时,它也增加了另一个需要检查的地方。从可以用测试的最小 HTTP 边界开始,只有在真正减少维护负担时才添加库。
选择边界并承受其限制
内容安全 API 没有通用赢家。按相同的带标签 fixture、确切的文本和图像形态、结构化输出行为、延迟预算、数据处理、速率限制、用量核算以及审核员需要的运营工具来比较候选方案。供应商中立的 HTTP 契约让这种比较更容易,因为应用可以保持其策略和遥测稳定,而分类器可以变化。
问题是,当产品需要专业审核提供商的领域控制、独立管理的策略工具或需要模型调用之外功能的受监管审核流程时,基于聊天的分类器就不适用了。在这些情况下,使用专业服务并在它周围保持相同的评估和执行分离。当团队无法保留带标签的 fixture 或为 review 分支配备人员时,这也不是一个好的选择;三向契约创建的工作必须有负责人。
我不确定在当前候选者在该策略的边缘案例上测试之前,哪个模型系列会为特定租户策略胜出。这种不确定性是有用的信息。它告诉你,在争论排行榜之前先投资于工具。
交接清单可以用散文概括:验证 schema、对策略和 prompt 进行版本控制、附加租户身份、记录用量和延迟、限制速率限制重试、使写入幂等、将 review 路由到人工队列、保护原始内容,并在更改模型或执行规则之前运行 fixture 集。部署后,分别检查 false allow 和 false block,并确保每个租户都能看到自己流量的成本。