一次请求是怎么拼出来的
陪读蛙按什么顺序拼出最终发送的提示词,以及它拿模型返回的文本做了些什么。
你填的那两个字段并不是真正离开浏览器的内容。陪读蛙会追加规则块、替换变量、附上术语;而在模型回复落到页面上之前,它还要对回复做不少处理。本页按顺序把这两半都走一遍。
这里讲的都是 LLM 提供商。Google 翻译、微软翻译和 DeepL 不接收提示词,以下各步与它们无关。
请求是怎么拼出来的
1. 挑出段落
内容脚本在段落接近视口时把它们收集起来,剔掉被跳过规则或小段落过滤排除的那些,其余的交给队列。在 LLM 提供商上,队列会把彼此兼容的段落合并成一次请求——同一个提供商、同一对语言、同一份页面上下文——直到撞上请求控制页面上的字符数和段落数上限。这一组就是 {{input}} 的内容。
2. 选定提示词
陪读蛙按你选中的提示词 id 去找。命中内置的就用内置的;否则在你自己的提示词里找;两边都找不到——比如你把当时正在使用的提示词删掉了——它会回落到通用,而不是直接失败。
3. 追加规则块
额外的指令会追加到系统提示词上,而且只在需要的时候追加。这就是同一个提示词在不同段落上会产生不同请求的原因。
| 规则块 | 什么时候追加 |
|---|---|
| 多段落规则 | 本次请求带了不止一个段落 |
| 已是目标语言规则 | 本次请求带了不止一个段落,且仅限网页翻译 |
| HTML 标记保护规则 | 文本里含有 data-rf-attr 标记 |
| 占位符保护规则 | 文本里含有带编号的公式占位符 |
只带一个段落的请求一个都不会有。字幕翻译会拿到多段落规则,但永远不会拿到「已是目标语言」那条。
4. 替换变量
这时才把 {{...}} 这些名称替换掉,两个字段都替换,取值见提示词变量。只有已知名称会被替换,其他内容原样保留。
5. 附上术语
最后,把你术语库里真正出现在这段文本中的术语追加到系统提示词后面。放在最后是故意的:术语是你自己输入的文本,如果在替换变量之前就拼进去,一条恰好含有变量名的术语就能改写它周围的提示词。
一次成型的请求
一篇英文文章的两个段落,翻译成简体中文,用通用提示词,AI 智能上下文关闭。发出去的系统提示词是:
You are a professional Simplified Mandarin Chinese native translator who needs to fluently translate text into Simplified Mandarin Chinese.
## Translation Rules
1. Output only the translated content, without explanations or additional content ...
2. The returned translation must maintain exactly the same number of paragraphs and format as the original text.
3. If the text contains HTML tags, consider where the tags should be placed in the translation while maintaining fluency.
4. For content that should not be translated (such as proper nouns, code, etc.), keep the original text.
## Document Metadata for Context Awareness
Webpage title: Why your timestamps are eight hours off — devblog
Webpage summary: No summary available
## Multi-paragraph Translation Rules
1. If input contains a standalone line containing only %%, use a standalone %% line in your output ...
2. **CRITICAL**: Treat %% as a separator only when it appears on its own line ...
## OUTPUT FORMAT:
- **Single paragraph input** → Output translation directly (no separators, no extra text)
- **Multi-paragraph input** → Put %% on its own line between translations
## Already-translated Input Rule
Output only {{NO_TRANSLATION_NEEDED}} when only non-translatable names, brands, handles, URLs, numbers, or code differ from Simplified Mandarin Chinese. A foreign-language phrase or clause must be translated.提示词是:
Translate to Simplified Mandarin Chinese:
Every timestamp you store without a zone is a bug waiting for a plane ticket.
%%
The fix is boring: store UTC, render local, and never let the two meet in a database column.注意冒号后面那两个空行——它们来自提示词字段本身,也就是 Translate to {{targetLanguage}}: 之后跟两个空行再跟 {{input}}。
回复是怎么处理的
1. 去空白、切分
回复先做首尾去空白,然后按「整行只有 %%」的行切开。每一段再各自去一次空白,并与请求中相同位置的段落对应起来。出现在句子中间、代码示例里或引文里的 %% 不算分隔符——只有除它之外什么都没有的那一行才算。
2. 校验数量
陪读蛙要求切出来的段数和发出去的完全一致。数量不对是整组的彻底失败,而不是部分成功:这次请求会以递增的间隔重试最多三次,如果还是对不上,这一组里的每个段落会被单独重发一遍。这更慢也更贵,所以一个鼓励模型加评论、或者把段落合并的提示词,不只是不整齐,而是真的费钱。
3. 处理「不需要翻译」
{{NO_TRANSLATION_NEEDED}} 是我们告诉模型的一个答案:当某个段落本来就是用你的目标语言写的时候,就返回它。当某一段恰好只是这个标记时,陪读蛙会把译文当作空,而空译文什么都不渲染——双语模式下那个段落底下不会出现第二行,仅译文模式下原文继续显示。
这个标记会原样写入缓存,所以以这种方式结案的段落下次访问时不会再发一遍请求。它被故意做成变量的样子:变量替换只替换它认识的名称,所以这个标记能毫发无损地穿过整个提示词拼装过程,每次到达模型时的写法都完全一致。
4. 丢掉什么也没说的译文
模型拿到本来就是目标语言的文本时,常常不用上面那个标记,而是把原文带着一点表面差异回抄一遍——空白重排、直引号变成弯引号、标点变全角。陪读蛙会在把这些差异折叠掉之后拿回复和原文比较,一致就同样当作空。这就是混合语言页面上有些段落明明发过请求、却看不到译文的原因。
5. 核对标记
如果发送的文本带了 data-rf-attr 标记,回复必须把每一个都原样返回一次,并且挂在同一种元素上。标记缺失、重复、被凭空造出来、或者跑到了另一种标签上,这个段落会直接判为失败——而不是渲染出一个指向别处的链接。
带编号的公式占位符处理得宽松一些。丢了或重复了占位符的回复仍然会显示出来——缺失的公式会被补在后面——但它不会写入缓存,这样下次访问会重新试一遍,而不是让这个段落永久保持退化状态。
6. 存下来
活下来的结果写入翻译缓存,页面随之更新。
缓存的键
一条缓存译文的键里包含了——除别的东西之外——最终成型的系统提示词和提示词:追加规则块之后、替换变量之后、附上术语之后的那两段。
由此带来三个值得知道的结果:
- 改提示词会让所有用旧提示词翻出来的译文失去归属。 你已经读过的页面会被重新翻译,也会重新计费。
- 改术语库只会影响那些命中了术语的段落。 一条术语都没命中的段落,发出的提示词和「完全没有术语库」逐字节一致,所以它的缓存条目不受影响。
- 开或关 AI 智能上下文会改变每一个键,因为摘要变量变了——从一份真实摘要变成
No summary available,或者反过来。
在设置 → 网页翻译里清除缓存,会删掉已存的译文和网页摘要,但不会动你的提示词、提供商和术语库。
