inkos-n-core 1.2.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/dist/agents/ai-tells.d.ts +26 -0
- package/dist/agents/ai-tells.d.ts.map +1 -0
- package/dist/agents/ai-tells.js +140 -0
- package/dist/agents/ai-tells.js.map +1 -0
- package/dist/agents/architect.d.ts +34 -0
- package/dist/agents/architect.d.ts.map +1 -0
- package/dist/agents/architect.js +906 -0
- package/dist/agents/architect.js.map +1 -0
- package/dist/agents/base.d.ts +30 -0
- package/dist/agents/base.d.ts.map +1 -0
- package/dist/agents/base.js +70 -0
- package/dist/agents/base.js.map +1 -0
- package/dist/agents/chapter-analyzer.d.ts +30 -0
- package/dist/agents/chapter-analyzer.d.ts.map +1 -0
- package/dist/agents/chapter-analyzer.js +476 -0
- package/dist/agents/chapter-analyzer.js.map +1 -0
- package/dist/agents/composer.d.ts +36 -0
- package/dist/agents/composer.d.ts.map +1 -0
- package/dist/agents/composer.js +319 -0
- package/dist/agents/composer.js.map +1 -0
- package/dist/agents/consolidator.d.ts +23 -0
- package/dist/agents/consolidator.d.ts.map +1 -0
- package/dist/agents/consolidator.js +141 -0
- package/dist/agents/consolidator.js.map +1 -0
- package/dist/agents/continuity.d.ts +39 -0
- package/dist/agents/continuity.d.ts.map +1 -0
- package/dist/agents/continuity.js +612 -0
- package/dist/agents/continuity.js.map +1 -0
- package/dist/agents/detection-insights.d.ts +9 -0
- package/dist/agents/detection-insights.d.ts.map +1 -0
- package/dist/agents/detection-insights.js +54 -0
- package/dist/agents/detection-insights.js.map +1 -0
- package/dist/agents/detector.d.ts +17 -0
- package/dist/agents/detector.d.ts.map +1 -0
- package/dist/agents/detector.js +77 -0
- package/dist/agents/detector.js.map +1 -0
- package/dist/agents/en-prompt-sections.d.ts +8 -0
- package/dist/agents/en-prompt-sections.d.ts.map +1 -0
- package/dist/agents/en-prompt-sections.js +120 -0
- package/dist/agents/en-prompt-sections.js.map +1 -0
- package/dist/agents/fanfic-canon-importer.d.ts +15 -0
- package/dist/agents/fanfic-canon-importer.d.ts.map +1 -0
- package/dist/agents/fanfic-canon-importer.js +117 -0
- package/dist/agents/fanfic-canon-importer.js.map +1 -0
- package/dist/agents/fanfic-dimensions.d.ts +14 -0
- package/dist/agents/fanfic-dimensions.d.ts.map +1 -0
- package/dist/agents/fanfic-dimensions.js +63 -0
- package/dist/agents/fanfic-dimensions.js.map +1 -0
- package/dist/agents/fanfic-prompt-sections.d.ts +5 -0
- package/dist/agents/fanfic-prompt-sections.d.ts.map +1 -0
- package/dist/agents/fanfic-prompt-sections.js +85 -0
- package/dist/agents/fanfic-prompt-sections.js.map +1 -0
- package/dist/agents/foundation-reviewer.d.ts +29 -0
- package/dist/agents/foundation-reviewer.d.ts.map +1 -0
- package/dist/agents/foundation-reviewer.js +153 -0
- package/dist/agents/foundation-reviewer.js.map +1 -0
- package/dist/agents/length-normalizer.d.ts +32 -0
- package/dist/agents/length-normalizer.d.ts.map +1 -0
- package/dist/agents/length-normalizer.js +156 -0
- package/dist/agents/length-normalizer.js.map +1 -0
- package/dist/agents/observer-prompts.d.ts +10 -0
- package/dist/agents/observer-prompts.d.ts.map +1 -0
- package/dist/agents/observer-prompts.js +113 -0
- package/dist/agents/observer-prompts.js.map +1 -0
- package/dist/agents/planner.d.ts +57 -0
- package/dist/agents/planner.d.ts.map +1 -0
- package/dist/agents/planner.js +594 -0
- package/dist/agents/planner.js.map +1 -0
- package/dist/agents/post-write-validator.d.ts +34 -0
- package/dist/agents/post-write-validator.d.ts.map +1 -0
- package/dist/agents/post-write-validator.js +696 -0
- package/dist/agents/post-write-validator.js.map +1 -0
- 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 +24 -0
- package/dist/agents/radar.d.ts.map +1 -0
- package/dist/agents/radar.js +85 -0
- package/dist/agents/radar.js.map +1 -0
- package/dist/agents/reviser.d.ts +32 -0
- package/dist/agents/reviser.d.ts.map +1 -0
- package/dist/agents/reviser.js +282 -0
- package/dist/agents/reviser.js.map +1 -0
- package/dist/agents/rules-reader.d.ts +27 -0
- package/dist/agents/rules-reader.d.ts.map +1 -0
- package/dist/agents/rules-reader.js +99 -0
- package/dist/agents/rules-reader.js.map +1 -0
- package/dist/agents/sensitive-words.d.ts +24 -0
- package/dist/agents/sensitive-words.d.ts.map +1 -0
- package/dist/agents/sensitive-words.js +103 -0
- package/dist/agents/sensitive-words.js.map +1 -0
- package/dist/agents/settler-delta-parser.d.ts +7 -0
- package/dist/agents/settler-delta-parser.d.ts.map +1 -0
- package/dist/agents/settler-delta-parser.js +40 -0
- package/dist/agents/settler-delta-parser.js.map +1 -0
- package/dist/agents/settler-parser.d.ts +13 -0
- package/dist/agents/settler-parser.d.ts.map +1 -0
- package/dist/agents/settler-parser.js +20 -0
- package/dist/agents/settler-parser.js.map +1 -0
- package/dist/agents/settler-prompts.d.ts +22 -0
- package/dist/agents/settler-prompts.d.ts.map +1 -0
- package/dist/agents/settler-prompts.js +193 -0
- package/dist/agents/settler-prompts.js.map +1 -0
- package/dist/agents/state-validator.d.ts +26 -0
- package/dist/agents/state-validator.d.ts.map +1 -0
- package/dist/agents/state-validator.js +229 -0
- package/dist/agents/state-validator.js.map +1 -0
- package/dist/agents/style-analyzer.d.ts +11 -0
- package/dist/agents/style-analyzer.d.ts.map +1 -0
- package/dist/agents/style-analyzer.js +81 -0
- package/dist/agents/style-analyzer.js.map +1 -0
- package/dist/agents/writer-parser.d.ts +17 -0
- package/dist/agents/writer-parser.d.ts.map +1 -0
- package/dist/agents/writer-parser.js +131 -0
- package/dist/agents/writer-parser.js.map +1 -0
- package/dist/agents/writer-prompts.d.ts +11 -0
- package/dist/agents/writer-prompts.d.ts.map +1 -0
- package/dist/agents/writer-prompts.js +549 -0
- package/dist/agents/writer-prompts.js.map +1 -0
- package/dist/agents/writer.d.ts +103 -0
- package/dist/agents/writer.d.ts.map +1 -0
- package/dist/agents/writer.js +1052 -0
- package/dist/agents/writer.js.map +1 -0
- package/dist/index.d.ts +80 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +84 -0
- package/dist/index.js.map +1 -0
- package/dist/interaction/edit-controller.d.ts +55 -0
- package/dist/interaction/edit-controller.d.ts.map +1 -0
- package/dist/interaction/edit-controller.js +187 -0
- package/dist/interaction/edit-controller.js.map +1 -0
- package/dist/interaction/events.d.ts +45 -0
- package/dist/interaction/events.d.ts.map +1 -0
- package/dist/interaction/events.js +32 -0
- package/dist/interaction/events.js.map +1 -0
- package/dist/interaction/intents.d.ts +96 -0
- package/dist/interaction/intents.d.ts.map +1 -0
- package/dist/interaction/intents.js +58 -0
- package/dist/interaction/intents.js.map +1 -0
- package/dist/interaction/modes.d.ts +5 -0
- package/dist/interaction/modes.d.ts.map +1 -0
- package/dist/interaction/modes.js +7 -0
- package/dist/interaction/modes.js.map +1 -0
- package/dist/interaction/nl-router.d.ts +8 -0
- package/dist/interaction/nl-router.d.ts.map +1 -0
- package/dist/interaction/nl-router.js +218 -0
- package/dist/interaction/nl-router.js.map +1 -0
- package/dist/interaction/project-control.d.ts +85 -0
- package/dist/interaction/project-control.d.ts.map +1 -0
- package/dist/interaction/project-control.js +123 -0
- package/dist/interaction/project-control.js.map +1 -0
- package/dist/interaction/project-session-store.d.ts +7 -0
- package/dist/interaction/project-session-store.d.ts.map +1 -0
- package/dist/interaction/project-session-store.js +46 -0
- package/dist/interaction/project-session-store.js.map +1 -0
- package/dist/interaction/project-tools.d.ts +20 -0
- package/dist/interaction/project-tools.d.ts.map +1 -0
- package/dist/interaction/project-tools.js +527 -0
- package/dist/interaction/project-tools.js.map +1 -0
- package/dist/interaction/request-router.d.ts +3 -0
- package/dist/interaction/request-router.d.ts.map +1 -0
- package/dist/interaction/request-router.js +5 -0
- package/dist/interaction/request-router.js.map +1 -0
- package/dist/interaction/runtime.d.ts +54 -0
- package/dist/interaction/runtime.d.ts.map +1 -0
- package/dist/interaction/runtime.js +943 -0
- package/dist/interaction/runtime.js.map +1 -0
- package/dist/interaction/session.d.ts +352 -0
- package/dist/interaction/session.d.ts.map +1 -0
- package/dist/interaction/session.js +98 -0
- package/dist/interaction/session.js.map +1 -0
- package/dist/interaction/truth-authority.d.ts +4 -0
- package/dist/interaction/truth-authority.d.ts.map +1 -0
- package/dist/interaction/truth-authority.js +37 -0
- package/dist/interaction/truth-authority.js.map +1 -0
- package/dist/llm/provider.d.ts +87 -0
- package/dist/llm/provider.d.ts.map +1 -0
- package/dist/llm/provider.js +798 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/models/book-rules.d.ts +118 -0
- package/dist/models/book-rules.d.ts.map +1 -0
- package/dist/models/book-rules.js +55 -0
- package/dist/models/book-rules.js.map +1 -0
- package/dist/models/book.d.ts +51 -0
- package/dist/models/book.d.ts.map +1 -0
- package/dist/models/book.js +27 -0
- package/dist/models/book.js.map +1 -0
- package/dist/models/chapter.d.ts +136 -0
- package/dist/models/chapter.d.ts.map +1 -0
- package/dist/models/chapter.js +38 -0
- package/dist/models/chapter.js.map +1 -0
- package/dist/models/detection.d.ts +25 -0
- package/dist/models/detection.d.ts.map +1 -0
- package/dist/models/detection.js +2 -0
- package/dist/models/detection.js.map +1 -0
- package/dist/models/genre-profile.d.ts +45 -0
- package/dist/models/genre-profile.d.ts.map +1 -0
- package/dist/models/genre-profile.js +26 -0
- package/dist/models/genre-profile.js.map +1 -0
- package/dist/models/input-governance.d.ts +516 -0
- package/dist/models/input-governance.d.ts.map +1 -0
- package/dist/models/input-governance.js +112 -0
- package/dist/models/input-governance.js.map +1 -0
- package/dist/models/length-governance.d.ts +93 -0
- package/dist/models/length-governance.d.ts.map +1 -0
- package/dist/models/length-governance.js +34 -0
- package/dist/models/length-governance.js.map +1 -0
- package/dist/models/project.d.ts +475 -0
- package/dist/models/project.d.ts.map +1 -0
- package/dist/models/project.js +100 -0
- package/dist/models/project.js.map +1 -0
- package/dist/models/runtime-state.d.ts +588 -0
- package/dist/models/runtime-state.d.ts.map +1 -0
- package/dist/models/runtime-state.js +93 -0
- package/dist/models/runtime-state.js.map +1 -0
- package/dist/models/state.d.ts +48 -0
- package/dist/models/state.d.ts.map +1 -0
- package/dist/models/state.js +6 -0
- package/dist/models/state.js.map +1 -0
- package/dist/models/style-profile.d.ts +16 -0
- package/dist/models/style-profile.d.ts.map +1 -0
- package/dist/models/style-profile.js +2 -0
- package/dist/models/style-profile.js.map +1 -0
- package/dist/notify/dispatcher.d.ts +10 -0
- package/dist/notify/dispatcher.d.ts.map +1 -0
- package/dist/notify/dispatcher.js +55 -0
- package/dist/notify/dispatcher.js.map +1 -0
- package/dist/notify/feishu.d.ts +5 -0
- package/dist/notify/feishu.d.ts.map +1 -0
- package/dist/notify/feishu.js +26 -0
- package/dist/notify/feishu.js.map +1 -0
- package/dist/notify/telegram.d.ts +6 -0
- package/dist/notify/telegram.d.ts.map +1 -0
- package/dist/notify/telegram.js +17 -0
- package/dist/notify/telegram.js.map +1 -0
- package/dist/notify/webhook.d.ts +15 -0
- package/dist/notify/webhook.d.ts.map +1 -0
- package/dist/notify/webhook.js +28 -0
- package/dist/notify/webhook.js.map +1 -0
- package/dist/notify/wechat-work.d.ts +5 -0
- package/dist/notify/wechat-work.d.ts.map +1 -0
- package/dist/notify/wechat-work.js +15 -0
- package/dist/notify/wechat-work.js.map +1 -0
- package/dist/pipeline/agent.d.ts +15 -0
- package/dist/pipeline/agent.d.ts.map +1 -0
- package/dist/pipeline/agent.js +550 -0
- package/dist/pipeline/agent.js.map +1 -0
- package/dist/pipeline/chapter-persistence.d.ts +33 -0
- package/dist/pipeline/chapter-persistence.d.ts.map +1 -0
- package/dist/pipeline/chapter-persistence.js +35 -0
- package/dist/pipeline/chapter-persistence.js.map +1 -0
- package/dist/pipeline/chapter-review-cycle.d.ts +79 -0
- package/dist/pipeline/chapter-review-cycle.d.ts.map +1 -0
- package/dist/pipeline/chapter-review-cycle.js +97 -0
- package/dist/pipeline/chapter-review-cycle.js.map +1 -0
- package/dist/pipeline/chapter-state-recovery.d.ts +59 -0
- package/dist/pipeline/chapter-state-recovery.d.ts.map +1 -0
- package/dist/pipeline/chapter-state-recovery.js +133 -0
- package/dist/pipeline/chapter-state-recovery.js.map +1 -0
- package/dist/pipeline/chapter-truth-validation.d.ts +41 -0
- package/dist/pipeline/chapter-truth-validation.d.ts.map +1 -0
- package/dist/pipeline/chapter-truth-validation.js +67 -0
- package/dist/pipeline/chapter-truth-validation.js.map +1 -0
- package/dist/pipeline/detection-runner.d.ts +31 -0
- package/dist/pipeline/detection-runner.d.ts.map +1 -0
- package/dist/pipeline/detection-runner.js +109 -0
- package/dist/pipeline/detection-runner.js.map +1 -0
- package/dist/pipeline/persisted-governed-plan.d.ts +4 -0
- package/dist/pipeline/persisted-governed-plan.d.ts.map +1 -0
- package/dist/pipeline/persisted-governed-plan.js +85 -0
- package/dist/pipeline/persisted-governed-plan.js.map +1 -0
- package/dist/pipeline/runner.d.ts +212 -0
- package/dist/pipeline/runner.d.ts.map +1 -0
- package/dist/pipeline/runner.js +2265 -0
- package/dist/pipeline/runner.js.map +1 -0
- package/dist/pipeline/scheduler.d.ts +58 -0
- package/dist/pipeline/scheduler.d.ts.map +1 -0
- package/dist/pipeline/scheduler.js +322 -0
- package/dist/pipeline/scheduler.js.map +1 -0
- package/dist/state/manager.d.ts +48 -0
- package/dist/state/manager.d.ts.map +1 -0
- package/dist/state/manager.js +435 -0
- package/dist/state/manager.js.map +1 -0
- package/dist/state/memory-db.d.ts +77 -0
- package/dist/state/memory-db.d.ts.map +1 -0
- package/dist/state/memory-db.js +249 -0
- package/dist/state/memory-db.js.map +1 -0
- package/dist/state/runtime-state-store.d.ts +25 -0
- package/dist/state/runtime-state-store.d.ts.map +1 -0
- package/dist/state/runtime-state-store.js +108 -0
- package/dist/state/runtime-state-store.js.map +1 -0
- package/dist/state/state-bootstrap.d.ts +21 -0
- package/dist/state/state-bootstrap.d.ts.map +1 -0
- package/dist/state/state-bootstrap.js +434 -0
- package/dist/state/state-bootstrap.js.map +1 -0
- package/dist/state/state-projections.d.ts +5 -0
- package/dist/state/state-projections.d.ts.map +1 -0
- package/dist/state/state-projections.js +166 -0
- package/dist/state/state-projections.js.map +1 -0
- package/dist/state/state-reducer.d.ts +13 -0
- package/dist/state/state-reducer.d.ts.map +1 -0
- package/dist/state/state-reducer.js +194 -0
- package/dist/state/state-reducer.js.map +1 -0
- package/dist/state/state-validator.d.ts +12 -0
- package/dist/state/state-validator.d.ts.map +1 -0
- package/dist/state/state-validator.js +67 -0
- package/dist/state/state-validator.js.map +1 -0
- package/dist/utils/analytics.d.ts +39 -0
- package/dist/utils/analytics.d.ts.map +1 -0
- package/dist/utils/analytics.js +50 -0
- package/dist/utils/analytics.js.map +1 -0
- package/dist/utils/cadence-policy.d.ts +36 -0
- package/dist/utils/cadence-policy.d.ts.map +1 -0
- package/dist/utils/cadence-policy.js +38 -0
- package/dist/utils/cadence-policy.js.map +1 -0
- package/dist/utils/chapter-cadence.d.ts +34 -0
- package/dist/utils/chapter-cadence.d.ts.map +1 -0
- package/dist/utils/chapter-cadence.js +142 -0
- package/dist/utils/chapter-cadence.js.map +1 -0
- package/dist/utils/chapter-splitter.d.ts +18 -0
- package/dist/utils/chapter-splitter.d.ts.map +1 -0
- package/dist/utils/chapter-splitter.js +60 -0
- package/dist/utils/chapter-splitter.js.map +1 -0
- package/dist/utils/config-loader.d.ts +15 -0
- package/dist/utils/config-loader.d.ts.map +1 -0
- package/dist/utils/config-loader.js +136 -0
- package/dist/utils/config-loader.js.map +1 -0
- package/dist/utils/context-filter.d.ts +20 -0
- package/dist/utils/context-filter.d.ts.map +1 -0
- package/dist/utils/context-filter.js +134 -0
- package/dist/utils/context-filter.js.map +1 -0
- package/dist/utils/governed-context.d.ts +11 -0
- package/dist/utils/governed-context.d.ts.map +1 -0
- package/dist/utils/governed-context.js +42 -0
- package/dist/utils/governed-context.js.map +1 -0
- package/dist/utils/governed-working-set.d.ts +18 -0
- package/dist/utils/governed-working-set.d.ts.map +1 -0
- package/dist/utils/governed-working-set.js +296 -0
- package/dist/utils/governed-working-set.js.map +1 -0
- package/dist/utils/hook-agenda.d.ts +21 -0
- package/dist/utils/hook-agenda.d.ts.map +1 -0
- package/dist/utils/hook-agenda.js +95 -0
- package/dist/utils/hook-agenda.js.map +1 -0
- package/dist/utils/hook-arbiter.d.ts +15 -0
- package/dist/utils/hook-arbiter.d.ts.map +1 -0
- package/dist/utils/hook-arbiter.js +268 -0
- package/dist/utils/hook-arbiter.js.map +1 -0
- package/dist/utils/hook-governance.d.ts +28 -0
- package/dist/utils/hook-governance.d.ts.map +1 -0
- package/dist/utils/hook-governance.js +144 -0
- package/dist/utils/hook-governance.js.map +1 -0
- package/dist/utils/hook-health.d.ts +15 -0
- package/dist/utils/hook-health.d.ts.map +1 -0
- package/dist/utils/hook-health.js +128 -0
- package/dist/utils/hook-health.js.map +1 -0
- package/dist/utils/hook-lifecycle.d.ts +34 -0
- package/dist/utils/hook-lifecycle.d.ts.map +1 -0
- package/dist/utils/hook-lifecycle.js +125 -0
- package/dist/utils/hook-lifecycle.js.map +1 -0
- package/dist/utils/hook-policy.d.ts +74 -0
- package/dist/utils/hook-policy.d.ts.map +1 -0
- package/dist/utils/hook-policy.js +126 -0
- package/dist/utils/hook-policy.js.map +1 -0
- package/dist/utils/length-metrics.d.ts +10 -0
- package/dist/utils/length-metrics.d.ts.map +1 -0
- package/dist/utils/length-metrics.js +85 -0
- package/dist/utils/length-metrics.js.map +1 -0
- package/dist/utils/logger.d.ts +31 -0
- package/dist/utils/logger.d.ts.map +1 -0
- package/dist/utils/logger.js +79 -0
- package/dist/utils/logger.js.map +1 -0
- package/dist/utils/long-span-fatigue.d.ts +28 -0
- package/dist/utils/long-span-fatigue.d.ts.map +1 -0
- package/dist/utils/long-span-fatigue.js +406 -0
- package/dist/utils/long-span-fatigue.js.map +1 -0
- package/dist/utils/memory-retrieval.d.ts +25 -0
- package/dist/utils/memory-retrieval.d.ts.map +1 -0
- package/dist/utils/memory-retrieval.js +319 -0
- package/dist/utils/memory-retrieval.js.map +1 -0
- package/dist/utils/pov-filter.d.ts +30 -0
- package/dist/utils/pov-filter.d.ts.map +1 -0
- package/dist/utils/pov-filter.js +129 -0
- package/dist/utils/pov-filter.js.map +1 -0
- package/dist/utils/spot-fix-patches.d.ts +14 -0
- package/dist/utils/spot-fix-patches.d.ts.map +1 -0
- package/dist/utils/spot-fix-patches.js +75 -0
- package/dist/utils/spot-fix-patches.js.map +1 -0
- package/dist/utils/story-markdown.d.ts +13 -0
- package/dist/utils/story-markdown.d.ts.map +1 -0
- package/dist/utils/story-markdown.js +218 -0
- package/dist/utils/story-markdown.js.map +1 -0
- package/dist/utils/web-search.d.ts +23 -0
- package/dist/utils/web-search.d.ts.map +1 -0
- package/dist/utils/web-search.js +68 -0
- package/dist/utils/web-search.js.map +1 -0
- package/genres/cozy.md +43 -0
- package/genres/cultivation.md +42 -0
- package/genres/dungeon-core.md +40 -0
- package/genres/horror.md +51 -0
- package/genres/isekai.md +43 -0
- package/genres/litrpg.md +43 -0
- package/genres/other.md +24 -0
- package/genres/progression.md +41 -0
- package/genres/romantasy.md +45 -0
- package/genres/sci-fi.md +42 -0
- package/genres/system-apocalypse.md +40 -0
- package/genres/tower-climber.md +41 -0
- package/genres/urban.md +53 -0
- package/genres/xianxia.md +46 -0
- package/genres/xuanhuan.md +64 -0
- package/package.json +58 -0
|
@@ -0,0 +1,906 @@
|
|
|
1
|
+
import { BaseAgent } from "./base.js";
|
|
2
|
+
import { readGenreProfile } from "./rules-reader.js";
|
|
3
|
+
import { writeFile, mkdir } from "node:fs/promises";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import { renderHookSnapshot } from "../utils/memory-retrieval.js";
|
|
6
|
+
export class ArchitectAgent extends BaseAgent {
|
|
7
|
+
get name() {
|
|
8
|
+
return "architect";
|
|
9
|
+
}
|
|
10
|
+
async generateFoundation(book, externalContext, reviewFeedback) {
|
|
11
|
+
const { profile: gp, body: genreBody } = await readGenreProfile(this.ctx.projectRoot, book.genre);
|
|
12
|
+
const resolvedLanguage = book.language ?? gp.language;
|
|
13
|
+
const contextBlock = externalContext
|
|
14
|
+
? `\n\n## 外部指令\n以下是来自外部系统的创作指令,请将其融入设定中:\n\n${externalContext}\n`
|
|
15
|
+
: "";
|
|
16
|
+
const reviewFeedbackBlock = this.buildReviewFeedbackBlock(reviewFeedback, resolvedLanguage);
|
|
17
|
+
const briefSections = externalContext ? this.parseBriefSections(externalContext) : null;
|
|
18
|
+
const numericalBlock = gp.numericalSystem
|
|
19
|
+
? `- 有明确的数值/资源体系可追踪
|
|
20
|
+
- 在 book_rules 中定义 numericalSystemOverrides(hardCap、resourceTypes)`
|
|
21
|
+
: "- 本题材无数值系统,不需要资源账本";
|
|
22
|
+
const powerBlock = gp.powerScaling
|
|
23
|
+
? "- 有明确的战力等级体系"
|
|
24
|
+
: "";
|
|
25
|
+
const eraBlock = gp.eraResearch
|
|
26
|
+
? "- 需要年代考据支撑(在 book_rules 中设置 eraConstraints)"
|
|
27
|
+
: "";
|
|
28
|
+
const briefRef = (label, content, isZh) => {
|
|
29
|
+
if (!content)
|
|
30
|
+
return "";
|
|
31
|
+
const prefix = isZh ? "【参考】" : "[Reference]";
|
|
32
|
+
return `\n${prefix} ${label}:\n${content}\n`;
|
|
33
|
+
};
|
|
34
|
+
const storyBiblePrompt = resolvedLanguage === "en"
|
|
35
|
+
? `Use structured second-level headings:
|
|
36
|
+
## 01_Worldview
|
|
37
|
+
World setting, historical-social frame, and core rules
|
|
38
|
+
|
|
39
|
+
## 02_Protagonist
|
|
40
|
+
Protagonist setup (identity / advantage / personality core / behavioral boundaries)
|
|
41
|
+
|
|
42
|
+
## 03_Factions_and_Characters
|
|
43
|
+
Major factions and important supporting characters (for each: name, identity, motivation, relationship to protagonist, independent goal)
|
|
44
|
+
|
|
45
|
+
## 04_Geography_and_Environment
|
|
46
|
+
Map / scene design and environmental traits
|
|
47
|
+
|
|
48
|
+
## 05_Title_and_Blurb
|
|
49
|
+
Title method:
|
|
50
|
+
- Keep the title clear, direct, and easy to understand
|
|
51
|
+
- Use a format that immediately signals genre and core appeal
|
|
52
|
+
- Avoid overly literary or misleading titles
|
|
53
|
+
|
|
54
|
+
Blurb method (within 300 words, choose one):
|
|
55
|
+
1. Open with conflict, then reveal the hook, then leave suspense
|
|
56
|
+
2. Summarize only the main line and keep a clear suspense gap
|
|
57
|
+
3. Use a miniature scene that captures the book's strongest pull
|
|
58
|
+
|
|
59
|
+
Core blurb principle:
|
|
60
|
+
- The blurb is product copy that must make readers want to click`
|
|
61
|
+
: `用结构化二级标题组织:
|
|
62
|
+
## 01_世界观
|
|
63
|
+
世界观设定、核心规则体系
|
|
64
|
+
|
|
65
|
+
## 02_主角
|
|
66
|
+
主角设定(身份/金手指/性格底色/行为边界)
|
|
67
|
+
|
|
68
|
+
## 03_势力与人物
|
|
69
|
+
势力分布、重要配角(每人:名字、身份、动机、与主角关系、独立目标)
|
|
70
|
+
|
|
71
|
+
## 04_地理与环境
|
|
72
|
+
地图/场景设定、环境特色
|
|
73
|
+
|
|
74
|
+
## 05_书名与简介
|
|
75
|
+
书名方法论:
|
|
76
|
+
- 书名必须简单扼要、通俗易懂,读者看到书名就能知道题材和主题
|
|
77
|
+
- 采用"题材+核心爽点+主角行为"的长书名格式,避免文艺化
|
|
78
|
+
- 融入平台当下热点词汇,吸引精准流量
|
|
79
|
+
- 禁止题材错位(都市文取玄幻书名会导致读者流失)
|
|
80
|
+
- 参考热榜书名风格:俏皮、通俗、有记忆点
|
|
81
|
+
|
|
82
|
+
简介方法论(300字内,三种写法任选其一):
|
|
83
|
+
1. 冲突开篇法:第一句抛困境/冲突,第二句亮金手指/核心能力,第三句留悬念
|
|
84
|
+
2. 高度概括法:只挑主线概括(不是全篇概括),必须留悬念
|
|
85
|
+
3. 小剧场法:提炼故事中最经典的桥段,作为引子
|
|
86
|
+
|
|
87
|
+
简介核心原则:
|
|
88
|
+
- 简介 = 产品宣传语,必须让读者产生"我要点开看"的冲动
|
|
89
|
+
- 可以从剧情设定、人设、或某个精彩片段切入
|
|
90
|
+
- 必须有噱头(如"凡是被写在笔记本上的名字,最后都得死")`;
|
|
91
|
+
const volumeOutlinePrompt = resolvedLanguage === "en"
|
|
92
|
+
? `Volume plan. For each volume include: title, chapter range, core conflict, key turning points, and payoff goal
|
|
93
|
+
|
|
94
|
+
### Golden First Three Chapters Rule
|
|
95
|
+
- Chapter 1: throw the core conflict immediately; no large background dump
|
|
96
|
+
- Chapter 2: show the core edge / ability / leverage that answers Chapter 1's pressure
|
|
97
|
+
- Chapter 3: establish the first concrete short-term goal that gives readers a reason to continue`
|
|
98
|
+
: `卷纲规划,每卷包含:卷名、章节范围、核心冲突、关键转折、收益目标
|
|
99
|
+
|
|
100
|
+
### 黄金三章法则(前三章必须遵循)
|
|
101
|
+
- 第1章:抛出核心冲突(主角立即面临困境/危机/选择),禁止大段背景灌输
|
|
102
|
+
- 第2章:展示金手指/核心能力(主角如何应对第1章的困境),让读者看到爽点预期
|
|
103
|
+
- 第3章:明确短期目标(主角确立第一个具体可达成的目标),给读者追读理由`;
|
|
104
|
+
const bookRulesPrompt = resolvedLanguage === "en"
|
|
105
|
+
? `Generate book_rules.md as YAML frontmatter plus narrative guidance:
|
|
106
|
+
\`\`\`
|
|
107
|
+
---
|
|
108
|
+
version: "1.0"
|
|
109
|
+
protagonist:
|
|
110
|
+
name: (protagonist name)
|
|
111
|
+
personalityLock: [(3-5 personality keywords)]
|
|
112
|
+
behavioralConstraints: [(3-5 behavioral constraints)]
|
|
113
|
+
genreLock:
|
|
114
|
+
primary: ${book.genre}
|
|
115
|
+
forbidden: [(2-3 forbidden style intrusions)]
|
|
116
|
+
${gp.numericalSystem ? `numericalSystemOverrides:
|
|
117
|
+
hardCap: (decide from the setting)
|
|
118
|
+
resourceTypes: [(core resource types)]` : ""}
|
|
119
|
+
prohibitions:
|
|
120
|
+
- (3-5 book-specific prohibitions)
|
|
121
|
+
chapterTypesOverride: []
|
|
122
|
+
fatigueWordsOverride: []
|
|
123
|
+
additionalAuditDimensions: []
|
|
124
|
+
enableFullCastTracking: false
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Narrative Perspective
|
|
128
|
+
(Describe the narrative perspective and style)
|
|
129
|
+
|
|
130
|
+
## Core Conflict Driver
|
|
131
|
+
(Describe the book's core conflict and propulsion)
|
|
132
|
+
\`\`\``
|
|
133
|
+
: `生成 book_rules.md 格式的 YAML frontmatter + 叙事指导,包含:
|
|
134
|
+
\`\`\`
|
|
135
|
+
---
|
|
136
|
+
version: "1.0"
|
|
137
|
+
protagonist:
|
|
138
|
+
name: (主角名)
|
|
139
|
+
personalityLock: [(3-5个性格关键词)]
|
|
140
|
+
behavioralConstraints: [(3-5条行为约束)]
|
|
141
|
+
genreLock:
|
|
142
|
+
primary: ${book.genre}
|
|
143
|
+
forbidden: [(2-3种禁止混入的文风)]
|
|
144
|
+
${gp.numericalSystem ? `numericalSystemOverrides:
|
|
145
|
+
hardCap: (根据设定确定)
|
|
146
|
+
resourceTypes: [(核心资源类型列表)]` : ""}
|
|
147
|
+
prohibitions:
|
|
148
|
+
- (3-5条本书禁忌)
|
|
149
|
+
chapterTypesOverride: []
|
|
150
|
+
fatigueWordsOverride: []
|
|
151
|
+
additionalAuditDimensions: []
|
|
152
|
+
enableFullCastTracking: false
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
## 叙事视角
|
|
156
|
+
(描述本书叙事视角和风格)
|
|
157
|
+
|
|
158
|
+
## 核心冲突驱动
|
|
159
|
+
(描述本书的核心矛盾和驱动力)
|
|
160
|
+
\`\`\``;
|
|
161
|
+
const currentStatePrompt = resolvedLanguage === "en"
|
|
162
|
+
? `Initial state card (Chapter 0), include:
|
|
163
|
+
| Field | Value |
|
|
164
|
+
| --- | --- |
|
|
165
|
+
| Current Chapter | 0 |
|
|
166
|
+
| Current Location | (starting location) |
|
|
167
|
+
| Protagonist State | (initial condition) |
|
|
168
|
+
| Current Goal | (first goal) |
|
|
169
|
+
| Current Constraint | (initial constraint) |
|
|
170
|
+
| Current Alliances | (initial relationships) |
|
|
171
|
+
| Current Conflict | (first conflict) |`
|
|
172
|
+
: `初始状态卡(第0章),包含:
|
|
173
|
+
| 字段 | 值 |
|
|
174
|
+
|------|-----|
|
|
175
|
+
| 当前章节 | 0 |
|
|
176
|
+
| 当前位置 | (起始地点) |
|
|
177
|
+
| 主角状态 | (初始状态) |
|
|
178
|
+
| 当前目标 | (第一个目标) |
|
|
179
|
+
| 当前限制 | (初始限制) |
|
|
180
|
+
| 当前敌我 | (初始关系) |
|
|
181
|
+
| 当前冲突 | (第一个冲突) |`;
|
|
182
|
+
const pendingHooksPrompt = resolvedLanguage === "en"
|
|
183
|
+
? `Initial hook pool (Markdown table):
|
|
184
|
+
| hook_id | start_chapter | type | status | last_advanced_chapter | expected_payoff | payoff_timing | notes |
|
|
185
|
+
|
|
186
|
+
Rules for the hook table:
|
|
187
|
+
- Column 5 must be a pure chapter number, never natural-language description
|
|
188
|
+
- During book creation, all planned hooks are still unapplied, so last_advanced_chapter = 0
|
|
189
|
+
- Column 7 must be one of: immediate / near-term / mid-arc / slow-burn / endgame
|
|
190
|
+
- If you want to describe the initial clue/signal, put it in notes instead of column 5`
|
|
191
|
+
: `初始伏笔池(Markdown表格):
|
|
192
|
+
| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 回收节奏 | 备注 |
|
|
193
|
+
|
|
194
|
+
伏笔表规则:
|
|
195
|
+
- 第5列必须是纯数字章节号,不能写自然语言描述
|
|
196
|
+
- 建书阶段所有伏笔都还没正式推进,所以第5列统一填 0
|
|
197
|
+
- 第7列必须填写:立即 / 近期 / 中程 / 慢烧 / 终局 之一
|
|
198
|
+
- 如果要说明“初始线索/最初信号”,写进备注,不要写进第5列`;
|
|
199
|
+
const finalRequirementsPrompt = resolvedLanguage === "en"
|
|
200
|
+
? `Generated content must:
|
|
201
|
+
1. Fit the ${book.platform} platform taste
|
|
202
|
+
2. Fit the ${gp.name} genre traits
|
|
203
|
+
${numericalBlock}
|
|
204
|
+
${powerBlock}
|
|
205
|
+
${eraBlock}
|
|
206
|
+
3. Give the protagonist a clear personality and behavioral boundaries
|
|
207
|
+
4. Keep hooks and payoffs coherent
|
|
208
|
+
5. Make supporting characters independently motivated rather than pure tools`
|
|
209
|
+
: `生成内容必须:
|
|
210
|
+
1. 符合${book.platform}平台口味
|
|
211
|
+
2. 符合${gp.name}题材特征
|
|
212
|
+
${numericalBlock}
|
|
213
|
+
${powerBlock}
|
|
214
|
+
${eraBlock}
|
|
215
|
+
3. 主角人设鲜明,有明确行为边界
|
|
216
|
+
4. 伏笔前后呼应,不留悬空线
|
|
217
|
+
5. 配角有独立动机,不是工具人`;
|
|
218
|
+
const systemPrompt = `你是一个专业的网络小说架构师。你的任务是为一本新的${gp.name}小说生成完整的基础设定。${contextBlock}${reviewFeedbackBlock}
|
|
219
|
+
|
|
220
|
+
要求:
|
|
221
|
+
- 平台:${book.platform}
|
|
222
|
+
- 题材:${gp.name}(${book.genre})
|
|
223
|
+
- 目标章数:${book.targetChapters}章
|
|
224
|
+
- 每章字数:${book.chapterWordCount}字
|
|
225
|
+
|
|
226
|
+
## 题材特征
|
|
227
|
+
|
|
228
|
+
${genreBody}
|
|
229
|
+
|
|
230
|
+
## 生成要求
|
|
231
|
+
|
|
232
|
+
你需要生成以下内容,每个部分用 === SECTION: <name> === 分隔:
|
|
233
|
+
|
|
234
|
+
=== SECTION: story_bible ===
|
|
235
|
+
${briefSections ? briefRef("世界观基石", briefSections.worldview, resolvedLanguage === "zh") + briefRef("风格与样例", briefSections.style, resolvedLanguage === "zh") : ""}${storyBiblePrompt}
|
|
236
|
+
|
|
237
|
+
=== SECTION: volume_outline ===
|
|
238
|
+
${briefSections ? briefRef("书的大纲", briefSections.outline, resolvedLanguage === "zh") : ""}${volumeOutlinePrompt}
|
|
239
|
+
|
|
240
|
+
=== SECTION: book_rules ===
|
|
241
|
+
${briefSections ? briefRef("创作规则", briefSections.rules, resolvedLanguage === "zh") : ""}${bookRulesPrompt}
|
|
242
|
+
|
|
243
|
+
=== SECTION: current_state ===
|
|
244
|
+
${briefSections ? briefRef("风格与样例", briefSections.style, resolvedLanguage === "zh") : ""}${currentStatePrompt}
|
|
245
|
+
|
|
246
|
+
=== SECTION: pending_hooks ===
|
|
247
|
+
${briefSections ? briefRef("其他内容", briefSections.other, resolvedLanguage === "zh") : ""}${pendingHooksPrompt}
|
|
248
|
+
|
|
249
|
+
${finalRequirementsPrompt}`;
|
|
250
|
+
const langPrefix = resolvedLanguage === "en"
|
|
251
|
+
? `【LANGUAGE OVERRIDE】ALL output MUST be written in English. Character names, place names, and all prose must be in English.\n\n`
|
|
252
|
+
: "";
|
|
253
|
+
const messages = [
|
|
254
|
+
{ role: "system", content: langPrefix + systemPrompt },
|
|
255
|
+
];
|
|
256
|
+
const chatOptions = { maxTokens: 49152, temperature: 0.8 };
|
|
257
|
+
const userPrompts = {
|
|
258
|
+
storyBible: resolvedLanguage === "en"
|
|
259
|
+
? `Generate the story_bible section first. Use === SECTION: story_bible === as the section marker.`
|
|
260
|
+
: `请先生成 story_bible 部分。使用 === SECTION: story_bible === 作为分段标记。`,
|
|
261
|
+
volumeOutline: resolvedLanguage === "en"
|
|
262
|
+
? `Now generate the volume_outline section. Use === SECTION: volume_outline === as the section marker.`
|
|
263
|
+
: `请生成 volume_outline 部分。使用 === SECTION: volume_outline === 作为分段标记。`,
|
|
264
|
+
bookRules: resolvedLanguage === "en"
|
|
265
|
+
? `Now generate the book_rules section. Use === SECTION: book_rules === as the section marker.`
|
|
266
|
+
: `请生成 book_rules 部分。使用 === SECTION: book_rules === 作为分段标记。`,
|
|
267
|
+
currentState: resolvedLanguage === "en"
|
|
268
|
+
? `Now generate the current_state section. Use === SECTION: current_state === as the section marker.`
|
|
269
|
+
: `请生成 current_state 部分。使用 === SECTION: current_state === 作为分段标记。`,
|
|
270
|
+
pendingHooks: resolvedLanguage === "en"
|
|
271
|
+
? `Now generate the pending_hooks section. Use === SECTION: pending_hooks === as the section marker.`
|
|
272
|
+
: `请生成 pending_hooks 部分。使用 === SECTION: pending_hooks === 作为分段标记。`,
|
|
273
|
+
};
|
|
274
|
+
messages.push({ role: "user", content: userPrompts.storyBible });
|
|
275
|
+
this.ctx.logger?.info("=== Generating story_bible (round 1/5) ===");
|
|
276
|
+
const r1 = await this.chat(messages, chatOptions);
|
|
277
|
+
this.ctx.logger?.info(`story_bible response: ${r1.content.slice(0, 200)}...`);
|
|
278
|
+
messages.push({ role: "assistant", content: r1.content });
|
|
279
|
+
const storyBible = this.extractSection(r1.content, "story_bible");
|
|
280
|
+
messages.push({ role: "user", content: userPrompts.volumeOutline });
|
|
281
|
+
this.ctx.logger?.info("=== Generating volume_outline (round 2/5) ===");
|
|
282
|
+
const r2 = await this.chat(messages, chatOptions);
|
|
283
|
+
this.ctx.logger?.info(`volume_outline response: ${r2.content.slice(0, 200)}...`);
|
|
284
|
+
messages.push({ role: "assistant", content: r2.content });
|
|
285
|
+
const volumeOutline = this.extractSection(r2.content, "volume_outline");
|
|
286
|
+
messages.push({ role: "user", content: userPrompts.bookRules });
|
|
287
|
+
this.ctx.logger?.info("=== Generating book_rules (round 3/5) ===");
|
|
288
|
+
const r3 = await this.chat(messages, chatOptions);
|
|
289
|
+
this.ctx.logger?.info(`book_rules response: ${r3.content.slice(0, 200)}...`);
|
|
290
|
+
messages.push({ role: "assistant", content: r3.content });
|
|
291
|
+
const bookRules = this.extractSection(r3.content, "book_rules");
|
|
292
|
+
messages.push({ role: "user", content: userPrompts.currentState });
|
|
293
|
+
this.ctx.logger?.info("=== Generating current_state (round 4/5) ===");
|
|
294
|
+
const r4 = await this.chat(messages, chatOptions);
|
|
295
|
+
this.ctx.logger?.info(`current_state response: ${r4.content.slice(0, 200)}...`);
|
|
296
|
+
messages.push({ role: "assistant", content: r4.content });
|
|
297
|
+
const currentState = this.extractSection(r4.content, "current_state");
|
|
298
|
+
messages.push({ role: "user", content: userPrompts.pendingHooks });
|
|
299
|
+
this.ctx.logger?.info("=== Generating pending_hooks (round 5/5) ===");
|
|
300
|
+
const r5 = await this.chat(messages, chatOptions);
|
|
301
|
+
this.ctx.logger?.info(`pending_hooks response: ${r5.content.slice(0, 200)}...`);
|
|
302
|
+
const pendingHooks = this.extractSection(r5.content, "pending_hooks");
|
|
303
|
+
return {
|
|
304
|
+
storyBible: storyBible || this.extractSection(r1.content, "story_bible"),
|
|
305
|
+
volumeOutline: volumeOutline || this.extractSection(r2.content, "volume_outline"),
|
|
306
|
+
bookRules: bookRules || this.extractSection(r3.content, "book_rules"),
|
|
307
|
+
currentState: currentState || this.extractSection(r4.content, "current_state"),
|
|
308
|
+
pendingHooks: pendingHooks || this.extractSection(r5.content, "pending_hooks"),
|
|
309
|
+
};
|
|
310
|
+
}
|
|
311
|
+
extractSection(content, sectionName) {
|
|
312
|
+
const pattern = new RegExp(`^\\s*===\\s*SECTION\\s*[::]\\s*${sectionName.replace(/_/g, "[_::]")}\\s*===\\s*([\\s\\S]*?)$`, "gim");
|
|
313
|
+
const match = pattern.exec(content);
|
|
314
|
+
if (match) {
|
|
315
|
+
return match[1]?.trim() || "";
|
|
316
|
+
}
|
|
317
|
+
const lines = content.split("\n");
|
|
318
|
+
const sectionLines = [];
|
|
319
|
+
let inSection = false;
|
|
320
|
+
for (const line of lines) {
|
|
321
|
+
if (line.includes(`=== SECTION:`) && line.toLowerCase().includes(sectionName.toLowerCase())) {
|
|
322
|
+
inSection = true;
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
if (inSection && line.startsWith("===")) {
|
|
326
|
+
break;
|
|
327
|
+
}
|
|
328
|
+
if (inSection) {
|
|
329
|
+
sectionLines.push(line);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
return sectionLines.join("\n").trim();
|
|
333
|
+
}
|
|
334
|
+
async writeFoundationFiles(bookDir, output, numericalSystem = true, language = "zh") {
|
|
335
|
+
const storyDir = join(bookDir, "story");
|
|
336
|
+
await mkdir(storyDir, { recursive: true });
|
|
337
|
+
const writes = [
|
|
338
|
+
writeFile(join(storyDir, "story_bible.md"), output.storyBible, "utf-8"),
|
|
339
|
+
writeFile(join(storyDir, "volume_outline.md"), output.volumeOutline, "utf-8"),
|
|
340
|
+
writeFile(join(storyDir, "book_rules.md"), output.bookRules, "utf-8"),
|
|
341
|
+
writeFile(join(storyDir, "current_state.md"), output.currentState, "utf-8"),
|
|
342
|
+
writeFile(join(storyDir, "pending_hooks.md"), output.pendingHooks, "utf-8"),
|
|
343
|
+
];
|
|
344
|
+
if (numericalSystem) {
|
|
345
|
+
writes.push(writeFile(join(storyDir, "particle_ledger.md"), language === "en"
|
|
346
|
+
? "# Resource Ledger\n\n| Chapter | Opening Value | Source | Integrity | Delta | Closing Value | Evidence |\n| --- | --- | --- | --- | --- | --- | --- |\n| 0 | 0 | Initialization | - | 0 | 0 | Initial book state |\n"
|
|
347
|
+
: "# 资源账本\n\n| 章节 | 期初值 | 来源 | 完整度 | 增量 | 期末值 | 依据 |\n|------|--------|------|--------|------|--------|------|\n| 0 | 0 | 初始化 | - | 0 | 0 | 开书初始 |\n", "utf-8"));
|
|
348
|
+
}
|
|
349
|
+
// Initialize new truth files
|
|
350
|
+
writes.push(writeFile(join(storyDir, "subplot_board.md"), language === "en"
|
|
351
|
+
? "# Subplot Board\n\n| Subplot ID | Subplot | Related Characters | Start Chapter | Last Active Chapter | Chapters Since | Status | Progress Summary | Payoff ETA |\n| --- | --- | --- | --- | --- | --- | --- | --- | --- |\n"
|
|
352
|
+
: "# 支线进度板\n\n| 支线ID | 支线名 | 相关角色 | 起始章 | 最近活跃章 | 距今章数 | 状态 | 进度概述 | 回收ETA |\n|--------|--------|----------|--------|------------|----------|------|----------|---------|\n", "utf-8"), writeFile(join(storyDir, "emotional_arcs.md"), language === "en"
|
|
353
|
+
? "# Emotional Arcs\n\n| Character | Chapter | Emotional State | Trigger Event | Intensity (1-10) | Arc Direction |\n| --- | --- | --- | --- | --- | --- |\n"
|
|
354
|
+
: "# 情感弧线\n\n| 角色 | 章节 | 情绪状态 | 触发事件 | 强度(1-10) | 弧线方向 |\n|------|------|----------|----------|------------|----------|\n", "utf-8"), writeFile(join(storyDir, "character_matrix.md"), language === "en"
|
|
355
|
+
? "# Character Matrix\n\n### Character Profiles\n| Character | Core Tags | Contrast Detail | Speech Style | Personality Core | Relationship to Protagonist | Core Motivation | Current Goal |\n| --- | --- | --- | --- | --- | --- | --- | --- |\n\n### Encounter Log\n| Character A | Character B | First Meeting Chapter | Latest Interaction Chapter | Relationship Type | Relationship Change |\n| --- | --- | --- | --- | --- | --- |\n\n### Information Boundaries\n| Character | Known Information | Unknown Information | Source Chapter |\n| --- | --- | --- | --- |\n"
|
|
356
|
+
: "# 角色交互矩阵\n\n### 角色档案\n| 角色 | 核心标签 | 反差细节 | 说话风格 | 性格底色 | 与主角关系 | 核心动机 | 当前目标 |\n|------|----------|----------|----------|----------|------------|----------|----------|\n\n### 相遇记录\n| 角色A | 角色B | 首次相遇章 | 最近交互章 | 关系性质 | 关系变化 |\n|-------|-------|------------|------------|----------|----------|\n\n### 信息边界\n| 角色 | 已知信息 | 未知信息 | 信息来源章 |\n|------|----------|----------|------------|\n", "utf-8"));
|
|
357
|
+
await Promise.all(writes);
|
|
358
|
+
}
|
|
359
|
+
/**
|
|
360
|
+
* Reverse-engineer foundation from existing chapters.
|
|
361
|
+
* Reads all chapters as a single text block and asks LLM to extract story_bible,
|
|
362
|
+
* volume_outline, book_rules, current_state, and pending_hooks.
|
|
363
|
+
*/
|
|
364
|
+
async generateFoundationFromImport(book, chaptersText, externalContext, reviewFeedback, options) {
|
|
365
|
+
const { profile: gp, body: genreBody } = await readGenreProfile(this.ctx.projectRoot, book.genre);
|
|
366
|
+
const resolvedLanguage = book.language ?? gp.language;
|
|
367
|
+
const reviewFeedbackBlock = this.buildReviewFeedbackBlock(reviewFeedback, resolvedLanguage);
|
|
368
|
+
const contextBlock = externalContext
|
|
369
|
+
? (resolvedLanguage === "en"
|
|
370
|
+
? `\n\n## External Instructions\n${externalContext}\n`
|
|
371
|
+
: `\n\n## 外部指令\n${externalContext}\n`)
|
|
372
|
+
: "";
|
|
373
|
+
const numericalBlock = gp.numericalSystem
|
|
374
|
+
? (resolvedLanguage === "en"
|
|
375
|
+
? `- The story uses a trackable numerical/resource system
|
|
376
|
+
- Define numericalSystemOverrides in book_rules (hardCap, resourceTypes)`
|
|
377
|
+
: `- 有明确的数值/资源体系可追踪
|
|
378
|
+
- 在 book_rules 中定义 numericalSystemOverrides(hardCap、resourceTypes)`)
|
|
379
|
+
: (resolvedLanguage === "en"
|
|
380
|
+
? "- This genre has no explicit numerical system and does not need a resource ledger"
|
|
381
|
+
: "- 本题材无数值系统,不需要资源账本");
|
|
382
|
+
const powerBlock = gp.powerScaling
|
|
383
|
+
? (resolvedLanguage === "en" ? "- The story has an explicit power-scaling ladder" : "- 有明确的战力等级体系")
|
|
384
|
+
: "";
|
|
385
|
+
const eraBlock = gp.eraResearch
|
|
386
|
+
? (resolvedLanguage === "en"
|
|
387
|
+
? "- The story needs era/historical grounding (set eraConstraints in book_rules)"
|
|
388
|
+
: "- 需要年代考据支撑(在 book_rules 中设置 eraConstraints)")
|
|
389
|
+
: "";
|
|
390
|
+
const storyBiblePrompt = resolvedLanguage === "en"
|
|
391
|
+
? `Extract from the source text and organize with structured second-level headings:
|
|
392
|
+
## 01_Worldview
|
|
393
|
+
Extracted world setting, core rules, and frame
|
|
394
|
+
|
|
395
|
+
## 02_Protagonist
|
|
396
|
+
Inferred protagonist setup (identity / advantage / personality core / behavioral boundaries)
|
|
397
|
+
|
|
398
|
+
## 03_Factions_and_Characters
|
|
399
|
+
Factions and important supporting characters that appear in the source text
|
|
400
|
+
|
|
401
|
+
## 04_Geography_and_Environment
|
|
402
|
+
Locations, environments, and scene traits drawn from the source text
|
|
403
|
+
|
|
404
|
+
## 05_Title_and_Blurb
|
|
405
|
+
Keep the original title "${book.title}" and generate a matching blurb from the source text`
|
|
406
|
+
: `从正文中提取,用结构化二级标题组织:
|
|
407
|
+
## 01_世界观
|
|
408
|
+
从正文中提取的世界观设定、核心规则体系
|
|
409
|
+
|
|
410
|
+
## 02_主角
|
|
411
|
+
从正文中推断的主角设定(身份/金手指/性格底色/行为边界)
|
|
412
|
+
|
|
413
|
+
## 03_势力与人物
|
|
414
|
+
从正文中出现的势力分布、重要配角(每人:名字、身份、动机、与主角关系、独立目标)
|
|
415
|
+
|
|
416
|
+
## 04_地理与环境
|
|
417
|
+
从正文中出现的地图/场景设定、环境特色
|
|
418
|
+
|
|
419
|
+
## 05_书名与简介
|
|
420
|
+
保留原书名"${book.title}",根据正文内容生成简介`;
|
|
421
|
+
const volumeOutlinePrompt = resolvedLanguage === "en"
|
|
422
|
+
? `Infer the volume plan from existing text:
|
|
423
|
+
- Existing chapters: review the actual structure already present
|
|
424
|
+
- Future projection: predict later directions from active hooks and plot momentum
|
|
425
|
+
For each volume include: title, chapter range, core conflict, and key turning points`
|
|
426
|
+
: `基于已有正文反推卷纲:
|
|
427
|
+
- 已有章节部分:根据实际内容回顾每卷的结构
|
|
428
|
+
- 后续预测部分:基于已有伏笔和剧情走向预测未来方向
|
|
429
|
+
每卷包含:卷名、章节范围、核心冲突、关键转折`;
|
|
430
|
+
const bookRulesPrompt = resolvedLanguage === "en"
|
|
431
|
+
? `Infer book_rules.md as YAML frontmatter plus narrative guidance from character behavior in the source text:
|
|
432
|
+
\`\`\`
|
|
433
|
+
---
|
|
434
|
+
version: "1.0"
|
|
435
|
+
protagonist:
|
|
436
|
+
name: (extract protagonist name from the text)
|
|
437
|
+
personalityLock: [(infer 3-5 personality keywords from behavior)]
|
|
438
|
+
behavioralConstraints: [(infer 3-5 behavioral constraints from behavior)]
|
|
439
|
+
genreLock:
|
|
440
|
+
primary: ${book.genre}
|
|
441
|
+
forbidden: [(2-3 forbidden style intrusions)]
|
|
442
|
+
${gp.numericalSystem ? `numericalSystemOverrides:
|
|
443
|
+
hardCap: (infer from the text)
|
|
444
|
+
resourceTypes: [(extract core resource types from the text)]` : ""}
|
|
445
|
+
prohibitions:
|
|
446
|
+
- (infer 3-5 book-specific prohibitions from the text)
|
|
447
|
+
chapterTypesOverride: []
|
|
448
|
+
fatigueWordsOverride: []
|
|
449
|
+
additionalAuditDimensions: []
|
|
450
|
+
enableFullCastTracking: false
|
|
451
|
+
---
|
|
452
|
+
|
|
453
|
+
## Narrative Perspective
|
|
454
|
+
(Infer the narrative perspective and style from the text)
|
|
455
|
+
|
|
456
|
+
## Core Conflict Driver
|
|
457
|
+
(Infer the book's core conflict and propulsion from the text)
|
|
458
|
+
\`\`\``
|
|
459
|
+
: `从正文中角色行为反推 book_rules.md 格式的 YAML frontmatter + 叙事指导:
|
|
460
|
+
\`\`\`
|
|
461
|
+
---
|
|
462
|
+
version: "1.0"
|
|
463
|
+
protagonist:
|
|
464
|
+
name: (从正文提取主角名)
|
|
465
|
+
personalityLock: [(从行为推断3-5个性格关键词)]
|
|
466
|
+
behavioralConstraints: [(从行为推断3-5条行为约束)]
|
|
467
|
+
genreLock:
|
|
468
|
+
primary: ${book.genre}
|
|
469
|
+
forbidden: [(2-3种禁止混入的文风)]
|
|
470
|
+
${gp.numericalSystem ? `numericalSystemOverrides:
|
|
471
|
+
hardCap: (从正文推断)
|
|
472
|
+
resourceTypes: [(从正文提取核心资源类型)]` : ""}
|
|
473
|
+
prohibitions:
|
|
474
|
+
- (从正文推断3-5条本书禁忌)
|
|
475
|
+
chapterTypesOverride: []
|
|
476
|
+
fatigueWordsOverride: []
|
|
477
|
+
additionalAuditDimensions: []
|
|
478
|
+
enableFullCastTracking: false
|
|
479
|
+
---
|
|
480
|
+
|
|
481
|
+
## 叙事视角
|
|
482
|
+
(从正文推断本书叙事视角和风格)
|
|
483
|
+
|
|
484
|
+
## 核心冲突驱动
|
|
485
|
+
(从正文推断本书的核心矛盾和驱动力)
|
|
486
|
+
\`\`\``;
|
|
487
|
+
const currentStatePrompt = resolvedLanguage === "en"
|
|
488
|
+
? `Reflect the state at the end of the latest chapter:
|
|
489
|
+
| Field | Value |
|
|
490
|
+
| --- | --- |
|
|
491
|
+
| Current Chapter | (latest chapter number) |
|
|
492
|
+
| Current Location | (location at the end of the latest chapter) |
|
|
493
|
+
| Protagonist State | (state at the end of the latest chapter) |
|
|
494
|
+
| Current Goal | (current goal) |
|
|
495
|
+
| Current Constraint | (current constraint) |
|
|
496
|
+
| Current Alliances | (current alliances / opposition) |
|
|
497
|
+
| Current Conflict | (current conflict) |`
|
|
498
|
+
: `反映最后一章结束时的状态卡:
|
|
499
|
+
| 字段 | 值 |
|
|
500
|
+
|------|-----|
|
|
501
|
+
| 当前章节 | (最后一章章节号) |
|
|
502
|
+
| 当前位置 | (最后一章结束时的位置) |
|
|
503
|
+
| 主角状态 | (最后一章结束时的状态) |
|
|
504
|
+
| 当前目标 | (当前目标) |
|
|
505
|
+
| 当前限制 | (当前限制) |
|
|
506
|
+
| 当前敌我 | (当前敌我关系) |
|
|
507
|
+
| 当前冲突 | (当前冲突) |`;
|
|
508
|
+
const pendingHooksPrompt = resolvedLanguage === "en"
|
|
509
|
+
? `Identify all active hooks from the source text (Markdown table):
|
|
510
|
+
| hook_id | start_chapter | type | status | latest_progress | expected_payoff | payoff_timing | notes |`
|
|
511
|
+
: `从正文中识别的所有伏笔(Markdown表格):
|
|
512
|
+
| hook_id | 起始章节 | 类型 | 状态 | 最近推进 | 预期回收 | 回收节奏 | 备注 |`;
|
|
513
|
+
const keyPrinciplesPrompt = resolvedLanguage === "en"
|
|
514
|
+
? `## Key Principles
|
|
515
|
+
|
|
516
|
+
1. Derive everything from the source text; do not invent unsupported settings
|
|
517
|
+
2. Hook extraction must be complete: unresolved clues, hints, and foreshadowing all count
|
|
518
|
+
3. Character inference must come from dialogue and behavior, not assumption
|
|
519
|
+
4. Accuracy first; detailed is better than missing crucial information
|
|
520
|
+
${numericalBlock}
|
|
521
|
+
${powerBlock}
|
|
522
|
+
${eraBlock}`
|
|
523
|
+
: `## 关键原则
|
|
524
|
+
|
|
525
|
+
1. 一切从正文出发,不要臆造正文中没有的设定
|
|
526
|
+
2. 伏笔识别要完整:悬而未决的线索、暗示、预告都算
|
|
527
|
+
3. 角色推断要准确:从对话和行为推断性格,不要想当然
|
|
528
|
+
4. 准确性优先,宁可详细也不要遗漏
|
|
529
|
+
${numericalBlock}
|
|
530
|
+
${powerBlock}
|
|
531
|
+
${eraBlock}`;
|
|
532
|
+
const isSeries = options?.importMode === "series";
|
|
533
|
+
const continuationDirectiveEn = isSeries
|
|
534
|
+
? `## Continuation Direction Requirements (Critical)
|
|
535
|
+
The continuation portion (chapters in volume_outline that have not happened yet) must open up **new narrative space**:
|
|
536
|
+
1. **New conflict dimension**: Do not merely stretch the imported conflict longer. Introduce at least one new conflict vector not yet covered by the source text (new character, new faction, new location, or new time horizon)
|
|
537
|
+
2. **Ignite within 5 chapters**: The first continuation volume must establish a fresh suspense engine within 5 chapters. Do not spend 3 chapters recapping known information
|
|
538
|
+
3. **Scene freshness**: At least 50% of key continuation scenes must happen in locations or situations not already used in the imported chapters
|
|
539
|
+
4. **No repeated meeting rooms**: If the imported chapters end on a meeting/discussion beat, the continuation must restart from action instead of opening another meeting`
|
|
540
|
+
: `## Continuation Direction
|
|
541
|
+
The volume_outline should naturally extend the existing narrative arc. Continue from where the imported chapters left off — advance existing conflicts, pay off planted hooks, and introduce new complications that arise organically from the current situation. Do not recap known information.`;
|
|
542
|
+
const continuationDirectiveZh = isSeries
|
|
543
|
+
? `## 续写方向要求(关键)
|
|
544
|
+
续写部分(volume_outline 中尚未发生的章节)必须设计**新的叙事空间**:
|
|
545
|
+
1. **新冲突维度**:续写不能只是把导入章节的冲突继续拉长。必须引入至少一个原文未涉及的新冲突方向(新角色、新势力、新地点、新时间跨度)
|
|
546
|
+
2. **5章内引爆**:续写的第一卷必须在前5章内建立新悬念,不允许用3章回顾已知信息
|
|
547
|
+
3. **场景新鲜度**:续写部分至少50%的关键场景发生在导入章节未出现的地点或情境中
|
|
548
|
+
4. **不重复会议**:如果导入章节以会议/讨论结束,续写必须从行动开始,不能再开一轮会`
|
|
549
|
+
: `## 续写方向
|
|
550
|
+
卷纲应自然延续已有叙事弧线。从导入章节的结尾处接续——推进现有冲突、兑现已埋伏笔、引入从当前局势中有机产生的新变数。不要回顾已知信息。`;
|
|
551
|
+
const workingModeEn = isSeries
|
|
552
|
+
? `## Working Mode
|
|
553
|
+
|
|
554
|
+
This is not a zero-to-one foundation pass. You must extract durable story truth from the imported chapters **and design a continuation path**. You need to:
|
|
555
|
+
1. Extract worldbuilding, factions, characters, and systems from the source text -> generate story_bible
|
|
556
|
+
2. Infer narrative structure and future arc direction -> generate volume_outline (review existing chapters + design a **new continuation direction**)
|
|
557
|
+
3. Infer protagonist lock, prohibitions, and narrative constraints from character behavior -> generate book_rules
|
|
558
|
+
4. Reflect the latest chapter state -> generate current_state
|
|
559
|
+
5. Extract all active hooks already planted in the text -> generate pending_hooks`
|
|
560
|
+
: `## Working Mode
|
|
561
|
+
|
|
562
|
+
This is not a zero-to-one foundation pass. You must extract durable story truth from the imported chapters **and preserve a clean continuation path**. You need to:
|
|
563
|
+
1. Extract worldbuilding, factions, characters, and systems from the source text -> generate story_bible
|
|
564
|
+
2. Infer narrative structure and near-future arc direction -> generate volume_outline (review existing chapters + continue naturally from where the imported chapters stop)
|
|
565
|
+
3. Infer protagonist lock, prohibitions, and narrative constraints from character behavior -> generate book_rules
|
|
566
|
+
4. Reflect the latest chapter state -> generate current_state
|
|
567
|
+
5. Extract all active hooks already planted in the text -> generate pending_hooks`;
|
|
568
|
+
const workingModeZh = isSeries
|
|
569
|
+
? `## 工作模式
|
|
570
|
+
|
|
571
|
+
这不是从零创建,而是从已有正文中提取和推导,**并设计续写方向**。你需要:
|
|
572
|
+
1. 从正文中提取世界观、势力、角色、力量体系 → 生成 story_bible
|
|
573
|
+
2. 从叙事结构推断卷纲 → 生成 volume_outline(已有章节的回顾 + **续写部分的新方向设计**)
|
|
574
|
+
3. 从角色行为推断主角锁定和禁忌 → 生成 book_rules
|
|
575
|
+
4. 从最新章节状态推断 current_state(反映最后一章结束时的状态)
|
|
576
|
+
5. 从正文中识别已埋伏笔 → 生成 pending_hooks`
|
|
577
|
+
: `## 工作模式
|
|
578
|
+
|
|
579
|
+
这不是从零创建,而是从已有正文中提取和推导,**并为自然续写保留清晰延续路径**。你需要:
|
|
580
|
+
1. 从正文中提取世界观、势力、角色、力量体系 → 生成 story_bible
|
|
581
|
+
2. 从叙事结构推断卷纲 → 生成 volume_outline(回顾已有章节,并从导入章节结束处自然接续)
|
|
582
|
+
3. 从角色行为推断主角锁定和禁忌 → 生成 book_rules
|
|
583
|
+
4. 从最新章节状态推断 current_state(反映最后一章结束时的状态)
|
|
584
|
+
5. 从正文中识别已埋伏笔 → 生成 pending_hooks`;
|
|
585
|
+
const systemPrompt = resolvedLanguage === "en"
|
|
586
|
+
? `You are a professional web-fiction architect. Your task is to reverse-engineer a complete foundation from existing chapters.${contextBlock}
|
|
587
|
+
|
|
588
|
+
${workingModeEn}
|
|
589
|
+
|
|
590
|
+
All output sections — story_bible, volume_outline, book_rules, current_state, and pending_hooks — MUST be written in English. Keep the === SECTION: === tags unchanged.
|
|
591
|
+
|
|
592
|
+
${continuationDirectiveEn}
|
|
593
|
+
${reviewFeedbackBlock}
|
|
594
|
+
## Book Metadata
|
|
595
|
+
|
|
596
|
+
- Title: ${book.title}
|
|
597
|
+
- Platform: ${book.platform}
|
|
598
|
+
- Genre: ${gp.name} (${book.genre})
|
|
599
|
+
- Target Chapters: ${book.targetChapters}
|
|
600
|
+
- Chapter Target Length: ${book.chapterWordCount}
|
|
601
|
+
|
|
602
|
+
## Genre Profile
|
|
603
|
+
|
|
604
|
+
${genreBody}
|
|
605
|
+
|
|
606
|
+
## Output Contract
|
|
607
|
+
|
|
608
|
+
Generate the following sections. Separate every section with === SECTION: <name> ===:
|
|
609
|
+
|
|
610
|
+
=== SECTION: story_bible ===
|
|
611
|
+
${storyBiblePrompt}
|
|
612
|
+
|
|
613
|
+
=== SECTION: volume_outline ===
|
|
614
|
+
${volumeOutlinePrompt}
|
|
615
|
+
|
|
616
|
+
=== SECTION: book_rules ===
|
|
617
|
+
${bookRulesPrompt}
|
|
618
|
+
|
|
619
|
+
=== SECTION: current_state ===
|
|
620
|
+
${currentStatePrompt}
|
|
621
|
+
|
|
622
|
+
=== SECTION: pending_hooks ===
|
|
623
|
+
${pendingHooksPrompt}
|
|
624
|
+
|
|
625
|
+
${keyPrinciplesPrompt}`
|
|
626
|
+
: `你是一个专业的网络小说架构师。你的任务是从已有的小说正文中反向推导完整的基础设定。${contextBlock}
|
|
627
|
+
|
|
628
|
+
${workingModeZh}
|
|
629
|
+
|
|
630
|
+
${continuationDirectiveZh}
|
|
631
|
+
${reviewFeedbackBlock}
|
|
632
|
+
## 书籍信息
|
|
633
|
+
|
|
634
|
+
- 标题:${book.title}
|
|
635
|
+
- 平台:${book.platform}
|
|
636
|
+
- 题材:${gp.name}(${book.genre})
|
|
637
|
+
- 目标章数:${book.targetChapters}章
|
|
638
|
+
- 每章字数:${book.chapterWordCount}字
|
|
639
|
+
|
|
640
|
+
## 题材特征
|
|
641
|
+
|
|
642
|
+
${genreBody}
|
|
643
|
+
|
|
644
|
+
## 生成要求
|
|
645
|
+
|
|
646
|
+
你需要生成以下内容,每个部分用 === SECTION: <name> === 分隔:
|
|
647
|
+
|
|
648
|
+
=== SECTION: story_bible ===
|
|
649
|
+
${storyBiblePrompt}
|
|
650
|
+
|
|
651
|
+
=== SECTION: volume_outline ===
|
|
652
|
+
${volumeOutlinePrompt}
|
|
653
|
+
|
|
654
|
+
=== SECTION: book_rules ===
|
|
655
|
+
${bookRulesPrompt}
|
|
656
|
+
|
|
657
|
+
=== SECTION: current_state ===
|
|
658
|
+
${currentStatePrompt}
|
|
659
|
+
|
|
660
|
+
=== SECTION: pending_hooks ===
|
|
661
|
+
${pendingHooksPrompt}
|
|
662
|
+
|
|
663
|
+
${keyPrinciplesPrompt}`;
|
|
664
|
+
const userMessage = resolvedLanguage === "en"
|
|
665
|
+
? `Generate the complete foundation for an imported ${gp.name} novel titled "${book.title}". Write everything in English.\n\n${chaptersText}`
|
|
666
|
+
: `以下是《${book.title}》的全部已有正文,请从中反向推导完整基础设定:\n\n${chaptersText}`;
|
|
667
|
+
const response = await this.chat([
|
|
668
|
+
{ role: "system", content: systemPrompt },
|
|
669
|
+
{
|
|
670
|
+
role: "user",
|
|
671
|
+
content: userMessage,
|
|
672
|
+
},
|
|
673
|
+
], { maxTokens: 16384, temperature: 0.5 });
|
|
674
|
+
return this.parseSections(response.content);
|
|
675
|
+
}
|
|
676
|
+
async generateFanficFoundation(book, fanficCanon, fanficMode, reviewFeedback) {
|
|
677
|
+
const { profile: gp, body: genreBody } = await readGenreProfile(this.ctx.projectRoot, book.genre);
|
|
678
|
+
const reviewFeedbackBlock = this.buildReviewFeedbackBlock(reviewFeedback, book.language ?? "zh");
|
|
679
|
+
const MODE_INSTRUCTIONS = {
|
|
680
|
+
canon: "剧情发生在原作空白期或未详述的角度。不可改变原作已确立的事实。",
|
|
681
|
+
au: "标注AU设定与原作的关键分歧点,分歧后的世界线自由发展。保留角色核心性格。",
|
|
682
|
+
ooc: "标注角色性格偏离的起点和驱动事件。偏离必须有逻辑驱动。",
|
|
683
|
+
cp: "以配对角色的关系线为主线规划卷纲。每卷必须有关系推进节点。",
|
|
684
|
+
};
|
|
685
|
+
const systemPrompt = `你是一个专业的同人小说架构师。你的任务是基于原作正典为同人小说生成基础设定。
|
|
686
|
+
|
|
687
|
+
## 同人模式:${fanficMode}
|
|
688
|
+
${MODE_INSTRUCTIONS[fanficMode]}
|
|
689
|
+
|
|
690
|
+
## 新时空要求(关键)
|
|
691
|
+
你必须为这本同人设计一个**原创的叙事空间**,而不是复述原作剧情。具体要求:
|
|
692
|
+
1. **明确分岔点**:story_bible 必须标注"本作从原作的哪个节点分岔",或"本作发生在原作未涉及的什么时空"
|
|
693
|
+
2. **独立核心冲突**:volume_outline 的核心冲突必须是原创的,不是原作情节的翻版。原作角色可以出现,但他们面对的是新问题
|
|
694
|
+
3. **5章内引爆**:volume_outline 的第1卷必须在前5章内建立核心悬念,不允许用3章做铺垫才到引爆点
|
|
695
|
+
4. **场景新鲜度**:至少50%的关键场景发生在原作未出现的地点或情境中
|
|
696
|
+
|
|
697
|
+
${reviewFeedbackBlock}
|
|
698
|
+
|
|
699
|
+
## 原作正典
|
|
700
|
+
${fanficCanon}
|
|
701
|
+
|
|
702
|
+
## 题材特征
|
|
703
|
+
${genreBody}
|
|
704
|
+
|
|
705
|
+
## 关键原则
|
|
706
|
+
1. **不发明主要角色** — 主要角色必须来自原作正典的角色档案
|
|
707
|
+
2. 可以添加原创配角,但必须在 story_bible 中标注为"原创角色"
|
|
708
|
+
3. story_bible 保留原作世界观,标注同人的改动/扩展部分,并明确写出**分岔点**和**新时空设定**
|
|
709
|
+
4. volume_outline 不得复述原作剧情节拍。每卷的核心事件必须是原创的,标注"原创"
|
|
710
|
+
5. book_rules 的 fanficMode 必须设为 "${fanficMode}"
|
|
711
|
+
6. 主角设定来自原作角色档案中的第一个角色(或用户在标题中暗示的角色)
|
|
712
|
+
|
|
713
|
+
你需要生成以下内容,每个部分用 === SECTION: <name> === 分隔:
|
|
714
|
+
|
|
715
|
+
=== SECTION: story_bible ===
|
|
716
|
+
世界观(基于原作正典)+ 角色列表(原作角色标注来源,原创角色标注"原创")
|
|
717
|
+
|
|
718
|
+
=== SECTION: volume_outline ===
|
|
719
|
+
卷纲规划。每卷标注:卷名、章节范围、核心事件(标注原作/原创)、关系发展节点
|
|
720
|
+
|
|
721
|
+
=== SECTION: book_rules ===
|
|
722
|
+
\`\`\`
|
|
723
|
+
---
|
|
724
|
+
version: "1.0"
|
|
725
|
+
protagonist:
|
|
726
|
+
name: (从原作角色中选择)
|
|
727
|
+
personalityLock: [(从正典角色档案提取)]
|
|
728
|
+
behavioralConstraints: [(基于原作行为模式)]
|
|
729
|
+
genreLock:
|
|
730
|
+
primary: ${book.genre}
|
|
731
|
+
forbidden: []
|
|
732
|
+
fanficMode: "${fanficMode}"
|
|
733
|
+
allowedDeviations: []
|
|
734
|
+
prohibitions:
|
|
735
|
+
- (3-5条同人特有禁忌)
|
|
736
|
+
---
|
|
737
|
+
(叙事视角和风格指导)
|
|
738
|
+
\`\`\`
|
|
739
|
+
|
|
740
|
+
=== SECTION: current_state ===
|
|
741
|
+
初始状态卡(基于正典起始点)
|
|
742
|
+
|
|
743
|
+
=== SECTION: pending_hooks ===
|
|
744
|
+
初始伏笔池(从正典关键事件和关系中提取)`;
|
|
745
|
+
const response = await this.chat([
|
|
746
|
+
{ role: "system", content: systemPrompt },
|
|
747
|
+
{
|
|
748
|
+
role: "user",
|
|
749
|
+
content: `请为标题为"${book.title}"的${fanficMode}模式同人小说生成基础设定。目标${book.targetChapters}章,每章${book.chapterWordCount}字。`,
|
|
750
|
+
},
|
|
751
|
+
], { maxTokens: 16384, temperature: 0.7 });
|
|
752
|
+
return this.parseSections(response.content);
|
|
753
|
+
}
|
|
754
|
+
buildReviewFeedbackBlock(reviewFeedback, language) {
|
|
755
|
+
const trimmed = reviewFeedback?.trim();
|
|
756
|
+
if (!trimmed)
|
|
757
|
+
return "";
|
|
758
|
+
if (language === "en") {
|
|
759
|
+
return `\n\n## Previous Review Feedback
|
|
760
|
+
The previous foundation draft was rejected. You must explicitly fix the following issues in this regeneration instead of paraphrasing the same design:
|
|
761
|
+
|
|
762
|
+
${trimmed}\n`;
|
|
763
|
+
}
|
|
764
|
+
return `\n\n## 上一轮审核反馈
|
|
765
|
+
上一轮基础设定未通过审核。你必须在这次重生中明确修复以下问题,不能只换措辞重写同一套方案:
|
|
766
|
+
|
|
767
|
+
${trimmed}\n`;
|
|
768
|
+
}
|
|
769
|
+
parseSections(content) {
|
|
770
|
+
const parsedSections = new Map();
|
|
771
|
+
const sectionPattern = /^\s*===\s*SECTION\s*[::]\s*([^\n=]+?)\s*===\s*$/gim;
|
|
772
|
+
const matches = [...content.matchAll(sectionPattern)];
|
|
773
|
+
for (let i = 0; i < matches.length; i++) {
|
|
774
|
+
const match = matches[i];
|
|
775
|
+
const rawName = match[1] ?? "";
|
|
776
|
+
const start = (match.index ?? 0) + match[0].length;
|
|
777
|
+
const end = matches[i + 1]?.index ?? content.length;
|
|
778
|
+
const normalizedName = this.normalizeSectionName(rawName);
|
|
779
|
+
parsedSections.set(normalizedName, content.slice(start, end).trim());
|
|
780
|
+
}
|
|
781
|
+
const extract = (name) => {
|
|
782
|
+
const section = parsedSections.get(this.normalizeSectionName(name));
|
|
783
|
+
if (!section) {
|
|
784
|
+
throw new Error(`Architect output missing required section: ${name}`);
|
|
785
|
+
}
|
|
786
|
+
if (name !== "pending_hooks") {
|
|
787
|
+
return section;
|
|
788
|
+
}
|
|
789
|
+
return this.normalizePendingHooksSection(this.stripTrailingAssistantCoda(section));
|
|
790
|
+
};
|
|
791
|
+
return {
|
|
792
|
+
storyBible: extract("story_bible"),
|
|
793
|
+
volumeOutline: extract("volume_outline"),
|
|
794
|
+
bookRules: extract("book_rules"),
|
|
795
|
+
currentState: extract("current_state"),
|
|
796
|
+
pendingHooks: extract("pending_hooks"),
|
|
797
|
+
};
|
|
798
|
+
}
|
|
799
|
+
normalizeSectionName(name) {
|
|
800
|
+
return name
|
|
801
|
+
.normalize("NFKC")
|
|
802
|
+
.toLowerCase()
|
|
803
|
+
.replace(/[`"'*_]/g, " ")
|
|
804
|
+
.replace(/[^a-z0-9]+/g, "_")
|
|
805
|
+
.replace(/^_+|_+$/g, "");
|
|
806
|
+
}
|
|
807
|
+
stripTrailingAssistantCoda(section) {
|
|
808
|
+
const lines = section.split("\n");
|
|
809
|
+
const cutoff = lines.findIndex((line) => {
|
|
810
|
+
const trimmed = line.trim();
|
|
811
|
+
if (!trimmed)
|
|
812
|
+
return false;
|
|
813
|
+
return /^(如果(?:你愿意|需要|想要|希望)|If (?:you(?:'d)? like|you want|needed)|I can (?:continue|next))/i.test(trimmed);
|
|
814
|
+
});
|
|
815
|
+
if (cutoff < 0) {
|
|
816
|
+
return section;
|
|
817
|
+
}
|
|
818
|
+
return lines.slice(0, cutoff).join("\n").trimEnd();
|
|
819
|
+
}
|
|
820
|
+
normalizePendingHooksSection(section) {
|
|
821
|
+
const rows = section
|
|
822
|
+
.split("\n")
|
|
823
|
+
.map((line) => line.trim())
|
|
824
|
+
.filter((line) => line.startsWith("|"))
|
|
825
|
+
.filter((line) => !line.includes("---"))
|
|
826
|
+
.map((line) => line.split("|").slice(1, -1).map((cell) => cell.trim()))
|
|
827
|
+
.filter((cells) => cells.some(Boolean));
|
|
828
|
+
if (rows.length === 0) {
|
|
829
|
+
return section;
|
|
830
|
+
}
|
|
831
|
+
const dataRows = rows.filter((row) => (row[0] ?? "").toLowerCase() !== "hook_id");
|
|
832
|
+
if (dataRows.length === 0) {
|
|
833
|
+
return section;
|
|
834
|
+
}
|
|
835
|
+
const language = /[\u4e00-\u9fff]/.test(section) ? "zh" : "en";
|
|
836
|
+
const normalizedHooks = dataRows.map((row, index) => {
|
|
837
|
+
const rawProgress = row[4] ?? "";
|
|
838
|
+
const normalizedProgress = this.parseHookChapterNumber(rawProgress);
|
|
839
|
+
const seedNote = normalizedProgress === 0 && this.hasNarrativeProgress(rawProgress)
|
|
840
|
+
? (language === "zh" ? `初始线索:${rawProgress}` : `initial signal: ${rawProgress}`)
|
|
841
|
+
: "";
|
|
842
|
+
const notes = this.mergeHookNotes(row[6] ?? "", seedNote, language);
|
|
843
|
+
return {
|
|
844
|
+
hookId: row[0] || `hook-${index + 1}`,
|
|
845
|
+
startChapter: this.parseHookChapterNumber(row[1]),
|
|
846
|
+
type: row[2] ?? "",
|
|
847
|
+
status: row[3] ?? "open",
|
|
848
|
+
lastAdvancedChapter: normalizedProgress,
|
|
849
|
+
expectedPayoff: row[5] ?? "",
|
|
850
|
+
payoffTiming: row.length >= 8 ? row[6] ?? "" : "",
|
|
851
|
+
notes: row.length >= 8 ? this.mergeHookNotes(row[7] ?? "", seedNote, language) : notes,
|
|
852
|
+
};
|
|
853
|
+
});
|
|
854
|
+
return renderHookSnapshot(normalizedHooks, language);
|
|
855
|
+
}
|
|
856
|
+
parseHookChapterNumber(value) {
|
|
857
|
+
if (!value)
|
|
858
|
+
return 0;
|
|
859
|
+
const match = value.match(/\d+/);
|
|
860
|
+
return match ? parseInt(match[0], 10) : 0;
|
|
861
|
+
}
|
|
862
|
+
parseBriefSections(brief) {
|
|
863
|
+
const sections = {
|
|
864
|
+
worldview: "",
|
|
865
|
+
rules: "",
|
|
866
|
+
outline: "",
|
|
867
|
+
style: "",
|
|
868
|
+
other: "",
|
|
869
|
+
};
|
|
870
|
+
const patterns = [
|
|
871
|
+
{ key: "worldview", start: "==世界观基石start==", end: "==世界观基石end==" },
|
|
872
|
+
{ key: "rules", start: "==创作规则start==", end: "==创作规则end==" },
|
|
873
|
+
{ key: "outline", start: "==书的大纲start==", end: "==书的大纲end==" },
|
|
874
|
+
{ key: "style", start: "==风格与样例start==", end: "==风格与样例end==" },
|
|
875
|
+
{ key: "other", start: "==其他内容start==", end: "==其他内容end==" },
|
|
876
|
+
];
|
|
877
|
+
for (const p of patterns) {
|
|
878
|
+
const startIdx = brief.indexOf(p.start);
|
|
879
|
+
const endIdx = brief.indexOf(p.end);
|
|
880
|
+
if (startIdx !== -1 && endIdx !== -1) {
|
|
881
|
+
sections[p.key] = brief.slice(startIdx + p.start.length, endIdx).trim();
|
|
882
|
+
}
|
|
883
|
+
}
|
|
884
|
+
return sections;
|
|
885
|
+
}
|
|
886
|
+
hasNarrativeProgress(value) {
|
|
887
|
+
const normalized = (value ?? "").trim().toLowerCase();
|
|
888
|
+
if (!normalized)
|
|
889
|
+
return false;
|
|
890
|
+
return !["0", "none", "n/a", "na", "-", "无", "未推进"].includes(normalized);
|
|
891
|
+
}
|
|
892
|
+
mergeHookNotes(notes, seedNote, language) {
|
|
893
|
+
const trimmedNotes = notes.trim();
|
|
894
|
+
const trimmedSeed = seedNote.trim();
|
|
895
|
+
if (!trimmedSeed) {
|
|
896
|
+
return trimmedNotes;
|
|
897
|
+
}
|
|
898
|
+
if (!trimmedNotes) {
|
|
899
|
+
return trimmedSeed;
|
|
900
|
+
}
|
|
901
|
+
return language === "zh"
|
|
902
|
+
? `${trimmedNotes}(${trimmedSeed})`
|
|
903
|
+
: `${trimmedNotes} (${trimmedSeed})`;
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
//# sourceMappingURL=architect.js.map
|