feat: 人格文档双层架构 — persona.md全量注入system prompt + RAG黑名单过滤
- PersonaConfig 新增 NarrativePersona 字段,在 system prompt 的「你的身份」和「你的性格」之间插入「## 你的叙事人格」 - Loader.NewLoader 自动读取同目录 persona.md 注入所有 config;Reload 同步热更新 - RAG IngestDirectory 跳过 persona.md 和 _index.md,避免重复检索浪费token - 新增 docs/dev-plan/12-next-phase-optimization.md 下一阶段优化大纲 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -17,6 +17,11 @@ type PersonaConfig struct {
|
||||
ThinkingGuidelines ThinkingGuidelines `yaml:"thinking_guidelines"`
|
||||
MemoryGuidelines MemoryGuidelines `yaml:"memory_guidelines"`
|
||||
ReflectionGuidelines ReflectionGuidelines `yaml:"reflection_guidelines"`
|
||||
|
||||
// NarrativePersona is the full text of persona.md, injected verbatim
|
||||
// into the system prompt as the narrative personality layer.
|
||||
// Not parsed from YAML — loaded from persona.md alongside the YAML config.
|
||||
NarrativePersona string `yaml:"-"`
|
||||
}
|
||||
|
||||
// BuildSystemPrompt 构建系统Prompt (向后兼容,不含心情)
|
||||
@@ -43,8 +48,11 @@ func (pc *PersonaConfig) BuildSystemPromptWithMood(userName string, affectionLev
|
||||
## 你的身份
|
||||
%s
|
||||
|
||||
## 你的叙事人格
|
||||
%s
|
||||
|
||||
## 你的性格
|
||||
`, pc.Identity.TrueName, pc.Identity.Essence)
|
||||
`, pc.Identity.TrueName, pc.Identity.Essence, pc.NarrativePersona)
|
||||
|
||||
for _, trait := range pc.Personality.CoreTraits {
|
||||
prompt += fmt.Sprintf("- %s: %s\n", trait.Name, trait.Description)
|
||||
|
||||
@@ -3,6 +3,7 @@ package persona
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
|
||||
"gopkg.in/yaml.v3"
|
||||
@@ -54,6 +55,17 @@ func NewLoader(personaDir string) (*Loader, error) {
|
||||
return nil, fmt.Errorf("未找到任何人格配置文件")
|
||||
}
|
||||
|
||||
// Load narrative persona from persona.md (same directory as YAML configs).
|
||||
// This is the full-text personality narrative injected verbatim into the system prompt.
|
||||
personaMDPath := filepath.Join(personaDir, "persona.md")
|
||||
if data, err := os.ReadFile(personaMDPath); err == nil {
|
||||
// Attach to every loaded persona config.
|
||||
narrative := string(data)
|
||||
for _, cfg := range l.configs {
|
||||
cfg.NarrativePersona = narrative
|
||||
}
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
@@ -82,6 +94,12 @@ func (l *Loader) Reload(name string, path string) error {
|
||||
}
|
||||
|
||||
l.mu.Lock()
|
||||
// Re-read persona.md to keep narrative persona in sync.
|
||||
personaDir := filepath.Dir(path)
|
||||
personaMDPath := filepath.Join(personaDir, "persona.md")
|
||||
if data, err := os.ReadFile(personaMDPath); err == nil {
|
||||
cfg.NarrativePersona = string(data)
|
||||
}
|
||||
l.configs[name] = &cfg
|
||||
l.mu.Unlock()
|
||||
|
||||
|
||||
Reference in New Issue
Block a user