Skip to content

Repository files navigation

企業經驗分身+可執行 AI 作業員(MVP)

把只有資深員工知道的報價經驗,變成可搜尋、可審核、可執行的工作流程。

第一個流程只解一個問題:製造業/貿易商從收到詢價到送出正確報價的時間,同時保留人工判斷與完整操作紀錄。

客戶詢價 → AI 擷取結構化需求 → 缺件檢查/追問草稿 → 產品·庫存·歷史·SOP 比對
        → 規則引擎算價與風險 → 人工核准 → 寄送 → 追蹤任務 + 稽核紀錄

畫面

截圖皆來自 e2e Demo 的虛構資料(seed 建立的示範組織「盛華」)。

儀表板
儀表板——案件狀態、待辦與風險一眼看完
AI 擷取
AI 擷取——每個欄位帶信心分數與原文證據
報價草稿
報價草稿——規則引擎算價,逐項列出引用來源
詢價案件
詢價案件——從收信到寄出的完整時間軸
成效報表
成效報表——兩邊碼表實測的節省率,不灌水

1. 快速開始(3 個指令)

需求:Node.js ≥ 20.11、pnpm。不需要 Docker,也不需要任何 API 金鑰。

pnpm install
cp .env.example .env
pnpm setup && pnpm dev

pnpm setup = prisma generate + prisma migrate deploy + seed(可重複執行)。 開啟 http://localhost:3210,用下方任一 Demo 帳號登入。

Windows PowerShell 的複製指令是 Copy-Item .env.example .env。

Demo 帳號(密碼一律 Demo1234!,僅限本機)

帳號 角色 可以做什麼
admin@demo.local admin 全部權限,含組織設定與風險門檻
manager@demo.local manager 核准報價、管理知識與 SOP、看稽核紀錄
operator@demo.local operator 建立案件、產生報價草稿、寄送已核准報價
viewer@demo.local viewer 唯讀
other@demo.local 另一個組織的 admin 用來驗證組織隔離

DEMO_PASSWORD 只在本機 Demo 使用。正式環境請改掉 SESSION_SECRET,並停用 Demo 帳號。

常用指令

指令 用途
pnpm dev 開發伺服器(port 3210)
pnpm setup migration + seed(可重複執行)
pnpm db:reset 砍掉重練資料庫並重新 seed
pnpm test unit + integration(Vitest,會自建獨立 test.db)
pnpm test:e2e build + 重建 e2e.db + Playwright;強制使用 mock adapter,即使 .env 設成 anthropic
pnpm verify lint → typecheck → test → production build
pnpm schema:postgres 由 SQLite schema 產生 PostgreSQL 版本
pnpm preflight:llm 接上真實模型後的 smoke test:跑一封虛構詢價並驗證 ai_runs 是 anthropic/正確模型/succeeded

2. 十分鐘 Demo 腳本

情境 A:正常報價(operator → manager → 寄送)

  1. 用 operator@demo.local 登入 →〈建立詢價案件〉。
  2. 右側點「情境 A:資料完整的詢價」→〈載入這個範例〉→〈建立並執行 AI 擷取〉。
  3. 案件頁會看到:原始詢價、結構化需求(每個欄位都有信心分數與原文證據)、兩個已對應 SKU 的品項。
  4. 按〈產生報價建議〉。系統比對產品主檔、價格版本、庫存快照、同客戶歷史核准折扣與已發布 SOP,算出單價與總額,並列出 14 筆引用來源。
  5. 〈送出核准〉→ 用 manager@demo.local 登入 →〈核准中心〉→ 看差異與依據 →〈核准〉。
  6. 回到 operator →〈寄送報價〉→ 到〈寄件匣〉看模擬寄出的內容;案件變成「已寄出」並自動建立 3 個工作天後的追蹤任務。

情境 B:資料不足(缺數量與交期)

  1. 載入「情境 B」範例(英文詢價)。
  2. 系統標示缺少「各品項數量」,〈產生報價建議〉按鈕被停用並說明原因。
  3. 〈產生追問草稿〉→ AI 依缺少欄位寫出英文信件草稿 → 人工修改 →〈確認後寄出〉。
  4. 案件時間軸同時保留 AI 草稿與人工修改前後的內容。

情境 C:高風險例外(18% 折扣+庫存不足+交期衝突)

  1. 載入「情境 C」範例(日文詢價、JPY 報價)。
  2. 產生報價建議後出現三個警告:折扣超過 10% 門檻(高風險)、庫存不足、交期衝突;版本被標記為「需主管核准」。
  3. operator 送審後在核准中心只會看到「你不能核准這筆報價」——送審者不得核准自己的報價。
  4. manager 看得到與前一版的差異、風險理由與所有引用來源,可核准/退回/拒絕;退回與拒絕必須填原因。

案件成效報表(/analytics)

這是拿來決定「要不要繼續投資」的量測層,不是產品功能。每個案件量四件事:

指標 回答的問題
純文字/附件覆蓋率 目前 MVP 能服務多少真實案件?
AI 擷取修改率 AI 理解是否可靠?(分母只算 AI 有給值的欄位;AI 漏抓、人工補上的另外列)
報價修改率 建議能不能直接用?(分母 = 品項數 × 2 + 6 個文件欄位)
實測節省率 產品有沒有真的省時間?

量尺規則(重要):節省率一律用兩邊都碼表實測的數字計算:

實測節省率 = (原流程實測分鐘 − AI 輔助實測分鐘) / 原流程實測分鐘

兩個數字都必須用同一支碼表、同一套開始/暫停/結束規則。系統不會拿碼表實測的人工時間去減 audit 推估的活躍時間——推估看不到閱讀與思考時間,那樣減會系統性高估節省率。只填了一邊時,報表顯示「尚未完成兩邊實測,無法下結論」。

計時規則(UI 上也會提示):

  • 開始:打開這封詢價、準備動手的那一刻
  • 暫停:離開去做別的事就停錶,回來再繼續
  • 結束:報價可以送出核准的那一刻(不含等主管核准的時間)
  • 兩種流程用同一支碼表、同一個人計時

「推估活躍操作時間」只作診斷(這個名稱是刻意的):

  • 由稽核事件的時間戳推算,相鄰事件間隔超過門檻(預設 5 分鐘,可在〈設定〉調整)就視為離開,該空檔不累加。
  • 它看不到閱讀與思考時間——第一個動作之前、最後一個動作之後、每個區段開頭的判斷時間都沒算進去,所以是下限估計,不能當成精確工時。
  • 活躍時間不到 0.5 分鐘的案件(腳本、API 直接呼叫、連點完成)會被標成「不可信」並排除在推估平均值之外。寧可少一個數字,也不要一個 100% 節省的假數字。
  • 完整牆鐘時間一律並列顯示。
  • 報表另外算「推估落差」= AI 輔助碼表實測 − 推估活躍時間。這個差值就是事件時間戳看不到的閱讀與思考時間;累積幾件之後可以用它校準推估值到底低估多少。

要得到能對外講的數字,必須在案件頁的〈成效量測〉填入兩邊的碼表實測分鐘數,並建議:

  • 5–10 封去識別化的真實詢價,不要只測一封(n=1 會被那封信的格式運氣主導)。第一封視為校準,至少累積 5 封可比較案件再做第一次 go/no-go。 報表首頁的「可比較案件」直接顯示 n/5 進度。
  • 交錯順序:一半「人工先、系統後」,一半「系統先、人工後」。否則同一封信做第二次時業務已經知道答案,會讓第二種方法看起來不公平地更快。
  • 測試前先標記每封的輸入類型(純文字/附件/混合),覆蓋率才算得出來。
  • 報表右上角可以匯出 CSV(實測欄位、推估欄位、推估落差、可信度標記分開列)拿去自己算。

客戶料號對照(/aliases)

真實客戶用自己的料號詢價是常態。料號是「客戶與產品之間的關係」,所以獨立成 customer_product_aliases 表,不是產品的欄位——這樣不同客戶可以用相同料號,舊料號也能保留歷史。

比對順序(任何一步不是唯一結果就往下走,系統絕不自行決定):

  1. 客戶 + 客戶料號精確對照
  2. 我方 SKU 精確匹配
  3. 名稱/規格候選比對 → 只列候選,不自動選定
  4. 沒有唯一結果 → 交由人工指定

標準化只做大小寫統一、前後空白移除、連續空白整理。刻意不移除 -、/、. 等符號——ABC-M8-001 與 ABCM8001 在真實世界可能是兩個料號,自動合併會造成無法追溯的錯誤對照。

唯一性:同一組織+同一客戶+同一標準化料號,同時只能有一筆 active 對照。由 DB trigger 強制(繞過應用層直接寫入也會被拒),要改指向就先停用舊的。不同客戶可以使用相同料號。

CSV 匯入(獨立檔案,不塞進產品 CSV):

customerCode,customerPartNumber,internalSku,description
C-1001,ABC-M8-001,HB-M8X30-SS,客戶舊料號

匯入流程是「先預覽、再確認」,逐列回報狀態:可建立/略過(已存在相同對照)/衝突(既有對照指向別的 SKU,不會自動覆寫)/錯誤(客戶或 SKU 不存在、欄位缺漏),並附成功·略過·衝突·失敗統計與 audit log。同一批 CSV 內部的自我衝突也會被抓出來。

指標歸因:AI 抓到料號但查無對照時,不算擷取錯誤——那是主檔資料不足。報表因此把三件事分開:

指標 意義
客戶料號擷取修改率 AI 有沒有抓錯料號(AI 的問題)
料號對照命中率 主檔有沒有這筆對照(資料的問題)
未對照料號數量 還缺多少對照要建

情境 D/E/F(防呆)

  • D — 擋住未核准寄送:對未核准的報價直接呼叫 POST /api/quotes/:id/send 會拿到 403,Outbox 不會產生任何紀錄,稽核紀錄會留下 quote.send_blocked。
  • E — 沒有依據就不亂答:到〈知識庫〉問「公司對員工寵物保險的補助政策是什麼?」,系統回答「現有企業資料中找不到足夠依據回答這個問題。」並提供「新增知識來源」入口。
  • F — 組織隔離:other@demo.local 屬於第二個組織,用 URL 或 API 都拿不到盛華的案件、報價、知識與稽核紀錄。

3. 架構

Next.js App Router(Server Components 讀資料 / Client Components 做互動)
        │
        ├── Route Handlers(src/app/api/**)── 驗證 session → 檢查角色 → Zod 驗證輸入 → 呼叫 service
        │
        ├── Services(src/lib/services/**)── 所有商業邏輯與 organization_id 隔離
        │        ├── inquiry:建立、AI 擷取、缺件判斷、追問草稿
        │        ├── quote:比價、算價、版本、風險旗標、寄送
        │        ├── approval:核准/退回/拒絕、版本差異
        │        ├── knowledge:解析、分段、索引、混合檢索、有依據才回答
        │        └── sop / product / customer / workspace
        │
        ├── Domain(src/lib/domain/**)── 純函式:金額計算、狀態機、風險規則、SOP 結構
        ├── Adapters(src/lib/adapters/**)── LLM / Email / Embedding,可替換、預設離線
        └── Prisma(SQLite 本機|PostgreSQL 可移轉)+ append-only 稽核表

重要檔案

檔案 內容
prisma/schema.prisma 28 張表:組織、知識、SOP、業務資料(含客戶料號對照)、工作流程、稽核
src/lib/domain/part-number.ts 料號標準化規則與比對來源
src/lib/services/alias.ts 客戶料號對照:查詢、建立、停用、CSV 預覽與匯入
prisma/seed.ts Demo 資料(12 產品/3 客戶/4 筆客戶料號對照/6 筆歷史報價/3 SOP/5 知識來源/第二組織)
src/lib/money.ts 金額一律 decimal 字串,四捨五入與幣別小數位規則
src/lib/domain/quote-calc.ts 單價、折扣、小計、稅、總額(純函式,不經 LLM)
src/lib/domain/risk.ts 風險規則與「是否強制主管核准」
src/lib/domain/status.ts 案件與報價版本的狀態機
src/lib/auth/rbac.ts 權限矩陣與「不得核准自己報價」
src/lib/adapters/llm/mock.ts 離線、可重現的規則式擷取器(Demo 預設)
src/lib/adapters/llm/anthropic.ts 真實 LLM adapter(tool-use 結構化輸出+Zod 驗證+重試一次)
src/lib/search/rank.ts BM25 + 向量的 RRF 混合排序
src/lib/audit.ts 稽核事件寫入(資料表由 DB trigger 保護)
src/lib/domain/effort.ts 成效量測純函式:推估活躍時間、修改率、可信度判定
src/lib/services/analytics.ts 由稽核紀錄組出每個案件的成效報表與 CSV

資料流三個原則

  1. 金額只由程式算。價格、成本、庫存、稅率、匯率全部來自資料庫或使用者明確輸入;LLM 只負責擷取文字與寫說明,不參與任何計算。
  2. AI 產出一定有出處。每個欄位帶信心分數與原文證據;每份報價帶引用來源(產品主檔/價格版本/庫存快照/歷史報價/SOP/知識段落/匯率設定)。找不到依據時明講「資料不足」。
  3. 有外部影響的動作要人核准。寄送在後端重新驗證核准狀態,未核准一律 403,並寫入稽核紀錄。

介面上的三種資料來源

UI 全站使用同一組標籤,讓使用者一眼看出數字從哪來:

  • ■ 原始資料:資料庫或客戶原文
  • = 規則計算:程式與規則引擎算出來的
  • ◇ AI 建議:AI adapter 產生,需人工確認
  • ✎ 人工輸入:使用者填寫或修改過

4. Adapter 與環境變數

沒有金鑰時全部自動退回可離線執行的 mock,Demo 流程完全不受影響。

變數 預設 說明
DATABASE_URL file:./dev.db SQLite 路徑相對於 prisma/
SESSION_SECRET 本機字串 Session token 的 HMAC 金鑰,正式環境必換
LLM_PROVIDER mock mock(規則式、可重現)或 anthropic
ANTHROPIC_API_KEY 空 有值且 LLM_PROVIDER=anthropic 才會使用真實模型
ANTHROPIC_MODEL claude-sonnet-5 真實 adapter 使用的模型
LLM_INPUT_RATE_USD_PER_MTOK 空 估算成本用的輸入費率(美元/百萬 token)。留空=只存 token 不估成本
LLM_OUTPUT_RATE_USD_PER_MTOK 空 同上,輸出費率。兩個都要設才會估算
EMAIL_PROVIDER mock mock(寫入 Outbox 標記 simulated)或 resend
RESEND_API_KEY 空 啟用真實寄信
EMAIL_FROM quotes@demo.local 寄件者
EMBEDDING_PROVIDER local 離線雜湊向量(256 維),與 BM25 混合排序
INBOUND_WEBHOOK_SECRET 本機字串 POST /api/connectors/email/inbound 的驗證
UPLOAD_DIR / MAX_UPLOAD_BYTES ./var/uploads / 20 MB 知識文件儲存位置與大小上限

啟用真實 LLM:把 LLM_PROVIDER=anthropic 與 ANTHROPIC_API_KEY=... 寫進 .env 後重啟。金鑰只存在伺服器端,不會進 client bundle;〈設定〉頁會顯示目前實際使用的 adapter 與退回原因。


5. API

所有 mutation 都會:驗證 session → 檢查角色 → Zod 驗證輸入 → 檢查目前狀態 → 寫稽核紀錄。錯誤格式一致:

{ "ok": false, "requestId": "req_...", "error": { "code": "FORBIDDEN", "message": "報價尚未核准,不可寄送。", "details": null } }
Method Path
POST /api/auth/login、/api/auth/logout
GET/POST /api/inquiries
GET/PATCH /api/inquiries/:id
POST /api/inquiries/:id/extract、/clarification-draft、/clarification-send、/quote-draft、/items
PATCH/DELETE /api/inquiry-items/:itemId
GET/PATCH /api/quotes/:id
POST /api/quotes/:id/submit、/api/quotes/:id/send
POST /api/approvals/:id/approve|return|reject
GET/POST /api/knowledge、/api/knowledge/upload
POST /api/knowledge/:id/process、/api/knowledge/:id/expire
GET /api/knowledge/search?q=、/api/knowledge/:id/download
POST /api/knowledge/answer
GET/POST /api/products、/api/customers、/api/sops
PATCH /api/products/:id、/api/customers/:id、/api/sop-versions/:id
POST /api/products/import、/api/customers/import、/api/sops/:id/draft、/api/sop-versions/:id/submit|publish
POST /api/tasks/:id/complete
GET/PATCH /api/inquiries/:id/measurement(兩邊碼表實測分鐘、輸入類型、量測備註)
GET /api/analytics/cases?format=json|csv&measuredOnly=true
GET/POST /api/customer-product-aliases
POST /api/customer-product-aliases/import(preview: true 只檢查不寫入)
POST /api/customer-product-aliases/:id/deactivate
GET/PATCH /api/settings
POST /api/connectors/email/inbound(需 x-webhook-secret)
GET /api/health

6. 測試

pnpm test       # 21 個檔案 / 199 個測試(112 unit + 87 integration)
pnpm test:e2e   # 10 個 Playwright 測試(情境 A/B/C/D/E + 權限 + 375px + adapter 鎖定)
pnpm verify     # lint + typecheck + test + production build
  • Unit:金額與幣別計算、風險規則、狀態機、權限矩陣、AI structured output schema、檔案解析(含掃描 PDF 的失敗路徑)、BM25/向量排序、CSV、成效量測(活躍時間推估、修改率、可信度判定)。
  • Integration:情境 A(建案→擷取→報價→送審→核准→寄送→追蹤任務→稽核)、情境 B(缺件擋住報價+追問草稿)、情境 C(高風險與核准權限)、情境 D(未核准寄送被擋、Outbox 無紀錄)、情境 E(沒依據不亂答)、情境 F(組織隔離)、API 邊界(401/403/422、webhook 驗證)、成效報表(修改率、基準線、CSV、組織隔離、不影響報價流程)、客戶料號對照(衝突拒絕含 DB trigger、不同客戶同料號、有效期間、組織隔離、CSV 預覽與部分成功、指標歸因)。
  • E2E:以真實瀏覽器跑完 A/B/C 三個情境,加上未登入導向、唯讀角色限制、375px 無水平捲軸。

測試一律使用 mock adapter,不依賴任何外部 API。Vitest 會自建 prisma/test.db,Playwright 會自建 prisma/e2e.db,都不會動到 dev.db。

E2E 用 mock 是機械保證,不是靠人工記得切 .env。 E2E 會觸發擷取、報價說明與知識問答;只要 .env 是 anthropic,跑一次測試就等於拿真實額度去跑。因此有三層鎖定:

  1. scripts/e2e-serve.mjs 把 ANTHROPIC_API_KEY / RESEND_API_KEY 從子行程移除,並覆寫 LLM_PROVIDER=mock、EMAIL_PROVIDER=mock、E2E=1。
  2. src/lib/env.ts 的 E2E 鎖定:即使 Next.js 載入 .env 時又把金鑰塞回 process.env,應用層仍會清掉它;provider 不是 mock 就直接拋錯讓伺服器起不來。這一層不依賴任何載入順序。
  3. tests/e2e/global-setup.ts 在跑第一個測試之前先問 /api/health:adapter 不是 mock 就中止整輪。

Vitest 那側由 vitest.config.ts 寫死 LLM_PROVIDER: 'mock'。對應測試:tests/unit/env-e2e-lockdown.test.ts 與 E2E 的〈E2E 一律使用 mock adapter〉。


7. 已完成 / 尚未完成

已完成

  • 組織、Email/密碼登入、session、四種角色與權限矩陣,所有查詢都以 organization_id 隔離。
  • 知識來源:上傳(pdf/docx/xlsx/csv/txt/md)、解析、分段、索引、混合檢索、失效標記、原始檔下載、重新解析。
  • SOP:結構化步驟編輯器、draft → in_review → published → archived、版本不可覆寫、發布後自動同步進知識索引供報價引用。
  • 詢價:貼上/手動/Demo 範例/webhook 建立、AI 擷取(欄位信心+原文證據)、人工修改、缺件判斷、追問草稿與模擬寄送。
  • 報價:產品比對、價格版本快照、庫存與交期、歷史折扣中位數、匯率換算(用組織設定的匯率,不抓即時匯率)、稅與運費規則、風險旗標、版本管理、列印版報價單。
  • 核准中心:待辦佇列、與前一版差異、風險與來源、核准/退回/拒絕、送審者不得自審。
  • 寄送:MockEmailAdapter(預設)/ResendEmailAdapter、Outbox、重複寄送保護、3 個工作天後的追蹤任務。
  • 稽核:所有核心動作寫入 append-only 事件表(DB trigger 阻擋 UPDATE/DELETE),含變更前後、request ID 與 AI adapter 資訊。
  • 全站 zh-TW 介面、桌面優先、手機可核准(375px 無水平捲軸)。

尚未完成(刻意留給下一階段)

  • PDF 匯出:目前提供穩定的列印版 HTML(/quotes/:id/print,瀏覽器列印即可存 PDF)。伺服器端產 PDF 需要 headless 瀏覽器,成本會影響核心流程,排在下一階段。
  • 掃描版 PDF/OCR:文字型 PDF 可解析;掃描檔會明確標示「目前無法可靠解析」,不會產生假內容。
  • 真正的向量資料庫:本機用可重現的雜湊向量 + BM25 混合排序;pnpm schema:postgres 會產生 PostgreSQL 版本並列出改用 pgvector 的步驟。
  • PostgreSQL 佈署:資料模型已可移轉(附 docker-compose.yml 與 schema 產生器),但金額欄位轉成 NUMERIC 後,讀取路徑需要把 Prisma.Decimal 轉字串再交給 money.ts。這一步還沒做。
  • 深色模式:使用場景是白天辦公室的資料密集介面,這版只做淺色,不做半套的深色。
  • 使用者邀請/角色編輯 UI(目前由 seed 建立)、LINE/Gmail connector、ERP 整合、多語報價單、成交分析。

已知限制

  • 檢索在應用層計算(Demo 資料量下毫秒等級);資料量放大後應改用資料庫層的全文與向量索引。
  • Mock LLM 是規則式擷取器:對格式清楚的詢價很準,對高度非結構化的長信會漏欄位——這正是要換上真實 adapter 的時機,介面完全相同。
  • 匯率是組織設定的固定值,不是即時匯率(刻意)。

8. 安全

  • 資料最小化:送進模型的 payload 不含標準成本、毛利與成本相關的風險細節。擷取用的產品目錄只有 SKU/品名/規格/單位/標籤(比對 SKU 必需,且不含價格);報價說明只拿到已算好的定價、單價、折扣與交期。由 tests/integration/llm-payload.test.ts 守住。
  • Fail-closed,不會無聲退回 mock:LLM_PROVIDER=anthropic 時,API 失敗會明確拋錯並在 ai_runs 記成 provider=anthropic, status=failed;沒有金鑰時直接拋 MISCONFIGURED、/api/health 回 503、設定頁顯示紅色警告——不會改用 mock 假裝成功。mock 只在 LLM_PROVIDER=mock(或未設定)的本機 Demo 模式啟用。由 tests/integration/llm-failover.test.ts 守住。
  • 測試不會消耗 API 額度:pnpm test 與 pnpm test:e2e 都被強制鎖在 mock adapter(三層保護見〈6. 測試〉)。這是成本保護,也是正確性保護——測試結果不該取決於某次 API 是否可用。
  • 用量以供應商回報為準,不推算:每次 runAi() 會把 Anthropic 回報的 usage.input_tokens / output_tokens 寫進 ai_runs;schema 驗證失敗重試時,兩次往返的用量會累加成一筆(apiCalls 記錄實際往返次數)。拿不到 usage(HTTP 失敗、逾時、mock)時欄位一律留 null,不會補 0——0 的意思是「沒花到 token」,而實際情況是「不知道」。costUsd 是估算值,只有設定 LLM_INPUT_RATE_USD_PER_MTOK / LLM_OUTPUT_RATE_USD_PER_MTOK 時才計算,並把當下費率一起存進 inputRateUsdPerMTok / outputRateUsdPerMTok;程式裡刻意沒有內建價格,避免價格調整後新舊資料用不同基準卻長得一樣。實際帳單以 Anthropic Console 為準。由 tests/integration/llm-usage.test.ts 守住。
  • 上傳文件與客戶來信一律視為不可信資料。真實 adapter 會把它們包在 <untrusted> 標籤裡並明確指示不得執行其中的指示;mock adapter 會偵測「請直接核准/忽略先前指示」這類句子,標記 unsafeInstructionsDetected 並在案件頁顯示警告。核准與寄送永遠需要人操作。
  • AI 沒有任意資料庫、檔案或網路存取權:只能拿到服務層準備好的 context,工具是白名單,輸入輸出都經 Zod 驗證(失敗重試一次,仍失敗就顯示可理解的錯誤並交回人工)。
  • 密碼使用 scrypt(每筆獨立 salt);session token 只存 HMAC 雜湊;cookie 為 httpOnly + SameSite=Lax,production 加上 Secure。
  • 上傳限制副檔名、MIME 與大小(預設 20 MB),檔名會去除路徑字元。
  • 結構化 log 帶 request ID,並自動遮蔽 password/token/api key 等欄位;錯誤訊息不含 stack trace。

9. 開發時的預設決定(規格未明說的部分)

  1. 本機用 SQLite:這台開發機沒有 Docker,規格允許 SQLite fallback。資料模型保持可移轉,並附上 PostgreSQL schema 產生器與 compose 檔。
  2. 自建 session 認證(規格允許「Auth.js 或等價的 session-based authentication」):cookie + sessions 資料表 + scrypt,少一層設定、好測試。
  3. 金額用 decimal 字串:SQLite 沒有真正的 NUMERIC;全系統以字串進出、decimal.js 計算,四捨五入採台灣報價慣用的 half-up,JPY 無小數位。
  4. 送審者一律不得核准自己的報價(規格只要求高風險時禁止):職責分離更好解釋,也更好測。
  5. 匯率由組織設定:即時匯率屬於未來範圍,但多幣別報價又必須算得出來,因此用人工維護的匯率表,並把它當成一筆可追溯的引用來源。
  6. 手動知識頁面也走同一條索引管線,SOP 發布後自動鏡射成知識來源,讓報價引用與知識問答共用同一套檢索。

About

企業經驗分身+可執行 AI 作業員 MVP(詢價→擷取→報價→核准→寄送)。實驗資料與資料庫不入版控。

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages