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:
Blizzard
2026-06-30 14:59:52 +08:00
parent aa61590b6c
commit 5736ad145e
12 changed files with 306 additions and 1 deletions
+18
View File
@@ -20,6 +20,9 @@ const (
MemoryExtract = "memory.extract" // dispatcher:长期记忆对账
)
// Known 是平台受管的全部 prompt key(供管理端列出可配项;网关进程不登记默认,靠它枚举)。
var Known = []string{GraphExtract, EvalQuality, EvalRefine, GuardJailbreak, CoordinatorLead, MemoryExtract}
// Registry 持有默认与覆盖;Get 取覆盖优先、否则默认。并发安全。
type Registry struct {
mu sync.RWMutex
@@ -53,6 +56,21 @@ func (r *Registry) SetOverride(key, text string) {
r.overrides[key] = text
}
// ApplyOverrides 用给定集合整体替换当前覆盖集(控制面热下发:DB 激活集为权威)。
// 空值条目忽略;传空 map 即清空所有覆盖、全回退内置默认。
func ApplyOverrides(m map[string]string) { global.ApplyOverrides(m) }
func (r *Registry) ApplyOverrides(m map[string]string) {
r.mu.Lock()
defer r.mu.Unlock()
next := make(map[string]string, len(m))
for k, v := range m {
if v != "" {
next[k] = v
}
}
r.overrides = next
}
// Get 取生效内容:有覆盖用覆盖,否则用默认(都没有则空串)。
func Get(key string) string { return global.Get(key) }
func (r *Registry) Get(key string) string {