perf: 拆分规划策略

This commit is contained in:
voocel
2026-03-13 00:19:21 +08:00
parent 16e790a372
commit 7488198461
24 changed files with 1543 additions and 487 deletions

View File

@@ -4,6 +4,7 @@ import (
"context"
"encoding/json"
"fmt"
"os"
"strings"
"github.com/voocel/agentcore/schema"
@@ -25,7 +26,9 @@ type References struct {
ContentExpansion string
DialogueWriting string
// V2
StyleReference string // 风格补充参考(可为空)
StyleReference string // 风格补充参考(可为空)
LongformPlanning string // 通用长篇规划参考
Differentiation string // 通用差异化设计参考
}
// ContextTool 组装当前章节所需上下文。
@@ -60,22 +63,47 @@ func (t *ContextTool) Execute(_ context.Context, args json.RawMessage) (json.Raw
}
result := make(map[string]any)
var warnings []string
seenWarnings := make(map[string]struct{})
warn := func(scope string, err error) {
if err == nil || os.IsNotExist(err) {
return
}
msg := fmt.Sprintf("%s 读取失败: %v", scope, err)
if _, ok := seenWarnings[msg]; ok {
return
}
seenWarnings[msg] = struct{}{}
warnings = append(warnings, msg)
}
// 加载基础设定
if premise, err := t.store.LoadPremise(); err == nil && premise != "" {
result["premise"] = premise
} else {
warn("premise", err)
}
if outline, err := t.store.LoadOutline(); err == nil && outline != nil {
result["outline"] = outline
} else {
warn("outline", err)
}
if rules, err := t.store.LoadWorldRules(); err == nil && len(rules) > 0 {
result["world_rules"] = rules
} else {
warn("world_rules", err)
}
if a.Chapter > 0 {
// 根据总章节数计算上下文策略
profile := domain.NewContextProfile(0)
progress, _ := t.store.LoadProgress()
progress, err := t.store.LoadProgress()
warn("progress", err)
runMeta, err := t.store.LoadRunMeta()
warn("run_meta", err)
if runMeta != nil && runMeta.PlanningTier != "" {
result["planning_tier"] = runMeta.PlanningTier
}
if progress != nil && progress.TotalChapters > 0 {
profile = domain.NewContextProfile(progress.TotalChapters)
}
@@ -86,26 +114,32 @@ func (t *ContextTool) Execute(_ context.Context, args json.RawMessage) (json.Raw
// 角色加载Layered 模式优先用快照,回退到原始设定
if profile.Layered {
t.loadLayeredCharacters(result, a.Chapter)
t.loadLayeredCharacters(result, a.Chapter, warn)
} else {
t.loadFilteredCharacters(result, a.Chapter)
t.loadFilteredCharacters(result, a.Chapter, warn)
}
// Writer/Editor 模式:加载章节相关上下文
if entry, err := t.store.GetChapterOutline(a.Chapter); err == nil {
result["current_chapter_outline"] = entry
} else {
warn("current_chapter_outline", err)
}
// 摘要加载:分层 vs 扁平
if profile.Layered {
t.loadLayeredSummaries(result, a.Chapter, profile.SummaryWindow)
t.loadLayeredSummaries(result, a.Chapter, profile.SummaryWindow, warn)
} else if profile.FullContext {
if summaries, err := t.store.LoadAllSummaries(a.Chapter); err == nil && len(summaries) > 0 {
result["recent_summaries"] = summaries
} else {
warn("recent_summaries", err)
}
} else {
if summaries, err := t.store.LoadRecentSummaries(a.Chapter, profile.SummaryWindow); err == nil && len(summaries) > 0 {
result["recent_summaries"] = summaries
} else {
warn("recent_summaries", err)
}
}
@@ -113,29 +147,41 @@ func (t *ContextTool) Execute(_ context.Context, args json.RawMessage) (json.Raw
if profile.FullContext {
if timeline, err := t.store.LoadTimeline(); err == nil && len(timeline) > 0 {
result["timeline"] = timeline
} else {
warn("timeline", err)
}
} else {
if timeline, err := t.store.LoadRecentTimeline(a.Chapter, profile.TimelineWindow); err == nil && len(timeline) > 0 {
result["timeline"] = timeline
} else {
warn("timeline", err)
}
}
// foreshadow短篇全量否则只取未回收条目
if profile.FullContext {
if foreshadow, err := t.store.LoadForeshadowLedger(); err == nil && len(foreshadow) > 0 {
result["foreshadow_ledger"] = foreshadow
} else {
warn("foreshadow_ledger", err)
}
} else {
if foreshadow, err := t.store.LoadActiveForeshadow(); err == nil && len(foreshadow) > 0 {
result["foreshadow_ledger"] = foreshadow
} else {
warn("foreshadow_ledger", err)
}
}
// relationships保持全量pair-key 去重,数据量天然可控)
if relationships, err := t.store.LoadRelationships(); err == nil && len(relationships) > 0 {
result["relationship_state"] = relationships
} else {
warn("relationship_state", err)
}
// 状态变化:最近 5 章的角色/实体状态变化
if changes, err := t.store.LoadRecentStateChanges(a.Chapter, 5); err == nil && len(changes) > 0 {
result["recent_state_changes"] = changes
} else {
warn("recent_state_changes", err)
}
// Layered 模式:注入当前卷弧位置 + 弧目标/卷主题
@@ -159,6 +205,8 @@ func (t *ContextTool) Execute(_ context.Context, args json.RawMessage) (json.Raw
break
}
}
} else {
warn("layered_outline", err)
}
result["position"] = pos
}
@@ -180,26 +228,42 @@ func (t *ContextTool) Execute(_ context.Context, args json.RawMessage) (json.Raw
// 加载已有的章节规划(支持场景恢复跳过已完成场景)
if plan, err := t.store.LoadChapterPlan(a.Chapter); err == nil && plan != nil {
result["chapter_plan"] = plan
} else {
warn("chapter_plan", err)
}
// 写作参考资料分阶段加载
result["references"] = t.writerReferences(a.Chapter)
} else {
runMeta, err := t.store.LoadRunMeta()
warn("run_meta", err)
if runMeta != nil && runMeta.PlanningTier != "" {
result["planning_tier"] = runMeta.PlanningTier
}
// Architect 模式:全量角色 + 模板
if chars, err := t.store.LoadCharacters(); err == nil && chars != nil {
result["characters"] = chars
} else {
warn("characters", err)
}
// Architect 模式下也加载分层大纲(弧级规划需要看全貌)
if layered, err := t.store.LoadLayeredOutline(); err == nil && len(layered) > 0 {
result["layered_outline"] = layered
} else {
warn("layered_outline", err)
}
// 加载已有的弧摘要(弧级规划时需要参考前续弧的内容)
if volSummaries, err := t.store.LoadAllVolumeSummaries(); err == nil && len(volSummaries) > 0 {
result["volume_summaries"] = volSummaries
} else {
warn("volume_summaries", err)
}
result["references"] = t.architectReferences()
}
if len(warnings) > 0 {
result["_warnings"] = warnings
}
result["_loading_summary"] = buildLoadingSummary(result, a.Chapter)
return json.Marshal(result)
}
@@ -213,6 +277,9 @@ func buildLoadingSummary(result map[string]any, chapter int) string {
} else {
parts = append(parts, "architect")
}
if tier, ok := result["planning_tier"].(domain.PlanningTier); ok && tier != "" {
parts = append(parts, fmt.Sprintf("tier=%s", tier))
}
// 卷弧位置
if pos, ok := result["position"].(map[string]any); ok {
@@ -272,6 +339,9 @@ func buildLoadingSummary(result map[string]any, chapter int) string {
if refs, ok := result["references"].(map[string]string); ok && len(refs) > 0 {
items = append(items, fmt.Sprintf("参考:%d项", len(refs)))
}
if warnings, ok := result["_warnings"].([]string); ok && len(warnings) > 0 {
items = append(items, fmt.Sprintf("告警:%d", len(warnings)))
}
if len(items) > 0 {
parts = append(parts, strings.Join(items, " "))
@@ -309,15 +379,20 @@ func sliceLen(v any) int {
// loadFilteredCharacters 按 Tier 和场景出场过滤角色。
// core/important 始终返回secondary/decorative 只在当前章节大纲提及时返回。
func (t *ContextTool) loadFilteredCharacters(result map[string]any, chapter int) {
func (t *ContextTool) loadFilteredCharacters(result map[string]any, chapter int, warn func(string, error)) {
chars, err := t.store.LoadCharacters()
if err != nil || len(chars) == 0 {
if err != nil {
warn("characters", err)
return
}
if len(chars) == 0 {
return
}
// 获取当前章节大纲的场景描述,用于匹配次要角色
entry, err := t.store.GetChapterOutline(chapter)
if err != nil {
warn("current_chapter_outline", err)
result["characters"] = chars
return
}
@@ -351,12 +426,15 @@ func matchCharacter(text string, c domain.Character) bool {
}
// loadLayeredSummaries 分层摘要加载:卷摘要 + 当前卷弧摘要 + 弧内章摘要。
func (t *ContextTool) loadLayeredSummaries(result map[string]any, chapter, summaryWindow int) {
func (t *ContextTool) loadLayeredSummaries(result map[string]any, chapter, summaryWindow int, warn func(string, error)) {
vol, arc, err := t.store.LocateChapter(chapter)
if err != nil {
warn("layered_outline_position", err)
// 回退到扁平模式
if summaries, err := t.store.LoadRecentSummaries(chapter, summaryWindow); err == nil && len(summaries) > 0 {
result["recent_summaries"] = summaries
} else {
warn("recent_summaries", err)
}
return
}
@@ -364,6 +442,8 @@ func (t *ContextTool) loadLayeredSummaries(result map[string]any, chapter, summa
// 1. 已完成卷的卷摘要
if volSummaries, err := t.store.LoadAllVolumeSummaries(); err == nil && len(volSummaries) > 0 {
result["volume_summaries"] = volSummaries
} else {
warn("volume_summaries", err)
}
// 2. 当前卷内已完成弧的弧摘要(不含当前弧)
@@ -377,25 +457,30 @@ func (t *ContextTool) loadLayeredSummaries(result map[string]any, chapter, summa
if len(prior) > 0 {
result["arc_summaries"] = prior
}
} else {
warn("arc_summaries", err)
}
// 3. 当前弧内最近 N 章的章摘要
if summaries, err := t.store.LoadRecentSummaries(chapter, summaryWindow); err == nil && len(summaries) > 0 {
result["recent_summaries"] = summaries
} else {
warn("recent_summaries", err)
}
}
// loadLayeredCharacters Layered 模式下的角色加载:优先用最近快照,回退到原始设定 + Tier 过滤。
func (t *ContextTool) loadLayeredCharacters(result map[string]any, chapter int) {
func (t *ContextTool) loadLayeredCharacters(result map[string]any, chapter int, warn func(string, error)) {
snapshots, err := t.store.LoadLatestSnapshots()
if err == nil && len(snapshots) > 0 {
result["character_snapshots"] = snapshots
// 同时保留原始设定中的 core/important 角色(快照可能不含新登场角色)
t.loadFilteredCharacters(result, chapter)
t.loadFilteredCharacters(result, chapter, warn)
return
}
warn("character_snapshots", err)
// 无快照时回退到原始设定
t.loadFilteredCharacters(result, chapter)
t.loadFilteredCharacters(result, chapter, warn)
}
// writerReferences 返回写作参考资料。章节 1 返回全量,后续章节裁剪掉不再需要的模板。
@@ -431,6 +516,9 @@ func (t *ContextTool) architectReferences() map[string]string {
}
add("outline_template", t.refs.OutlineTemplate)
add("character_template", t.refs.CharacterTemplate)
add("longform_planning", t.refs.LongformPlanning)
add("differentiation", t.refs.Differentiation)
add("style_reference", t.refs.StyleReference)
return refs
}