T-C-R-E-I
告別 AI 幻覺:Google 提示工程 T-C-R-E-I 五步驟實戰詳解
大型語言模型 (LLM) 為什麼會產生「幻覺」—— 也就是一本正經地說出錯誤或捏造的資訊。那麼,身為開發者或使用者的我們,該如何應對?
最快、最有效的方式,就是掌握「提示工程」(Prompt Engineering)。
市面上的提示工程方法論大同小異,但我個人特別推薦 Google 在其「Prompting Essentials」教學課程(此課程為 Google Generative AI 學習路徑的一部分)中提出的 T-C-R-E-I 五步驟核心。
這五個字母分別代表 Task (任務)、Context (背景)、References (參考資料)、Evaluate (評估) 與 Iterate (迭代)。Google 甚至為它提供了一個巧妙的口訣:「Thoughtfully Create Really Excellent Inputs」(深思熟慮地創造優質輸入)。
T-C-R-E-I 不只是一套理論,它更像是一套專為工程師設計的SOP。它將模糊的「溝通問題」轉化為結構化的「工程問題」,幫助我們大幅降低 AI 幻覺的機率。
以下,我們將深入解析 T-C-R-E-I 的每一步,並對照工程實務中的概念,說明如何寫出精準、可控的提示詞。
1. Task (任務):拒絕模糊,提供精確的「API Contract」
許多 AI 幻覺的根源,來自於「輸入太模糊」。
當你只對 LLM 說「幫我寫程式」,它會根據訓練語料庫中「最常見的程式」來拼湊答案,結果很可能是 Python 或 JavaScript,而且完全不符合你的真實需求。
這就像在工程團隊中缺少 API Contract (API 合約):如果產品經理只丟一句「幫我做個會員系統」,開發團隊最終交付的可能是 SOAP API、REST API 或 GraphQL,完全無法對接。
「Task」的核心是「定錨」,你必須清楚告訴模型:
- 技術棧:例如 Java 17 / Spring Boot 3.3 / JDK 17
- 功能範圍:例如 GET /users/{id} 查詢 API
- 非功能性需求:例如必須支援 OpenAPI 文件輸出、錯誤要符合 RFC 7807
這些明確的指示,會直接限縮模型在「語料搜尋空間」中的選擇範圍。定義越清楚,幻覺機率就越低。
- ❌ 壞例子: 幫我寫程式。
- ✅ 好例子: (雖然「用 Java 寫一個查詢使用者的 API」好一點,但仍缺少關鍵約束)
[角色] 你是資深後END工程師(Java 17 / Spring Boot 3.3)。
[任務] 實作一支查詢使用者 API:
- 方法與路由:GET /api/v1/users/{id}
- 需求:回傳指定 id 的基本資料,找不到要回 RFC 7807 問題細節
- 相依:PostgreSQL、Spring Web、Spring Validation、springdoc-openapi
[輸入約束]
- path variable id:UUID 格式(如 8-4-4-4-12)
- 接受 Header:X-Request-Id(若缺省,自動生成)
[輸出格式]
- 200:application/json,欄位 { id, name, email }
- 404:application/problem+json(RFC 7807),欄位 { type,title,status,detail,instance }
- 422:同上,detail 說明 id 格式錯誤
[非功能性]
- 程式碼風格:Controller→Service→Repository 三層
- 測試覆蓋:對 Service 寫 3 組單元測試(成功/不存在/無效UUID)
[交付]
- Controller/Service 介面與骨架程式碼
- 1 份 OpenAPI YAML(/v3/api-docs 可導出)
- 測試案例名稱清楚標明情境
2. Context (背景):對齊受眾的「Use Case」
沒有背景的提示,就像把一個新人丟進陌生的專案,卻不給他任何需求文件。
模型不知道「受眾是誰」、「語氣要怎樣」、「應用情境在哪裡」,它只能輸出最「一般」的答案。上下文 (Context) 的價值在於:
- 對齊受眾: 同一段程式碼,解釋給 Junior 要有教學口吻;對 Senior 則要點出架構考量。
- 定義輸出風格: 是要寫成技術文件?還是成為簡報上的 Bullet Points?
- 縮小語境空間: 避免模型回覆一堆它「以為有用」、但其實無關的廢話。
在工程世界裡,Context 就像 需求文件中的使用情境 (Use Case / User Story)。缺少它,AI 的輸出永遠無法對齊 Stakeholder 的期待。
- ❌ 壞例子: 解釋這段程式碼。
- ✅ 好例子:
[角色] 你是帶新人入門的後端教練。
[讀者] 剛入職 3 個月的 Java 新人,懂基本語法,但不了解 Spring Boot 架構。
[任務] 以教學筆記解說下列 Service 程式碼的設計意圖與常見錯誤。
[呈現格式]
- 1 段「這段程式在解決什麼問題」
- 3 條「關鍵設計決策」(每條 ≤ 40 字)
- 1 段「新手常犯錯」+對應修正
- 1 小段「延伸閱讀」:列 2 個關鍵字(如 “Transactional boundary”, “DTO vs Entity”)
[語氣] 親和、避免術語堆砌,必要術語加括號解釋。 [篇幅] 300–450 字。 [素材] (貼上你的程式碼片段)
3. References (參考資料):限制知識邊界的「Spec / Schema」
這一步是 RAG (檢索增強生成) 的核心精神:讓模型知道「只能用這些知識回答」。
如果你把 ERD (實體關聯圖) 丟進去,AI 就不敢亂幫你加「password」欄位;如果你沒給,它就會依照過往的訓練資料隨機編造。
模型並非故意亂掰,而是「找不到正確依據,只好硬湊」。
因此,References 最重要的一點是加入「禁止臆測條款」:明確告訴模型「如果參考資料中沒有答案,就回答『無資料』或『不知道』」。這能直接砍掉大部分的幻覺。
在工程實務中,這就像:
-
API 設計: 如同 Swagger (OpenAPI) 定義先行,程式必須按照 Schema 實作。
-
DB Schema: 若不提供欄位設計,AI 可能隨便補一個「last_login_time」。
-
文件生成: 附上官方 Spec,避免 AI 胡亂創建不存在的參數。
-
❌ 壞例子: 請幫我設計資料庫表格。
-
✅ 好例子:
[角色] 你是資深資料庫設計師(PostgreSQL 15)。
[任務] 根據以下 ERD 產生 CREATE TABLE 語句,並加上必要索引與外鍵约束。
[設計規則]
- 命名:表 snake_case,單數名詞;主鍵 {table}_id
- 時間:全部使用 timestamZ;欄位 created_at, updated_at
- 外鍵:on delete restrict
- 索引:對查詢條件與外鍵欄位建立 btree 索引
- 禁止臆測: 若 ERD 無資訊,輸出“無資料”,不要補創任何欄位。
[輸出格式]
- 以多段 SQL 區塊呈現,每段前加註解說明該表用途。
- 最後輸出「核對清單」:列出 1) 外鍵完整性 2) 索引覆蓋 3) 命名一致性 是否滿足(是/否)。
[素材] (在此貼上你的 ERD 或欄位表)
4. Evaluate (評估):導入「Code Review + 測試驗收」
許多人常犯的錯誤是:「AI 輸出看起來很合理」,就直接複製貼上拿去用,結果上線才發現踩坑。
這跟工程實務一樣:程式跑得動,不代表它就正確、可維護。
Evaluate (評估) 的重點在於引入驗收機制:
- 驗證正確性: 透過單元測試或靜態檢查工具,確認 AI 輸出的程式碼可執行、可測。
- 檢查一致性: 確保命名規則、錯誤處理格式是否統一。
- 引入「紅隊思維」: 主動要求模型「列出可能缺漏的測試案例」或「指出此答案可能的風險」。
這等於是幫模型多加一層 自我 QA,也是把 AI「自信但錯誤」的特性反過來利用:讓它先替自己挑毛病。
- ❌ 壞例子: 直接把 AI 產生的程式碼丟上線。
- ✅ 好例子: (在 Task 提示詞中就已埋入評估機制)
[角色] 你是測試工程師(JUnit5/AssertJ)。
[任務] 針對 UserService.findById(UUID) 產生單元測試。
[覆蓋需求]
- 成功:存在的 id 回傳正確 User
- 邊界:不存在 id → 拋 NotFoundException,訊息含該 id
- 無效:非 UUID 格式 → 拋 IllegalArgumentException
[輸出格式]
- 測試方法清單(方法名、Arrange/Act/Assert 摘要)
- 關鍵斷言片段
- 自我檢查: 列出可能缺補的 2 項測試並說明為何未納入。
[輸入資料] 使用 Fakes,不連資料庫。
5. Iterate (迭代):小步快跑的「CI/CD Pipeline」
許多人使用 AI 的方式還停留在「一次丟一大坨,結果不滿意就全部重來」。這種「推倒重建」的模式,效率其實非常低。
正確的做法是「差分式迭代」(Differential Iteration):
- 指出錯誤點 (例如:你的 OpenAPI YAML 缺少 422 錯誤碼)
- 指定修正範圍 (僅修正錯誤碼部分,其他保持不變)
- 加入評分規則 (若輸出不符合 RFC 7807 schema,請自行再修正一次)
這樣模型就會在「上一版」的基礎上進行修改,而不是每次都重新生成。這就像工程中的 CI/CD Pipeline:每次只提交小修小補 (small commits),跑過自動化驗收,再往下一步推進。
- ❌ 壞例子: (不滿意)「你寫錯了,重寫。」
- ✅ 好例子:
[情境] 你上次輸出的 OpenAPI YAML 有 3 個問題:
- 缺少 422 Unprocessable Entity 的 schema
- 路由 /api/v1/users/{id} 少了 path 參數格式範例
- 描述文字過長(>120 字)
[任務] 僅針對上述 3 點修正,其他內容保持不變(差分編輯)。
[評分規準]
- 正確性:是否包含 422 且 schema 符合 RFC 7807(0/1)
- 完整性:path 參數是否附 UUID 範例(0/1)
- 可讀性:每段描述 ≤ 120 字(0/1) → 得分未達 3/3 時,自行再迭代一次並附「修正說明」
[輸出格式]
- 只輸出修正後的 YAML 區塊
- 最後附上「本次修正說明」3 行內
總結:像工程師一樣思考
Google 的 T-C-R-E-I 框架,是將提示工程「系統化」的絕佳實踐。它讓我們跳脫「通靈」般的猜測,轉而使用工程師熟悉的語言來駕馭 AI:
- Task (任務):就像定義嚴謹的 API Contract
- Context (背景):就像對齊需求的 Use Case
- References (參考資料):就像限制邊界的 官方 Spec / Schema
- Evaluate (評估):就像確保品質的 測試 + Code Review
- Iterate (迭代):就像持續交付的 CI/CD Pipeline
下次當你覺得 AI 又在「胡說八道」時,不妨試著用 T-C-R-E-I 的框架檢查一下:是否你的提示詞,也缺少了上述的某個關鍵環節?