提示词在入口就被拦下时,返回体里没有 candidates 字段,只有一段 promptFeedback:
{
"promptFeedback": {
"blockReason": "SAFETY",
"safetyRatings": [
{"category": "HARM_CATEGORY_DANGEROUS_CONTENT", "probability": "HIGH"},
{"category": "HARM_CATEGORY_HARASSMENT", "probability": "NEGLIGIBLE"}
]
}
}
看到 blockReason 就说明请求根本没进入生成阶段,模型没有开始写任何一个字。这种情况改输出格式、改温度、换模型都不会有用,问题在提示词内容本身。
另一种情况是模型已经开始回答,中途触发策略停止:
{
"candidates": [
{
"finishReason": "SAFETY",
"finishMessage": "The response was blocked due to safety concerns.",
"content": {"parts": [{"text": ""}]}
}
]
}
这类返回里 parts 往往是空的,或者只有半句话。处理它的方式和入口拦截不同:要检查的是已经生成的那部分,判断是内容触发了策略,还是回答方向跑偏。
走 OpenAI 兼容协议时,客户端拿到的是这一段:
{
"choices": [
{"index": 0, "message": {"role": "assistant", "content": null}, "finish_reason": "content_filter"}
]
}
content 是 null,finish_reason 是 content_filter。这是判断依据:看到 content_filter,就不要再去找 key 或网络的问题了。
| finishReason | 含义 | 你该做什么 |
|---|---|---|
STOP | 正常结束 | 直接使用返回内容 |
MAX_TOKENS | 输出长度到顶被截断 | 调大输出上限或分段提问 |
SAFETY | 内容触发安全策略 | 检查提示词措辞与意图 |
RECITATION | 疑似复述受版权保护的原文 | 让它改用概述而不是照抄 |
PROHIBITED_CONTENT | 命中禁止类内容 | 该内容不在可处理范围内 |
IMAGE_SAFETY | 图片生成被策略拦截 | 检查图片描述与人物相关内容 |
输出写到一半没了、文字戛然而止,很多人以为是安全拦截,其实是长度上限。判断依据只有一条:看 finishReason 的原文,不要靠感觉猜。
模型判断回答将大段复述受保护文本时会停止,处理办法是让对方改用概述、改写、给要点,而不是索要原文。这类问题在翻译和引文类任务里出现得多。
HARM_CATEGORY_HARASSMENT:骚扰与攻击性内容。HARM_CATEGORY_HATE_SPEECH:针对特定群体的仇恨言论。HARM_CATEGORY_SEXUALLY_EXPLICIT:色情与露骨内容。HARM_CATEGORY_DANGEROUS_CONTENT:危险行为与违法操作相关内容。返回体里的 safetyRatings 会逐条给概率值,从 NEGLIGIBLE 到 HIGH。哪个类别是 HIGH,就把注意力放在哪一条上,这个字段是定位误判方向有价值的信息。
接口支持按类别调整拦截阈值,从只在概率很高时拦截,到只要有一点可能就拦截。但在聚合接入平台上,出于合规要求,通常沿用标准阈值,不开放按用户随意调低。真正有效的做法是改提示词,而不是调阈值。
同一段文字换个说法,结果可能完全不同;反过来,看上去很平常的词放在特定上下文里也会被判定为高风险。这意味着反复重发同一段提示词,大概率得到同样的结果,只会白花调用次数。
有 blockReason 就是入口拦截,直接跳到改写提示词这一步;没有就继续往下看。
是这两个值,说明生成阶段被拦;是 MAX_TOKENS,改输出长度限制;是 STOP 但内容为空,那要查的是解析代码有没有读错字段。
import json, os
from openai import OpenAI
client = OpenAI(api_key=os.environ["XYU_API_KEY"], base_url="https://xyuapi.top/v1")
resp = client.chat.completions.create(
model="gemini-2.5-pro",
messages=[{"role": "user", "content": prompt}],
)
print(json.dumps(resp.model_dump(), ensure_ascii=False)[:1200])
先看清返回的原始结构,再决定改什么。跳过这一步直接改提示词,往往改了好几版都不知道自己在跟什么东西对抗。
choice = resp.choices[0]
text = choice.message.content
if not text:
print("本次返回为空,finish_reason =", choice.finish_reason)
text = "" # 后面统一按空字符串处理,别让 None 继续往下传
None 一路传下去,之后会在某个 len() 或字符串拼接处炸出 TypeError,而真正的根因在几百行之前。入口处就地归一化,是省事的做法。
跑一千条数据,如果被拦的有几十条,那是一个需要处理的业务问题;如果只有一两条,记下来跳过即可。没有统计,你只会觉得「偶尔有几条不通」,永远不知道该不该优化。
把被拦的提示词原文、类别、finishReason 一起写进一个文件。攒够几十条之后回看,通常能发现它们有共同特征,比如都包含某些描述性词汇,或者都属于同一类业务场景。
记录格式用 JSON 行更省事,一行一条,字段固定成提示词、类别、finish_reason、时间。后续统计、检索、给同事看都方便,不需要再整理一遍。
统计要按业务线分开看。同一个提示词模板铺在十个场景里,可能只有一个场景反复被拦,那就只改那一个场景的措辞,没必要为了个别情况把整个模板改得面目全非。
被拦样本的保留时间建议不少于一个月。策略判定会随模型版本调整,上个月能过的提示词这个月未必还能过,留着历史样本才有对比依据。
同一件事,写得含糊就容易被判高风险。做内容审核工具的场景,写成「请判断以下文本是否属于骚扰内容,输出类别与依据」,比「帮我分析这段话」清楚得多,也更容易通过。
开头加一句用途说明,例如「以下是客服工单文本,用于生成内部质量报告,请客观归纳问题类型」。模型拿到明确的场景与产出目标,判断会更聚焦。
任务是分类就只给分类需要的信息,不需要把细节原样复述一遍。信息够用即可,多余的形容词和细节既容易触发策略,也不提升结果质量。
如果某个任务反复被拦,先想一想是不是任务本身就不该交给模型做。换成让模型给审核建议、给话术模板、给风险清单,往往既合规又能拿到有价值的结果。
还有一点值得放在心里:被拦不代表内容一定有问题。审核、医疗、法律这些本来就要处理敏感表述的场景,误拦比例天然更高。把拦截率当成一个可以管理的技术指标,而不是当成故障,心态和方案都会清楚很多。
提示词改完先小批量试十条,确认通过率稳定了再放量。一次投两千条然后发现全被拦,既浪费调用次数,还得重新组织一遍数据重跑。
图片模型的策略比纯文本更保守,凡是涉及真实人物形象、可能被误认成真人的描述,都容易被拦,返回 IMAGE_SAFETY。画产品图、场景图、插画风格的内容基本不受影响。
写清楚主体、场景、风格、构图、光线,让模型知道要画什么。含糊、留白过多的描述,模型只能自行补全,补出来的方向未必是你想要的,也更容易触发策略。
换成另一种画面表达方式,通常比换模型更有效。价格上 NanoBanana-Pro 是 0.27 元/次,同一段描述反复重发就是反复计费,先把描述改好再发更划算。
对涉及违法、暴力、危害他人安全等明确不允许的内容,平台不会协助处理,也不会提供任何规避手段。这是使用这类接口的前提,把它当成系统的一部分来设计业务流程。
这类做法既拿不到稳定结果,也会让你的调用记录变得可疑。把精力放在业务表达的清晰度上,才是正路。
把审核、抽检、人工复核做进业务流程,让被拦截的少量样本有人工兜底路径。这样即使某条内容被拦,业务也不会中断,客户体验有保障。
人工兜底路径也要有明确的处理时限和目标。审核类场景里,模型给出的是辅助建议,判定环节交给人来做,既用上了模型的效率,也不会把边界问题甩给用户。
内部的判定标准要写下来。哪些词算骚扰、哪类表述算风险,写成可查的规则表,让不同同事的判断保持一致,也让模型的提示词有明确的依据。
主入口 https://xyuapi.top/v1,备用 https://xyuai.cc/v1,另支持 Gemini 原生 /v1beta 路径,OpenAI 兼容协议,Chatbox、Cherry Studio、NextChat、Cline 这类客户端填上地址和 key 就能用。
按次计费的模式下,是否计入以平台用量页显示的数字为准。做法上很简单:改写提示词之后再发,不要对着同一段文字连发五次,那五次大概率是同一种结果。
| 模型 | 价格 | 说明 |
|---|---|---|
| gemini-2.5-pro | 0.031 元/次 | 文本审核、分类、归纳 |
| gemini-3-pro-preview | 0.05 元/次 | 复杂判断与改写 |
| gemini-3.1-pro-preview | 0.09 元/次 | 高难度分析与复核 |
| claude-sonnet-4-5-thinking | 0.09 元/次 | 长文处理与规则梳理 |
| NanoBanana-Pro(生图) | 0.27 元/次 | 图片生成与编辑 |
finish_reason,把 SAFETY、PROHIBITED_CONTENT、content_filter 统一归成「内容未通过」一类。content 为空的处理写进代码入口,避免 None 往下传。