大家好,我是马丁,我前两天在使用Dify构建写小说的AI应用,一个演示版本我已经公开了。地址是
体验版地址
因为使用的是自己的主机,不是云服务器,偶尔的情况可能会出现网络不稳定的情况,耐心等下就行了。
感兴趣的可以自己尝试下,如果想在自己的dify平台上运行、修改,在文末有DSL配置,导入即可。
点进去的界面栈这样:
我还是先说下,这玩意怎么用吧,小说类型可以填写多个,比如玄幻、悬疑、爱情,故事元素你可以随便写,比如爱情、高智商、犯罪、德玛西亚等;小说主题,你也可以写一个简单的故事概要。
章节数量和每章字数,因为是演示版,所以都做了限制,最多有3个章节,每章最多800个字。
风格特点字段是设定小说写作的风格,比如:故事节奏紧密、很多的人物对话、通过对话推进故事进展、人物心理描写细腻、环境描写细致等等。
感兴趣的可以自己尝试下,因为这个工作流中有很多的节点,所有运行时间比较长。
这个小说应用使用到的大模型是gemma2:9b,通过ollama在本地主机上运行的,更大参数的模型效果会更好,可惜,我电脑电脑跑不起来~~
整个工作流中包含12个节点,还有迭代中的写其他章节的节点。
感受
第一个感觉就是,还是比较依赖大模型的,我试过豆包的128K的模型和32K的模型,感觉还是128K的会更好点。也试过使用本地的qwen2:7b和gemm2:9b的模型,gemm2的效果要比前者好点。
第二个问题是,在第一次让大模型给小说根据建议完善的时候,出来的是完整的修改后的小说,但是第二次让它根据建议完善小说的时候,模型返回的有时候就不是修改后的小说了。这个可能是模型的理解能力还是不太够造成的,如果换成更大的模型可能会好点。我试过使用豆包的32k的模型,也是这样。
第三个问题是,但小说的整体字数很多时,因为模型没法一次性给出完整的小说,所以需要分章节输出,但是分章节输出给出的效果就是,每个章节之间的衔接不好,显得比较突兀。这个原因可能是自己的提示词或者工作流设置的还是不够完善造成的。我是把每个章节的大纲都给到模型,然后让它输出第几个章节的内容。这样,它只记得前面章节的大纲,没法知道具体的章节内容,所以才会出现这种情况。可能的优化方法是让大模型把之前的章节内容进行总结,然后再拿着当前的章节大纲一起给到模型,这样可能会好点,但是我没有试过,你有兴趣可以自己测试下~~
DSL配置
app: description: 无论多长的小说都可以搞定 icon: ? icon_background: '#FFEAD5' mode: workflow name: 小说创作-本地演示版kind: appversion: 0.1.1workflow: conversation_variables: [] environment_variables: [] features: file_upload: image: enabled: false number_limits: 3 transfer_methods: - local_file - remote_url opening_statement: '' retriever_resource: enabled: false sensitive_word_avoidance: enabled: false speech_to_text: enabled: false suggested_questions: [] suggested_questions_after_answer: enabled: false text_to_speech: enabled: false language: '' voice: '' graph: edges: - data: isInIteration: false sourceType: iteration targetType: template-transform id: 1724160609005-source-1724169251517-target selected: false source: '1724160609005' sourceHandle: source target: '1724169251517' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: llm id: 1724222200431-source-1724222941382-target selected: false source: '1724222200431' sourceHandle: source target: '1724222941382' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: llm id: 1724222941382-source-1724159722707-target selected: false source: '1724222941382' sourceHandle: source target: '1724159722707' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: llm id: 1724159722707-source-1724224003000-target selected: false source: '1724159722707' sourceHandle: source target: '1724224003000' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: parameter-extractor id: 1724225313253-source-1724229051754-target selected: false source: '1724225313253' sourceHandle: source target: '1724229051754' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: parameter-extractor targetType: iteration id: 1724229051754-source-1724160609005-target selected: false source: '1724229051754' sourceHandle: source target: '1724160609005' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: llm id: 1724224003000-source-1724225313253-target selected: false source: '1724224003000' sourceHandle: source target: '1724225313253' targetHandle: target type: custom zIndex: 0 - data: isInIteration: true iteration_id: '1724160609005' sourceType: if-else targetType: llm id: 1724234539814-true-1724234821042-target selected: false source: '1724234539814' sourceHandle: 'true' target: '1724234821042' targetHandle: target type: custom zIndex: 1002 - data: isInIteration: true iteration_id: '1724160609005' sourceType: llm targetType: llm id: 1724234821042-source-1724345102373-target source: '1724234821042' sourceHandle: source target: '1724345102373' targetHandle: target type: custom zIndex: 1002 - data: isInIteration: true iteration_id: '1724160609005' sourceType: llm targetType: llm id: 1724345102373-source-1724345707464-target source: '1724345102373' sourceHandle: source target: '1724345707464' targetHandle: target type: custom zIndex: 1002 - data: isInIteration: false sourceType: template-transform targetType: llm id: 1724169251517-source-1724346159780-target source: '1724169251517' sourceHandle: source target: '1724346159780' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: llm id: 1724346159780-source-1724346394465-target source: '1724346159780' sourceHandle: source target: '1724346394465' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: start targetType: llm id: 1724159427762-source-1724222200431-target source: '1724159427762' sourceHandle: source target: '1724222200431' targetHandle: target type: custom zIndex: 0 - data: isInIteration: false sourceType: llm targetType: end id: 1724346394465-source-1724160077784-target source: '1724346394465' sourceHandle: source target: '1724160077784' targetHandle: target type: custom zIndex: 0 nodes: - data: desc: '' selected: true title: 开始 type: start variables: - label: 小说类型 max_length: 48 options: [] required: true type: text-input variable: type - label: 章节数量 max_length: 48 options: - '1' - '2' - '3' required: true type: select variable: chapter_count - label: 故事元素 max_length: 48 options: [] required: true type: text-input variable: element - label: 每章字数 max_length: 48 options: - '100' - '400' - '800' required: true type: select variable: chapter_zifu_num - label: 小说主题 max_length: 1280 options: [] required: false type: paragraph variable: topic - label: 写作特点 max_length: 2000 options: [] required: false type: paragraph variable: features height: 326 id: '1724159427762' position: x: -713.3201798028794 y: 227.78492012105585 positionAbsolute: x: -713.3201798028794 y: 227.78492012105585 selected: true sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: 主要角色背景 model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: d311d7c9-ba2c-4152-a66d-b68d2a6c8381 role: system text: '你是一名作家,你的任务是根据小说的已知信息设定小说的主要角色和背景。 具体目标:参考下面已知小说信息,创造3-5个主要角色,为每个角色提供一个详细的背景描述,包括外贸、性格、历史和动机。 输出规范要求:每个角色的描述不超过150个字。 -------------下面是已知小说信息------------ 故事背景:{{#1724222200431.text#}} 故事概览:{{#1724222941382.text#}} ' selected: false title: 角色背景 type: llm variables: [] vision: enabled: false height: 177 id: '1724159722707' position: x: 579.9188172305878 y: 241.58087147858419 positionAbsolute: x: 579.9188172305878 y: 241.58087147858419 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: desc: '' outputs: - value_selector: - '1724346394465' - text variable: result selected: false title: 结束 type: end height: 131 id: '1724160077784' position: x: 4156.014626634642 y: -105.74727794845569 positionAbsolute: x: 4156.014626634642 y: -105.74727794845569 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: desc: '' height: 282 iterator_selector: - '1724229051754' - chapter_dagangs output_selector: - '1724234821042' - text output_type: array[string] selected: false startNodeType: if-else start_node_id: '1724234539814' title: 迭代 type: iteration width: 1459.8824245199337 height: 282 id: '1724160609005' position: x: 1639.1874875601834 y: 255.6557646854409 positionAbsolute: x: 1639.1874875601834 y: 255.6557646854409 selected: false sourcePosition: right targetPosition: left type: custom width: 1460 zIndex: 1 - data: desc: 将多个章节合并 selected: false template: '{{ first_content + ''\r\n'' + (arg1|join(''\r\n'')) }}' title: 合并章节 type: template-transform variables: - value_selector: - '1724160609005' - output variable: arg1 - value_selector: - '1724225313253' - text variable: first_content height: 111 id: '1724169251517' position: x: 2969.6151885105674 y: -105.74727794845569 positionAbsolute: x: 2969.6151885105674 y: -105.74727794845569 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: 故事背景 model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: efb3e47b-1e10-4441-992a-8d94ababf5f7 role: system text: '你是一名作家,你的任务是设定一个引人入胜的小说背景和主题。 具体目标是:参考下面的已知小说信息,创建一个原创的小说背景,包括故事发生的地址、时代背景(如现代、未来等)、主题(如爱情、奋斗、复仇等)。 输出规范要求:提供200字以内的描述,确保背景和主题具有吸引力和独特性。 -------------下面是已知小说信息------------ 小说类型:{{#1724159427762.type#}} 相关元素:{{#1724159427762.element#}} 主要内容:{{#1724159427762.topic#}} ' selected: false title: background type: llm variables: [] vision: enabled: false height: 177 id: '1724222200431' position: x: 6.357518373344192 y: 241.58087147858419 positionAbsolute: x: 6.357518373344192 y: 241.58087147858419 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: 故事概览 model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: cf91162c-0f57-4f8d-b404-9b32316d7336 role: system text: '你是一名作家,你现在的任务是概述小说的主要情节。 具体目标:参考下面的已知小说信息,提供一个简洁的故事概览,包括起始事件、主要冲突、高潮和结局。 输出规范要求:300字以内的故事概述,突出主要转折点和冲突。 -------------下面是已知小说信息------------ 小说类型:{{#1724159427762.type#}} 相关元素:{{#1724159427762.element#}} 故事背景:{{#1724222200431.text#}}' selected: false title: 故事概览 type: llm variables: [] vision: enabled: false height: 177 id: '1724222941382' position: x: 298.2756640441767 y: 241.58087147858419 positionAbsolute: x: 298.2756640441767 y: 241.58087147858419 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: 章节大纲 model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: 3bbf57eb-d139-4d23-815d-b972035f03fb role: system text: '你是一名作家,你现在的任务是制定小说的章节大纲。 具体目标:参考下面的已知小说信息,规划每一章节的重点内容和目标。 输出规范要求:输出{{#1724159427762.chapter_count#}}个章节,每章包含章节标题和名称。 ---------------下面是已知小说信息------------ 故事概览:{{#1724222941382.text#}} 主要角色和背景:{{#1724159722707.text#}} ' selected: false title: 章节大纲 type: llm variables: [] vision: enabled: false height: 177 id: '1724224003000' position: x: 889.6485637527737 y: 241.58087147858419 positionAbsolute: x: 889.6485637527737 y: 241.58087147858419 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: 第一章 model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: 2efd975d-8818-4864-a6f8-8fa0fe13b70e role: system text: '你是一名作家,你的任务是创作小说的第一章。 具体目标:参考小说的已知信息,写出引人入胜的第一章,设定故事背景、引入主要角色。 输出规范要求:大约{{#1724159427762.chapter_zifu_num#}}字,风格符合整篇小说设定的背景和主题。要包含章节标题和章节内容。中文回复。 ' - role: user text: '---------------下面是已知小说信息------------ 故事概览:{{#1724222941382.text#}} 写作特点:{{#1724159427762.features#}} 主要角色和背景:{{#1724159722707.text#}} 章节大纲:{{#1724224003000.text#}}' selected: false title: 第一章 type: llm variables: [] vision: enabled: false height: 177 id: '1724225313253' position: x: 1193.788123088926 y: 278.8887084417923 positionAbsolute: x: 1193.788123088926 y: 278.8887084417923 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: desc: '' instruction: '根据小说的章节信息生成一个JSON对象,Example:{ "chapter_dagangs":["章节标题:章节内容","章节标题:章节内容"] } chapter_dagangs是一个数组,每个元素包含章节的标题和内容。 ' model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama parameters: - description: 章节大纲数组 name: chapter_dagangs required: false type: array[string] query: - '1724224003000' - text reasoning_mode: prompt selected: false title: 提取章节大纲列表 type: parameter-extractor variables: [] height: 143 id: '1724229051754' position: x: 1790.2461793411233 y: 241.58087147858419 positionAbsolute: x: 1790.2461793411233 y: 241.58087147858419 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: cases: - case_id: 'true' conditions: - comparison_operator: '>' id: fc8f5e36-d56c-42fa-b20b-803330b62aa1 value: '0' varType: number variable_selector: - '1724160609005' - index id: 'true' logical_operator: and desc: '' isInIteration: true isIterationStart: true iteration_id: '1724160609005' selected: false title: 条件分支 type: if-else extent: parent height: 185 id: '1724234539814' parentId: '1724160609005' position: x: 117 y: 85 positionAbsolute: x: 1756.1874875601834 y: 340.6557646854409 selected: false sourcePosition: right targetPosition: left type: custom width: 244 zIndex: 1001 - data: context: enabled: true variable_selector: - '1724159427762' - chapter_zifu_num desc: '' isInIteration: true iteration_id: '1724160609005' model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: 5c4a3648-24a0-45a8-bc78-a5b71e43bd24 role: system text: '你是一名作家,你的任务是完成【待完成章节】部分的章节的创作。 具体目标:根据大纲完成创作,确保故事的连续和逻辑性。完成章节{{#1724160609005.item#}}内容的创作。 输出规范要求:约{{#context#}}个字,保证故事节奏和风格的一致性。要输出章节标题和章节内容,格式:‘章节标题...:章节内容...’。中文回复。 --------------下面是已知小说信息------------ 故事概览:{{#1724222941382.text#}} 主要角色和背景:{{#1724159722707.text#}} 写作特点和风格:{{#1724159427762.features#}} 章节大纲:{{#1724224003000.text#}} 【待完成章节】{{#1724160609005.item#}} ' selected: false title: 其他章节内容 type: llm variables: [] vision: enabled: false extent: parent height: 143 id: '1724234821042' parentId: '1724160609005' position: x: 590.4318958188042 y: 85 positionAbsolute: x: 2229.6193833789875 y: 340.6557646854409 selected: false sourcePosition: right targetPosition: left type: custom width: 244 zIndex: 1002 - data: context: enabled: false variable_selector: [] desc: '' isInIteration: true iteration_id: '1724160609005' model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: 1a3a5741-56a8-46f4-8c3c-c38e7693df63 role: system text: '你是一名小说审核,你的任务是审核下面的小说内容,找出不符合逻辑或者不够好的地方。并给出建议。 输出格式要求: “”“ 修改建议:xxxxx ”“” 中文回复。 ' - role: user text: '以下是你要审核的小说: {{#1724234821042.text#}}' selected: false title: 章节审核 type: llm variables: [] vision: enabled: false extent: parent height: 143 id: '1724345102373' parentId: '1724160609005' position: x: 895.8824245199339 y: 85 positionAbsolute: x: 2535.0699120801173 y: 340.6557646854409 selected: false sourcePosition: right targetPosition: left type: custom width: 244 zIndex: 1002 - data: context: enabled: false variable_selector: [] desc: '' isInIteration: true iteration_id: '1724160609005' model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: a96b281e-d078-449d-ad08-006f772e21a9 role: system text: '根据下面小说章节的修改建议,对小说章节进行完善和修改。 返回格式要求:返回修改好的小说章节。中文回复。 ' - id: 3040c4f8-206b-494e-94de-d5d731cf4836 role: user text: '小说章节内容:{{#1724234821042.text#}} 修改建议:{{#1724345102373.text#}} ' selected: false title: LLM 9 type: llm variables: [] vision: enabled: false extent: parent height: 143 id: '1724345707464' parentId: '1724160609005' position: x: 1200 y: 85 positionAbsolute: x: 2839.1874875601834 y: 340.6557646854409 selected: false sourcePosition: right targetPosition: left type: custom width: 244 zIndex: 1002 - data: context: enabled: false variable_selector: [] desc: '' model: completion_params: temperature: 0.7 mode: chat name: gemma2 provider: ollama prompt_template: - id: 0b321168-973d-43dd-ab2f-4ffac07f1349 role: system text: '你是一名小说审核,你的任务是对下面的小说进行审核,给出修改建议。中文回复。 ' - id: 6f30d6d8-38b1-4312-8641-255913814844 role: user text: ' 要审核的小说内容如下: {{#1724169251517.output#}}' selected: false title: 修改建议 type: llm variables: [] vision: enabled: false height: 143 id: '1724346159780' position: x: 3288.342027657574 y: -130.06431854542257 positionAbsolute: x: 3288.342027657574 y: -130.06431854542257 selected: false sourcePosition: right targetPosition: left type: custom width: 244 - data: context: enabled: false variable_selector: [] desc: '' model: completion_params: temperature: 0.7 mode: chat name: lite-32k provider: volcengine_maas prompt_template: - id: eea89eda-40c7-4562-aef5-67f09d3c1cf7 role: system text: '你的任务是根据小说的修改建议对小说进行完善、修改,使其达到要求。 任务:根据建议,重写下面待完善的小说。 目标:返回重写后的整篇小说。 返回格式要求:你要返回完整、修改后的小说整篇内容。 ' - id: 52a3fa5f-ade9-4119-b49b-58245b9f5566 role: user text: '待完善小说内容是: {{#1724169251517.output#}} 小说修改建议: {{#1724346159780.text#}} ' selected: false title: 完善小说 type: llm variables: [] vision: enabled: false height: 143 id: '1724346394465' position: x: 3653.0090804133365 y: -130.06431854542257 positionAbsolute: x: 3653.0090804133365 y: -130.06431854542257 selected: false sourcePosition: right targetPosition: left type: custom width: 244 viewport: x: 218.43058816414282 y: 166.9688453821999 zoom: 0.25
好了,这期就说到这里,我是马丁,下期再见~