大家好,我是马丁,我前两天在使用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: app
version: 0.1.1
workflow:
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
好了,这期就说到这里,我是马丁,下期再见~