Documentation
¶
Index ¶
- type ChapterPlan
- type ListByNovelOptions
- type Store
- func (s *Store) ChapterDeletionBlockers(ctx context.Context, novelID, chapterID int64) ([]deletion.Blocker, error)
- func (s *Store) GetPlans(ctx context.Context, novelID int64) ([]ChapterPlan, error)
- func (s *Store) ListAfter(ctx context.Context, novelID int64, chapterNum int) ([]TimelineEntry, error)
- func (s *Store) ListBefore(ctx context.Context, novelID int64, chapterNum int, limit int) ([]TimelineEntry, error)
- func (s *Store) ListByNovel(ctx context.Context, novelID int64, opts ListByNovelOptions) (*storage.PageResult[TimelineEntry], error)
- func (s *Store) ListPendingBefore(ctx context.Context, novelID int64, chapterNum int) ([]TimelineEntry, error)
- func (s *Store) SavePlan(ctx context.Context, plan *ChapterPlan) error
- type TimelineEntry
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type ChapterPlan ¶
type ChapterPlan struct {
NovelID int64 `json:"novel_id"`
Scope string `json:"scope"` // "next" | "near" | "far"
Content string `json:"content"` // 计划内容,自然语言,LLM 填写
}
ChapterPlan 是 AI 对章节的规划,按 scope 分为三个层级。
设计原则:
- 表级保证每个小说每个 scope 只有一条记录((novel_id, scope) 联合唯一约束)。 next/near/far 表达的是"当前规划状态"而非"历史规划记录"——AI 修改规划时直接 UPSERT 覆盖。
- 一个小说固定 3 行,不会增长。旧版本的计划没有查询价值,不需要追留历史。
- 与 Python 的关键差异:Python 将章节计划混入 timeline_entries 表(category=chapter_plan), 与其他 category 共用字段和查询逻辑,导致注入策略复杂。Go 独立为 chapter_plans 表,字段精简。
- 无 target_chapter:时效性由 scope 自然表达,不需要绑定具体章节号。
注入策略(第 N 章写作时系统自动拼入,AI 无感知):
- next:全量注入(完整 content)
- near:全量注入(完整 content)
- far :全量注入(完整 content) 固定 3 行,不需要折索引也不需要截断。
MCP 工具实现参考:
- get_timeline:内部查询 WHERE novel_id = ?,返回 {next, near, far} 三个槽位
- add_timeline_entry:scope="next"/"near"/"far" 时 UPSERT(ON CONFLICT (novel_id, scope) DO UPDATE)
- update_timeline_entry:按 (novel_id, scope) 定位并更新 content
type ListByNovelOptions ¶
type ListByNovelOptions struct {
PageParams storage.PageParams
Search string // 空字符串=不过滤,按 title LIKE OR content LIKE 模糊匹配
Category string // 空字符串=不过滤,"foreshadowing"/"user_directive"
Status string // 空字符串=不过滤,"pending"/"resolved"/"abandoned"
Order string // 空字符串=默认 target_reading_number ASC, importance DESC
}
ListByNovelOptions 是 ListByNovel 的可选参数。
type Store ¶
Store 管理 ChapterPlan 和 TimelineEntry 持久化。DB 导出供调用方做简单 CRUD。
func (*Store) ChapterDeletionBlockers ¶ added in v1.6.0
func (s *Store) ChapterDeletionBlockers(ctx context.Context, novelID, chapterID int64) ([]deletion.Blocker, error)
ChapterDeletionBlockers 返回引用指定章节的时间线条目。
func (*Store) GetPlans ¶
GetPlans 返回某小说的全部章节计划(next/near/far 三个槽位)。 从 plans/{scope}.md 文件读取,文件不存在时 content 为空字符串。
func (*Store) ListAfter ¶
func (s *Store) ListAfter(ctx context.Context, novelID int64, chapterNum int) ([]TimelineEntry, error)
ListAfter 取 target_chapter >= fromChapter 的全部条目,不论状态,兜底截断 100。
func (*Store) ListBefore ¶
func (s *Store) ListBefore(ctx context.Context, novelID int64, chapterNum int, limit int) ([]TimelineEntry, error)
ListBefore 取 target_chapter < beforeChapter 的最近 limit 条,不论状态。
func (*Store) ListByNovel ¶
func (s *Store) ListByNovel(ctx context.Context, novelID int64, opts ListByNovelOptions) (*storage.PageResult[TimelineEntry], error)
ListByNovel 分页列出某小说的伏笔/用户指令,支持搜索、分类和状态过滤。 前端全量拉取(Size=-1)和全局搜索复用。废弃 ListByChapterRange(前端传 0,0 全量等价) 和 SearchByNovel(搜索改走 ListByNovel(Search))。
func (*Store) ListPendingBefore ¶
func (s *Store) ListPendingBefore(ctx context.Context, novelID int64, chapterNum int) ([]TimelineEntry, error)
ListPendingBefore 取 target_chapter < chapterNum 且 pending 的全部条目,兜底截断 100。 按 target_chapter DESC——最近的在前,远古的在后,先展示最相关的。
type TimelineEntry ¶
type TimelineEntry struct {
ID int64 `gorm:"column:id;primaryKey;autoIncrement" json:"id"`
NovelID int64 `gorm:"column:novel_id;not null;index" json:"novel_id"`
Category string `gorm:"column:category;not null;index" json:"category"` // "foreshadowing" | "user_directive",约束枚举
Status string `gorm:"column:status;not null;index" json:"status"` // "pending" | "resolved" | "abandoned"
Title string `gorm:"column:title;not null" json:"title"` // 简短标题
Content string `gorm:"column:content" json:"content"` // 详细描述
DetailJSON string `gorm:"column:detail_json" json:"detail_json"` // JSON,category 相关结构化数据(伏笔类型、提示文本等)
TargetReadingNumber int `gorm:"column:target_reading_number;not null;default:0" json:"target_reading_number"` // 预计回收的阅读位置,主排序键;未来章节尚无 ID
Importance int `gorm:"column:importance;default:3" json:"importance"` // 重要度 1-5,默认 3。同 target_chapter 内的次排序键
SourceChapterID *int64 `gorm:"column:source_chapter_id;index" json:"source_chapter_id"` // 在哪章创建/埋下的,创建后不可变
Source string `gorm:"column:source" json:"source"` // "ai" | "user",谁创建的
ResolvedChapterID *int64 `gorm:"column:resolved_chapter_id;index" json:"resolved_chapter_id"` // 在哪章回收,nil 表示未回收
CreatedAt time.Time `gorm:"column:created_at;autoCreateTime" json:"created_at"`
UpdatedAt time.Time `gorm:"column:updated_at;autoUpdateTime" json:"updated_at"`
}
TimelineEntry 是伏笔和用户创作意图的追踪条目。
与 Python 版本的差异(26 字段 → 13 字段):
保留的优化:
- category 约束枚举(foreshadowing | user_directive)——系统级分类驱动不同的查询和注入策略, 不同于偏好和角色关系的自由文本,这里需要精确的行为区分。
- target_chapter 必填(NOT NULL)作为主排序键,不作为过滤条件。 LLM 估算不准确不影响(只排不滤),review agent 每章写完后校准。
- 混合注入:近期窗口全量 + 全局索引一行一条。不依赖 target_chapter 做过滤, 只依赖它排序。不准确不会丢数据。
砍掉的字段及理由:
- extra_metadata → content 和 detail_json 已覆盖扩展需求
- related_entry_ids → Python 写入但从未被查询遍历,纯粹占位
- tags → 自由标签可放入 detail_json
- arc_id → Python 从未在任何查询或业务逻辑中使用,等 story_arc 真正实现后再加
- sequence → Python 从未被排序或过滤使用
- version → 编辑历史审计,非 AI 写作核心需求
- last_editor → source 字段已区分 ai/user
- original_ai_output → Python 保存但从不回读
- resolved_at → 可通过 resolved_chapter 推导
- time_horizon → target_chapter 已表达时间远近,无需额外字段
排序规则:target_chapter ASC, importance DESC
- 下一章的伏笔排最前,第 300 章的排后面
- 同一章的按重要度排列
- target_chapter 必填,不存在 NULL 排序问题
注入策略(第 N 章写作时系统自动拼入,AI 无感知):
- 近期窗口全量:target_chapter 在 [N-3, N+5] 且 status != resolved/abandoned → 完整 title + content + detail_json,按 target_chapter ASC
- 全部索引:所有未解决条目,target_chapter ASC, importance DESC,100 条截断 → 每行一条:id | category | title | target_chapter | importance | source_chapter
- 系统提示词强制要求:索引中如存在与当前章节相关的条目,必须在写作前调用 get_timeline 获取完整信息,不得凭索引标题猜测内容
Review Agent 职责(每章写完后自动触发):
- 故事进展超出预期 → 调整 target_chapter(第 200 章 → 第 250 章)
- 伏笔已被故事自然回收但未标记 → 标记 status=resolved, resolved_chapter=当前章
- 与当前剧情冲突的条目 → 上报主 agent,主 agent 决定修改或通知用户
- 暂时考虑review agent提供意见给主agent,再去更新状态,也就是说创作完,先维护状态,然后启动review,同时review 章节内容和状态维护的正确性
MCP 工具实现参考(3 个工具,和 Python 一致):
- get_timeline:统一返回 chapter_plans + time_entries,内部查两张表,AI 无感知表结构
- add_timeline_entry:批量创建 1-6 条,事务写入。内部根据 category 路由到此表
- update_timeline_entry:更新单条(title/content/importance/target_chapter/status), 标记 resolved 时记录 resolved_chapter