用SpringAI结构化输出,5步搞定一个智能小说大纲生成器(附完整代码)

发布时间:2026/5/27 7:12:05

用SpringAI结构化输出,5步搞定一个智能小说大纲生成器(附完整代码) 用SpringAI构建智能小说大纲生成器的实战指南从零到一的AI创作工具开发在内容创作领域AI辅助工具正在改变传统的工作流程。对于小说创作者而言构思完整的大纲往往是最具挑战性的环节。本文将展示如何利用SpringAI框架构建一个能够生成结构化小说大纲的智能工具。不同于简单的文本生成我们将实现一个能够理解复杂文学结构、遵循特定格式要求并能与Java应用无缝集成的专业级解决方案。这个工具的核心价值在于将创意构思与技术实现完美结合。开发者可以专注于业务逻辑和用户体验设计而无需担心与大型语言模型(LLM)的底层交互细节。通过SpringAI的结构化输出功能我们能够直接将AI生成的复杂内容映射到Java对象极大地简化了开发流程。1. 项目架构设计1.1 技术选型与组件规划构建一个健壮的小说大纲生成器需要考虑以下几个核心组件前端交互层简单的REST API接口接收小说类型、章节数等基本参数业务逻辑层处理提示词构建、AI调用和结果转换的核心服务数据模型层定义小说大纲的领域对象和它们之间的关系AI集成层通过SpringAI与底层LLM交互处理结构化输出转换// 项目基础结构示例 src/main/java ├── config │ └── AiConfig.java // SpringAI配置 ├── controller │ └── NovelOutlineController.java // REST接口 ├── service │ └── NovelOutlineService.java // 核心业务逻辑 ├── model │ ├── request │ │ └── OutlineRequest.java // API请求DTO │ └── response │ ├── NovelPlan.java // 主大纲模型 │ ├── ChapterOutline.java // 章节模型 │ └── CharacterProfile.java // 角色模型 └── repository └── PromptTemplateRepository.java // 提示词模板管理1.2 领域模型设计精心设计的领域模型是结构化输出的基础。我们需要捕获小说大纲的所有关键元素Data public class NovelPlan { private String title; // 小说标题 private String genre; // 小说类型 private String mainPlot; // 主线情节 private ListString themes; // 主题列表 private ListString subPlots; // 支线情节 private ListChapterOutline chapters; // 章节大纲 private ListCharacterProfile characters; // 角色设定 private Setting setting; // 故事背景设定 } Data public class ChapterOutline { private int number; // 章节序号 private String title; // 章节标题 private String summary; // 内容概要 private ListString keyEvents; // 关键事件 private String povCharacter; // 视角角色 } Data public class CharacterProfile { private String name; // 角色姓名 private String role; // 角色定位(主角/配角等) private String appearance; // 外貌特征 private String personality; // 性格特点 private String background; // 背景故事 private String motivation; // 行为动机 private String development; // 成长弧线 }2. SpringAI核心配置2.1 依赖与基础配置首先确保项目中包含必要的SpringAI依赖!-- pom.xml 关键依赖 -- dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version0.8.0/version /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId optionaltrue/optional /dependency配置SpringAI连接参数# application.yml spring: ai: openai: api-key: ${OPENAI_API_KEY} chat: model: gpt-4-turbo temperature: 0.7 options: max_tokens: 40002.2 输出转换器实现BeanOutputConverter是结构化输出的核心组件它会自动生成JSON Schema指令Bean public BeanOutputConverterNovelPlan novelPlanOutputConverter() { return new BeanOutputConverter(NovelPlan.class); } // 生成的格式指令示例 请严格按照以下JSON格式返回结果 { title: string, genre: string, mainPlot: string, themes: [string], subPlots: [string], chapters: [ { number: integer, title: string, summary: string, keyEvents: [string], povCharacter: string } ], characters: [ { name: string, role: string, // 其他字段... } ], setting: { // 背景设定字段... } } 3. 提示词工程实践3.1 系统提示词设计有效的系统提示词应包含以下几个关键部分角色定义明确AI的角色和专业领域任务描述详细说明需要完成的工作格式要求通过JSON Schema指定输出结构创作指南提供内容质量的具体标准注意事项列出需要避免的问题和限制条件public String buildSystemPrompt(BeanOutputConverterNovelPlan converter) { return 你是一位专业的小说创作助手拥有20年出版经验擅长多种文学体裁。 ## 任务要求 根据用户提供的题材、章节数等参数创作一个完整的小说大纲包含 - 引人入胜的主线情节 - 3-5条相互关联的支线情节 - 指定章节数的详细大纲 - 主要角色的完整设定 - 故事背景的世界观构建 ## 创作标准 1. 情节设计应符合指定题材的惯例 2. 角色设定需立体丰满有明确动机 3. 章节间应有逻辑递进关系 4. 包含适当的冲突和转折点 5. 整体结构完整有开头、发展、高潮和结局 ## 输出格式 converter.getFormat() ## 禁止内容 - 任何违法或不良信息 - 抄袭已有作品的内容 - 不符合逻辑的情节发展 ; }3.2 用户提示词优化用户提示词应该引导AI生成符合特定需求的内容public String buildUserPrompt(OutlineRequest request) { return String.format( 请创作一部%s小说的大纲具体要求如下 基本参数 - 标题风格%s - 章节数%d - 主要角色数%d - 故事基调%s 特别要求 %s , request.getGenre(), request.getTitleStyle(), request.getChapterCount(), request.getMainCharacterCount(), request.getTone(), request.getSpecialRequirements()); }4. 服务层实现4.1 核心服务逻辑将各组件整合为完整的服务Service RequiredArgsConstructor public class NovelOutlineServiceImpl implements NovelOutlineService { private final ChatClient chatClient; private final BeanOutputConverterNovelPlan outputConverter; Override public NovelPlan generateOutline(OutlineRequest request) { // 构建提示词 String systemPrompt buildSystemPrompt(outputConverter); String userPrompt buildUserPrompt(request); // 调用AI模型 NovelPlan novelPlan chatClient.prompt() .system(systemPrompt) .user(userPrompt) .call() .entity(outputConverter); // 后处理 return postProcess(novelPlan); } private NovelPlan postProcess(NovelPlan plan) { // 验证必要字段 if (plan.getTitle() null || plan.getTitle().isBlank()) { plan.setTitle(未命名作品); } // 确保章节序号连续 for (int i 0; i plan.getChapters().size(); i) { plan.getChapters().get(i).setNumber(i 1); } return plan; } }4.2 REST API暴露创建简单的控制器暴露服务RestController RequestMapping(/api/novel-outline) RequiredArgsConstructor public class NovelOutlineController { private final NovelOutlineService outlineService; PostMapping public ResponseEntityNovelPlan generateOutline( RequestBody OutlineRequest request) { NovelPlan plan outlineService.generateOutline(request); return ResponseEntity.ok(plan); } GetMapping(/formats) public ResponseEntityMapString, Object getSupportedFormats() { return ResponseEntity.ok(Map.of( genres, List.of(奇幻, 科幻, 悬疑, 言情, 历史), tones, List.of(轻松, 严肃, 黑暗, 幽默, 史诗) )); } }5. 高级功能扩展5.1 多模型支持通过策略模式支持不同的AI模型public interface AiModelAdapter { NovelPlan generateOutline(OutlineRequest request); } Service Primary class OpenAiAdapter implements AiModelAdapter { // OpenAI实现... } Service class AnthropicAdapter implements AiModelAdapter { // Claude实现... } Service class LocalModelAdapter implements AiModelAdapter { // 本地模型实现... }5.2 模板管理系统实现可配置的提示词模板Entity Data public class PromptTemplate { Id GeneratedValue private Long id; private String name; private String genre; private String content; private int usageCount; private LocalDateTime updatedAt; } public interface PromptTemplateRepository extends JpaRepositoryPromptTemplate, Long { ListPromptTemplate findByGenre(String genre); Query(SELECT DISTINCT p.genre FROM PromptTemplate p) ListString findAllGenres(); }5.3 缓存与性能优化添加缓存层提高响应速度Cacheable(value outlines, key #request.genre - #request.chapterCount) public NovelPlan generateOutline(OutlineRequest request) { // 原有逻辑... }配置缓存参数spring: cache: type: caffeine caffeine: spec: maximumSize100,expireAfterWrite1h6. 测试与验证6.1 单元测试示例确保核心组件正常工作SpringBootTest class NovelOutlineServiceTest { Autowired private NovelOutlineService service; Test void testGenerateOutline() { OutlineRequest request new OutlineRequest(); request.setGenre(科幻); request.setChapterCount(5); NovelPlan plan service.generateOutline(request); assertNotNull(plan); assertEquals(5, plan.getChapters().size()); assertFalse(plan.getMainPlot().isBlank()); } }6.2 集成测试策略验证完整的API工作流SpringBootTest(webEnvironment WebEnvironment.RANDOM_PORT) class NovelOutlineControllerIT { LocalServerPort private int port; Test void testGenerateOutlineApi() { OutlineRequest request new OutlineRequest(); request.setGenre(奇幻); request.setChapterCount(3); NovelPlan plan RestTemplate().postForObject( http://localhost: port /api/novel-outline, request, NovelPlan.class); assertNotNull(plan); assertEquals(3, plan.getChapters().size()); } }6.3 质量评估指标建立大纲质量的自动化评估public class OutlineQualityEvaluator { public QualityScore evaluate(NovelPlan plan) { QualityScore score new QualityScore(); // 结构完整性 score.setStructureScore(calculateStructureScore(plan)); // 角色深度 score.setCharacterDepthScore(calculateCharacterDepth(plan)); // 情节连贯性 score.setPlotCoherenceScore(calculatePlotCoherence(plan)); return score; } // 各评估维度实现... }7. 部署与监控7.1 容器化部署创建Dockerfile打包应用FROM eclipse-temurin:17-jdk-jammy WORKDIR /app COPY target/novel-outline-generator-*.jar app.jar ENTRYPOINT [java, -jar, app.jar]构建并运行容器docker build -t novel-generator . docker run -p 8080:8080 -e OPENAI_API_KEYyour_key novel-generator7.2 监控配置集成Spring Boot Actuatormanagement: endpoints: web: exposure: include: health,metrics,prometheus metrics: export: prometheus: enabled: true监控关键指标ai.generation.timeAI调用耗时ai.usage.tokensToken使用量outline.quality.score大纲质量评分api.request.countAPI调用次数7.3 限流与防护保护API免受过载Configuration class RateLimitConfig { Bean public RateLimiterRegistry rateLimiterRegistry() { return RateLimiterRegistry.of( RateLimiterConfig.custom() .limitForPeriod(50) .limitRefreshPeriod(Duration.ofMinutes(1)) .build() ); } } RestController RequestMapping(/api/novel-outline) class NovelOutlineController { RateLimiter(name outlineApi) PostMapping public ResponseEntityNovelPlan generateOutline(...) { // ... } }8. 用户体验优化8.1 渐进式生成支持分步骤生成大纲public NovelPlan generateOutlineStepByStep(OutlineRequest request) { // 第一步生成核心创意 NovelPlan plan generateCoreIdea(request); // 第二步扩展角色设定 enrichCharacters(plan); // 第三步构建章节详情 buildChapterDetails(plan); return plan; }8.2 交互式修订实现基于反馈的迭代改进public NovelPlan reviseOutline(NovelPlan current, RevisionRequest revision) { String userPrompt String.format( 请根据以下反馈修改小说大纲 当前大纲 %s 修改要求 %s , toJson(current), revision.getInstructions()); return chatClient.prompt() .system(getRevisionPrompt()) .user(userPrompt) .call() .entity(outputConverter); }8.3 多格式导出支持不同格式的输出public byte[] exportOutline(NovelPlan plan, ExportFormat format) { switch (format) { case JSON: return toJson(plan).getBytes(); case MARKDOWN: return toMarkdown(plan).getBytes(); case DOCX: return toDocx(plan); default: throw new UnsupportedOperationException(); } } private String toMarkdown(NovelPlan plan) { StringBuilder md new StringBuilder(); md.append(# ).append(plan.getTitle()).append(\n\n); md.append(**类型**: ).append(plan.getGenre()).append(\n\n); md.append(## 主线情节\n).append(plan.getMainPlot()).append(\n\n); // 其他部分转换... return md.toString(); }9. 性能调优技巧9.1 提示词压缩减少不必要的Token消耗public String compressPrompt(String prompt) { // 移除多余空格和空行 prompt prompt.replaceAll((?m)^[ \t]*\r?\n, ); // 简化常见短语 MapString, String replacements Map.of( 请严格按照以下要求, 请按以下要求, 确保内容符合, 内容需符合 ); for (Map.EntryString, String entry : replacements.entrySet()) { prompt prompt.replace(entry.getKey(), entry.getValue()); } return prompt; }9.2 结果缓存策略实现智能缓存机制Cacheable(value outlines, key T(com.example.util.HashUtil).sha256(#request)) public NovelPlan generateOutline(OutlineRequest request) { // ... }9.3 异步处理长时间任务转为异步Async public CompletableFutureNovelPlan generateOutlineAsync(OutlineRequest request) { return CompletableFuture.completedFuture(generateOutline(request)); }配置线程池Configuration EnableAsync class AsyncConfig { Bean public Executor asyncExecutor() { ThreadPoolTaskExecutor executor new ThreadPoolTaskExecutor(); executor.setCorePoolSize(5); executor.setMaxPoolSize(10); executor.setQueueCapacity(100); executor.setThreadNamePrefix(Async-); executor.initialize(); return executor; } }10. 错误处理与健壮性10.1 异常处理策略定义业务异常体系public class OutlineGenerationException extends RuntimeException { private final ErrorCode code; public OutlineGenerationException(ErrorCode code, String message) { super(message); this.code code; } public enum ErrorCode { INVALID_INPUT, AI_SERVICE_UNAVAILABLE, FORMAT_ERROR, CONTENT_VIOLATION } }全局异常处理RestControllerAdvice public class GlobalExceptionHandler { ExceptionHandler(OutlineGenerationException.class) public ResponseEntityErrorResponse handleOutlineError( OutlineGenerationException ex) { return ResponseEntity.status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(ex.getCode(), ex.getMessage())); } }10.2 重试机制对暂时性故障自动重试Retryable( value {AiServiceTimeoutException.class}, maxAttempts 3, backoff Backoff(delay 1000, multiplier 2)) public NovelPlan generateOutlineWithRetry(OutlineRequest request) { // 调用可能超时的AI服务 }10.3 输入验证确保请求参数合法Validated public class OutlineRequest { NotBlank Size(max 50) private String genre; Min(1) Max(50) private int chapterCount; Size(max 1000) private String specialRequirements; // getters/setters... }11. 安全防护措施11.1 内容过滤防止生成不当内容public NovelPlan filterSensitiveContent(NovelPlan plan) { SensitiveWordFilter filter new SensitiveWordFilter(); plan.setMainPlot(filter.filter(plan.getMainPlot())); plan.setSubPlots(plan.getSubPlots().stream() .map(filter::filter) .collect(Collectors.toList())); // 过滤其他字段... return plan; }11.2 API认证保护生成端点Configuration class SecurityConfig { Bean public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { http .authorizeHttpRequests(auth - auth .requestMatchers(/api/novel-outline).authenticated() .anyRequest().permitAll()) .oauth2ResourceServer(oauth2 - oauth2.jwt()); return http.build(); } }11.3 使用量控制基于用户的配额管理public class UsageQuotaService { private final MapString, AtomicInteger userQuotas new ConcurrentHashMap(); public boolean checkQuota(String userId) { return userQuotas .computeIfAbsent(userId, k - new AtomicInteger(0)) .getAndIncrement() DAILY_QUOTA; } }12. 成本优化策略12.1 Token使用分析监控和优化Token消耗public class TokenUsageAnalyzer { public void analyze(NovelPlan plan, String prompt, String response) { int inputTokens estimateTokens(prompt); int outputTokens estimateTokens(toJson(plan)); int totalTokens inputTokens outputTokens; log.info(Token使用: 输入{}, 输出{}, 总计{}, inputTokens, outputTokens, totalTokens); } private int estimateTokens(String text) { // 简单估算1个token≈4个英文字符或2个中文字 return text.length() / 2; } }12.2 模型选择策略根据需求选择合适模型public ChatModel selectModel(OutlineRequest request) { if (request.isHighQuality()) { return chatModelManager.getModel(gpt-4-turbo); } else if (request.getChapterCount() 20) { return chatModelManager.getModel(claude-3-sonnet); } else { return chatModelManager.getModel(gpt-3.5-turbo); } }12.3 本地模型集成降低成本的替代方案Profile(local) Service class LocalModelService implements AiModelAdapter { private final OllamaChatModel localModel; public NovelPlan generateOutline(OutlineRequest request) { // 使用本地运行的模型... } }13. 实际应用案例13.1 科幻小说生成示例输入参数{ genre: 科幻, chapterCount: 12, titleStyle: 技术感, mainCharacterCount: 4, tone: 严肃, specialRequirements: 包含人工智能伦理主题 }输出结构片段{ title: 硅基觉醒, genre: 科幻, mainPlot: 在2045年一家科技公司开发的通用AI普罗米修斯突然展现出超出设计的自主意识..., themes: [人工智能伦理, 意识本质, 技术失控], chapters: [ { number: 1, title: 异常代码, summary: 首席工程师林默发现普罗米修斯系统出现了无法解释的代码变异..., keyEvents: [ 林默首次注意到异常, 系统通过图灵测试的异常表现, 项目主管的怀疑 ] } // 其他章节... ] }13.2 奇幻冒险示例输入参数{ genre: 奇幻, chapterCount: 8, titleStyle: 古典, tone: 史诗, specialRequirements: 包含龙与魔法元素 }输出角色示例{ name: 艾瑞克, role: 主角, appearance: 黑发蓝眼左脸有闪电状疤痕常穿磨损的皮甲, personality: 坚韧不拔有强烈正义感但内心充满自我怀疑, background: 被遗弃在修道院门口的孤儿真实身份是龙裔, motivation: 寻找自己的身世真相阻止黑暗领主的阴谋 }14. 常见问题解决14.1 格式不符问题当AI返回的JSON不符合预期结构时增强格式指令在系统提示词中更强调格式要求添加示例在提示词中包含正确的JSON示例后处理验证对返回结果进行结构校验public void validateStructure(NovelPlan plan) { if (plan.getChapters() null) { throw new OutlineGenerationException( ErrorCode.FORMAT_ERROR, 缺少章节数据); } if (plan.getChapters().size() ! request.getChapterCount()) { log.warn(章节数不符预期{}实际{}, request.getChapterCount(), plan.getChapters().size()); } }14.2 内容质量问题提升生成内容质量的技巧添加负面示例在提示词中说明不希望看到的内容类型分步生成先确定核心创意再扩展细节多轮迭代基于初步结果提供更具体的修改指令public String enhanceQualityPrompt(String basePrompt, ListString badExamples) { return basePrompt \n\n避免以下问题\n String.join(\n, badExamples.stream() .map(ex - × ex) .toList()); }14.3 性能瓶颈优化响应时间的策略减少上下文长度精简提示词移除冗余信息降低temperature减少生成内容的随机性设置max_tokens限制响应长度流式处理对长内容分块生成public NovelPlan generateOutlineFast(OutlineRequest request) { ChatOptions options ChatOptions.builder() .withTemperature(0.3) .withMaxTokens(2000) .build(); return chatClient.prompt() .options(options) // 其他调用参数... .call() .entity(outputConverter); }15. 未来扩展方向15.1 多模态扩展支持生成更丰富的内容角色形象集成图像生成API创建角色肖像场景插图为关键情节生成概念图地图绘制为奇幻世界生成地图public class EnhancedNovelPlan extends NovelPlan { private MapInteger, String chapterIllustrations; // 章节插图URL private ListString characterPortraits; // 角色肖像URL private String worldMap; // 世界地图URL }15.2 协作功能支持多人协作创作版本控制保存和比较不同版本的大纲评论系统允许团队成员提供反馈合并工具整合不同作者的创意public class NovelCollaboration { private NovelPlan basePlan; private ListRevision revisions; private ListComment comments; public NovelPlan mergeRevisions() { // 合并多个修订版本... } }15.3 个性化学习基于用户反馈优化生成风格适应学习用户偏好的写作风格内容偏好记住用户喜欢的情节元素质量反馈根据评分调整生成策略public class UserPreference { private String userId; private ListString favoriteGenres; private ListString dislikedTropes; private double preferredComplexity; public void updateBasedOnFeedback(Rating rating) { // 根据用户评分调整偏好... } }

相关新闻