feat(prompts): v2 DB 控制面热切换 —— 版本留存 + 不重启即生效
在 v1(注册表+文件覆盖)上加 DB 管理层与热切换,镜像 model-config 控制面: - store: sundynix_prompt 表(key/version/content/active) + ActivePrompts/ListPrompts/ CreateVersion/Activate/Deactivate - 控制面: ServePrompts/RequestActivePrompts(+Retry)/PublishPromptsUpdated/SubscribePromptsUpdated; prompts.ApplyOverrides 整体替换覆盖集(DB 激活集为权威) - gateway API: GET/POST /api/v1/prompts、version/activate/deactivate;激活/撤销即广播 - dispatcher/mcp-go: 启动拉激活集 + 订阅热更新(不重启) - live: 建版本→激活→mcp-go 图谱抽取 2→0→回滚 2(全程不重启);deactivate 回退代码默认;版本可回溯 Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,7 @@ func OpenPostgres(dsn string) *Postgres {
|
||||
migrateLegacyIntIDs(db)
|
||||
migrateDocLinkToID(db)
|
||||
|
||||
if err := db.AutoMigrate(&User{}, &Task{}, &Eval{}, &LLMModel{}, &KB{}, &Doc{}, &Agent{}, &DocLink{}, &Pricing{}); err != nil {
|
||||
if err := db.AutoMigrate(&User{}, &Task{}, &Eval{}, &LLMModel{}, &KB{}, &Doc{}, &Agent{}, &DocLink{}, &Pricing{}, &Prompt{}); err != nil {
|
||||
log.Printf("[store] postgres AutoMigrate 失败,降级运行: %v", err)
|
||||
return &Postgres{}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,82 @@
|
||||
package store
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"gorm.io/gorm"
|
||||
)
|
||||
|
||||
// Prompt 是一条受管系统提示词的某个版本。表名 sundynix_prompt,(key,version) 唯一。
|
||||
// 平台级配置(非按 owner),由管理端维护:建版本 → 激活某版 → 控制面热下发各服务。
|
||||
type Prompt struct {
|
||||
BaseModel
|
||||
Key string `gorm:"size:64;uniqueIndex:idx_prompt_kv;index"` // 受管 prompt 键,如 graph.extract
|
||||
Version int `gorm:"uniqueIndex:idx_prompt_kv"` // 该 key 下递增版本号
|
||||
Content string `gorm:"type:text"` // 提示词正文
|
||||
Active bool `gorm:"index"` // 是否为该 key 当前激活版(每 key 至多一条)
|
||||
Note string `gorm:"size:200"` // 版本说明
|
||||
}
|
||||
|
||||
func (Prompt) TableName() string { return "sundynix_prompt" }
|
||||
|
||||
// ActivePrompts 返回全部激活版的 key→content(控制面下发用)。
|
||||
func (p *Postgres) ActivePrompts(ctx context.Context) map[string]string {
|
||||
out := map[string]string{}
|
||||
if p.db == nil {
|
||||
return out
|
||||
}
|
||||
var rows []Prompt
|
||||
if err := p.db.WithContext(ctx).Where("active = ?", true).Find(&rows).Error; err != nil {
|
||||
return out
|
||||
}
|
||||
for _, r := range rows {
|
||||
out[r.Key] = r.Content
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// ListPrompts 返回全部 prompt 版本(按 key、version 排序,供管理端浏览/对比/回滚)。
|
||||
func (p *Postgres) ListPrompts(ctx context.Context) ([]Prompt, error) {
|
||||
if p.db == nil {
|
||||
return nil, nil
|
||||
}
|
||||
var rows []Prompt
|
||||
err := p.db.WithContext(ctx).Order("key asc, version asc").Find(&rows).Error
|
||||
return rows, err
|
||||
}
|
||||
|
||||
// CreatePromptVersion 为某 key 新建一个版本(version=该 key 现有最大+1,默认不激活),返回新版本号。
|
||||
func (p *Postgres) CreatePromptVersion(ctx context.Context, key, content, note string) (int, error) {
|
||||
if p.db == nil {
|
||||
return 0, nil
|
||||
}
|
||||
var maxV struct{ V int }
|
||||
p.db.WithContext(ctx).Model(&Prompt{}).Select("coalesce(max(version),0) as v").Where("key = ?", key).Scan(&maxV)
|
||||
v := maxV.V + 1
|
||||
row := Prompt{Key: key, Version: v, Content: content, Note: note, Active: false}
|
||||
if err := p.db.WithContext(ctx).Create(&row).Error; err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// DeactivatePrompt 撤销某 key 的全部激活(控制面下发后该 key 回退到代码内置默认)。
|
||||
func (p *Postgres) DeactivatePrompt(ctx context.Context, key string) error {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
return p.db.WithContext(ctx).Model(&Prompt{}).Where("key = ?", key).Update("active", false).Error
|
||||
}
|
||||
|
||||
// ActivatePrompt 激活某 key 的指定版本(同 key 其它版本置非激活)。事务保证每 key 至多一个激活版。
|
||||
func (p *Postgres) ActivatePrompt(ctx context.Context, key string, version int) error {
|
||||
if p.db == nil {
|
||||
return nil
|
||||
}
|
||||
return p.db.WithContext(ctx).Transaction(func(tx *gorm.DB) error {
|
||||
if err := tx.Model(&Prompt{}).Where("key = ?", key).Update("active", false).Error; err != nil {
|
||||
return err
|
||||
}
|
||||
return tx.Model(&Prompt{}).Where("key = ? AND version = ?", key, version).Update("active", true).Error
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user