大家好,我是代码龟仙人,我前两天在使用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-