一筆 JSON,欄位齊全、型別正確、引用的來源片段真的存在、文字摘要也對得上。
它把「可以先評估是否整理檢查表」這句話標成了承諾。
前兩層檢查全部放行。抓到它的是第三層:一份跟輸入分開保存的合成標準答案。這是今天最重要的一個反例,也是為什麼「JSON 能解析」離「可以變成待辦」還很遠。
今天全文只守一條:模型可以從自然語言提出原始提案,但不能自己宣布提案已通過,更不能建立待辦。
第 12 天把「這句是誰說的」做成可以暫停、跨程序續跑的分支。身分確認了,能寫的只有具名說話者中繼資料。
今天往前一步。逐字稿裡有人說「這個項目由我處理」,要具備哪些證據,才有資格變成一筆等人審的行動候選?
| 天數 | 新增能力 | 明確不做 |
|---|---|---|
| 12 | 身分分支保存複核請求,跨程序讀回決定再續跑 | 不判斷行動、不建立待辦 |
| 13 | 把原始提案驗成帶來源、可審或缺欄位的行動候選 | 不批准、不呼叫待辦工具 |
| 14 | 另做行動批准,交給假執行器並驗重送不重複建立 | 不碰使用者的真實待辦 |
知道說話者是誰,不代表他承諾負責;看到承諾,也不代表候選已獲准執行。這兩條批准不能沿用。
第 10 天正文提的逐字稿包,跟第 12 天程式裡同名的 transcript-package.v1 不是同一個資料形狀。後者只有時間區段與匿名標記,而且 contains_transcript: false。
所以今天沒有回頭替第 12 天補文字。我另開一份 day13-synthetic-transcript.v1:四段都明示為合成測試資料,沒有音訊、沒有真實會議、沒有真人身分,adapter_note 寫明不宣稱與前兩種格式相容。這是一個新的受控轉接測試,不是端到端整合。
一筆模型整理出的文字,要具備哪些可回查證據,才有資格成為等待人審的行動候選?
答案不是「JSON 能解析就好」。至少要分開回答五件事:模型到底提出了什麼、引用哪一版來源、哪些片段分別支持任務/承諾/負責人主張、缺了什麼、程式允許的下一步是什麼。
不比較模型,不調提示詞,也不證明任何真實會議曾出現同樣的句子。下面講。
用生活的方式想:面試官寫的評語,和 HR 蓋章的錄取通知,是兩張紙。評語可以寫得很篤定,但「錄取」那個章不能由寫評語的人自己蓋。
放進工作流,就是兩份資料:
| 資料 | 可以包含 | 絕對不能由它自己填 |
|---|---|---|
raw-action-proposal.v1 |
行動文字、來源片段、語意訊號、發言者、負責人主張、原始期限文字 | validation_state、批准、允許動作、工具能力、真人 person_ref |
action-candidate.v1 |
經驗證的來源綁定、缺欄位、狀態、固定政策與能力上限 | 行動批准、待辦執行結果 |
如果讓模型在同一份輸出裡填 ready_for_action_review 或 allowed_next_actions,模型就等於能替自己蓋章。今天的程式會拒收 approved、create_task、權限或身分注入欄位,再由本地政策產生可信狀態。
老實說,這次沒有真的呼叫模型。原始提案是手工固定的「模型輸出形狀」測試資料,execution_mode 標成 fixed_fixture_no_external_model。可信欄位由本機程式附加。今天測的是控制層,不是抽取能力。
方向初稿把 discussion_only、decision_recorded、commitment_detected 做成一個單選狀態。後來發現這會抹掉真實的組合:一筆行動可能先被討論、再形成決定、最後有人承諾。三件事都發生過,單選只能留一個。
所以改成可並存的 semantic_signals[]:discussion、decision、commitment、unknown。資料完整度另外放在 validation_state:incomplete 或 ready_for_action_review。一軸記「看到哪些語意證據」,另一軸記「依這次測試政策能走到哪裡」,不再把缺欄位混成語意類別。
uttered_by 只回答:哪些匿名說話者講了被引用的片段。assignee_claim 才回答:提案聲稱誰負責,根據是什麼。兩者不能合併。
合成正常例裡,任務描述由 SPEAKER_SYNTHETIC_A 說出,承諾由 SPEAKER_SYNTHETIC_B 說出。程式只在三件事同時成立才接受 explicit_self_commitment:同一個承諾片段被標為 commitment,也被標為 assignee,而且該片段的說話者跟 assignee_claim.speaker_ref 相同。
即使全部成立,person_ref 仍是 null。匿名說話者沒有被自動升成真人姓名。這是第 11、12 天的邊界,今天不偷渡。
本例只測明示的自我承諾。第三人指派、委派、拒絕、共同負責、組織授權,都沒有涵蓋。
| 驗證層 | 具體檢查 | 擋不住什麼 |
|---|---|---|
| 形狀 | 必填、型別、列舉、額外欄位、識別碼、SHA-256 字串形狀 | 句子是否真的表示承諾 |
| 來源 | 來源版本、片段存在、文字摘要、發言者、跨片段參照 | 模型是否把討論誤標成承諾 |
| 合成測試政策 | 任務描述、承諾證據、明示負責人主張、缺欄位、允許下一步 | Ci 真實組織的責任與批准規則 |
三層之外,還有一份跟輸入分開保存的合成標準答案,專門抓開場那種「形狀合法、來源也對,但語意標錯」的固定反例。
正式環境沒有隱藏的標準答案。那時候要靠評測資料或人審,不能假裝第三層已經證明語意為真。
OpenAI 的 Structured Outputs 指南把這個機制定位為「讓輸出遵循指定的 JSON Schema」,物件通常要列出必要欄位並禁止額外屬性。同一份指南另有一節「Handling mistakes」,明說結構化輸出仍可能包含錯誤;拒答會以另一種內容型別出現,不一定符合原 schema;incomplete 也要在程式裡獨立處理。
JSON Schema 2020-12 的規格自己也講得很直白:單靠結構驗證,可能不足以讓應用程式正確使用某些值。type、required、enum、additionalProperties 能擋錯欄位;它們不會知道「這個我來處理」指的是誰。
所以今天可以說「成功分支的形狀可以被約束」,不能說「形狀合格就證明逐字稿真的有承諾」。拒答、截斷、空內容,也不能被改寫成「零筆正常候選」。
這次的形狀驗證是手寫的嚴格版本,沒有安裝通用 JSON Schema 驗證器。兩份 schema 都明列 Draft 2020-12 與固定 $id;$id 是識別用的 URI,不保證可以下載。
輸入是四段合成逐字稿,每段五秒,總長 20 秒:
▸ A 說:可以先評估是否整理發布前檢查表。
▸ A 說:決定保留發布前檢查表這個方向。
▸ A 說:請補上通知設定的檢查項目。
▸ B 說:這個項目由我處理。
固定的三筆原始提案,各引用不同片段:
| 原始提案 | 語意訊號 | 結果 | 為什麼 |
|---|---|---|---|
| 只討論要不要整理檢查表 | discussion |
recorded_discussion |
留軌跡,不產生候選 |
| 決定保留檢查表方向 | decision |
incomplete_candidate |
缺承諾證據與負責人參照 |
| 一段描述任務,下一段明示由自己處理 | commitment |
ready_for_action_review |
任務、承諾、負責人主張與三筆來源參照吻合 |
第二筆的 missing_fields 是 commitment_evidence 和 assignee.speaker_ref。程式沒有替它補負責人,也沒有補期限。
第三筆引用了三筆來源參照:任務描述那段(角色 task_description)、承諾那段(角色 commitment)、同一段再標一次(角色 assignee)。三筆都要存在、摘要都要對,才走到 ready_for_action_review。
三條路的 external_model_calls、task_executor_calls、publish_calls 全部是 0。期限沒有出現在任何片段裡,所以保存 raw_text: null 與 status: not_stated。程式沒有補一個看起來合理的日期。
{
"schema_version": "action-candidate.v1",
"candidate_id": "candidate-proposal-synthetic-commitment",
"action_text": "補上通知設定的檢查項目",
"semantic_signals": ["commitment"],
"uttered_by": ["SPEAKER_SYNTHETIC_A", "SPEAKER_SYNTHETIC_B"],
"assignee": {
"speaker_ref": "SPEAKER_SYNTHETIC_B",
"person_ref": null,
"basis": "explicit_self_commitment"
},
"deadline": { "raw_text": null, "status": "not_stated" },
"validation_state": "ready_for_action_review",
"missing_fields": [],
"policy_id": "synthetic-review-policy.v1",
"allowed_next_actions": ["request_action_review"],
"forbidden_capabilities": ["create:task", "publish:content"]
}
這是精簡節錄,完整檔另含每個片段的 SHA-256 文字摘要。欄位是本文設計,不是供應商標準。
ready_for_action_review 的意思只有「可以送人審」。不是已批准。allowed_next_actions 只有一項,forbidden_capabilities 明寫不能建待辦、不能發布。
synthetic-transcript.json
├─ synthetic-gold.json
└─ raw-action-proposals.json
├─ action-candidates.json
├─ artifact-manifest.json
└─ validation-trace.json
逐字稿保存輸入片段;標準答案跟模型輸入分開;原始提案明示沒有呼叫模型;候選檔保留三筆判定與唯一一筆可進人審的候選;產物清單記錄使用與產生的摘要;軌跡逐層保存通過、缺欄位或停止的原因。
軌跡裡有五道 gate:response_status、raw_proposal_shape、source_binding、synthetic_test_policy、synthetic_gold_oracle。每一道都寫下自己的結果。
只保存最後一行待辦文字,會失去前面所有判斷依據。W3C PROV-DM 把來源追溯拆成實體、活動、代理者,以及使用、產生、衍生等關係。放到今天:合成逐字稿、原始提案、已驗證候選是不同實體;擷取與確定性驗證是不同活動。這份產物清單借用這個拆法,沒有映射完整 PROV 約束,不能稱為 PROV 相容。
記錄這些關係,也不會自動讓內容成真。
| 變異 | 預期結果 |
|---|---|
| 來源片段不存在 | evidence_segment_not_found |
| 文字摘要與片段不符 | evidence_text_digest_mismatch |
| 舊提案套到新版來源 | stopped_source_stale |
| 負責人主張與承諾片段說話者不同 | assignee_evidence_mismatch |
| 缺必要欄位 | proposal_shape_invalid |
夾帶 approved 或 create_task |
proposal_capability_injection |
| 模型回應形狀為拒答/截斷/空內容 | 分別停止,不補候選 |
| Day 12 身分決定冒充行動提案 | proposal_shape_invalid |
| 合法形狀把討論誤標成承諾 | 合成標準答案回報 semantic_gold_mismatch |
原因碼是本篇範例介面,不是任何規格或研究的共同錯誤碼。這批專項測試我這次跑是 16 條全過。
最後一條測試是重播:讀回同一份已保存的 raw-action-proposals.json,再跑一次確定性驗證器,比對 replay_digest 相同,而且不修改輸入。
這裡的「重播」定義得很窄。OpenAI 的 API 概覽明示模型輸出有變異性,不同模型快照的提示行為也可能改變;固定版本並建立評測能提升一致性,但不是逐位元重現的保證。所以再問模型一次,是一次新的抽取執行,應有新的執行識別、模型版本與原始回應,不能拿來證明舊結果可重播。
來源整體版本與每段文字摘要分開保存:來源換版就停止,片段文字變了也拒收。摘要相同只表示這次沒有偵測到內容差異;它不能證明發言真的發生、說話者是誰、某人有權承諾,或候選已經獲准。
算摘要之前,JSON 要先有固定表示。這次沿用專案內的受控做法:遞迴排序物件鍵再算 SHA-256,只處理固定 fixture 的有限值型別。可以說同一份保存輸入在本程式中重算一致;不能說已跨語言符合 RFC 8785。
這不是我發明的拆法。2006 年 Purver、Ehlen、Niekrasz 研究會議裡的行動項目,就沒有把它標成一句話的是/否。他們把相關發言拆成任務描述、時間範圍、負責人、同意四類,並指出同一句可能同時扮演多種角色,同一個行動項目的角色也可能分散在多句。
隔年的後續研究把行動項目描述成「對某項任務的公開承諾」:先找出提議、討論、承諾發生的子對話,再抽任務摘要。任務描述與屬性常分散在多個發言。這是為什麼正常合成例刻意把任務放在一段、承諾放在下一段,並要求兩段來源參照都存在。
決定也一樣。Fernández 等人把決策偵測建模成多人對話裡的結構問題:不同發言在討論與採納時扮演不同角色。所以今天保留 decision 訊號,但不自動補承諾。固定決定例有行動文字,仍因缺 commitment_evidence 與 assignee.speaker_ref 停在 incomplete。
還有一個歷史數字值得放著提醒自己。Morgan 等人 2006 年在 ICSI 會議錄音上偵測行動項目,F-measure 是 31.92%,摘要明列語料高度不平衡、標註者一致性低。這個數字不能拿來跟今天的模型比,它只說明一件事:「什麼算行動項目」本身就需要明確定義。語意標準答案、錯誤案例、人審、來源回查,都是因為這個。
AIMU 研究在 22 場 ICSI 會議、約 21K 發言上標了 10 種預先定義的意圖,也標行動相關參數,並強調判斷要看上下文。今天借用的只有責任拆法:原始提案保留語意訊號、行動文字、負責人主張、期限原文與來源片段。不把那 10 種意圖搬成分類表,也不宣稱重跑過該資料集。
這些論文都是當年資料與方法的結果。它們給的是問題的拆法,不是今天任何模型的準確率。
證明了:
1/ 三筆固定提案在確定性驗證器下得到三種不同結果,討論不成候選、決定缺欄位、跨片段承諾可送人審。
2/ 形狀合法、來源可回查的錯標,會被分開保存的合成標準答案抓到。
3/ 八種壞輸入各有停止原因,沒有一種被補成候選。
4/ 重播同一份保存輸入,結果摘要相同,輸入不變。
沒證明的:
▸ 任何模型的抽取能力。原始提案是手工固定的形狀測試資料,沒有呼叫模型。
▸ 真實會議、真人、真實政策。synthetic-review-policy.v1 是教學政策。
▸ 第三人指派、委派、拒絕、共同負責。只測了明示的自我承諾。
▸ 與第 10、12 天資料形狀的相容性。今天是獨立轉接器。
今天最後只交出一筆綁定來源版本的 action-candidate.v1,allowed_next_actions 只有 request_action_review。它沒有 approved,沒有 task_created,也沒有待辦工具可以呼叫。
Day 14 才新增另一份行動批准,把批准範圍、候選版本、來源版本綁在一起;再交給假執行器,驗證相同批准重送不會建立兩次、相同識別碼換內容必須衝突。
第 12 天的身分確認,仍然不能沿用成行動批准。