@actalk/inkos-core 0.1.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/dist/agents/architect.d.ts +3 -3
- package/dist/agents/architect.d.ts.map +1 -1
- package/dist/agents/architect.js +91 -16
- package/dist/agents/architect.js.map +1 -1
- package/dist/agents/continuity.d.ts +1 -1
- package/dist/agents/continuity.d.ts.map +1 -1
- package/dist/agents/continuity.js +88 -17
- package/dist/agents/continuity.js.map +1 -1
- package/dist/agents/radar-source.d.ts +38 -0
- package/dist/agents/radar-source.d.ts.map +1 -0
- package/dist/agents/radar-source.js +92 -0
- package/dist/agents/radar-source.js.map +1 -0
- package/dist/agents/radar.d.ts +4 -1
- package/dist/agents/radar.d.ts.map +1 -1
- package/dist/agents/radar.js +35 -10
- package/dist/agents/radar.js.map +1 -1
- package/dist/agents/reviser.d.ts +2 -1
- package/dist/agents/reviser.d.ts.map +1 -1
- package/dist/agents/reviser.js +40 -18
- package/dist/agents/reviser.js.map +1 -1
- package/dist/agents/rules-reader.d.ts +26 -0
- package/dist/agents/rules-reader.d.ts.map +1 -0
- package/dist/agents/rules-reader.js +86 -0
- package/dist/agents/rules-reader.js.map +1 -0
- package/dist/agents/writer-prompts.d.ts +5 -0
- package/dist/agents/writer-prompts.d.ts.map +1 -0
- package/dist/agents/writer-prompts.js +289 -0
- package/dist/agents/writer-prompts.js.map +1 -0
- package/dist/agents/writer.d.ts +2 -2
- package/dist/agents/writer.d.ts.map +1 -1
- package/dist/agents/writer.js +31 -165
- package/dist/agents/writer.js.map +1 -1
- package/dist/index.d.ts +8 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/dist/models/book-rules.d.ts +112 -0
- package/dist/models/book-rules.d.ts.map +1 -0
- package/dist/models/book-rules.js +45 -0
- package/dist/models/book-rules.js.map +1 -0
- package/dist/models/genre-profile.d.ts +42 -0
- package/dist/models/genre-profile.d.ts.map +1 -0
- package/dist/models/genre-profile.js +25 -0
- package/dist/models/genre-profile.js.map +1 -0
- package/dist/pipeline/agent.d.ts +14 -0
- package/dist/pipeline/agent.d.ts.map +1 -0
- package/dist/pipeline/agent.js +345 -0
- package/dist/pipeline/agent.js.map +1 -0
- package/dist/pipeline/runner.d.ts +49 -0
- package/dist/pipeline/runner.d.ts.map +1 -1
- package/dist/pipeline/runner.js +221 -11
- package/dist/pipeline/runner.js.map +1 -1
- package/genres/horror.md +51 -0
- package/genres/other.md +24 -0
- package/genres/urban.md +53 -0
- package/genres/xianxia.md +46 -0
- package/genres/xuanhuan.md +64 -0
- package/package.json +14 -9
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 InkOS Contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -3,14 +3,14 @@ import type { BookConfig } from "../models/book.js";
|
|
|
3
3
|
export interface ArchitectOutput {
|
|
4
4
|
readonly storyBible: string;
|
|
5
5
|
readonly volumeOutline: string;
|
|
6
|
-
readonly
|
|
6
|
+
readonly bookRules: string;
|
|
7
7
|
readonly currentState: string;
|
|
8
8
|
readonly pendingHooks: string;
|
|
9
9
|
}
|
|
10
10
|
export declare class ArchitectAgent extends BaseAgent {
|
|
11
11
|
get name(): string;
|
|
12
|
-
generateFoundation(book: BookConfig): Promise<ArchitectOutput>;
|
|
13
|
-
writeFoundationFiles(bookDir: string, output: ArchitectOutput): Promise<void>;
|
|
12
|
+
generateFoundation(book: BookConfig, externalContext?: string): Promise<ArchitectOutput>;
|
|
13
|
+
writeFoundationFiles(bookDir: string, output: ArchitectOutput, numericalSystem?: boolean): Promise<void>;
|
|
14
14
|
private parseSections;
|
|
15
15
|
}
|
|
16
16
|
//# sourceMappingURL=architect.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architect.d.ts","sourceRoot":"","sources":["../../src/agents/architect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"architect.d.ts","sourceRoot":"","sources":["../../src/agents/architect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAC;AAMpD,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;CAC/B;AAED,qBAAa,cAAe,SAAQ,SAAS;IAC3C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,kBAAkB,CAAC,IAAI,EAAE,UAAU,EAAE,eAAe,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,eAAe,CAAC;IAkIxF,oBAAoB,CACxB,OAAO,EAAE,MAAM,EACf,MAAM,EAAE,eAAe,EACvB,eAAe,GAAE,OAAc,GAC9B,OAAO,CAAC,IAAI,CAAC;IAyBhB,OAAO,CAAC,aAAa;CAiBtB"}
|
package/dist/agents/architect.js
CHANGED
|
@@ -1,29 +1,97 @@
|
|
|
1
1
|
import { BaseAgent } from "./base.js";
|
|
2
|
+
import { readGenreProfile } from "./rules-reader.js";
|
|
2
3
|
import { writeFile, mkdir } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
4
5
|
export class ArchitectAgent extends BaseAgent {
|
|
5
6
|
get name() {
|
|
6
7
|
return "architect";
|
|
7
8
|
}
|
|
8
|
-
async generateFoundation(book) {
|
|
9
|
-
const
|
|
9
|
+
async generateFoundation(book, externalContext) {
|
|
10
|
+
const { profile: gp, body: genreBody } = await readGenreProfile(this.ctx.projectRoot, book.genre);
|
|
11
|
+
const contextBlock = externalContext
|
|
12
|
+
? `\n\n## 外部指令\n以下是来自外部系统的创作指令,请将其融入设定中:\n\n${externalContext}\n`
|
|
13
|
+
: "";
|
|
14
|
+
const numericalBlock = gp.numericalSystem
|
|
15
|
+
? `- 有明确的数值/资源体系可追踪
|
|
16
|
+
- 在 book_rules 中定义 numericalSystemOverrides(hardCap、resourceTypes)`
|
|
17
|
+
: "- 本题材无数值系统,不需要资源账本";
|
|
18
|
+
const powerBlock = gp.powerScaling
|
|
19
|
+
? "- 有明确的战力等级体系"
|
|
20
|
+
: "";
|
|
21
|
+
const eraBlock = gp.eraResearch
|
|
22
|
+
? "- 需要年代考据支撑(在 book_rules 中设置 eraConstraints)"
|
|
23
|
+
: "";
|
|
24
|
+
const systemPrompt = `你是一个专业的网络小说架构师。你的任务是为一本新的${gp.name}小说生成完整的基础设定。${contextBlock}
|
|
10
25
|
|
|
11
26
|
要求:
|
|
12
27
|
- 平台:${book.platform}
|
|
13
|
-
- 题材:${book.genre}
|
|
28
|
+
- 题材:${gp.name}(${book.genre})
|
|
14
29
|
- 目标章数:${book.targetChapters}章
|
|
15
30
|
- 每章字数:${book.chapterWordCount}字
|
|
16
31
|
|
|
32
|
+
## 题材特征
|
|
33
|
+
|
|
34
|
+
${genreBody}
|
|
35
|
+
|
|
36
|
+
## 生成要求
|
|
37
|
+
|
|
17
38
|
你需要生成以下内容,每个部分用 === SECTION: <name> === 分隔:
|
|
18
39
|
|
|
19
40
|
=== SECTION: story_bible ===
|
|
20
|
-
|
|
41
|
+
用结构化二级标题组织:
|
|
42
|
+
## 01_世界观
|
|
43
|
+
世界观设定、核心规则体系
|
|
44
|
+
|
|
45
|
+
## 02_主角
|
|
46
|
+
主角设定(身份/金手指/性格底色/行为边界)
|
|
47
|
+
|
|
48
|
+
## 03_势力与人物
|
|
49
|
+
势力分布、重要配角(每人:名字、身份、动机、与主角关系、独立目标)
|
|
50
|
+
|
|
51
|
+
## 04_地理与环境
|
|
52
|
+
地图/场景设定、环境特色
|
|
53
|
+
|
|
54
|
+
## 05_书名与简介
|
|
55
|
+
- 书名建议:采用"题材+核心爽点+主角行为"的长书名格式,避免文艺化
|
|
56
|
+
- 简介(300字内):第一句抛困境,第二句亮金手指/核心能力,第三句留悬念
|
|
21
57
|
|
|
22
58
|
=== SECTION: volume_outline ===
|
|
23
59
|
卷纲规划,每卷包含:卷名、章节范围、核心冲突、关键转折、收益目标
|
|
24
60
|
|
|
25
|
-
|
|
26
|
-
|
|
61
|
+
### 黄金三章法则(前三章必须遵循)
|
|
62
|
+
- 第1章:抛出核心冲突(主角立即面临困境/危机/选择),禁止大段背景灌输
|
|
63
|
+
- 第2章:展示金手指/核心能力(主角如何应对第1章的困境),让读者看到爽点预期
|
|
64
|
+
- 第3章:明确短期目标(主角确立第一个具体可达成的目标),给读者追读理由
|
|
65
|
+
|
|
66
|
+
=== SECTION: book_rules ===
|
|
67
|
+
生成 book_rules.md 格式的 YAML frontmatter + 叙事指导,包含:
|
|
68
|
+
\`\`\`
|
|
69
|
+
---
|
|
70
|
+
version: "1.0"
|
|
71
|
+
protagonist:
|
|
72
|
+
name: (主角名)
|
|
73
|
+
personalityLock: [(3-5个性格关键词)]
|
|
74
|
+
behavioralConstraints: [(3-5条行为约束)]
|
|
75
|
+
genreLock:
|
|
76
|
+
primary: ${book.genre}
|
|
77
|
+
forbidden: [(2-3种禁止混入的文风)]
|
|
78
|
+
${gp.numericalSystem ? `numericalSystemOverrides:
|
|
79
|
+
hardCap: (根据设定确定)
|
|
80
|
+
resourceTypes: [(核心资源类型列表)]` : ""}
|
|
81
|
+
prohibitions:
|
|
82
|
+
- (3-5条本书禁忌)
|
|
83
|
+
chapterTypesOverride: []
|
|
84
|
+
fatigueWordsOverride: []
|
|
85
|
+
additionalAuditDimensions: []
|
|
86
|
+
enableFullCastTracking: false
|
|
87
|
+
---
|
|
88
|
+
|
|
89
|
+
## 叙事视角
|
|
90
|
+
(描述本书叙事视角和风格)
|
|
91
|
+
|
|
92
|
+
## 核心冲突驱动
|
|
93
|
+
(描述本书的核心矛盾和驱动力)
|
|
94
|
+
\`\`\`
|
|
27
95
|
|
|
28
96
|
=== SECTION: current_state ===
|
|
29
97
|
初始状态卡(第0章),包含:
|
|
@@ -43,29 +111,36 @@ export class ArchitectAgent extends BaseAgent {
|
|
|
43
111
|
|
|
44
112
|
生成内容必须:
|
|
45
113
|
1. 符合${book.platform}平台口味
|
|
46
|
-
2.
|
|
47
|
-
|
|
48
|
-
|
|
114
|
+
2. 符合${gp.name}题材特征
|
|
115
|
+
${numericalBlock}
|
|
116
|
+
${powerBlock}
|
|
117
|
+
${eraBlock}
|
|
118
|
+
3. 主角人设鲜明,有明确行为边界
|
|
119
|
+
4. 伏笔前后呼应,不留悬空线
|
|
120
|
+
5. 配角有独立动机,不是工具人`;
|
|
49
121
|
const response = await this.chat([
|
|
50
122
|
{ role: "system", content: systemPrompt },
|
|
51
123
|
{
|
|
52
124
|
role: "user",
|
|
53
|
-
content: `请为标题为"${book.title}"的${
|
|
125
|
+
content: `请为标题为"${book.title}"的${gp.name}小说生成完整基础设定。`,
|
|
54
126
|
},
|
|
55
127
|
], { maxTokens: 16384, temperature: 0.8 });
|
|
56
128
|
return this.parseSections(response.content);
|
|
57
129
|
}
|
|
58
|
-
async writeFoundationFiles(bookDir, output) {
|
|
130
|
+
async writeFoundationFiles(bookDir, output, numericalSystem = true) {
|
|
59
131
|
const storyDir = join(bookDir, "story");
|
|
60
132
|
await mkdir(storyDir, { recursive: true });
|
|
61
|
-
|
|
133
|
+
const writes = [
|
|
62
134
|
writeFile(join(storyDir, "story_bible.md"), output.storyBible, "utf-8"),
|
|
63
135
|
writeFile(join(storyDir, "volume_outline.md"), output.volumeOutline, "utf-8"),
|
|
64
|
-
writeFile(join(storyDir, "
|
|
136
|
+
writeFile(join(storyDir, "book_rules.md"), output.bookRules, "utf-8"),
|
|
65
137
|
writeFile(join(storyDir, "current_state.md"), output.currentState, "utf-8"),
|
|
66
138
|
writeFile(join(storyDir, "pending_hooks.md"), output.pendingHooks, "utf-8"),
|
|
67
|
-
|
|
68
|
-
|
|
139
|
+
];
|
|
140
|
+
if (numericalSystem) {
|
|
141
|
+
writes.push(writeFile(join(storyDir, "particle_ledger.md"), "# 资源账本\n\n| 章节 | 期初值 | 来源 | 完整度 | 增量 | 期末值 | 依据 |\n|------|--------|------|--------|------|--------|------|\n| 0 | 0 | 初始化 | - | 0 | 0 | 开书初始 |\n", "utf-8"));
|
|
142
|
+
}
|
|
143
|
+
await Promise.all(writes);
|
|
69
144
|
}
|
|
70
145
|
parseSections(content) {
|
|
71
146
|
const extract = (name) => {
|
|
@@ -76,7 +151,7 @@ export class ArchitectAgent extends BaseAgent {
|
|
|
76
151
|
return {
|
|
77
152
|
storyBible: extract("story_bible"),
|
|
78
153
|
volumeOutline: extract("volume_outline"),
|
|
79
|
-
|
|
154
|
+
bookRules: extract("book_rules"),
|
|
80
155
|
currentState: extract("current_state"),
|
|
81
156
|
pendingHooks: extract("pending_hooks"),
|
|
82
157
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"architect.js","sourceRoot":"","sources":["../../src/agents/architect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAGtC,OAAO,
|
|
1
|
+
{"version":3,"file":"architect.js","sourceRoot":"","sources":["../../src/agents/architect.ts"],"names":[],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ export interface AuditIssue {
|
|
|
12
12
|
}
|
|
13
13
|
export declare class ContinuityAuditor extends BaseAgent {
|
|
14
14
|
get name(): string;
|
|
15
|
-
auditChapter(bookDir: string, chapterContent: string, chapterNumber: number): Promise<AuditResult>;
|
|
15
|
+
auditChapter(bookDir: string, chapterContent: string, chapterNumber: number, genre?: string): Promise<AuditResult>;
|
|
16
16
|
private parseAuditResult;
|
|
17
17
|
private readFileSafe;
|
|
18
18
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"continuity.d.ts","sourceRoot":"","sources":["../../src/agents/continuity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"continuity.d.ts","sourceRoot":"","sources":["../../src/agents/continuity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AAOtC,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAC;IACzB,QAAQ,CAAC,MAAM,EAAE,aAAa,CAAC,UAAU,CAAC,CAAC;IAC3C,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;CAC1B;AAED,MAAM,WAAW,UAAU;IACzB,QAAQ,CAAC,QAAQ,EAAE,UAAU,GAAG,SAAS,GAAG,MAAM,CAAC;IACnD,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;CAC7B;AA0ED,qBAAa,iBAAkB,SAAQ,SAAS;IAC9C,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,YAAY,CAChB,OAAO,EAAE,MAAM,EACf,cAAc,EAAE,MAAM,EACtB,aAAa,EAAE,MAAM,EACrB,KAAK,CAAC,EAAE,MAAM,GACb,OAAO,CAAC,WAAW,CAAC;IA8EvB,OAAO,CAAC,gBAAgB;YAwCV,YAAY;CAO3B"}
|
|
@@ -1,30 +1,101 @@
|
|
|
1
1
|
import { BaseAgent } from "./base.js";
|
|
2
|
+
import { readGenreProfile, readBookRules } from "./rules-reader.js";
|
|
2
3
|
import { readFile } from "node:fs/promises";
|
|
3
4
|
import { join } from "node:path";
|
|
5
|
+
// Dimension ID → name mapping
|
|
6
|
+
const DIMENSION_MAP = {
|
|
7
|
+
1: "OOC检查",
|
|
8
|
+
2: "时间线检查",
|
|
9
|
+
3: "设定冲突",
|
|
10
|
+
4: "战力崩坏",
|
|
11
|
+
5: "数值检查",
|
|
12
|
+
6: "伏笔检查",
|
|
13
|
+
7: "节奏检查",
|
|
14
|
+
8: "文风检查",
|
|
15
|
+
9: "信息越界",
|
|
16
|
+
10: "词汇疲劳",
|
|
17
|
+
11: "利益链断裂",
|
|
18
|
+
12: "年代考据",
|
|
19
|
+
13: "配角降智",
|
|
20
|
+
14: "配角工具人化",
|
|
21
|
+
15: "爽点虚化",
|
|
22
|
+
16: "台词失真",
|
|
23
|
+
17: "流水账",
|
|
24
|
+
18: "知识库污染",
|
|
25
|
+
19: "视角一致性",
|
|
26
|
+
};
|
|
27
|
+
function buildDimensionList(gp, bookRules) {
|
|
28
|
+
const activeIds = new Set(gp.auditDimensions);
|
|
29
|
+
// Add book-level additional dimensions
|
|
30
|
+
if (bookRules?.additionalAuditDimensions) {
|
|
31
|
+
for (const d of bookRules.additionalAuditDimensions) {
|
|
32
|
+
if (typeof d === "number")
|
|
33
|
+
activeIds.add(d);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Conditional overrides
|
|
37
|
+
if (gp.eraResearch || bookRules?.eraConstraints?.enabled) {
|
|
38
|
+
activeIds.add(12);
|
|
39
|
+
}
|
|
40
|
+
const dims = [];
|
|
41
|
+
for (const id of [...activeIds].sort((a, b) => a - b)) {
|
|
42
|
+
const name = DIMENSION_MAP[id];
|
|
43
|
+
if (!name)
|
|
44
|
+
continue;
|
|
45
|
+
let note = "";
|
|
46
|
+
if (id === 10 && gp.fatigueWords.length > 0) {
|
|
47
|
+
const words = bookRules?.fatigueWordsOverride && bookRules.fatigueWordsOverride.length > 0
|
|
48
|
+
? bookRules.fatigueWordsOverride
|
|
49
|
+
: gp.fatigueWords;
|
|
50
|
+
note = `高疲劳词:${words.join("、")}。同时检查AI标记词(仿佛/不禁/宛如/竟然/忽然/猛地)密度,每3000字超过1次即warning`;
|
|
51
|
+
}
|
|
52
|
+
if (id === 15 && gp.satisfactionTypes.length > 0) {
|
|
53
|
+
note = `爽点类型:${gp.satisfactionTypes.join("、")}`;
|
|
54
|
+
}
|
|
55
|
+
if (id === 12 && bookRules?.eraConstraints) {
|
|
56
|
+
const era = bookRules.eraConstraints;
|
|
57
|
+
const parts = [era.period, era.region].filter(Boolean);
|
|
58
|
+
if (parts.length > 0)
|
|
59
|
+
note = `年代:${parts.join(",")}`;
|
|
60
|
+
}
|
|
61
|
+
if (id === 19) {
|
|
62
|
+
note = "检查视角切换是否有过渡、是否与设定视角一致";
|
|
63
|
+
}
|
|
64
|
+
dims.push({ id, name, note });
|
|
65
|
+
}
|
|
66
|
+
return dims;
|
|
67
|
+
}
|
|
4
68
|
export class ContinuityAuditor extends BaseAgent {
|
|
5
69
|
get name() {
|
|
6
70
|
return "continuity-auditor";
|
|
7
71
|
}
|
|
8
|
-
async auditChapter(bookDir, chapterContent, chapterNumber) {
|
|
9
|
-
const [currentState, ledger, hooks,
|
|
72
|
+
async auditChapter(bookDir, chapterContent, chapterNumber, genre) {
|
|
73
|
+
const [currentState, ledger, hooks, styleGuideRaw] = await Promise.all([
|
|
10
74
|
this.readFileSafe(join(bookDir, "story/current_state.md")),
|
|
11
75
|
this.readFileSafe(join(bookDir, "story/particle_ledger.md")),
|
|
12
76
|
this.readFileSafe(join(bookDir, "story/pending_hooks.md")),
|
|
13
77
|
this.readFileSafe(join(bookDir, "story/style_guide.md")),
|
|
14
78
|
]);
|
|
15
|
-
|
|
79
|
+
// Load genre profile and book rules
|
|
80
|
+
const genreId = genre ?? "other";
|
|
81
|
+
const { profile: gp } = await readGenreProfile(this.ctx.projectRoot, genreId);
|
|
82
|
+
const parsedRules = await readBookRules(bookDir);
|
|
83
|
+
const bookRules = parsedRules?.rules ?? null;
|
|
84
|
+
// Fallback: use book_rules body when style_guide.md doesn't exist
|
|
85
|
+
const styleGuide = styleGuideRaw !== "(文件不存在)"
|
|
86
|
+
? styleGuideRaw
|
|
87
|
+
: (parsedRules?.body ?? "(无文风指南)");
|
|
88
|
+
const dimensions = buildDimensionList(gp, bookRules);
|
|
89
|
+
const dimList = dimensions
|
|
90
|
+
.map((d) => `${d.id}. ${d.name}${d.note ? `(${d.note})` : ""}`)
|
|
91
|
+
.join("\n");
|
|
92
|
+
const protagonistBlock = bookRules?.protagonist
|
|
93
|
+
? `\n主角人设锁定:${bookRules.protagonist.name},${bookRules.protagonist.personalityLock.join("、")},行为约束:${bookRules.protagonist.behavioralConstraints.join("、")}`
|
|
94
|
+
: "";
|
|
95
|
+
const systemPrompt = `你是一位严格的${gp.name}网络小说审稿编辑。你的任务是对章节进行连续性、一致性和质量审查。${protagonistBlock}
|
|
16
96
|
|
|
17
97
|
审查维度:
|
|
18
|
-
|
|
19
|
-
2. 时间线检查:时间/地点是否连贯
|
|
20
|
-
3. 设定冲突:是否违反已确立的世界观规则
|
|
21
|
-
4. 战力崩坏:是否出现不合理的实力变化
|
|
22
|
-
5. 数值检查:资源/数值变动是否与账本一致
|
|
23
|
-
6. 伏笔检查:是否有遗漏或矛盾的伏笔
|
|
24
|
-
7. 节奏检查:是否拖沓或跳跃
|
|
25
|
-
8. 文风检查:是否偏离风格指南
|
|
26
|
-
9. 信息越界:角色是否知道了不该知道的信息
|
|
27
|
-
10. 词汇疲劳:是否有过度重复的表达
|
|
98
|
+
${dimList}
|
|
28
99
|
|
|
29
100
|
输出格式必须为 JSON:
|
|
30
101
|
{
|
|
@@ -41,14 +112,14 @@ export class ContinuityAuditor extends BaseAgent {
|
|
|
41
112
|
}
|
|
42
113
|
|
|
43
114
|
只有当存在 critical 级别问题时,passed 才为 false。`;
|
|
115
|
+
const ledgerBlock = gp.numericalSystem
|
|
116
|
+
? `\n## 资源账本\n${ledger}`
|
|
117
|
+
: "";
|
|
44
118
|
const userPrompt = `请审查第${chapterNumber}章。
|
|
45
119
|
|
|
46
120
|
## 当前状态卡
|
|
47
121
|
${currentState}
|
|
48
|
-
|
|
49
|
-
## 资源账本
|
|
50
|
-
${ledger}
|
|
51
|
-
|
|
122
|
+
${ledgerBlock}
|
|
52
123
|
## 伏笔池
|
|
53
124
|
${hooks}
|
|
54
125
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"continuity.js","sourceRoot":"","sources":["../../src/agents/continuity.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;
|
|
1
|
+
{"version":3,"file":"continuity.js","sourceRoot":"","sources":["../../src/agents/continuity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
export interface RankingEntry {
|
|
2
|
+
readonly title: string;
|
|
3
|
+
readonly author: string;
|
|
4
|
+
readonly category: string;
|
|
5
|
+
readonly extra: string;
|
|
6
|
+
}
|
|
7
|
+
export interface PlatformRankings {
|
|
8
|
+
readonly platform: string;
|
|
9
|
+
readonly entries: ReadonlyArray<RankingEntry>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Pluggable data source for the Radar agent.
|
|
13
|
+
* Implement this interface to feed custom ranking/trend data
|
|
14
|
+
* (e.g. from OpenClaw, custom scrapers, paid APIs).
|
|
15
|
+
*/
|
|
16
|
+
export interface RadarSource {
|
|
17
|
+
readonly name: string;
|
|
18
|
+
fetch(): Promise<PlatformRankings>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wraps raw natural language text as a radar source.
|
|
22
|
+
* Use this to inject external analysis (e.g. from OpenClaw) into the radar pipeline.
|
|
23
|
+
*/
|
|
24
|
+
export declare class TextRadarSource implements RadarSource {
|
|
25
|
+
readonly name: string;
|
|
26
|
+
private readonly text;
|
|
27
|
+
constructor(text: string, name?: string);
|
|
28
|
+
fetch(): Promise<PlatformRankings>;
|
|
29
|
+
}
|
|
30
|
+
export declare class FanqieRadarSource implements RadarSource {
|
|
31
|
+
readonly name = "fanqie";
|
|
32
|
+
fetch(): Promise<PlatformRankings>;
|
|
33
|
+
}
|
|
34
|
+
export declare class QidianRadarSource implements RadarSource {
|
|
35
|
+
readonly name = "qidian";
|
|
36
|
+
fetch(): Promise<PlatformRankings>;
|
|
37
|
+
}
|
|
38
|
+
//# sourceMappingURL=radar-source.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radar-source.d.ts","sourceRoot":"","sources":["../../src/agents/radar-source.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAC1B,QAAQ,CAAC,OAAO,EAAE,aAAa,CAAC,YAAY,CAAC,CAAC;CAC/C;AAED;;;;GAIG;AACH,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC,CAAC;CACpC;AAED;;;GAGG;AACH,qBAAa,eAAgB,YAAW,WAAW;IACjD,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAS;gBAElB,IAAI,EAAE,MAAM,EAAE,IAAI,SAAa;IAKrC,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAMzC;AAWD,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,IAAI,YAAY;IAEnB,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC;CA8BzC;AAED,qBAAa,iBAAkB,YAAW,WAAW;IACnD,QAAQ,CAAC,IAAI,YAAY;IAEnB,KAAK,IAAI,OAAO,CAAC,gBAAgB,CAAC;CAgCzC"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Wraps raw natural language text as a radar source.
|
|
3
|
+
* Use this to inject external analysis (e.g. from OpenClaw) into the radar pipeline.
|
|
4
|
+
*/
|
|
5
|
+
export class TextRadarSource {
|
|
6
|
+
name;
|
|
7
|
+
text;
|
|
8
|
+
constructor(text, name = "external") {
|
|
9
|
+
this.name = name;
|
|
10
|
+
this.text = text;
|
|
11
|
+
}
|
|
12
|
+
async fetch() {
|
|
13
|
+
return {
|
|
14
|
+
platform: this.name,
|
|
15
|
+
entries: [{ title: this.text, author: "", category: "", extra: "[外部分析]" }],
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
}
|
|
19
|
+
// ---------------------------------------------------------------------------
|
|
20
|
+
// Built-in sources
|
|
21
|
+
// ---------------------------------------------------------------------------
|
|
22
|
+
const FANQIE_RANK_TYPES = [
|
|
23
|
+
{ sideType: 10, label: "热门榜" },
|
|
24
|
+
{ sideType: 13, label: "黑马榜" },
|
|
25
|
+
];
|
|
26
|
+
export class FanqieRadarSource {
|
|
27
|
+
name = "fanqie";
|
|
28
|
+
async fetch() {
|
|
29
|
+
const entries = [];
|
|
30
|
+
for (const { sideType, label } of FANQIE_RANK_TYPES) {
|
|
31
|
+
try {
|
|
32
|
+
const url = `https://api-lf.fanqiesdk.com/api/novel/channel/homepage/rank/rank_list/v2/?aid=13&limit=15&offset=0&side_type=${sideType}`;
|
|
33
|
+
const res = await globalThis.fetch(url, {
|
|
34
|
+
headers: { "User-Agent": "Mozilla/5.0 (compatible; InkOS/0.1)" },
|
|
35
|
+
});
|
|
36
|
+
if (!res.ok)
|
|
37
|
+
continue;
|
|
38
|
+
const data = (await res.json());
|
|
39
|
+
const list = data.data?.result;
|
|
40
|
+
if (!Array.isArray(list))
|
|
41
|
+
continue;
|
|
42
|
+
for (const item of list) {
|
|
43
|
+
const rec = item;
|
|
44
|
+
entries.push({
|
|
45
|
+
title: String(rec.book_name ?? ""),
|
|
46
|
+
author: String(rec.author ?? ""),
|
|
47
|
+
category: String(rec.category ?? ""),
|
|
48
|
+
extra: `[${label}]`,
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
// skip on network error
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
return { platform: "番茄小说", entries };
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
export class QidianRadarSource {
|
|
60
|
+
name = "qidian";
|
|
61
|
+
async fetch() {
|
|
62
|
+
const entries = [];
|
|
63
|
+
try {
|
|
64
|
+
const url = "https://www.qidian.com/rank/";
|
|
65
|
+
const res = await globalThis.fetch(url, {
|
|
66
|
+
headers: {
|
|
67
|
+
"User-Agent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/120.0.0.0 Safari/537.36",
|
|
68
|
+
},
|
|
69
|
+
});
|
|
70
|
+
if (!res.ok)
|
|
71
|
+
return { platform: "起点中文网", entries };
|
|
72
|
+
const html = await res.text();
|
|
73
|
+
const bookPattern = /<a[^>]*href="\/\/book\.qidian\.com\/info\/(\d+)"[^>]*>([^<]+)<\/a>/g;
|
|
74
|
+
let match;
|
|
75
|
+
const seen = new Set();
|
|
76
|
+
while ((match = bookPattern.exec(html)) !== null) {
|
|
77
|
+
const title = match[2].trim();
|
|
78
|
+
if (title && !seen.has(title) && title.length > 1 && title.length < 30) {
|
|
79
|
+
seen.add(title);
|
|
80
|
+
entries.push({ title, author: "", category: "", extra: "[起点热榜]" });
|
|
81
|
+
}
|
|
82
|
+
if (entries.length >= 20)
|
|
83
|
+
break;
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
catch {
|
|
87
|
+
// skip on network error
|
|
88
|
+
}
|
|
89
|
+
return { platform: "起点中文网", entries };
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
//# sourceMappingURL=radar-source.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"radar-source.js","sourceRoot":"","sources":["../../src/agents/radar-source.ts"],"names":[],"mappings":"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"}
|
package/dist/agents/radar.d.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { BaseAgent } from "./base.js";
|
|
2
2
|
import type { Platform, Genre } from "../models/book.js";
|
|
3
|
+
import type { RadarSource } from "./radar-source.js";
|
|
3
4
|
export interface RadarResult {
|
|
4
5
|
readonly recommendations: ReadonlyArray<RadarRecommendation>;
|
|
5
6
|
readonly marketSummary: string;
|
|
@@ -14,8 +15,10 @@ export interface RadarRecommendation {
|
|
|
14
15
|
readonly benchmarkTitles: ReadonlyArray<string>;
|
|
15
16
|
}
|
|
16
17
|
export declare class RadarAgent extends BaseAgent {
|
|
18
|
+
private readonly sources;
|
|
19
|
+
constructor(ctx: ConstructorParameters<typeof BaseAgent>[0], sources?: ReadonlyArray<RadarSource>);
|
|
17
20
|
get name(): string;
|
|
18
|
-
scan(
|
|
21
|
+
scan(): Promise<RadarResult>;
|
|
19
22
|
private parseResult;
|
|
20
23
|
}
|
|
21
24
|
//# sourceMappingURL=radar.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"radar.d.ts","sourceRoot":"","sources":["../../src/agents/radar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"radar.d.ts","sourceRoot":"","sources":["../../src/agents/radar.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,WAAW,CAAC;AACtC,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,mBAAmB,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAoB,MAAM,mBAAmB,CAAC;AAGvE,MAAM,WAAW,WAAW;IAC1B,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,mBAAmB,CAAC,CAAC;IAC7D,QAAQ,CAAC,aAAa,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC;IAC5B,QAAQ,CAAC,KAAK,EAAE,KAAK,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAC5B,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,eAAe,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;CACjD;AAsBD,qBAAa,UAAW,SAAQ,SAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAA6B;gBAGnD,GAAG,EAAE,qBAAqB,CAAC,OAAO,SAAS,CAAC,CAAC,CAAC,CAAC,EAC/C,OAAO,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC;IAMtC,IAAI,IAAI,IAAI,MAAM,CAEjB;IAEK,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IA+ClC,OAAO,CAAC,WAAW;CAiBpB"}
|
package/dist/agents/radar.js
CHANGED
|
@@ -1,18 +1,43 @@
|
|
|
1
1
|
import { BaseAgent } from "./base.js";
|
|
2
|
+
import { FanqieRadarSource, QidianRadarSource } from "./radar-source.js";
|
|
3
|
+
const DEFAULT_SOURCES = [
|
|
4
|
+
new FanqieRadarSource(),
|
|
5
|
+
new QidianRadarSource(),
|
|
6
|
+
];
|
|
7
|
+
function formatRankingsForPrompt(rankings) {
|
|
8
|
+
const sections = rankings
|
|
9
|
+
.filter((r) => r.entries.length > 0)
|
|
10
|
+
.map((r) => {
|
|
11
|
+
const lines = r.entries.map((e) => `- ${e.title}${e.author ? ` (${e.author})` : ""}${e.category ? ` [${e.category}]` : ""} ${e.extra}`);
|
|
12
|
+
return `### ${r.platform}\n${lines.join("\n")}`;
|
|
13
|
+
});
|
|
14
|
+
return sections.length > 0
|
|
15
|
+
? sections.join("\n\n")
|
|
16
|
+
: "(未能获取到实时排行数据,请基于你的知识分析)";
|
|
17
|
+
}
|
|
2
18
|
export class RadarAgent extends BaseAgent {
|
|
19
|
+
sources;
|
|
20
|
+
constructor(ctx, sources) {
|
|
21
|
+
super(ctx);
|
|
22
|
+
this.sources = sources ?? DEFAULT_SOURCES;
|
|
23
|
+
}
|
|
3
24
|
get name() {
|
|
4
25
|
return "radar";
|
|
5
26
|
}
|
|
6
|
-
async scan(
|
|
7
|
-
const
|
|
27
|
+
async scan() {
|
|
28
|
+
const rankings = await Promise.all(this.sources.map((s) => s.fetch()));
|
|
29
|
+
const rankingsText = formatRankingsForPrompt(rankings);
|
|
30
|
+
const systemPrompt = `你是一个专业的网络小说市场分析师。下面是从各平台实时抓取的排行榜数据,请基于这些真实数据分析市场趋势。
|
|
31
|
+
|
|
32
|
+
## 实时排行榜数据
|
|
8
33
|
|
|
9
|
-
|
|
34
|
+
${rankingsText}
|
|
10
35
|
|
|
11
36
|
分析维度:
|
|
12
|
-
1.
|
|
13
|
-
2.
|
|
14
|
-
3.
|
|
15
|
-
4.
|
|
37
|
+
1. 从排行榜数据中识别当前热门题材和标签
|
|
38
|
+
2. 分析哪些类型的作品占据榜单高位
|
|
39
|
+
3. 发现市场空白和机会点(榜单上缺少但有潜力的方向)
|
|
40
|
+
4. 风险提示(榜单上过度扎堆的题材)
|
|
16
41
|
|
|
17
42
|
输出格式必须为 JSON:
|
|
18
43
|
{
|
|
@@ -22,11 +47,11 @@ export class RadarAgent extends BaseAgent {
|
|
|
22
47
|
"genre": "题材类型",
|
|
23
48
|
"concept": "一句话概念描述",
|
|
24
49
|
"confidence": 0.0-1.0,
|
|
25
|
-
"reasoning": "
|
|
50
|
+
"reasoning": "推荐理由(引用具体榜单数据)",
|
|
26
51
|
"benchmarkTitles": ["对标书1", "对标书2"]
|
|
27
52
|
}
|
|
28
53
|
],
|
|
29
|
-
"marketSummary": "
|
|
54
|
+
"marketSummary": "整体市场概述(基于真实榜单数据)"
|
|
30
55
|
}
|
|
31
56
|
|
|
32
57
|
推荐数量:3-5个,按 confidence 降序排列。`;
|
|
@@ -34,7 +59,7 @@ export class RadarAgent extends BaseAgent {
|
|
|
34
59
|
{ role: "system", content: systemPrompt },
|
|
35
60
|
{
|
|
36
61
|
role: "user",
|
|
37
|
-
content:
|
|
62
|
+
content: `请基于上面的实时排行榜数据,分析当前网文市场热度,给出开书建议。`,
|
|
38
63
|
},
|
|
39
64
|
], { temperature: 0.6, maxTokens: 4096 });
|
|
40
65
|
return this.parseResult(response.content);
|