Skip to content

docs\zh_tw\manifest.md

maoxiaoyue edited this page May 14, 2026 · 1 revision

Manifest 機制詳解:專案的壓縮表示

HypGo Framework — Project Manifest 原理與運作機制 版本:0.8.1-alpha | 2026-04


Manifest 是什麼?

Manifest 是你整個專案的結構化快照。它把散落在數十個檔案中的路由、型別、設定,壓縮成一個 AI 可以在幾百 token 內讀完的 YAML/JSON 檔案。

你的專案(20 個 handler、10 個 model、5 個 service)
    │
    │  傳統做法:AI 讀全部原始碼 → ~15,000 tokens
    │
    ▼
.hyp/context.yaml(200 行)
    │
    │  HypGo 做法:AI 讀一個檔案 → ~500 tokens
    │
    ▼
AI 理解你的專案(壓縮比 30:1)

Manifest 不是文檔。它是框架在運行時從 Router、Config、Schema Registry 自動收集的即時資訊。它不會與程式碼脫節,因為它就是從程式碼產出的。


第一部分:Manifest 結構

完整範例

version: "1.0"
framework: HypGo
generated_at: "2026-04-03T14:30:00+08:00"
server:
  addr: ":8080"
  protocol: http2
  tls: true
routes:
  - method: GET
    path: /api/users
    handler_names:
      - controllers.(*UserController).List
    summary: "List all users"
    tags:
      - users
    output_type: UserListResp
  - method: POST
    path: /api/users
    handler_names:
      - controllers.(*UserController).Create
    summary: "Create user"
    tags:
      - users
    input_type: CreateUserReq
    output_type: UserResp
    responses:
      201: "User created"
      400: "Invalid input"
  - method: GET
    path: /api/users/:id
    handler_names:
      - controllers.(*UserController).Get
    summary: "Get user by ID"
    tags:
      - users
    output_type: UserResp
    responses:
      200: "User found"
      404: "User not found"
  - method: GET
    path: /health
    handler_names:
      - main.healthHandler
database:
  driver: postgres
  has_replicas: true

結構定義

type Manifest struct {
    Version     string          // 固定 "1.0"
    Framework   string          // 固定 "HypGo"
    GeneratedAt time.Time       // 生成時間
    Server      ServerInfo      // 伺服器設定
    Routes      []RouteManifest // 所有路由(含 schema metadata)
    Middleware  []string        // 已註冊的中間件(可選)
    Database    *DatabaseInfo   // 資料庫設定(可選,無 DB 則 nil)
}

每個路由包含的資訊

欄位 來源 說明
method Router HTTP 方法(GET/POST/PUT/DELETE)
path Router 路由路徑(含 :id*filepath
handler_names Router(reflect) Handler 函式名稱,用 runtime.FuncForPC 提取
summary Schema Registry 一句話描述(只有 Schema 路由才有)
description Schema Registry 詳細描述(可選)
tags Schema Registry 分類標籤(可選)
input_type Schema Registry Input struct 名稱,如 "CreateUserReq"
output_type Schema Registry Output struct 名稱,如 "UserResp"
responses Schema Registry 狀態碼 → 描述,如 {201: "Created"}

關鍵差異:傳統路由(r.GET("/health", handler))只有 methodpathhandler_names。Schema 路由額外有 summarytagsinput_typeoutput_typeresponses


第二部分:Manifest 如何產生

收集流程

Collector.Collect()
  │
  ├─ 1. collectRoutes()
  │     │
  │     ├─ router.Routes()
  │     │   → 遍歷所有 Radix Tree
  │     │   → 每個 leaf node 提取:method, path, handlers
  │     │   → 用 runtime.FuncForPC 取得 handler 函式名
  │     │   → 回傳 []RouteInfo
  │     │
  │     └─ 對每個 RouteInfo:
  │         schema.Global().Get(method, path)
  │         → 有 schema?加入 summary, tags, input_type, output_type, responses
  │         → 無 schema?只保留 method, path, handler_names
  │
  ├─ 2. collectServer()
  │     → config.Server.Addr
  │     → config.Server.Protocol
  │     → config.Server.TLS.Enabled
  │
  └─ 3. collectDatabase()
        → config.Database.Driver(空字串則回傳 nil)
        → config.Database.Replicas 數量 > 0?

排序:Routes 按 Path 字母序 + Method 字母序

Handler 名稱提取

// 從 handler 函式指標取得名稱
func nameOfFunction(f interface{}) string {
    full := runtime.FuncForPC(reflect.ValueOf(f).Pointer()).Name()
    // "github.com/myapp/app/controllers.(*UserController).Create-fm"
    //                                    ↓
    // 取最後一段 + 移除 -fm 後綴
    // → "controllers.(*UserController).Create"
}

三個產生 Manifest 的方式

方式 何時 命令/程式碼
CLI 手動執行 hyp context -o .hyp/context.yaml
AutoSync Server.Start() 自動 autosync.SyncSafe()
程式碼 任何時候 manifest.NewCollector(r, cfg).Collect()

第三部分:AutoSync 自動同步

運作流程

Server.Start()
  │
  ├─ autosync.New(config, router, appConfig, logger)
  │
  └─ autosync.SyncSafe()
       │
       ├─ config.Enabled == false?→ 跳過
       │
       ├─ os.MkdirAll(".hyp/", 0755)
       │
       ├─ manifest.NewCollector(router, appConfig).Collect()
       │   → 收集所有路由、設定、schema metadata
       │
       ├─ manifest.SaveToFile(".hyp/context.yaml.tmp", m, "yaml")
       │   → 寫入暫存檔(不直接覆蓋目標檔案)
       │
       ├─ os.Rename(".hyp/context.yaml.tmp", ".hyp/context.yaml")
       │   → 原子替換(防止寫入中途損壞)
       │
       └─ logger.Info("AutoSync: manifest saved to .hyp/context.yaml")

為什麼用原子寫入?

❌ 直接寫入(危險):
   os.Create(".hyp/context.yaml")  ← 此刻檔案被清空
   writer.Write(data)              ← 寫入中途斷電 → 檔案損壞
   file.Close()

✅ 原子寫入(安全):
   os.Create(".hyp/context.yaml.tmp")  ← 暫存檔
   writer.Write(data)                   ← 寫入暫存檔
   file.Close()
   os.Rename(".tmp", ".yaml")           ← 原子替換,全有或全無

如果 Rename 之前程式崩潰,.yaml 保持上一次的完整版本,.tmp 是不完整的 — 下次啟動會重新生成。

AutoSync 設定

autosync.Config{
    Enabled: true,                    // 預設 true
    Path:    ".hyp/context.yaml",     // 預設路徑
    Format:  "yaml",                  // "yaml" 或 "json"
}

第四部分:Manifest 不包含什麼

安全設計:結構 vs 資料

✅ Manifest 包含(結構資訊)    ❌ Manifest 不包含(敏感資料)
─────────────────────────────  ────────────────────────────
路由路徑 /api/users             資料庫密碼
HTTP 方法 POST                  DSN 連線字串
型別名稱 CreateUserReq          API key / token
欄位名稱 name, email            使用者資料
Handler 名稱 controllers.Create  環境變數值
Server addr :8080               TLS 證書內容
Protocol http2                  Redis 密碼
Database driver postgres         Session 資料
HasReplicas true                 Request/Response 實際值

設計原則:AI 看得到「結構」(路由長什麼樣、型別叫什麼名字),看不到「資料」(密碼是什麼、使用者存了什麼)。

collectDatabase 的過濾

func (c *Collector) collectDatabase() *DatabaseInfo {
    if c.config.Database.Driver == "" {
        return nil  // 沒設定 DB → 不出現在 manifest 中
    }
    return &DatabaseInfo{
        Driver:      c.config.Database.Driver,  // "postgres"(只有 driver 名)
        HasReplicas: len(c.config.Database.Replicas) > 0,  // true/false
        // ❌ 不包含 DSN
        // ❌ 不包含 Password
        // ❌ 不包含 Replica DSN
    }
}

第五部分:Manifest 如何被消費

5.1 AI 直接讀取

# AI 的第一個動作
cat .hyp/context.yaml

AI 從 manifest 中取得:

  • 所有 API 端點清單(method + path)
  • 每個端點的 Input/Output 型別名稱
  • Handler 函式名稱(知道要修改哪個檔案)
  • Server 設定(知道用什麼協議)
  • Database driver(知道用哪種 SQL)

5.2 hyp ai-rules 讀取

hyp ai-rules 會嘗試讀取 .hyp/context.yaml,如果存在,就把路由表注入到 AGENTS.md 等配置檔中:

func loadManifestIfExists(dir string) *manifest.Manifest {
    path := filepath.Join(dir, ".hyp", "context.yaml")
    data, err := os.ReadFile(path)
    if err != nil {
        return nil  // 不存在 → AI 配置檔只有靜態慣例,沒有動態路由
    }
    var m manifest.Manifest
    yaml.Unmarshal(data, &m)
    return &m
}

如果 manifest 存在,AGENTS.md 會包含:

## Current Routes

| Method | Path | Summary |
|--------|------|---------|
| GET | /api/users | List all users |
| POST | /api/users | Create user |
| GET | /health | - |

5.3 hyp context 手動生成

# YAML 到 stdout
hyp context

# JSON 格式
hyp context -f json

# 儲存到檔案
hyp context -o .hyp/context.yaml
hyp context -o manifest.json -f json

5.4 Server.Manifest() 程式碼存取

srv := server.New(cfg, log)

// 註冊路由後...
m := srv.Manifest()

// 輸出
manifest.WriteYAML(os.Stdout, m)

// 或直接存取欄位
for _, route := range m.Routes {
    fmt.Printf("%s %s → %s\n", route.Method, route.Path, route.InputType)
}

第六部分:Schema 路由 vs 傳統路由在 Manifest 中的差異

同一個 API,兩種寫法的 Manifest 比較

傳統路由

r.POST("/api/users", createUserHandler)

Manifest 輸出:

- method: POST
  path: /api/users
  handler_names:
    - main.createUserHandler
  # ← 沒有 summary、沒有 input_type、沒有 output_type

AI 看到這個:「有一個 POST /api/users,handler 叫 createUserHandler... 但我不知道它接受什麼、回傳什麼。讓我去讀原始碼...」→ 消耗 ~2,000 tokens。

Schema 路由

r.Schema(schema.Route{
    Method:  "POST",
    Path:    "/api/users",
    Summary: "Create user",
    Tags:    []string{"users"},
    Input:   CreateUserReq{},
    Output:  UserResp{},
    Responses: map[int]schema.ResponseSchema{
        201: {Description: "User created"},
        400: {Description: "Invalid input"},
    },
}).Handle(createUserHandler)

Manifest 輸出:

- method: POST
  path: /api/users
  handler_names:
    - main.createUserHandler
  summary: "Create user"
  tags:
    - users
  input_type: CreateUserReq
  output_type: UserResp
  responses:
    201: "User created"
    400: "Invalid input"

AI 看到這個:「POST /api/users,接受 CreateUserReq,回傳 UserResp,可能回 201 或 400。」→ 消耗 ~200 tokens。

差距量化

資訊 傳統路由 Schema 路由
路由路徑
Handler 名稱
一句話描述 ❌ 不知道 ✅ "Create user"
Input 型別 ❌ 要讀 handler 推斷 ✅ "CreateUserReq"
Output 型別 ❌ 要讀 handler 推斷 ✅ "UserResp"
可能的錯誤 ❌ 不知道 ✅ 201/400
AI 理解成本 ~2,000 tokens ~200 tokens

第七部分:Manifest 的壓縮原理

為什麼 30:1 壓縮比不是有損的

一個 handler 的原始碼可能是 50 行:

func (ctrl *UserController) Create(c *hypcontext.Context) {
    var req models.CreateUserReq
    if err := c.ShouldBindJSON(&req); err != nil {
        errors.AbortWithAppError(c, ErrUserInvalid.With("reason", err.Error()))
        return
    }

    user := &models.User{
        Name:  req.Name,
        Email: req.Email,
    }

    if _, err := db.WriteHypDB().NewInsert().Model(user).Exec(ctx); err != nil {
        errors.AbortWithAppError(c, ErrUserInvalid.With("reason", "db error"))
        return
    }

    c.JSON(201, models.UserResp{
        ID:    user.ID,
        Name:  user.Name,
        Email: user.Email,
    })
}

Manifest 把它壓縮成 6 行:

- method: POST
  path: /api/users
  summary: "Create user"
  input_type: CreateUserReq
  output_type: UserResp
  handler_names: [controllers.(*UserController).Create]

省略了什麼

  • 怎麼解析 Input(ShouldBindJSON)→ 這是慣例,不需要每次告訴 AI
  • 怎麼存 DB(NewInsert().Model())→ 這是實作細節
  • 怎麼處理錯誤(AbortWithAppError)→ 這是慣例

保留了什麼

  • API 端點是什麼(POST /api/users)→ AI 需要知道
  • 接受什麼(CreateUserReq)→ AI 需要知道
  • 回傳什麼(UserResp)→ AI 需要知道
  • 誰處理(controllers.Create)→ AI 需要知道

這就是為什麼不是有損壓縮:AI 做出正確決策所需的全部結構資訊都在 manifest 中。省略的只是可推斷的慣例和實作細節。


第八部分:最佳實踐

何時生成 Manifest

時機 方式 原因
Server 啟動 AutoSync(自動) 每次啟動都更新,保持同步
新增/修改路由後 hyp context -o .hyp/context.yaml 讓 AI 看到最新路由
新增路由後 hyp ai-rules 把路由表注入 AGENTS.md
CI/CD 中 hyp context -o manifest.yaml 歸檔 API 結構

是否加入版本控制?

策略 做法 適合
加入 git commit .hyp/context.yaml 團隊協作,新成員 clone 後立刻有 manifest
加入 .gitignore 每次 hyp run 自動生成 單人開發,避免不必要的 diff

搭配 hyp ai-rules 的工作流

# 1. 寫好 Schema 路由
# 2. 生成 manifest
hyp context -o .hyp/context.yaml

# 3. 生成 AI 配置檔(會讀取 manifest 注入路由表)
hyp ai-rules

# 4. 開啟 AI 工具 → AI 自動讀取 AGENTS.md → 知道所有路由

如果跳過步驟 2 直接跑步驟 3,AGENTS.md 只會有框架慣例,不會有路由表。先 manifest,再 ai-rules。


第九部分:Debug 與進階用法

查看當前 Manifest 內容

# YAML 到 stdout
hyp context

# JSON 格式
hyp context -f json

# 只看路由
hyp context | grep "path:"

程式碼中讀取 Manifest

import "gopkg.in/yaml.v3"

data, _ := os.ReadFile(".hyp/context.yaml")
var m manifest.Manifest
yaml.Unmarshal(data, &m)

fmt.Printf("Routes: %d\n", len(m.Routes))
for _, r := range m.Routes {
    fmt.Printf("  %s %s → %s\n", r.Method, r.Path, r.InputType)
}

比較兩個版本的 Manifest

# 在修改前後各生成一份,用 diff 比較
hyp context -o before.yaml
# ... 修改路由 ...
hyp context -o after.yaml
diff before.yaml after.yaml

驗證 Manifest 完整性

m := srv.Manifest()
for _, r := range m.Routes {
    if r.InputType == "" && (r.Method == "POST" || r.Method == "PUT") {
        fmt.Printf("⚠️  %s %s: missing Input type\n", r.Method, r.Path)
    }
    if r.Summary == "" {
        fmt.Printf("⚠️  %s %s: missing Summary\n", r.Method, r.Path)
    }
}

HypGo · Manifest 機制詳解 · 2026-04

HypGo

繁體中文 | English


中文文件

設計文件

套件

AI 協作工具鏈

CLI 命令


English Docs

Design Docs

Packages

AI Collaboration Toolchain

CLI Commands

Clone this wiki locally