Преглед изворни кода

支持省略“第”的章节写作意图识别

darknessomi пре 1 месец
родитељ
комит
50a150c8b2
2 измењених фајлова са 33 додато и 9 уклоњено
  1. 19 0
      src/lib/novel/task-router.spec.ts
  2. 14 9
      src/lib/novel/task-router.ts

+ 19 - 0
src/lib/novel/task-router.spec.ts

@@ -61,6 +61,25 @@ describe("routeTask chapter generation", () => {
     }
   })
 
+  it("routes chapter write requests without 第 with high confidence", () => {
+    for (const [text, chapterNumber] of [["编写 11 章", 11], ["编写 229 章", 229], ["编写第 5 章", 5]] as const) {
+      const route = routeTask(text)
+
+      expect(route.intent).toBe("write_chapter")
+      expect(route.chapterNumber).toBe(chapterNumber)
+      expect(route.confidence).toBeGreaterThanOrEqual(0.5)
+    }
+  })
+
+  it("does not treat small bare numerals as chapter sequence numbers", () => {
+    for (const text of ["写三章", "编写 5 章", "编写 10 章"]) {
+      const route = routeTask(text)
+
+      expect(route.intent).toBe("general_chat")
+      expect(route.chapterNumber).toBeUndefined()
+    }
+  })
+
   it("keeps Chinese later chapter numbers even when the prompt mentions 开篇 hooks", () => {
     const route = routeTask("写第五章,开篇要有钩子")
 

+ 14 - 9
src/lib/novel/task-router.ts

@@ -50,14 +50,17 @@ interface IntentPattern {
 
 /** 章号:阿拉伯数字或中文数字(与 parseChineseChapterNumber 字符集对齐) */
 const CHAPTER_NUM_TOKEN = String.raw`(?:\d+|[一二三四五六七八九十百〇零两]+)`
-const CHAPTER_REF = String.raw`第\s*${CHAPTER_NUM_TOKEN}\s*章`
+// 无“第”格式仅接受大于 10 的阿拉伯数字,避免把“写 5 章”(数量)
+// 误判为“写第 5 章”(序号)。11 及以上更符合省略“第”的章节号表达。
+const BARE_CHAPTER_NUM_TOKEN = String.raw`(?:1[1-9]|[2-9]\d|[1-9]\d{2,})`
+const CHAPTER_REF = String.raw`(?:第\s*${CHAPTER_NUM_TOKEN}|${BARE_CHAPTER_NUM_TOKEN})\s*章`
 
 const INTENT_PATTERNS: IntentPattern[] = [
   {
     intent: "write_chapter",
     patterns: [
-      new RegExp(String.raw`^(写|生成|创作|撰写)\s*(${CHAPTER_REF}|新章节|下一章)`),
-      new RegExp(String.raw`^(开始|帮我)(写|创作|生成)\s*(${CHAPTER_REF}|章节)`),
+      new RegExp(String.raw`^(写|编写|生成|创作|撰写)\s*(${CHAPTER_REF}|新章节|下一章)`),
+      new RegExp(String.raw`^(开始|帮我)(写|编写|创作|生成)\s*(${CHAPTER_REF}|章节)`),
       new RegExp(String.raw`生成\s*(${CHAPTER_REF}|新的?一?章)`),
       new RegExp(String.raw`写\s*${CHAPTER_REF}`),
       new RegExp(String.raw`(根据|按照).*(${CHAPTER_REF}).*(章纲|大纲|细纲).*(生成|写|创作|撰写).*(正文|章节)?`),
@@ -221,6 +224,7 @@ const INTENT_PATTERNS: IntentPattern[] = [
 const CHAPTER_NUMBER_PATTERNS = [
   /第\s*(\d+)\s*章/,
   /第\s*([一二三四五六七八九十百〇零两]+)\s*章/,
+  new RegExp(String.raw`(${BARE_CHAPTER_NUM_TOKEN})\s*章`),
   /chapter\s*(\d+)/i,
   /ch\.?\s*(\d+)/i,
 ]
@@ -325,15 +329,16 @@ function hasNextChapterContinuationWording(text: string): boolean {
   return NEXT_CHAPTER_CONTINUATION_RE.test(text.replace(/\s+/g, ""))
 }
 
-// 全文出现明确的“第N章”(N>3)时,说明用户的目标章节并非开篇章节,
+// 全文出现明确的“第N章”或“N章”(N>3)时,说明用户的目标章节并非开篇章节,
 // 文本里顺带提到的“开篇/第一章”只是写作要求或剧情引用。
 function hasExplicitLaterChapterNumber(text: string): boolean {
   const compact = text.replace(/\s+/g, "")
-  const explicit = compact.match(/第(\d+|[一二三四五六七八九十百〇零两]+)章/)
-  if (!explicit?.[1]) return false
-  const num = /^\d+$/.test(explicit[1])
-    ? Number(explicit[1])
-    : parseChineseChapterNumber(explicit[1])
+  const explicit = compact.match(new RegExp(String.raw`(?:第(${CHAPTER_NUM_TOKEN})|(${BARE_CHAPTER_NUM_TOKEN}))章`))
+  const token = explicit?.[1] ?? explicit?.[2]
+  if (!token) return false
+  const num = /^\d+$/.test(token)
+    ? Number(token)
+    : parseChineseChapterNumber(token)
   return Number.isFinite(num) && num > 3
 }