Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

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月03日T14: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

AltStyle によって変換されたページ (->オリジナル) /