AI 找不到資料時可以怎麼回答
以前 AI 助理讀自訂表格讀不到資料時,會直接說「這筆資料不存在」。這句話常常是錯的:一列資料看不到,可能是被 row policy 濾掉、可能是欄位被隱藏、可能是 SCP channel 範圍不含它、可能是這張表沒有被納入這個聊天室的洞察系統,也可能是有人的寫入還在等核准、還沒落地。
「我看不到」和「它不存在」是兩件不同的事,而使用者以前收到的是自信的那一句。所以現在只要讀到零列,就會附帶一個判定,而 AI 只有在判定允許時才可以否認它存在。
absence 物件
當讀取結果 total == 0,AI 的資料列查詢工具會附上一個 absence 物件,帶三種判定之一:
| 判定 | 代表什麼 | AI 可以怎麼說 |
|---|---|---|
not_found | 檢視沒有被收窄,也沒有任何未落地的寫入可以解釋這次找不到 | 可以說沒有這筆資料 |
not_visible | 檢視被收窄,或探測無法完成 | 不存在這件事未被證明:AI 既不能否認、也不能斷言它存在 |
held | 有一筆帶著這段文字的寫入被登記,但還沒落地 | 說明那筆請求的狀態,而不是說資料不存在 |
absence 物件是說「這筆資料不存在」的唯一依據。沒有它,誠實的回答是:在我能看到的範圍裡找不到。
held 判定,以及使用者接下來該做什麼
held 只在「已提出但不在表裡」的暫存變更狀態上成立:pending、applying、apply_failed、discarded。applied 刻意排除,因為那一列已經落地,讀得到。
每種狀態的下一步不同,AI 被要求要說清楚是哪一種:
pending/applying:還在等核准。沒有東西壞掉,就是需要有人去審。apply_failed:核准已經過了,但接著被這張表的某條規則拒絕。判定會附上reason,引用被拒絕的那筆寫入;正確的做法是修正reason指出的資料再重送。重新核准沒有用,因為核准早就完成了。discarded:那筆請求已被取消或駁回,沒有什麼在等。
探測不會洩漏資料
暫留探測回答的是「是不是有人未落地的寫入造成這次找不到」,這代表它要讀暫存變更,因此有兩層閘門。
「有哪些暫存列存在」交給與核准清單相同的查詢:管理者看得到這張表的紀錄,其他人只看得到自己提出的請求,加上目標資料列本來就通過他 row 層 ACL 的紀錄。
「哪些內容可以讀」是第二層、也更窄的閘門。探測回報的每個欄位都是從 payload 推導出來的,所以「可以列出但不能讀」的紀錄會被跳過,而跳過就會把這次掃描標記為不完整。
只有真的跑完的探測才能斷言「不存在」
任何沒有真的掃描的離開路徑都會把掃描標記為不完整,而不完整的掃描會把判定強制成 not_visible。這涵蓋:查詢裡沒有可用的比對字串、缺少主體、達到暫存掃描的列數上限、payload 太大走不完、caller 不能讀的 payload,以及探測本身失敗。
這個偏誤是刻意的:少報一個暫留是比較小的錯,而暫留永遠不會被憑空製造出來。
哪些參數會餵給探測
探測要比對的字串來自一組固定的參數。在 custom_tables_find_records 是 search_text,當成子字串比對值收集。在 custom_tables_query_records 是 filters 與 any_of——而且只有這兩個。比較新的那幾個讀取參數一個都不會貢獻:全域 q 文字搜尋與 computed_filters 述語對探測是不存在的。
所以,只用 q 收窄的 query_records 呼叫沒有可用的比對字串,掃描會被標記為不完整,判定被強制成 not_visible。它永遠不可能回 not_found,也永遠不會浮出一筆帶著那段文字的 held 寫入——即使那筆寫入真的存在。如果這次讀取的目的是判定一筆資料存不存在,就把那個字串放進 filters 的 eq 或 contains 條件,或改用 find_records,讓探測真的跑起來。q 是用來找到資料列的,不是用來證明資料列不存在的。
為什麼一筆資料會「看起來不存在」
AI 自己的說明現在把這些列為原因,你在處理使用者「系統把我的資料弄丟了」的回報時也值得知道:
- 所在聊天室從未被授權這張表,或該房的授權列收窄了這個房間能看到的範圍——自 2026-07-28 起,agent session 解析部門表只看所在聊天室的授權(釘選兼天花板),所以你在 REST 上讀得到的資料列,在這個房間裡可能是隱形的,管理者也一樣;見 effective permissions
- row policy 把那一列濾掉,包含每次請求才解析的
$me、$today、$now - 使用者用來過濾的欄位被 column ACL 隱藏
- SCP channel 範圍 在這個聊天室不含那一列
- 這張表沒有被納入這個聊天室的洞察系統
- 寫入確實存在,但還沒落地:等核准中、正在套用、被規則拒絕,或已被丟棄
只有最後一項是暫留,其他都是可見性問題,而可見性正是 not_visible 的用途。
被拒絕或被截斷的一輪是看得見的,不是空白
「找不到」有一個孿生的失敗模式:模型根本什麼都沒產出的那一輪。在 backend PR #1181 之前,provider 的拒絕或 token 上限截斷會被存成一則空的、成功的 assistant 訊息——succeed 為 true、沒有錯誤碼、沒有文字——Custom Tables 房間的使用者體驗到的,就是助理單純沒有回話。畫面上沒有東西可以反應,存下來的那一輪也沒有東西可以除錯。
現在這樣的一輪會存下一則看得見的婉拒,帶著 succeed = false 與 error_code:
| 成因 | error_code | 客戶讀到什麼 |
|---|---|---|
模型拒絕——Anthropic refusal、OpenAI content_filter、Gemini/Vertex 的 safety、prohibited_content、blocklist、spii、recitation、image_safety | model_refusal | 「抱歉,這個請求我無法回覆。」/Sorry, I can't respond to that request. |
回覆被截斷——max_tokens、length、model_context_window_exceeded | model_output_truncated | 「回覆在產生時被截斷,請縮小問題範圍後再試一次。」/The reply was cut off while being generated; please narrow the question and try again. |
在對話紀錄裡讀到這種訊息時,有三個性質很重要:
- 文字是伺服器撰寫、決定性的。provider 的任何字串都不會送到客戶眼前,而這則婉拒是普通的 assistant 內容——它會渲染、會進 thread、也會被計入,和其他回覆一樣。
- 語言由該輪對話文字的 CJK 偵測決定,不是由通道型別、也不是由房間語系決定。
- 這道防護只在答案為空時才啟動。模型拒絕但仍然產出文字時,內容不會被動;而空答案的停止原因若不在上述兩份清單內,仍然維持
succeed = true,只會留下一筆 log。
使用者要求的 JSON 區塊不再被剝掉
同一類空白輪還有第二個成因。Chat 後處理會刪掉外洩的 tool-call JSON,讓原始 tool call 永遠不會送到客戶眼前——而它過去會刪掉每一個帶 json 標記的圍欄區塊,包含使用者明確要求的那一個。在一個「就是要你回一個 JSON object」的房間裡,答案被剝掉,取而代之被存下來的是一則空的 assistant 訊息。
自 backend PR #1178 起,只有當 json 區塊的內容看起來像外洩的 tool call 時才會被刪除:內容含有 "tool" 或 "tool_calls",或同時含有 "name" 與 "arguments"/"parameters"。其他任何東西——陣列、一般 object、業務 payload——都會原樣保留,連 fence 一起。這是對原始內容做子字串比對,所以一個真正的答案若剛好同時帶著 name 與 parameters 欄位,仍然會被移除;替房間指定固定輸出形狀時,請避開這組 key 名稱。另外要注意:Final Answer Contract 在使用者要求恰好一個 JSON object 時,要的是沒有 fence 的裸物件——這條規則講的是仍然會走到後處理的「有 fence」情況。
AI 實際上能對一張表提出哪些問題——運算子、上限,以及沿路會撞到的拒絕——見 Agent 工具箱。
小型查找、分析與異動確認(v5.10.0)
custom_tables_find_records 的 matches 現在包含 {id, label, data},另有 total。data 使用可見的顯示欄名,補齊人員/附件顯示資料並移除隱藏欄位,因此小型文字查找可直接回答,不必再呼叫 analyst。條件查詢、計數、彙總、比較與跨表問題仍交給 analyst。
Analyst 對清單或單一值查找使用一次伺服器端篩選/排序查詢;彙總與衍生指標仍需驗證。對使用者的回答採業務名稱與人員顯示名稱,並說明列權限造成的可見範圍限制;只有明確要求時才顯示 ID。Schema 提示也會依欄位型別說明輸入格式、限制與唯讀計算欄位。
異動確認採人員與資料的業務名稱,只詢問一次,並可接受有上下文的自然語言同意。遭拒或參數錯誤不等於寫入成功;把遭拒的值改放其他欄位,需要使用者另行提出要求。批次工具只宣告目前 grant 允許的 action kinds。