Files
yingfungsolar/docs/superpowers/specs/2026-09-11-structure-ai-deploy-design.md
T
philip-cat 81fc515d6e AI Blog 改用 DeepSeek 官方並通用化提示詞
buildSystemPrompt 不再綁死太陽能行業,角色與公司資料改由
後台 ai_context_prompt / ai_business_context 提供;預設供應商
改為 https://api.deepseek.com 與 deepseek-flash。callChat 只在
baseUrl 含 deepseek.com 時加 thinking: { type: "disabled" },
並將 max_tokens 提升至 4000,避免思考模式令 temperature 失效
同正文被截斷。

同步更新 schemas、seed、/admin/ai 預設值同 placeholder,
以及 AGENTS.md、spec、plan 文件。
2026-09-12 00:31:26 +08:00

10 KiB
Raw Blame History

盈豐太陽能 — 結構優化 + AI Blog + 部署便利 (Design Spec)

  • 日期2026-09-11
  • 狀態:已與客戶確認方向
  • 背景:現有網站(見 2026-09-11-ying-fung-solar-design.md)功能完整,但想改善三樣嘢:
    1. 資料/表單處理散落手砌,欠型別化驗證層
    2. 想有 AI 生成 Blog 文章功能(參考 webtemplate,但簡化、唔要 Google keyword search
    3. 部署到 Cloudflare 嘅流程手續多,想更方便
  • 本 spec 取代先前 spec 嘅部署一節;其餘架構決策不變。
  • 2026-09-12 更新:AI Blog 提示詞已通用化、預設供應商轉 DeepSeek 官方,詳見 2026-09-12-ai-blog-template-deepseek-design.md;下表 ai_base_url / ai_chat_model 已同步為新預設。

1. 目標與非目標

目標

  1. 輕量 schema-first 驗證層:用 Zod 做「admin 表單輸入 + DB 寫入」嘅單一真相來源,得到 typed contract 好處,但零 codegen、零 API layer。
  2. AI Blog 生成:後台一鍵生成完整 Markdown 草稿(DeepSeek via OpenAI 相容端點),可選 Tavily 上網研究,支援關鍵字佇列。
  3. 部署便利:一鍵本機 setup script + Cloudflare Workers BuildsGit push 自動 deploy)。

非目標 (Out of scope)

  • 唔引入真 OpenAPI spec / codegen / 前後端分離 SPA(已評估唔啱一個一頁式官網)。
  • AI 封面圖生成(唔做)。
  • 定時/排程自動生成(唔做;只做後台手動觸發)。
  • Google Ads keyword discovery(唔做)。
  • 圖片上傳(維持以 URL 貼圖)。
  • 雙語、聯絡表單(維持現狀)。

2. Thread 1 — 輕量 schema-first 驗證層

2.1 依賴

新增 zodv4^4)。

2.2 結構

src/schemas/
├─ post.ts        postInput
├─ content.ts     contentItemInputkind enum 用 CONTENT_KINDS
├─ case.ts        caseInput
├─ settings.ts    由 SETTINGS_GROUPS 動態砌出可選 key 驗證
├─ ai.ts          aiSettingsInput + AI 輸出格式 schema
├─ keyword.ts     keywordInput
└─ index.ts       barrel(集中 re-export schema 同 z.infer 型別)
src/lib/form.ts   parseForm(formData, schema)
  • Schema 係「表單輸入 + DB 寫入」嘅唯一真相來源;型別一律 z.infer
  • settings-fields.ts 繼續做 UI metadata(標籤、分組),Zod 只負責 驗證,唔重複定義。
  • parseForm 介面:
    type ParseResult<T> =
      | { ok: true; data: T }
      | { ok: false; errors: Record<string, string> };
    function parseForm<T>(form: FormData, schema: ZodType<T>): ParseResult<T>;
    
    錯誤以 field -> 訊息 形式回傳,admin 表單喺對應欄位下顯示;未對應欄位嘅錯誤顯示喺表單頂。

2.3 改動

  • pages/admin/post/[id].astrocontent/[kind].astrocases.astrosettings.astroPOST handler 改用 parseForm 取代手砌 String(form.get(...)) 同手寫 if (!title)
  • data/content.ts:改用 src/schemas 匯出嘅型別(如 NewPost 形狀),與 schema 對齊。
  • 排序(up/down)、delete 等非表單欄位動作維持現狀(唔屬 schema 範圍)。

2.4 唔做

  • 唔改前台元件介面、唔改 DB schema(純驗證層)。
  • 唔引入 openapi.json、唔引入 client codegen。

3. Thread 2 — AI Blog 生成

3.1 資料模型(改 src/db/schema.tsnpm run db:generate

posts 加欄位:

欄位 型別 說明
focusKeyword text nullable 生成時帶入嘅焦點關鍵字(SEO 用)

新表 blog_keywords

欄位 型別 說明
id text PK UUID
keyword text notNull 關鍵字 / 主題
status text enum pending | generated | skipped,預設 pending 狀態
createdAt integer (timestamp_ms)
usedAt integer (timestamp_ms) nullable 生成時間
postId text nullable 生成出嚟嘅文章 id(可選連結)

索引:idx_keywords_status(status)

比 webtemplate 減去 Google Ads 專屬欄位(avg_monthly_searchescompetitionsource)。

3.2 Secrets 與設定

Secretswrangler secret / .dev.vars,加入 AppEnv):

  • AI_API_KEY
  • TAVILY_API_KEY

設定(D1 site_settings,後台可改):

Key 預設 說明
ai_enabled 1 總開關
ai_base_url https://api.deepseek.com OpenAI 相容端點(DeepSeek 官方)
ai_chat_model deepseek-flash 模型
ai_context_prompt "" 寫作風格 / 語氣
ai_business_context seed 由公司資料砌 公司背景(grounding
ai_web_search_enabled 0 Tavily 研究開關
ai_web_search_max_results 5 Tavily 結果數

3.3 生成流程(src/lib/ai.ts

POST /admin/ai
  action = "generate-topic"(自由輸入 topic
          | "generate-next"(攞下一個 pending keyword
        │
        ▼
generateBlogPost({ db, env, topic })
  1. 讀 ai_* 設定;檢查 ai_enabled + AI_API_KEY
  2. 若係 generate-next → 攞最舊一個 pending keywordmark 佢 "generated" + usedAt + postId
     generate-topic(自由輸入)唔會寫入佇列
  3. 若 ai_web_search_enabled + TAVILY_API_KEY → Tavily search → research_brief
  4. 組 system prompt
       ai_context_prompt + ai_business_context + research_brief
       + SEO/GEO 硬規則 + 輸出格式合約
  5. 呼叫 DeepSeek chat completionsOpenAI 相容 /chat/completions
  6. 解析輸出(TITLE / CONTENT / META_DESC),用 Zod 驗證格式;失敗回錯誤
  7. slugify + uniqueSlugautoExcerpt 補 excerpt
  8. 插入 postsstatus = "draft"、focusKeyword = topic)→ 回 post id
        │
        ▼
  redirect 去 /admin/post/<id> 覆核

執行方式:同步 await。 POST handler 直接等生成完成(約 1040 秒)再 redirect。原因:最簡單、admin 流程直觀,符合「唔使太複雜」;Workers 對 outbound fetch 等待唔計 CPU limit。若日後遇到超時,先改 Astro.locals.cfContext.waitUntil + 狀態欄(已知 adapter 有注入 locals.cfContext)。

提示合約:

  • 輸出用固定分隔格式(TITLE / CONTENT / META_DESC),比純 JSON 容忍度更高。
  • 內建規則:繁體中文(廣東話書面)、800–1200 字、H2/H3 結構、關鍵字放標題及首段、meta ≤ 160 字、GEOsummary-first + Q&A)。

3.4 Admin UI(新頁 /admin/ai

一頁過包含:

  1. AI 設定表單ai_* keys,密碼類 secret 只顯示「已設定 / 未設定」,唔顯示值)
  2. 關鍵字佇列:新增、刪除、標記 skipped;顯示狀態
  3. 生成掣generate-topic(自由輸入)+ generate-next(下一個 pending
  • 生成掣用 POST 表單 + 提交時 JS 顯示「生成中…」(純漸進增強,唔引入框架)。
  • /admin 首頁加入口連結去 /admin/ai

3.5 唔做

  • AI 圖、排程、Google Ads、多語。

4. Thread 3 — 部署便利

4.1 一鍵本機 setup — scripts/setup.mjsnpm run setup

互動式 Node script,逐步做:

  1. 檢查 wrangler whoami(未登入提示 npm run login)。
  2. wrangler d1 create yingfung-solar-db(若已存在/已填 id 則略過)→ 解析 output 拎 database_id
  3. wrangler kv namespace create CACHE → 解析 id。
  4. 將 id 寫入 wrangler.jsonc:以字串替換 PASTE_D1_DATABASE_ID_HERE / PASTE_KV_NAMESPACE_ID_HERE保留 JSONC 註解同行距,唔用 JSON.parse/stringify)。
  5. 提示輸入後台密碼 → wrangler secret put ADMIN_PASSWORD
  6. 套 migration + seedremote)。
  7. 可選:npm run build + npx wrangler deploy
  • 每一步 idempotent(重跑唔會整爛已設定嘅嘢)。
  • 唔會自動改 astro.config.mjssite(提示用戶手動改)。

4.2 Cloudflare Workers BuildsGit push 自動 deploy

  • 喺 Cloudflare Dashboard → Workers & Pages → 連接 Git repo。
  • Build commandnpm run build
  • Deploy commandnpx wrangler deploy
  • SecretsADMIN_PASSWORDAI_API_KEYTAVILY_API_KEY)喺 Dashboard 設定。
  • Migration 唔入 CI:改 schema 時手動跑 npm run db:migrate(避免每次 push 亂郁 DB)。README 寫明呢個分工。

4.3 唔做

  • 唔加 GitHub Actions。
  • 唔自動化 Cloudflare Dashboard 連 repo(需要人手授權)。

5. 分階段實作

階段 內容 完成標準
Phase 1 Zod schema-first 驗證層 npm run build 過;admin 表單驗證錯誤正常顯示
Phase 2 AI Blog + 關鍵字佇列 npm run db:generate 出 migrationnpm run dev 實測生成草稿
Phase 3 setup script + Workers Builds 文件 新 clone 行 npm run setup 可完成 provisionREADME 更新

三階段相對獨立,逐階段完成並 npm run build


6. 風險與對策

風險 對策
Zod 加入 admin POST 改動面大 逐個 admin POST handler 遷移;parseForm 提供一致 fallbackbuild 驗證
LLM 輸出格式唔穩定 用容錯分隔格式 + Zod 驗證,失敗回明確錯誤、唔插入壞資料
同步生成請求時間長 Workers I/O 等待唔計 CPU;若超時先改 cfContext.waitUntil + 狀態欄
wrangler.jsonc 有註解,程式改寫易壞 只做精準字串替換,唔 parse 整個檔
Workers Builds 跑 migration 風險 明確唔放 CImigration 手動執行
外部 AI 供應商停机 生成失敗只回錯誤,唔影響網站;設定可改 ai_base_url/model

7. 驗證

  • 每階段:npm run build(唯一 build/type 檢查)。
  • Phase 2npm run db:generatenpm run dev 手動生成一篇草稿並覆核。
  • Phase 3npm run setup 喺未 provision 環境 dry-run(或已有環境重跑,確認 idempotent)。

8. 文件同步

  • AGENTS.md(root):指令、部署流程、AI secret。
  • src/AGENTS.md:新 schemas//admin/ai 路由、AI 執行方式。
  • src/db/AGENTS.md:新 blog_keywords 表、posts.focusKeyword
  • src/data/AGENTS.md:關鍵字查詢、型別來源改為 schemas。
  • src/lib/AGENTS.mdai.tsform.tsenv.ts 新 secret。
  • README.mdsetup script、Workers Builds、AI 設定步驟。
  • migrations/AGENTS.md:新 migration。

9. 待客戶提供

  • AI_API_KEYDeepInfra 或同類 OpenAI 相容供應商)。
  • TAVILY_API_KEY(若用上網研究)。