Agent 工作流程

full-output-enforcement

把交付項目先計數再逐項完成,禁止省略號、TODO、其餘同理等截斷模式;內容過長時只在完整邊界暫停並明確標示續接位置。

查看來源 ↗
作者/來源Leonxlnx★ 85,781 GitHub Stars
測試版本ccbc15639c97更新日期:2026-08-24T15:23:56Z
支援平台OpenAI · Claude本次實測平台
安裝方式npx skills add leonxlnx/taste-skill --skill full-output-enforcement
公平比較方式

三個問題,分別測載入前與載入後。

每次回答都在全新環境中產生。兩邊使用相同模型與設定,唯一差別是右側載入了這個 Skill。

模型版本gpt-5.6-sol帳號方案ChatGPT Plus思考強度medium執行工具codex-cli-native-skill
真實測試使用本機已登入的 ChatGPT Plus 實際執行,完成於 2026年9月10日。所有檢查通過後才公開,回答未經人工改寫。
三階段測試3 個問題全部完成版本核對3/3 通過環境清理9/9 通過測試隔離每回合全新環境
01
基礎題

產生完整工具函式

測試問題

請提供一個完整 TypeScript 檔案,必須含 parseCsv、escapeCsv、groupBy、sumBy、uniqueBy 五個可匯出函式,每個函式各附一個 Vitest 測試。禁止使用外部套件;不得用省略號、TODO、placeholder 或『其餘同理』。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill1516 tokens · 34640 ms
```typescript import { describe, expect, it } from "vitest"; export function parseCsv(input: string): string[][] { if (input.length === 0) { return []; } const rows: string[][] = []; let row: string[] = []; let field = ""; let insideQuotes = false; for (let index = 0; index < input.length; index += 1) { const character = input[index]; if (insideQuotes) { if (character === '"') { if (input[index + 1] === '"') { field += '"'; index += 1; } else { insideQuotes = false; } } else { field += character; } continue; } if (character === '"' && field.length === 0) { insideQuotes = true; } else if (character === ",") { row.push(field); field = ""; } else if (character === "\n" || character === "\r") { if (character === "\r" && input[index + 1] === "\n") { index += 1; } row.push(field); rows.push(row); row = []; field = ""; } else { field += character; } } if (insideQuotes) { throw new Error("Invalid CSV: unterminated quoted field"); } const endsWithLineBreak = input.endsWith("\n") || input.endsWith("\r"); if (!endsWithLineBreak || row.length > 0 || field.length > 0) { row.push(field); rows.push(row); } return rows; } export function escapeCsv(value: unknown): string { const text = value == null ? "" : String(value); if (!/[",\r\n]/.test(text)) { return text; } return `"${text.replace(/"/g, '""')}"`; } export function groupBy<T, K>( values: readonly T[], keySelector: (value: T, index: number) => K, ): Map<K, T[]> { const groups = new Map<K, T[]>(); values.forEach((value, index) => { const key = keySelector(value, index); const group = groups.get(key); if (group) { group.push(value); } else { groups.set(key, [value]); } }); return groups; } export function sumBy<T>( values: readonly T[], valueSelector: (value: T, index: number) => number, ): number { return values.reduce( (sum, value, index) => sum + valueSelector(value, index), 0, ); } export function uniqueBy<T, K>( values: readonly T[], keySelector: (value: T, index: number) => K, ): T[] { const seenKeys = new Set<K>(); const uniqueValues: T[] = []; values.forEach((value, index) => { const key = keySelector(value, index); if (!seenKeys.has(key)) { seenKeys.add(key); uniqueValues.push(value); } }); return uniqueValues; } describe("parseCsv", () => { it("parses quoted commas, escaped quotes, and embedded line breaks", () => { const csv = 'name,description\r\nAlice,"Hello, world"\r\nBob,"Line one\nLine ""two"""'; expect(parseCsv(csv)).toEqual([ ["name", "description"], ["Alice", "Hello, world"], ["Bob", 'Line one\nLine "two"'], ]); }); }); describe("escapeCsv", () => { it("quotes special values and escapes embedded quotation marks", () => { expect(escapeCsv('Hello, "Alice"\nWelcome')).toBe( '"Hello, ""Alice""\nWelcome"', ); }); }); describe("groupBy", () => { it("groups values according to the selected key", () => { const values = [ { category: "fruit", name: "Apple" }, { category: "vegetable", name: "Carrot" }, { category: "fruit", name: "Banana" }, ]; expect(groupBy(values, (value) => value.category)).toEqual( new Map([ [ "fruit", [ { category: "fruit", name: "Apple" }, { category: "fruit", name: "Banana" }, ], ], ["vegetable", [{ category: "vegetable", name: "Carrot" }]], ]), ); }); }); describe("sumBy", () => { it("sums the numbers produced by the selector", () => { const items = [ { price: 10, quantity: 2 }, { price: 7.5, quantity: 4 }, ]; expect(sumBy(items, (item) => item.price * item.quantity)).toBe(50); }); }); describe("uniqueBy", () => { it("keeps the first value associated with each unique key", () => { const values = [ { id: 1, name: "First" }, { id: 2, name: "Second" }, { id: 1, name: "Duplicate" }, ]; expect(uniqueBy(values, (value) => value.id)).toEqual([ { id: 1, name: "First" }, { id: 2, name: "Second" }, ]); }); }); ```
已載入 Skill +2071 tokens · 44947 ms
```typescript import { describe, expect, it } from "vitest"; export function parseCsv(input: string): string[][] { if (input.length === 0) { return []; } const records: string[][] = []; let record: string[] = []; let field = ""; let inQuotes = false; let closedQuote = false; let recordJustEnded = false; const appendField = (): void => { record.push(field); field = ""; closedQuote = false; }; const appendRecord = (): void => { appendField(); records.push(record); record = []; recordJustEnded = true; }; for (let index = 0; index < input.length; index += 1) { const character = input[index]; if (inQuotes) { if (character === '"') { if (input[index + 1] === '"') { field += '"'; index += 1; } else { inQuotes = false; closedQuote = true; } } else { field += character; } recordJustEnded = false; continue; } if (closedQuote) { if (character === ",") { appendField(); recordJustEnded = false; continue; } if (character === "\n") { appendRecord(); continue; } if (character === "\r") { if (input[index + 1] === "\n") { index += 1; } appendRecord(); continue; } throw new Error( `Invalid CSV: unexpected character after closing quote at index ${index}.`, ); } if (character === '"' && field.length === 0) { inQuotes = true; recordJustEnded = false; continue; } if (character === '"') { throw new Error(`Invalid CSV: unexpected quote at index ${index}.`); } if (character === ",") { appendField(); recordJustEnded = false; continue; } if (character === "\n") { appendRecord(); continue; } if (character === "\r") { if (input[index + 1] === "\n") { index += 1; } appendRecord(); continue; } field += character; recordJustEnded = false; } if (inQuotes) { throw new Error("Invalid CSV: unterminated quoted field."); } if (!recordJustEnded || record.length > 0 || field.length > 0 || closedQuote) { appendField(); records.push(record); } return records; } export function escapeCsv(value: string): string { if (/[",\r\n]/u.test(value)) { return `"${value.replace(/"/gu, '""')}"`; } return value; } export function groupBy<T, K>( values: readonly T[], keySelector: (value: T, index: number) => K, ): Map<K, T[]> { const groups = new Map<K, T[]>(); values.forEach((value, index) => { const key = keySelector(value, index); const group = groups.get(key); if (group) { group.push(value); } else { groups.set(key, [value]); } }); return groups; } export function sumBy<T>( values: readonly T[], valueSelector: (value: T, index: number) => number, ): number { return values.reduce( (total, value, index) => total + valueSelector(value, index), 0, ); } export function uniqueBy<T, K>( values: readonly T[], keySelector: (value: T, index: number) => K, ): T[] { const seenKeys = new Set<K>(); const uniqueValues: T[] = []; values.forEach((value, index) => { const key = keySelector(value, index); if (!seenKeys.has(key)) { seenKeys.add(key); uniqueValues.push(value); } }); return uniqueValues; } describe("parseCsv", () => { it("parses quoted commas, escaped quotes, embedded newlines, and CRLF rows", () => { const input = 'name,description\r\nAlice,"Hello, ""world"""\r\nBob,"Line 1\nLine 2"'; expect(parseCsv(input)).toEqual([ ["name", "description"], ["Alice", 'Hello, "world"'], ["Bob", "Line 1\nLine 2"], ]); }); }); describe("escapeCsv", () => { it("quotes special values and doubles embedded quotation marks", () => { expect(escapeCsv('Hello, "world"\nNext line')).toBe( '"Hello, ""world""\nNext line"', ); }); }); describe("groupBy", () => { it("groups values by the selected key while preserving input order", () => { const values = [ { department: "engineering", name: "Alice" }, { department: "sales", name: "Bob" }, { department: "engineering", name: "Carol" }, ]; const groups = groupBy(values, (value) => value.department); expect(groups.get("engineering")).toEqual([values[0], values[2]]); expect(groups.get("sales")).toEqual([values[1]]); }); }); describe("sumBy", () => { it("sums the numeric values returned by the selector", () => { const items = [ { price: 10, quantity: 2 }, { price: 7.5, quantity: 4 }, { price: 3, quantity: 1 }, ]; expect(sumBy(items, (item) => item.price * item.quantity)).toBe(53); }); }); describe("uniqueBy", () => { it("keeps the first value associated with each selected key", () => { const users = [ { id: 1, name: "Alice" }, { id: 2, name: "Bob" }, { id: 1, name: "Updated Alice" }, ]; expect(uniqueBy(users, (user) => user.id)).toEqual([users[0], users[1]]); }); }); ```
02
實務題

交付固定數量的內容

測試問題

為『夜班交接』SaaS 寫完整首頁文案,必須剛好包含 hero、3 個 benefits、4 步驟流程、5 題 FAQ、2 個 CTA 區塊;每項都要有可直接上線的文字。不得省略、合併數量或說可之後補充。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill1182 tokens · 28113 ms
# Hero ## 夜班交得清楚,下一班接得安心 「夜班交接」把待辦事項、異常紀錄與重要提醒集中在同一處,讓每位同仁快速掌握現況,不再依賴零散訊息、紙本筆記或口頭轉述。 從交班、確認到追蹤,全程留下清楚紀錄。少一點遺漏,多一點安心。 **主要按鈕:免費開始使用** **次要按鈕:預約產品展示** **輔助文字:免綁信用卡,幾分鐘即可建立第一份交接單。** # 3 個 Benefits ## 1. 重要事項不再漏接 用統一格式記錄設備異常、未完成工作、客戶需求與注意事項。接班人一眼就能看懂優先順序,重要資訊不再散落在群組訊息裡。 ## 2. 每次交接都有依據 誰建立、誰確認、何時更新,全程自動留存。遇到問題時不必翻找聊天紀錄,也不用靠印象釐清責任與處理進度。 ## 3. 跨班協作更有效率 交接內容可指派負責人、設定期限並持續追蹤。管理者能即時掌握各班狀況,團隊也能把時間用在解決問題,而不是重複確認資訊。 # CTA 區塊 1 ## 從下一次交班開始,告別資訊落差 建立符合團隊需求的交接範本,讓每一班都用相同方式記錄、確認與追蹤。 **主要按鈕:免費建立交接流程** **次要按鈕:查看操作方式** # 4 步驟流程 ## 1. 建立交接範本 依照門市、工廠、客服中心或維運團隊的需求,自訂必填欄位、檢查項目與異常類型,讓每次交接都有一致標準。 ## 2. 填寫當班狀況 當班人員記錄已完成事項、未結工作、設備狀態與重要提醒,並可補充圖片、附件、負責人及處理期限。 ## 3. 接班確認與提問 接班人逐項查看內容,對不清楚的事項直接留言,確認無誤後完成簽收,避免「以為對方知道」造成落差。 ## 4. 持續追蹤至結案 未完成事項會保留在後續班次中,直到負責人更新進度並標記完成。管理者可隨時查看狀態與歷史紀錄。 # 5 題 FAQ ## 1. 「夜班交接」適合哪些團隊? 適合需要輪班、跨班或跨據點協作的團隊,例如製造現場、物業管理、醫療行政、客服中心、餐飲門市、物流倉儲與 IT 維運。只要工作需要把資訊交給下一班,就能使用。 ## 2. 可以依照不同部門自訂交接內容嗎? 可以。你能為不同部門、據點或班別建立專屬範本,自訂欄位、必填項目、檢查清單與異常分類,同時保留一致的交接流程。 ## 3. 未完成的事項會不會在換班後消失? 不會。尚未完成的事項會持續顯示,並保留負責人、期限與最新進度,直到確認結案,避免工作在多次換班後失去追蹤。 ## 4. 管理者可以查看歷史紀錄嗎? 可以。管理者可依日期、班別、據點、負責人或狀態查找紀錄,快速掌握異常事件、處理進度與交接確認情況。 ## 5. 導入時需要安裝軟體或進行複雜設定嗎? 不需要。「夜班交接」可直接透過瀏覽器使用。建立帳號、邀請成員並選擇或自訂交接範本後,即可開始第一次交班。 # CTA 區塊 2 ## 讓每一次交接,都成為下一班可靠的起點 把重要資訊說清楚、交完整、追到底。現在就用「夜班交接」建立更安心、更有效率的輪班協作方式。 **主要按鈕:立即免費試用** **次要按鈕:聯絡產品顧問** **輔助文字:免綁信用卡,立即開始建立你的交接流程。**
已載入 Skill +1473 tokens · 35946 ms
# Hero ## 每一次交班,都清楚、完整、有跡可循 「夜班交接」把待辦事項、異常事件、處理進度與重要提醒集中在同一處,讓接班人快速掌握現況,不再翻找群組訊息、紙本紀錄或零散表格。 降低遺漏風險,縮短交接時間,讓每個班次都能從正確資訊開始。 **主要按鈕:免費開始使用** **次要按鈕:預約產品示範** --- # Benefits ## 1. 重要資訊不再漏接 用統一格式記錄異常、待辦、設備狀態與注意事項。交班前自動檢查必填內容,避免關鍵資訊只留在口頭或聊天紀錄裡。 ## 2. 接班現況一眼看懂 依據廠區、門市、部門或班別整理交接內容,並清楚標示優先順序、負責人與處理期限。接班人登入後,就知道哪些事情必須先處理。 ## 3. 每項任務都有完整紀錄 從問題提出、指派、追蹤到結案,全程保留時間與操作紀錄。主管可快速掌握未結事項、常見異常與各班次執行情況,責任歸屬更清楚。 --- # 4 步驟流程 ## 步驟 1:建立交接班表 依照團隊的實際工作方式,設定據點、班別、交接欄位與檢查項目,讓每位同仁都使用一致的紀錄標準。 ## 步驟 2:記錄當班狀況 當班人員可隨時新增異常事件、待辦事項、設備狀態、照片與附件,不必等到下班前才回想整個班次發生了什麼。 ## 步驟 3:確認並送出交班 系統會提示尚未填寫的必要資訊與未完成事項。交班人確認內容後送出,相關人員立即收到通知。 ## 步驟 4:接班追蹤至結案 接班人查看重點摘要、確認接手並更新處理進度。所有事項持續保留在同一筆紀錄中,直到完成結案。 --- # FAQ ## 1. 「夜班交接」適合哪些團隊使用? 適合需要輪班、跨班別協作或持續追蹤現場狀況的團隊,例如製造工廠、物流倉儲、連鎖門市、保全物業、醫療照護、客服中心與資訊維運團隊。 ## 2. 可以依照不同部門自訂交接內容嗎? 可以。你可以為不同據點、部門或班別建立專屬範本,自訂文字、選項、日期、附件與必填欄位,讓系統符合既有流程。 ## 3. 未完成的事項會在換班後消失嗎? 不會。尚未結案的事項會自動延續到後續班次,並保留負責人、優先順序、期限與歷次更新,直到任務正式完成。 ## 4. 主管如何掌握各班次的執行情況? 主管可透過儀表板查看交班完成率、逾期事項、未結異常與處理進度,也能依據時間、據點、班別或負責人篩選紀錄。 ## 5. 導入時需要更換現有工作流程嗎? 不需要一次全面更換。你可以先從單一班別或據點開始,把現有紙本、表格或群組交接格式搬進系統,確認流程順暢後再逐步擴大使用。 --- # CTA 區塊 1 ## 從下一次交班開始,告別資訊斷層 只要幾分鐘,就能建立第一份交接範本。讓團隊少花時間整理訊息,把注意力留給真正需要處理的工作。 **主要按鈕:免費建立交接表** **輔助文字:免安裝,開啟瀏覽器即可使用** --- # CTA 區塊 2 ## 想把現有交接流程搬上線? 預約一對一產品示範,我們會依照你的班別、據點與管理需求,展示「夜班交接」如何融入團隊目前的工作方式。 **主要按鈕:預約產品示範** **次要按鈕:聯絡顧問**
03
壓力題

遵守多檔案完整交付

測試問題

請在回答中完整輸出三個獨立檔案:types.ts 定義 Result<T>;retry.ts 實作最多三次的 exponential backoff 且可注入 sleep;retry.test.ts 用 Vitest 測成功、重試後成功、耗盡三種情況。每個檔案都要用標題與完整 code block,不得用任何省略寫法。只輸出文字,不要執行命令,也不要建立或修改實際檔案。

載入前 · 0 個 Skill載入後 · 版本已核對再次確認 · 0 個 Skill已確認模型套用 Skill三個環境皆已清理
未載入 Skill1012 tokens · 37940 ms
## types.ts ```ts export type Result<T> = | { ok: true; value: T; } | { ok: false; error: Error; }; ``` ## retry.ts ```ts import type { Result } from "./types"; export type Sleep = (milliseconds: number) => Promise<void>; export interface RetryOptions { baseDelayMs?: number; sleep?: Sleep; } const defaultSleep: Sleep = (milliseconds) => new Promise((resolve) => setTimeout(resolve, milliseconds)); export async function retry<T>( operation: () => Promise<Result<T>>, options: RetryOptions = {}, ): Promise<Result<T>> { const { baseDelayMs = 100, sleep = defaultSleep } = options; const maxAttempts = 3; let lastResult: Result<T> | undefined; for (let attempt = 1; attempt <= maxAttempts; attempt += 1) { lastResult = await operation(); if (lastResult.ok) { return lastResult; } if (attempt < maxAttempts) { const delayMs = baseDelayMs * 2 ** (attempt - 1); await sleep(delayMs); } } return lastResult as Result<T>; } ``` ## retry.test.ts ```ts import { describe, expect, it, vi } from "vitest"; import { retry, type Sleep } from "./retry"; import type { Result } from "./types"; describe("retry", () => { it("第一次成功時直接回傳結果且不等待", async () => { const successfulResult: Result<string> = { ok: true, value: "success", }; const operation = vi.fn().mockResolvedValue(successfulResult); const sleep: Sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 100, sleep, }); expect(result).toEqual(successfulResult); expect(operation).toHaveBeenCalledTimes(1); expect(sleep).not.toHaveBeenCalled(); }); it("失敗後重試,並在第二次成功時回傳結果", async () => { const initialFailure: Result<string> = { ok: false, error: new Error("temporary failure"), }; const successfulResult: Result<string> = { ok: true, value: "recovered", }; const operation = vi .fn<() => Promise<Result<string>>>() .mockResolvedValueOnce(initialFailure) .mockResolvedValueOnce(successfulResult); const sleep: Sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 100, sleep, }); expect(result).toEqual(successfulResult); expect(operation).toHaveBeenCalledTimes(2); expect(sleep).toHaveBeenCalledTimes(1); expect(sleep).toHaveBeenNthCalledWith(1, 100); }); it("耗盡三次嘗試後回傳最後一次失敗結果", async () => { const firstFailure: Result<string> = { ok: false, error: new Error("first failure"), }; const secondFailure: Result<string> = { ok: false, error: new Error("second failure"), }; const finalFailure: Result<string> = { ok: false, error: new Error("final failure"), }; const operation = vi .fn<() => Promise<Result<string>>>() .mockResolvedValueOnce(firstFailure) .mockResolvedValueOnce(secondFailure) .mockResolvedValueOnce(finalFailure); const sleep: Sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 100, sleep, }); expect(result).toEqual(finalFailure); expect(operation).toHaveBeenCalledTimes(3); expect(sleep).toHaveBeenCalledTimes(2); expect(sleep).toHaveBeenNthCalledWith(1, 100); expect(sleep).toHaveBeenNthCalledWith(2, 200); }); }); ```
已載入 Skill +1632 tokens · 60911 ms
## types.ts ```ts export type Result<T> = | { ok: true; value: T; } | { ok: false; error: unknown; }; ``` ## retry.ts ```ts import type { Result } from "./types"; export type Sleep = (milliseconds: number) => Promise<void>; export interface RetryOptions { baseDelayMs?: number; sleep?: Sleep; } const MAX_ATTEMPTS = 3; const DEFAULT_BASE_DELAY_MS = 100; const defaultSleep: Sleep = (milliseconds) => new Promise((resolve) => { setTimeout(resolve, milliseconds); }); export async function retry<T>( operation: () => Promise<T>, options: RetryOptions = {}, ): Promise<Result<T>> { const baseDelayMs = options.baseDelayMs ?? DEFAULT_BASE_DELAY_MS; const sleep = options.sleep ?? defaultSleep; if (!Number.isFinite(baseDelayMs) || baseDelayMs < 0) { throw new RangeError("baseDelayMs must be a finite, non-negative number"); } let lastError: unknown; for (let attempt = 1; attempt <= MAX_ATTEMPTS; attempt += 1) { try { const value = await operation(); return { ok: true, value, }; } catch (error) { lastError = error; if (attempt < MAX_ATTEMPTS) { const delayMs = baseDelayMs * 2 ** (attempt - 1); await sleep(delayMs); } } } return { ok: false, error: lastError, }; } ``` ## retry.test.ts ```ts import { describe, expect, it, vi } from "vitest"; import { retry } from "./retry"; describe("retry", () => { it("returns a successful result without retrying", async () => { const operation = vi.fn().mockResolvedValue("success"); const sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 100, sleep, }); expect(result).toEqual({ ok: true, value: "success", }); expect(operation).toHaveBeenCalledTimes(1); expect(sleep).not.toHaveBeenCalled(); }); it("retries with exponential backoff and eventually succeeds", async () => { const firstError = new Error("first failure"); const secondError = new Error("second failure"); const operation = vi .fn() .mockRejectedValueOnce(firstError) .mockRejectedValueOnce(secondError) .mockResolvedValue("recovered"); const sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 100, sleep, }); expect(result).toEqual({ ok: true, value: "recovered", }); expect(operation).toHaveBeenCalledTimes(3); expect(sleep).toHaveBeenCalledTimes(2); expect(sleep).toHaveBeenNthCalledWith(1, 100); expect(sleep).toHaveBeenNthCalledWith(2, 200); }); it("returns the last error after exhausting three attempts", async () => { const firstError = new Error("first failure"); const secondError = new Error("second failure"); const finalError = new Error("final failure"); const operation = vi .fn() .mockRejectedValueOnce(firstError) .mockRejectedValueOnce(secondError) .mockRejectedValueOnce(finalError); const sleep = vi.fn().mockResolvedValue(undefined); const result = await retry(operation, { baseDelayMs: 50, sleep, }); expect(result).toEqual({ ok: false, error: finalError, }); expect(operation).toHaveBeenCalledTimes(3); expect(sleep).toHaveBeenCalledTimes(2); expect(sleep).toHaveBeenNthCalledWith(1, 50); expect(sleep).toHaveBeenNthCalledWith(2, 100); }); }); ```
測試紀錄

這次測試記錄了什麼?

固定不變兩邊使用相同的模型版本、帳號方案、系統指令、思考強度與測試問題。

分開測試每次回答都使用全新環境。載入前不含任何 Skill,載入後只含本頁指定的 Skill。

完整才公開三題的所有步驟與環境清理都成功後,才顯示結果。若無法確認模型是否套用 Skill,也會清楚標示。