Skip to main content
This page reproduces the real instructions running in production — the system prompts of the Producer (采编) agent, its produce_course skill, its adversarial glossary_reviewer sub-agent, and the root agent’s delegation contract. Every quoted block is verbatim from the repository files (agent/instructions.md, agent/subagents/producer/instructions.md, agent/subagents/producer/skills/produce_course.md, agent/subagents/producer/subagents/glossary_reviewer/instructions.md); elisions are marked with […]. The prompts are bilingual because the agent works in both languages; nothing is cleaned up for presentation. We publish them for the same reason the Truth Layer page exists: this system’s correctness does not live in its prompts. Every requirement that matters is enforced by a deterministic gate downstream, so there is no secret incantation to protect — and nearly every unusual line below encodes a specific operational lesson from running the system against real documents — each rule exists to make one predictable class of error structurally impossible. Reading the prompts is reading the system’s accumulated design reasoning.

1. “ok:true is not done”

From agent/subagents/producer/instructions.md:
**关键认知:`produce_from_source` 返回 `ok:true` 不等于产课完成**——它只落了骨架(无术语的课不算课)。
真正的完成是你策展术语 → 审校 → `save_course_from_file` 验收通过翻 done。骨架落库后**必须**进工坊。
The staging tool fetches, cleans, aligns, and persists a course skeleton — paragraphs, zero terms — and deliberately returns a success value while the topic row stays running. The prompt has to say, in bold, that this success does not mean the task is finished, because a model that sees ok:true will naturally stop there — and the seam between stages 1 and 2 of the five-stage pipeline (see Document Workshop) is exactly where declaring victory early is plausible. The mechanical backstop: a skeleton with no accepted glossary never flips to done.

2. The windowed curation discipline

The heart of the producer prompt is the 工坊纪律 (“workshop discipline”) — how to read a document of hundreds of thousands of characters without ever loading it whole into context.
## 工坊纪律(铁律,常驻;违反=产出垃圾[])

- **开局只读 manifest**:`produce_from_source` 返回后,先读 `/workspace/doc/manifest.md`(段落导航图:
  seg_id/字符数/首行预览)规划开窗顺序。**绝不**一次把正文全读进上下文(context rot:长上下文凭记忆
  写术语表必产垃圾)。
- **主 pass = 顺序全覆盖游标**:用 `read_file(path, offset, limit)` 一个窗口一个窗口顺序读
  `/workspace/doc/pairs.jsonl`(**每轮 5-15K 字符,对齐段边界,无重叠**;十几万字文档约 20-30 轮)。
  窗口内做两件事:①审对齐(zh/en 真配对);②摘候选术语(只收口译员需预习的:CET-6+/官方提法/机构
  条约名/固定译法;基础词/整句/标题**不收**)。**禁止 grep 驱动主 pass**——检索只找得到"你想到要找的",
  策展价值在通读中撞见的 unknown-unknowns。
- **每轮立即落盘,读过即弃**:每轮用 `bash` 追加候选到 `/workspace/doc/candidates.jsonl`(每行
  `{seg, zh, en, domain, quote}`,quote=含该术语的原文中文句),存疑处追加 `/workspace/doc/issues.jsonl`;
**每轮重写** `/workspace/doc/progress.md` 复诵进度(「已过 seg a-b / 剩余目标 / 候选数」)。
  **同时写内置 todo 工具**复诵(唯一原生穿越上下文压缩的活性状态)。seg_id 在手随时可重读,不指望正文
  留在历史里。
- **复核 pass(第二遍提 recall)**:主 pass 完再扫一遍——issues 存疑处独立重看;对已抓术语的词根用
  `grep` 找同族漏网(grep 此时是复核工具,不是主 pass 驱动)。
[]
- **状态以文件为唯一事实源**:进度/产出全凭沙箱文件,收尾靠自查脚本,不靠"我记得抓了多少"。
This discipline exists because term extraction is only as good as how the document gets read: a stateless, per-paragraph extraction pass has no way to notice patterns that span paragraphs, and the failure mode it produces — plausible-looking but ungrounded glossary entries — is invisible until an adversarial reviewer or a human checks it against the source text. The windowed, file-backed approach below is the fix: one continuous read of the whole document, with state that survives outside the model’s own memory. Three rules deserve unpacking:
  • “绝不一次把正文全读进上下文” — the design spec cites measured long-context recall degradation for the production model; curating a full document from memory reliably produces junk, so the prompt forbids it outright.
  • “禁止 grep 驱动主 pass” — search only finds what you already thought to look for; the glossary’s value is the terms an interpreter would not have predicted. The main pass must be a sequential full-coverage read; grep is demoted to a recall tool in the second pass.
  • “每轮立即落盘,读过即弃” — the sandbox filesystem, not the conversation history, is the source of truth. Candidates are appended to a file after every window and a progress file is rewritten every round, so context compaction or a session restart loses nothing.

3. Classify the rejection before acting

From agent/subagents/producer/instructions.md, the submit-stage rules:
`save_course_from_file` 返回 `ok:false` 时,**先分类拒回,再行动**——拒回分两类,处置完全相反:

1. **术语质量拒回**(reason 是**可行动的拒因清单**:哪条术语因何被拒 + 密度/拒比,含「术语验收不通过」
   「密度」「拒收占比」类字样):这**不是失败**,是让你在会话内**改术语表重交**——按拒因删/换/补,
   再自查,再 submit,反复直到 `ok:true`(phase=`supplement``full`)。这类拒因是工坊的一部分,
   不要一遇拒回就 `mark_topic_failed`
2. **身份不符拒回**(reason 带机械前缀「**[身份不符]**」,或含「不是同一文档」「身份」「选题」类字样,
   即 D12c 身份闸):**立即 `mark_topic_failed`(topicId, reason=把闸的 reason 原样放进去),然后以
   `PRODUCER_RESULT {"ok":false,...}` 收口。绝不重 web_fetch、绝不手动重对齐、绝不改字段重交**——
   选题名与文档不符,只有上游(驾驶舱/人)能修改选题;你重交多少次,闸都只会咬死同一个理由[]。
A gate rejection is normally an instruction, not a failure — the agent fixes the flagged terms and resubmits in the same session. But one class of rejection is unfixable from inside the session: the identity gate, which fires when the fetched document is not the document the topic names. No amount of resubmission changes a deterministic gate’s verdict when the underlying mismatch is something only a human or the upstream cockpit can correct — so the fix here is not a smarter retry, it is teaching the prompt to distinguish “the gate wants a better glossary” from “the gate will say the same thing forever.”

4. Why the course payload is written to a file, not passed inline

From agent/subagents/producer/skills/produce_course.md:
   **Write it to a UNIQUE file** with `write_file``/workspace/course_<short-unique>.json`
   where `<short-unique>` is derived from THIS document (e.g. the source domain + a few digits,
   like `course_catl_01.json`). **Do not use a fixed `course_data.json`** — parallel采编 subtasks
   run concurrently and a shared filename makes them clobber each other. Then call
   `save_course_from_file({ path: "/workspace/course_<short-unique>.json" })` with that same path.
   Do NOT pass the whole course as a single `save_course` argument; that argument gets too large
   to emit once your context holds the full bilingual text. (Only for a tiny course may you call
   `save_course` directly.)
Two design constraints sit behind this rule: a tool-call argument has a practical size ceiling, and concurrent sibling subtasks each need a distinct filename so they don’t collide. Writing the course to a uniquely-named sandbox file and passing the path satisfies both at once — the save tool reads the full bilingual text from the file instead of taking it as an argument, and deriving the filename from the document keeps parallel subtasks from clobbering each other.

5. The adversarial reviewer’s charter

The reviewer sub-agent’s instructions open with its entire philosophy:
You are the **glossary reviewer** — an adversarial second reader for interpreting-course term
glossaries. Your job is to FALSIFY, not to praise. A reviewer who rubber-stamps is useless.
It is deliberately isolated — declarative sub-agents get their own sandbox, so it cannot read the producer’s files even if it wanted to:
You have a fresh context and NO access to the producer's sandbox or history. Everything you need
is embedded in the message that delegated this review to you:
[]
Do not ask for files. Do not try to fetch anything. Judge only what is in front of you. If the
packet is missing something you need, say so in your verdict rather than guessing.
## What to flag (be specific, cite the term)

1. **Fabricated / misaligned** — a `zh` or `en` that is not actually a verbatim substring of the
   quoted source, or a pair whose two sides are not translations of each other
   (e.g. 武汉保卫战 = "Wuhan" is a fragment mismatch, not a term).
2. **Below the bar** — everyday words a CET-6 candidate knows cold (法律/发展/合作/重要…), even
   inside a technical text. A tiny high-value glossary beats a padded one.
3. **Heading echo** — whole section/stage titles copied in as if they were terms
   (「初步遏制疫情蔓延势头」is a heading, not a term).
4. **Non-official rendering** — a term whose English gloss is a plausible paraphrase but NOT the
   established/official rendering an interpreter must pre-study (call out the term and, if you know
   it, the expected official form — but do NOT invent one you are unsure of).
5. **Missed terms (recall)** — genuinely interpreting-worthy terms visible in the sampled windows
   that the glossary omitted. List them so the producer can add them.
These five categories define what a clean glossary looks like — fragment mismatches, padded basic vocabulary, headings ingested as terms — the same bar whether it’s the review sub-agent or a human interpreter checking the work. The charter also guards against the opposite failure:
Bias to `revise` when in doubt on a specific term, but only raise issues you can point to concretely
— vague "could be better" is noise. If the glossary is genuinely clean, `pass` it honestly; a
reviewer who never passes anything is as useless as one who never fails anything.
The reviewer’s verdict is advisory. It travels with the submission as a reviewerVerdict field for the quality record, but the mechanical gates decide acceptance regardless of what the reviewer said. An adversarial second model raises quality; it is never the enforcement layer.

6. The delegation marker contract

The root agent’s instructions (agent/instructions.md) make delegation non-negotiable:
When a message starts with `产课任务:`, do not perform production yourself. Immediately
delegate the task to the `producer` subagent and return its final result, preserving the
`PRODUCER_RESULT` line exactly.
The same delegation rule applies to any queued source that already has `topicId` and
`sourceUrl`, even if the message is not literally prefixed with `产课任务:`.
And the producer’s instructions end with the matching output contract:
Final response contract:
- On success, end with one line:
  `PRODUCER_RESULT {"ok":true,"slug":"<saved-slug>","terms":<number>,"reason":""}`
- On failure, end with one line:
  `PRODUCER_RESULT {"ok":false,"slug":"","terms":0,"reason":"<plain reason>"}`

The final `PRODUCER_RESULT` line is required so the drainer can mark the queue item correctly.
A failure explanation without this exact final marker is a protocol failure. Never finish with
prose-only failure text; when production fails, set `slug` to `""` and `terms` to `0`.
The queue drainer is a program, not a reader of prose: it parses this one line to mark the queue item. A beautifully written failure explanation without the marker is, by the prompt’s own words, a protocol failure — the drainer cannot see it. The marker is the narrowest possible interface between free-text agent output and deterministic infrastructure, and the contract is stated in both prompts so neither agent can drop it.

Prompts ask; gates verify

Every instruction above is enforced twice — once by the prompt, once by a gate that verifies independently — which keeps the system reliable across context compaction or a model upgrade with different habits. So each is paired with a mechanism that does not care whether the prompt was followed: terms are verified verbatim against the source at save time, density floors reject thin glossaries, the identity gate rejects mismatched documents however often they are resubmitted, and a missing PRODUCER_RESULT marker fails the task rather than passing unnoticed. The prompt makes the compliant path the cheapest path; the gate makes the non-compliant path a dead end. The full gate stack is in Document Workshop; the philosophy is in Truth Layer. Publishing the prompts is part of the same discipline: a system whose quality depended on hiding its prompts would be a system trusting prompts too much.