Documentation
¶
Overview ¶
Package rules 实现用户偏好的持久化输入层(Policy)。
Rule 是第四类事实,跟 Progress / Checkpoint / Artifact 并列,但性质相反: 前三类是系统输出,Rule 是用户意图的持久化输入。
设计约束(不可妥协):
- 工具只返事实,不返指令(Violation 是事实,由 editor 决定是否触发重写)
- 不引入新的 verdict 路径(复用 PendingRewrites)
- 不引入严格度字段(severity 由规则类型固定映射,editor 自主语义裁定)
- 不静默吞冲突(所有异常进 Bundle.Conflicts,让 LLM 与 /diag 可见)
- 不动 Flow Router(rule 不参与路由)
Index ¶
Constants ¶
const ChapterWordsDeviationThreshold = 0.20
ChapterWordsDeviationThreshold 定义 chapter_words 偏差升级为 error 的临界值(20%)。
Variables ¶
This section is empty.
Functions ¶
func DefaultHomeRulesDir ¶ added in v0.6.0
func DefaultHomeRulesDir() string
DefaultHomeRulesDir 拼出 ~/.ainovel/rules/ 目录的绝对路径。 home 解析失败返回空串(调用方据此跳过该来源)。
func DefaultProjectRulesPath ¶
DefaultProjectRulesPath 拼出 ./rules.md 的绝对路径(基于给定项目目录)。 调用方传入项目根,避免在 loader 内部依赖 cwd。
func EnsureHomeRulesDir ¶ added in v0.6.0
func EnsureHomeRulesDir()
EnsureHomeRulesDir 尽力创建 ~/.ainovel/rules/ 目录并写入 README.txt 引导, 让用户发现这个全局偏好扩展点、知道怎么写。 nice-to-have,非关键路径:home 解析失败或写入出错都静默吞掉,绝不阻断启动。
Types ¶
type Bundle ¶
type Bundle struct {
Structured Structured `json:"structured"`
Preferences string `json:"preferences"`
Sources []string `json:"sources"`
Conflicts []Conflict `json:"conflicts"`
}
Bundle 是合并后注入 working_memory.user_rules 的最终形态。
字段映射到 JSON 输出:
{
"structured": {...},
"preferences": "...合并 markdown...",
"sources": ["..."],
"conflicts": [...]
}
func Merge ¶
Merge 把 loader 返回的多个来源合并成最终 Bundle。
合并规则:
- 普通结构化字段:就近优先(后者覆盖前者),多来源声明同一字段且值不一致写 field_conflict
- fatigue_words:按词合并;同一词多来源声明且阈值不一致时,就近优先并写 field_conflict
- Markdown 正文:按来源顺序拼接,每段加来源标题,不覆盖
- sources:所有成功加载的文件路径
- conflicts:解析期 conflicts + 合并期 field_conflict
入参 layers 应已按 SourceKind 升序排好(loader.Load 的输出形态)。
type Conflict ¶
type Conflict struct {
Source string `json:"source"` // 文件路径(绝对或相对,按来源记录)
Kind ConflictKind `json:"kind"` // 冲突类型
Field string `json:"field,omitempty"` // 受影响字段名(如 forbidden_chars);parse_error 时为空
Detail string `json:"detail"` // 人类可读的详情(含来源列表 / 错误信息)
}
Conflict 一条冲突或异常记录。
永远不会阻断加载——所有异常都在这里暴露给 LLM 与 /diag,不静默处理。
type ConflictKind ¶
type ConflictKind string
ConflictKind 标记冲突或异常类型,便于 LLM 与诊断面板分类处理。
const ( // ConflictParseError — front matter 整体解析失败;正文仍作为偏好注入。 ConflictParseError ConflictKind = "parse_error" // ConflictUnknownField — 用户写了 Phase 1 未支持的字段(forward-compatible)。 ConflictUnknownField ConflictKind = "unknown_field" // ConflictTypeError — 字段类型错误(如 forbidden_chars 写成字符串);该字段丢弃。 ConflictTypeError ConflictKind = "type_error" // ConflictFieldConflict — 多来源同一结构化字段值不一致;就近优先生效。 ConflictFieldConflict ConflictKind = "field_conflict" // ConflictInvalidValue — 字段值格式非法(如 chapter_words: "abc");该字段丢弃。 ConflictInvalidValue ConflictKind = "invalid_value" )
type LoadOptions ¶
type LoadOptions struct {
// RulesFS 是 assets/rules 子树。约定根目录直接包含 default.md。
// 通常通过 fs.Sub(embedFS, "rules") 得到;nil 表示跳过内置规则。
RulesFS fs.FS
// HomeRulesDir 是 ~/.ainovel/rules/ 目录;loader 扫描其下所有顶层 .md(文件名字典序合并)。空表示跳过。
HomeRulesDir string
// ProjectRulesPath 是 ./rules.md(或调用方指定的项目根);空表示跳过。
ProjectRulesPath string
}
LoadOptions 是 Load 的输入参数。
文件不存在不算错误,loader 静默跳过;解析失败不阻断,conflicts 由 parser 写入 Parsed.Conflicts。
func DefaultOptions ¶
func DefaultOptions(rulesFS fs.FS) LoadOptions
DefaultOptions 根据当前工作目录构造常用 LoadOptions。
适合 Host 启动时调用一次,让 ContextTool / CommitChapterTool 复用同一份配置。 解析 cwd 失败时 ProjectRulesPath 留空(loader 会跳过该来源)。
路径语义:ProjectRulesPath 绑定 **当前工作目录(cwd)** 而非 outputDir。 用户 cd 到不同目录启动写不同的书,./rules.md 自然跟着 cwd 走;如需跨书共享, 放 ~/.ainovel/rules/ 全局目录即可(其下所有 .md 都会被加载)。
type Parsed ¶
type Parsed struct {
Source string // 文件路径
Kind SourceKind // 来源类型,用于合并优先级
Structured Structured // 该文件声明的 front matter 字段
Preference string // 该文件的 Markdown 正文(front matter 之外的部分)
Conflicts []Conflict // 该文件解析期间产生的 conflicts(未知字段 / 类型错误)
}
Parsed 是单份 rules.md 解析后的结果。
func Load ¶
func Load(opts LoadOptions) []Parsed
Load 按 Default → Global → Project 顺序读取,返回升序排好的 Parsed 列表。
merger 接收返回值后只需按列表顺序合并即可,后者覆盖前者。 不引入二阶段加载——Genre / Learned 等扩展层在真有内容前不开洞。
func Parse ¶
func Parse(source string, kind SourceKind, content []byte) Parsed
Parse 解析单份 rules.md 内容(front matter + Markdown)。
容错策略:
- front matter 整体解析失败:不阻断,正文仍作为偏好,conflicts 记录 parse_error
- 未知字段:丢弃,conflicts 记录 unknown_field
- 字段类型错误:丢弃该字段,conflicts 记录 type_error
- 字段值非法(如 chapter_words 无法解析为范围):丢弃,conflicts 记录 invalid_value
source 是文件路径,仅用于 conflicts.source;kind 决定优先级。
type Severity ¶
type Severity string
Severity 标记 Violation 的严重等级。 固定映射(用户不可配置):
forbidden_chars 出现 -> Error forbidden_phrases 出现 -> Error fatigue_words 超阈值 -> Warning chapter_words 偏差 < 20% -> Warning chapter_words 偏差 >= 20% -> Error
type SourceKind ¶
type SourceKind int
SourceKind 标记规则来源,用于合并时的就近优先排序。 值越大越就近:Project > Global > Default。
Phase 1.1 起只支持三层。Genre / Learned 层在实际题材库 / save_rule 落地前不开洞—— 真要扩展时再加常量并补 loader 即可,不留空架子。
const ( // SourceDefault — 项目内置默认规则(assets/rules/default.md),优先级最低。 SourceDefault SourceKind = iota // SourceGlobal — 用户全局偏好(~/.ainovel/rules/ 目录下所有 .md,按文件名字典序合并),跨书复用。 SourceGlobal // SourceProject — 本书规则(./rules.md),优先级最高。 SourceProject )
func (SourceKind) String ¶
func (k SourceKind) String() string
String 返回来源的可读名称,用于 markdown 拼接时的来源标题与 conflicts.detail。
type Structured ¶
type Structured struct {
Genre string `json:"genre,omitempty"`
ChapterWords *WordRange `json:"chapter_words,omitempty"`
ForbiddenChars []string `json:"forbidden_chars,omitempty"`
ForbiddenPhrases []string `json:"forbidden_phrases,omitempty"`
FatigueWords map[string]int `json:"fatigue_words,omitempty"`
}
Structured 装载 front matter 的结构化字段。
单文件解析时,Parsed.Structured 只填该文件声明的字段,其余保持零值。 合并后 Bundle.Structured 是各来源就近优先后的整体结果。
func (Structured) IsEmpty ¶
func (s Structured) IsEmpty() bool
IsEmpty 用于判定是否完全没有结构化规则;checker 可据此跳过。
type Violation ¶
type Violation struct {
Rule string `json:"rule"` // forbidden_chars / forbidden_phrases / fatigue_words / chapter_words
Target string `json:"target,omitempty"` // 具体违规对象(哪个词/字符);chapter_words 留空
Limit any `json:"limit,omitempty"` // 阈值;fatigue_words=int / chapter_words="3000-6000" / forbidden_*=空
Actual any `json:"actual"` // 实际值;fatigue_words/forbidden_*=出现次数 / chapter_words=本章字数
Deviation float64 `json:"deviation,omitempty"` // chapter_words 偏差率(0~1),其他规则留空
Severity Severity `json:"severity"` // error / warning
}
Violation 是 checker 的输出:本章违反了某条机械规则的事实陈述。
注意:commit_chapter 把 violations 透传到返回 JSON,不阻断 commit; editor 在审阅时把这些事实映射到现有七维(aesthetic/pacing/character/consistency), 由 LLM 自主决定是否升级 verdict 触发 polish/rewrite。