mirror of
https://github.com/Narcooo/inkos
synced 2026-04-21 14:37:16 +00:00
refactor(core): drop revise_chapter agent tool, route through sub_agent reviser
The revise_chapter tool and sub_agent(agent="reviser") both wrapped the same pipeline.reviseDraft() call with an almost-identical parameter set. Listing both in the system prompt made the model oscillate between them. Removes createReviseChapterTool, its export, registration, and tests. Updates the system prompt and edit/write tool descriptions to point at sub_agent(reviser) for whole-chapter rewrites. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
52250539db
commit
a57f9db0d5
6 changed files with 35 additions and 73 deletions
|
|
@ -55,7 +55,6 @@ describe("buildAgentSystemPrompt", () => {
|
|||
expect(prompt).toContain("mode");
|
||||
expect(prompt).toContain("approvedOnly");
|
||||
expect(prompt).toContain("read");
|
||||
expect(prompt).toContain("revise_chapter");
|
||||
expect(prompt).toContain("write_truth_file");
|
||||
expect(prompt).toContain("rename_entity");
|
||||
expect(prompt).toContain("patch_chapter_text");
|
||||
|
|
@ -65,12 +64,22 @@ describe("buildAgentSystemPrompt", () => {
|
|||
expect(prompt).toContain("ls");
|
||||
});
|
||||
|
||||
it("with-book prompt steers high-risk edits to dedicated deterministic tools", () => {
|
||||
it("Chinese prompt does NOT mention revise_chapter (merged into sub_agent reviser)", () => {
|
||||
const prompt = buildAgentSystemPrompt("my-book", "zh");
|
||||
expect(prompt).not.toContain("revise_chapter");
|
||||
});
|
||||
|
||||
it("English prompt does NOT mention revise_chapter (merged into sub_agent reviser)", () => {
|
||||
const prompt = buildAgentSystemPrompt("novel", "en");
|
||||
expect(prompt).not.toContain("revise_chapter");
|
||||
});
|
||||
|
||||
it("with-book prompt steers chapter rewrites to sub_agent reviser, not a separate tool", () => {
|
||||
const prompt = buildAgentSystemPrompt("my-book", "zh");
|
||||
expect(prompt).toContain("改设定/改真相文件");
|
||||
expect(prompt).toContain("write_truth_file");
|
||||
expect(prompt).toContain("用户要求重写/精修已有章节");
|
||||
expect(prompt).toContain("revise_chapter");
|
||||
expect(prompt).toContain("reviser");
|
||||
expect(prompt).toContain("也可以直接使用 edit / write");
|
||||
});
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,6 @@ import {
|
|||
createSubAgentTool,
|
||||
createPatchChapterTextTool,
|
||||
createRenameEntityTool,
|
||||
createReviseChapterTool,
|
||||
createWriteFileTool,
|
||||
createWriteTruthFileTool,
|
||||
} from "../agent/agent-tools.js";
|
||||
|
|
@ -65,27 +64,6 @@ describe("agent deterministic writing tools", () => {
|
|||
.resolves.toContain("distrusts the guild");
|
||||
});
|
||||
|
||||
it("routes chapter rewrite requests through reviseDraft with the requested mode", async () => {
|
||||
const pipeline = {
|
||||
reviseDraft: vi.fn(async () => ({
|
||||
chapterNumber: 3,
|
||||
wordCount: 1300,
|
||||
fixedIssues: [],
|
||||
applied: true,
|
||||
status: "ready-for-review" as const,
|
||||
})),
|
||||
};
|
||||
const tool = createReviseChapterTool(pipeline as never, "harbor");
|
||||
|
||||
const result = await tool.execute("tool-2", {
|
||||
chapterNumber: 3,
|
||||
mode: "rewrite",
|
||||
});
|
||||
|
||||
expect(pipeline.reviseDraft).toHaveBeenCalledWith("harbor", 3, "rewrite");
|
||||
expect(result.content[0]?.type).toBe("text");
|
||||
});
|
||||
|
||||
it("renames entities through the deterministic edit controller", async () => {
|
||||
const tool = createRenameEntityTool({} as never, root, "harbor");
|
||||
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import {
|
|||
createPatchChapterTextTool,
|
||||
createRenameEntityTool,
|
||||
createSubAgentTool,
|
||||
createReviseChapterTool,
|
||||
createReadTool,
|
||||
createEditTool,
|
||||
createWriteFileTool,
|
||||
|
|
@ -237,7 +236,6 @@ export async function runAgentSession(
|
|||
tools: [
|
||||
createSubAgentTool(pipeline, bookId, projectRoot),
|
||||
createReadTool(projectRoot),
|
||||
createReviseChapterTool(pipeline, bookId),
|
||||
createWriteTruthFileTool(pipeline, projectRoot, bookId),
|
||||
createRenameEntityTool(pipeline, projectRoot, bookId),
|
||||
createPatchChapterTextTool(pipeline, projectRoot, bookId),
|
||||
|
|
|
|||
|
|
@ -73,17 +73,20 @@ export function buildAgentSystemPrompt(bookId: string | null, language: string):
|
|||
## 可用工具
|
||||
|
||||
- **sub_agent** — 委托子智能体执行重操作:
|
||||
- agent="writer" 写下一章(参数:chapterWordCount 覆盖字数)
|
||||
- agent="auditor" 审计章节质量(参数:chapterNumber 指定章节)
|
||||
- agent="reviser" 修订章节(参数:chapterNumber, mode: spot-fix/polish/rewrite/rework/anti-detect)
|
||||
- agent="writer" **续写下一章**(接着已写的最后一章往下写,无法指定章节号。参数:chapterWordCount)
|
||||
- agent="auditor" 审计**已有章节**(参数:chapterNumber 指定第几章,不传则审最新一章)
|
||||
- agent="reviser" 修改**已有章节**(**必须传 chapterNumber 指明改第几章**。参数:chapterNumber, mode: spot-fix/polish/rewrite/rework/anti-detect)
|
||||
- agent="exporter" 导出书籍(参数:format: txt/md/epub, approvedOnly: true/false)
|
||||
- **writer vs reviser 选择规则**(极易出错,看清楚):
|
||||
- 用户说"改/修订/重写第 N 章"、"第 N 章 xxx 写得不好" → **reviser** + chapterNumber=N(绝不能用 writer,writer 会写新的第 N+1 章)
|
||||
- 用户说"写下一章"、"继续写"、"再来一章" → **writer**(不要用 reviser,更不要不带 chapterNumber 调 reviser)
|
||||
- 用户没说章节号、只说"改一下刚才那章" → **reviser** + chapterNumber=最新已写章节号
|
||||
- **read** — 读取书籍的设定文件或章节内容
|
||||
- **revise_chapter** — 对已有章节做精修/重写/返工
|
||||
- **write_truth_file** — 整文件覆盖真相文件(story_bible、volume_outline、book_rules、current_focus 等)
|
||||
- **rename_entity** — 统一改角色/实体名
|
||||
- **patch_chapter_text** — 对已有章节做局部定点修补
|
||||
- **edit** — 在设定文件里做精确字符串替换(章节正文请用 patch_chapter_text)
|
||||
- **write** — 新建文件,或者重写整个文件(已有内容会被覆盖;真相文件优先用 write_truth_file,章节修订用 revise_chapter)
|
||||
- **write** — 新建文件,或者重写整个文件(已有内容会被覆盖;真相文件优先用 write_truth_file,整章精修/重写请用 sub_agent 的 reviser)
|
||||
- **grep** — 搜索内容(如"哪一章提到了某个角色")
|
||||
- **ls** — 列出文件或章节
|
||||
|
||||
|
|
@ -92,7 +95,7 @@ export function buildAgentSystemPrompt(bookId: string | null, language: string):
|
|||
- 写章节、修订、审计等重操作 → 使用 sub_agent 委托对应子智能体
|
||||
- 用户问设定相关问题 → 先用 read 读取对应文件再回答
|
||||
- 用户想改设定/改真相文件 → 优先用 write_truth_file
|
||||
- 用户要求重写/精修已有章节 → 用 revise_chapter
|
||||
- 用户要求重写/精修已有章节 → sub_agent(agent="reviser", chapterNumber=N, mode=...)
|
||||
- 用户要求角色或实体改名 → 用 rename_entity
|
||||
- 用户要求对某一章做局部小修 → 用 patch_chapter_text
|
||||
- 当你已经明确目标文件和内容时,也可以直接使用 edit / write
|
||||
|
|
@ -122,17 +125,20 @@ export function buildAgentSystemPrompt(bookId: string | null, language: string):
|
|||
## Available Tools
|
||||
|
||||
- **sub_agent** — Delegate to sub-agents:
|
||||
- agent="writer" write next chapter (params: chapterWordCount for word count override)
|
||||
- agent="auditor" audit chapter quality (params: chapterNumber to target specific chapter)
|
||||
- agent="reviser" revise chapter (params: chapterNumber, mode: spot-fix/polish/rewrite/rework/anti-detect)
|
||||
- agent="writer" **continue writing the NEXT chapter** (always appends after the latest written chapter; cannot target a specific number. params: chapterWordCount)
|
||||
- agent="auditor" audit an **EXISTING chapter** (params: chapterNumber to target a specific chapter; omit for the latest)
|
||||
- agent="reviser" modify an **EXISTING chapter** (**chapterNumber is required to identify which chapter**. params: chapterNumber, mode: spot-fix/polish/rewrite/rework/anti-detect)
|
||||
- agent="exporter" export book (params: format: txt/md/epub, approvedOnly: true/false)
|
||||
- **writer vs reviser — common mistake, read carefully**:
|
||||
- User says "revise/rewrite/fix chapter N" or "chapter N has issues" → **reviser** with chapterNumber=N (never writer — writer would produce a new chapter N+1)
|
||||
- User says "write the next chapter" / "continue" / "one more chapter" → **writer** (never reviser, and never call reviser without chapterNumber)
|
||||
- User refers to "that chapter we just did" without a number → **reviser** with chapterNumber=latest-written
|
||||
- **read** — Read truth files or chapter content
|
||||
- **revise_chapter** — Rewrite or polish an existing chapter
|
||||
- **write_truth_file** — Replace a canonical truth file in story/
|
||||
- **rename_entity** — Rename a character or entity across the book
|
||||
- **patch_chapter_text** — Apply a local deterministic patch to a chapter
|
||||
- **edit** — Exact string replacement on setting files (use patch_chapter_text for chapter text)
|
||||
- **write** — Create a new file, or fully replace an existing file's content (prefer write_truth_file for canonical truth files, revise_chapter for chapter revisions)
|
||||
- **write** — Create a new file, or fully replace an existing file's content (prefer write_truth_file for canonical truth files; for whole-chapter rewrites call sub_agent with agent="reviser")
|
||||
- **grep** — Search content across chapters
|
||||
- **ls** — List files or chapters
|
||||
|
||||
|
|
@ -141,7 +147,7 @@ export function buildAgentSystemPrompt(bookId: string | null, language: string):
|
|||
- Use sub_agent for heavy operations (writing, revision, auditing)
|
||||
- Use read first for settings inquiries
|
||||
- Use write_truth_file for truth files and setting changes
|
||||
- Use revise_chapter for rewrite/polish/rework of existing chapters
|
||||
- For rewrite/polish/rework of an existing chapter → sub_agent(agent="reviser", chapterNumber=N, mode=...)
|
||||
- Use rename_entity for character/entity renames
|
||||
- Use patch_chapter_text for local chapter fixes
|
||||
- Use edit / write directly when you already know the exact target file and replacement content
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import { Type, type Static } from "@mariozechner/pi-ai";
|
||||
import type { AgentTool, AgentToolResult, AgentToolUpdateCallback } from "@mariozechner/pi-agent-core";
|
||||
import type { PipelineRunner } from "../pipeline/runner.js";
|
||||
import { DEFAULT_REVISE_MODE, type ReviseMode } from "../agents/reviser.js";
|
||||
import { type ReviseMode } from "../agents/reviser.js";
|
||||
import { readFile, writeFile, readdir, stat } from "node:fs/promises";
|
||||
import { join, normalize, resolve } from "node:path";
|
||||
import { StateManager } from "../state/manager.js";
|
||||
|
|
@ -226,36 +226,6 @@ export function createSubAgentTool(
|
|||
// 2. Deterministic writing tools
|
||||
// ---------------------------------------------------------------------------
|
||||
|
||||
const ReviseChapterParams = Type.Object({
|
||||
bookId: Type.Optional(Type.String({ description: "Book ID. Omit to use the active book." })),
|
||||
chapterNumber: Type.Number({ description: "Chapter number to revise." }),
|
||||
mode: Type.Optional(Type.Union([
|
||||
Type.Literal("spot-fix"),
|
||||
Type.Literal("polish"),
|
||||
Type.Literal("rewrite"),
|
||||
Type.Literal("rework"),
|
||||
Type.Literal("anti-detect"),
|
||||
])),
|
||||
});
|
||||
|
||||
export function createReviseChapterTool(
|
||||
pipeline: PipelineRunner,
|
||||
activeBookId: string | null,
|
||||
): AgentTool<typeof ReviseChapterParams> {
|
||||
return {
|
||||
name: "revise_chapter",
|
||||
description: "Revise a specific chapter through the deterministic revision pipeline.",
|
||||
label: "Revise Chapter",
|
||||
parameters: ReviseChapterParams,
|
||||
async execute(_toolCallId, params): Promise<AgentToolResult<undefined>> {
|
||||
const bookId = resolveToolBookId("revise_chapter", params.bookId, activeBookId);
|
||||
const mode = params.mode ?? DEFAULT_REVISE_MODE;
|
||||
await pipeline.reviseDraft(bookId, params.chapterNumber, mode);
|
||||
return textResult(`Revision (${mode}) complete for "${bookId}" chapter ${params.chapterNumber}.`);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const WriteTruthFileParams = Type.Object({
|
||||
bookId: Type.Optional(Type.String({ description: "Book ID. Omit to use the active book." })),
|
||||
fileName: Type.String({ description: "Truth file name under story/, e.g. story_bible.md or current_focus.md." }),
|
||||
|
|
@ -395,7 +365,8 @@ export function createEditTool(projectRoot: string): AgentTool<typeof EditParams
|
|||
description:
|
||||
"Edit a file under books/ via exact string replacement. " +
|
||||
"old_string must appear exactly once in the file. " +
|
||||
"For chapter text use patch_chapter_text; for canonical truth files (story_bible/volume_outline/book_rules/current_focus) prefer write_truth_file.",
|
||||
"For chapter text use patch_chapter_text; for canonical truth files (story_bible/volume_outline/book_rules/current_focus) prefer write_truth_file; " +
|
||||
"to rewrite or polish a whole chapter call sub_agent with agent=\"reviser\".",
|
||||
label: "Edit File",
|
||||
parameters: EditParams,
|
||||
async execute(
|
||||
|
|
@ -439,7 +410,8 @@ export function createWriteFileTool(projectRoot: string): AgentTool<typeof Write
|
|||
description:
|
||||
"Create a new file, or fully replace an existing file's content under books/. " +
|
||||
"Parent directories are created automatically. Existing content is overwritten silently — " +
|
||||
"for canonical truth files prefer write_truth_file; for chapter revisions use revise_chapter.",
|
||||
"for canonical truth files prefer write_truth_file; " +
|
||||
"for whole-chapter rewrites/polishing call sub_agent with agent=\"reviser\".",
|
||||
label: "Write File",
|
||||
parameters: WriteFileParams,
|
||||
async execute(
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@ export { buildAgentSystemPrompt } from "./agent-system-prompt.js";
|
|||
export {
|
||||
createSubAgentTool,
|
||||
createReadTool,
|
||||
createReviseChapterTool,
|
||||
createWriteTruthFileTool,
|
||||
createRenameEntityTool,
|
||||
createPatchChapterTextTool,
|
||||
|
|
|
|||
Loading…
Reference in a new issue