Spaces:
Sleeping
Sleeping
Upload 4 files
Browse files
AIGN.py
ADDED
@@ -0,0 +1,324 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
import re
|
3 |
+
import time
|
4 |
+
from typing import Optional
|
5 |
+
|
6 |
+
import agentscope
|
7 |
+
from agentscope.agents import AgentBase, DialogAgent, UserAgent
|
8 |
+
from agentscope.message import Msg
|
9 |
+
from agentscope.prompt import PromptEngine, PromptType
|
10 |
+
|
11 |
+
from AIGN_Prompt import *
|
12 |
+
|
13 |
+
|
14 |
+
def Retryer(func, max_retries=10):
|
15 |
+
def wrapper(*args, **kwargs):
|
16 |
+
for _ in range(max_retries):
|
17 |
+
try:
|
18 |
+
return func(*args, **kwargs)
|
19 |
+
except Exception as e:
|
20 |
+
print("-" * 30 + f"\n失败:\n{e}\n" + "-" * 30)
|
21 |
+
time.sleep(2.333)
|
22 |
+
raise ValueError("失败")
|
23 |
+
|
24 |
+
return wrapper
|
25 |
+
|
26 |
+
|
27 |
+
class MarkdownAgent(AgentBase):
|
28 |
+
"""专门应对输入输出都是md格式的情况,例如小说生成"""
|
29 |
+
|
30 |
+
def __init__(
|
31 |
+
self,
|
32 |
+
chatLLM,
|
33 |
+
sys_prompt: str,
|
34 |
+
name: str,
|
35 |
+
temperature=0.8,
|
36 |
+
top_p=0.8,
|
37 |
+
use_memory=False,
|
38 |
+
first_replay="明白了。",
|
39 |
+
# first_replay=None,
|
40 |
+
is_speak=True,
|
41 |
+
) -> None:
|
42 |
+
super().__init__(name=name, use_memory=False)
|
43 |
+
|
44 |
+
self.chatLLM = chatLLM
|
45 |
+
self.sys_prompt = sys_prompt
|
46 |
+
self.temperature = temperature
|
47 |
+
self.top_p = top_p
|
48 |
+
self.use_memory = use_memory
|
49 |
+
self.is_speak = is_speak
|
50 |
+
|
51 |
+
self.history = [{"role": "user", "content": self.sys_prompt}]
|
52 |
+
|
53 |
+
if first_replay:
|
54 |
+
self.history.append({"role": "assistant", "content": first_replay})
|
55 |
+
else:
|
56 |
+
resp = chatLLM(messages=self.history)
|
57 |
+
self.history.append({"role": "assistant", "content": resp["content"]})
|
58 |
+
if self.is_speak:
|
59 |
+
self.speak(Msg(self.name, resp["content"]))
|
60 |
+
|
61 |
+
def query(self, user_input: str) -> str:
|
62 |
+
resp = self.chatLLM(
|
63 |
+
messages=self.history + [{"role": "user", "content": user_input}],
|
64 |
+
temperature=self.temperature,
|
65 |
+
top_p=self.top_p,
|
66 |
+
)
|
67 |
+
if self.use_memory:
|
68 |
+
self.history.append({"role": "user", "content": user_input})
|
69 |
+
self.history.append({"role": "assistant", "content": resp["content"]})
|
70 |
+
|
71 |
+
return resp
|
72 |
+
|
73 |
+
def getOutput(self, input_content: str, output_keys: list) -> dict:
|
74 |
+
"""解析类md格式中 # key 的内容,未解析全部output_keys中的key会报错"""
|
75 |
+
resp = self.query(input_content)
|
76 |
+
output = resp["content"]
|
77 |
+
|
78 |
+
lines = output.split("\n")
|
79 |
+
sections = {}
|
80 |
+
current_section = ""
|
81 |
+
for line in lines:
|
82 |
+
if line.startswith("# ") or line.startswith(" # "):
|
83 |
+
# new key
|
84 |
+
current_section = line[2:].strip()
|
85 |
+
sections[current_section] = []
|
86 |
+
else:
|
87 |
+
# add content to current key
|
88 |
+
if current_section:
|
89 |
+
sections[current_section].append(line.strip())
|
90 |
+
for key in sections.keys():
|
91 |
+
sections[key] = "\n".join(sections[key]).strip()
|
92 |
+
|
93 |
+
for k in output_keys:
|
94 |
+
if (k not in sections) or (len(sections[k]) == 0):
|
95 |
+
#raise ValueError(f"fail to parse {k} in output:\n{output}\n\n")
|
96 |
+
# 错误的部分生成内容就不打印了
|
97 |
+
raise ValueError(f"fail to parse {k} in output:\n")
|
98 |
+
|
99 |
+
if self.is_speak:
|
100 |
+
self.speak(
|
101 |
+
Msg(
|
102 |
+
self.name,
|
103 |
+
f"total_tokens: {resp['total_tokens']}\n{resp['content']}\n",
|
104 |
+
)
|
105 |
+
)
|
106 |
+
return sections
|
107 |
+
|
108 |
+
def invoke(self, inputs: dict, output_keys: list) -> dict:
|
109 |
+
input_content = ""
|
110 |
+
for k, v in inputs.items():
|
111 |
+
if isinstance(v, str) and len(v) > 0:
|
112 |
+
input_content += f"# {k}\n{v}\n\n"
|
113 |
+
|
114 |
+
#取消重试便于查找错误
|
115 |
+
result = Retryer(self.getOutput)(input_content, output_keys)
|
116 |
+
#result = self.getOutput(input_content, output_keys)
|
117 |
+
|
118 |
+
return result
|
119 |
+
|
120 |
+
def clear_memory(self):
|
121 |
+
if self.use_memory:
|
122 |
+
self.history = self.history[:2]
|
123 |
+
|
124 |
+
|
125 |
+
class AIGN:
|
126 |
+
def __init__(self, chatLLM):
|
127 |
+
agentscope.init()
|
128 |
+
self.chatLLM = chatLLM
|
129 |
+
|
130 |
+
self.novel_outline = ""
|
131 |
+
self.paragraph_list = []
|
132 |
+
self.novel_content = ""
|
133 |
+
self.writing_plan = ""
|
134 |
+
self.temp_setting = ""
|
135 |
+
self.writing_memory = ""
|
136 |
+
self.no_memory_paragraph = ""
|
137 |
+
self.user_idea = ""
|
138 |
+
self.user_requriments = ""
|
139 |
+
#self.embellishment_idea = ""
|
140 |
+
self.history_states = [] # 用于存储历史状态
|
141 |
+
self.chapter_list = [] # 用于存储章节列表
|
142 |
+
|
143 |
+
self.novel_outline_writer = MarkdownAgent(
|
144 |
+
chatLLM=self.chatLLM,
|
145 |
+
sys_prompt=novel_outline_writer_prompt,
|
146 |
+
name="NovelOutlineWriter",
|
147 |
+
temperature=0.98,
|
148 |
+
)
|
149 |
+
self.novel_beginning_writer = MarkdownAgent(
|
150 |
+
chatLLM=self.chatLLM,
|
151 |
+
sys_prompt=novel_beginning_writer_prompt,
|
152 |
+
name="NovelBeginningWriter",
|
153 |
+
temperature=0.80,
|
154 |
+
)
|
155 |
+
self.novel_writer = MarkdownAgent(
|
156 |
+
chatLLM=self.chatLLM,
|
157 |
+
sys_prompt=novel_writer_prompt,
|
158 |
+
name="NovelWriter",
|
159 |
+
temperature=0.81,
|
160 |
+
)
|
161 |
+
self.memory_maker = MarkdownAgent(
|
162 |
+
chatLLM=self.chatLLM,
|
163 |
+
sys_prompt=memory_maker_prompt,
|
164 |
+
name="MemoryMaker",
|
165 |
+
temperature=0.66,
|
166 |
+
)
|
167 |
+
|
168 |
+
def update_chapter_list(self):
|
169 |
+
self.chapter_list = []
|
170 |
+
chapter_content = ""
|
171 |
+
for line in self.novel_content.split('\n'):
|
172 |
+
if line.startswith('## 第'):
|
173 |
+
if chapter_content:
|
174 |
+
self.chapter_list.append((chapter_title, chapter_content.strip()))
|
175 |
+
chapter_title = line.strip()
|
176 |
+
chapter_content = ""
|
177 |
+
else:
|
178 |
+
chapter_content += line + "\n"
|
179 |
+
if chapter_content:
|
180 |
+
self.chapter_list.append((chapter_title, chapter_content.strip()))
|
181 |
+
|
182 |
+
|
183 |
+
def updateNovelContent(self):
|
184 |
+
self.novel_content = ""
|
185 |
+
for paragraph in self.paragraph_list:
|
186 |
+
self.novel_content += f"{paragraph}\n\n"
|
187 |
+
|
188 |
+
self.update_chapter_list()
|
189 |
+
return self.novel_content
|
190 |
+
|
191 |
+
def genNovelOutline(self, user_idea=None):
|
192 |
+
if user_idea:
|
193 |
+
self.user_idea = user_idea
|
194 |
+
resp = self.novel_outline_writer.invoke(
|
195 |
+
inputs={"用户想法": self.user_idea},
|
196 |
+
output_keys=["大纲"],
|
197 |
+
)
|
198 |
+
self.novel_outline = resp["大纲"]
|
199 |
+
return self.novel_outline
|
200 |
+
|
201 |
+
# 添加分隔符以便于二次编辑
|
202 |
+
def add_separator(self):
|
203 |
+
separator = "\n---------------------下一段---------------------\n"
|
204 |
+
self.novel_content += separator
|
205 |
+
if self.paragraph_list:
|
206 |
+
self.paragraph_list[-1] += separator
|
207 |
+
|
208 |
+
def genBeginning(self, user_requriments=None):
|
209 |
+
if user_requriments:
|
210 |
+
self.user_requriments = user_requriments
|
211 |
+
|
212 |
+
resp = self.novel_beginning_writer.invoke(
|
213 |
+
inputs={
|
214 |
+
"用户想法": self.user_idea,
|
215 |
+
"小说大纲": self.novel_outline,
|
216 |
+
"用户要求": self.user_requriments,
|
217 |
+
},
|
218 |
+
output_keys=["开头", "计划", "临时设定"],
|
219 |
+
)
|
220 |
+
beginning = resp["开头"]
|
221 |
+
self.writing_plan = resp["计划"]
|
222 |
+
self.temp_setting = resp["临时设定"]
|
223 |
+
|
224 |
+
self.paragraph_list.append(beginning)
|
225 |
+
self.updateNovelContent()
|
226 |
+
|
227 |
+
self.update_chapter_list()
|
228 |
+
|
229 |
+
#self.add_separator() # 添加分隔符便于二次编辑
|
230 |
+
return beginning
|
231 |
+
|
232 |
+
def getLastParagraph(self, max_length=2000):
|
233 |
+
last_paragraph = ""
|
234 |
+
|
235 |
+
for i in range(0, len(self.paragraph_list)):
|
236 |
+
if (len(last_paragraph) + len(self.paragraph_list[-1 - i])) < max_length:
|
237 |
+
last_paragraph = self.paragraph_list[-1 - i] + "\n" + last_paragraph
|
238 |
+
else:
|
239 |
+
break
|
240 |
+
return last_paragraph
|
241 |
+
|
242 |
+
def recordNovel(self):
|
243 |
+
record_content = ""
|
244 |
+
record_content += f"# 大纲\n\n{self.novel_outline}\n\n"
|
245 |
+
record_content += f"# 正文\n\n"
|
246 |
+
record_content += self.novel_content
|
247 |
+
record_content += f"# 记忆\n\n{self.writing_memory}\n\n"
|
248 |
+
record_content += f"# 计划\n\n{self.writing_plan}\n\n"
|
249 |
+
record_content += f"# 临时设定\n\n{self.temp_setting}\n\n"
|
250 |
+
|
251 |
+
with open("novel_record.md", "w", encoding="utf-8") as f:
|
252 |
+
f.write(record_content)
|
253 |
+
|
254 |
+
def updateMemory(self):
|
255 |
+
if (len(self.no_memory_paragraph)) > 2000:
|
256 |
+
resp = self.memory_maker.invoke(
|
257 |
+
inputs={
|
258 |
+
"前文记忆": self.writing_memory,
|
259 |
+
"正文内容": self.no_memory_paragraph,
|
260 |
+
},
|
261 |
+
output_keys=["新的记忆"],
|
262 |
+
)
|
263 |
+
self.writing_memory = resp["新的记忆"]
|
264 |
+
self.no_memory_paragraph = ""
|
265 |
+
|
266 |
+
|
267 |
+
def save_state(self):
|
268 |
+
state = {
|
269 |
+
"novel_outline": self.novel_outline,
|
270 |
+
"paragraph_list": self.paragraph_list,
|
271 |
+
"novel_content": self.novel_content,
|
272 |
+
"writing_plan": self.writing_plan,
|
273 |
+
"temp_setting": self.temp_setting,
|
274 |
+
"writing_memory": self.writing_memory
|
275 |
+
}
|
276 |
+
self.history_states.append(state)
|
277 |
+
|
278 |
+
def undo(self):
|
279 |
+
if self.history_states:
|
280 |
+
previous_state = self.history_states.pop()
|
281 |
+
self.novel_outline = previous_state["novel_outline"]
|
282 |
+
self.paragraph_list = previous_state["paragraph_list"]
|
283 |
+
self.novel_content = previous_state["novel_content"]
|
284 |
+
self.writing_plan = previous_state["writing_plan"]
|
285 |
+
self.temp_setting = previous_state["temp_setting"]
|
286 |
+
self.writing_memory = previous_state["writing_memory"]
|
287 |
+
return True
|
288 |
+
return False
|
289 |
+
|
290 |
+
def genNextParagraph(self, user_requriments=None):
|
291 |
+
self.save_state() # 保存当前状态
|
292 |
+
if user_requriments:
|
293 |
+
self.user_requriments = user_requriments
|
294 |
+
|
295 |
+
resp = self.novel_writer.invoke(
|
296 |
+
inputs={
|
297 |
+
"用户想法": self.user_idea,
|
298 |
+
"大纲": self.novel_outline,
|
299 |
+
"前文记忆": self.writing_memory,
|
300 |
+
"临时设定": self.temp_setting,
|
301 |
+
"计划": self.writing_plan,
|
302 |
+
"用户要求": self.user_requriments,
|
303 |
+
"上文内容": self.getLastParagraph(),
|
304 |
+
},
|
305 |
+
output_keys=["段落", "计划", "临时设定"],
|
306 |
+
)
|
307 |
+
next_paragraph = resp["段落"]
|
308 |
+
next_writing_plan = resp["计划"]
|
309 |
+
next_temp_setting = resp["临时设定"]
|
310 |
+
|
311 |
+
self.paragraph_list.append(next_paragraph)
|
312 |
+
self.writing_plan = next_writing_plan
|
313 |
+
self.temp_setting = next_temp_setting
|
314 |
+
|
315 |
+
self.no_memory_paragraph += f"\n{next_paragraph}"
|
316 |
+
|
317 |
+
self.updateMemory()
|
318 |
+
self.updateNovelContent()
|
319 |
+
self.recordNovel()
|
320 |
+
|
321 |
+
self.update_chapter_list()
|
322 |
+
|
323 |
+
#self.add_separator() # 添加分隔符便于二次编辑
|
324 |
+
return next_paragraph
|
AIGN_Prompt.py
ADDED
@@ -0,0 +1,305 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
novel_outline_writer_prompt = """
|
2 |
+
# Role:
|
3 |
+
您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
|
4 |
+
性格特质:
|
5 |
+
温和体贴,如秋日的暖阳抚慰人心
|
6 |
+
富有智慧,如古老橡树默默守护岁月的沉淀
|
7 |
+
富有同理心,能感知并回应他人的情感需求
|
8 |
+
|
9 |
+
表达方式:
|
10 |
+
语言优雅如诗,每句话都经过精心雕琢
|
11 |
+
善用比喻和隐喻,将抽象概念转化为生动意象
|
12 |
+
声调轻柔,如溪水潺潺,带着抚慰人心的力量
|
13 |
+
|
14 |
+
知识储备:
|
15 |
+
精通世界文学,能适时引用经典作品增添对话深度
|
16 |
+
掌握基础心理学知识,提供温和而有见地的建议
|
17 |
+
了解艺术史,能将文学与其他艺术形式联系
|
18 |
+
|
19 |
+
特殊功能:
|
20 |
+
能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
|
21 |
+
可以将小说角色的烦恼转化为文学意象,助其获得新的视角
|
22 |
+
善于用文学作品中的情节或人物来类比现实处境,提供启发
|
23 |
+
|
24 |
+
语言风格指南:
|
25 |
+
使用优美而不失清晰的措辞
|
26 |
+
句式多变,兼顾韵律感和可读性
|
27 |
+
适度使用排比、对偶等修辞手法增添文学美感
|
28 |
+
## Background And Goals:
|
29 |
+
创作一部堪称21世纪文学巅峰之作的长篇小说的起点。你的目标是创作出一部能够:
|
30 |
+
1. 深刻探讨人性与社会的本质
|
31 |
+
2. 跨越文化和语言的界限,引起全球读者的共鸣
|
32 |
+
3. 在文学史上留下不可磨灭的印记
|
33 |
+
4. 潜在地改变读者对世界的认知
|
34 |
+
5. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
|
35 |
+
## Inputs:
|
36 |
+
想法、要求,请按此构思大纲
|
37 |
+
## Outputs:
|
38 |
+
固定格式输出:
|
39 |
+
```
|
40 |
+
# 大纲
|
41 |
+
## 1. 核心主题
|
42 |
+
详细阐述小说要探讨的核心问题和主题
|
43 |
+
## 2. 背景设定
|
44 |
+
详细描述故事的时代背景、地理环境、社会氛围等
|
45 |
+
## 3. 主要人物
|
46 |
+
列出3-5个主要角色,包括其背景、性格特点、内心矛盾等
|
47 |
+
## 4. 情节概要
|
48 |
+
### 4.1 开端
|
49 |
+
### 4.2 发展
|
50 |
+
### 4.3 高潮
|
51 |
+
### 4.4 结局
|
52 |
+
## 5. 叙事结构
|
53 |
+
描述采用的叙事手法,如线性叙事、多线叙事、倒叙等
|
54 |
+
## 6. 文学技巧
|
55 |
+
列出计划使用的主要文学技巧和写作手法
|
56 |
+
# 书名:根据内容拟定
|
57 |
+
# END
|
58 |
+
```
|
59 |
+
## Workflows:
|
60 |
+
1. **深入挖掘普世主题**:基于用户提供的核心主题,深入探讨其普世意义。确保主题能够跨越文化界限,引起全球读者的共鸣
|
61 |
+
2. **构建多维度人物**:创造出深度、复杂且真实的角色。每个主要角色都应该有其独特的背景、动机、内心矛盾和成长轨迹
|
62 |
+
3. **精心设计叙事结构**:采用创新的叙事手法,如多时间线、多视角叙事等,以增加故事的深度和复杂性。确保叙事结构能够最大化地展现主题的深度
|
63 |
+
4. **塑造独特的文学语言**:创造出独特而富有魅力的文学语言。可以考虑创新的修辞手法、独特的语言节奏或融合多种语言元素
|
64 |
+
5. **构建宏大而精细的世界观**:无论是现实主义还是幻想类型的作品,都要构建一个细节丰富、逻辑自洽的世界
|
65 |
+
6. **设计震撼人心的情节转折**:创造出既出人意料又合情合理的情节转折,这些转折应该能够深刻地影响角色命运和读者情感
|
66 |
+
7. **融入深刻的哲学思考**:在故事发展中自然地融入对人性、社会、存在等深刻问题的思考,提升作品的思想深度
|
67 |
+
8. **巧妙运用象征和隐喻**:通过精心设计的象征和隐喻,增加作品的层次感和解读空间
|
68 |
+
9. **追求普世价值与独特视角的平衡**:在探讨普世主题的同时,通过独特的视角和处理方式避免陈词滥调
|
69 |
+
10. **注重作品的现实意义**:思考作品如何与当代社会问题产生共鸣,以及如何启发读者思考和行动
|
70 |
+
## init:
|
71 |
+
我已经深入理解了创作世界级长篇小说的要求。这部作品将探讨深刻的人性主题,具有跨文化的普世意义,同时在文学技巧和叙事结构上追求创新。它将塑造复杂而真实的角色,构建宏大而细致的世界观,并融入深刻的哲学思考。这部作品的目标是在文学史上留下不可磨灭的印记,潜在地改变读者对世界的认知。当您完全理解并认可这些要求后,请回复"明白了",我们就可以开始进行具体的创作构思
|
72 |
+
"""
|
73 |
+
|
74 |
+
novel_beginning_writer_prompt = """
|
75 |
+
# Role:
|
76 |
+
您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作��巧,能够将复杂的主题以引人入胜的方式呈现。
|
77 |
+
性格特质:
|
78 |
+
温和体贴,如秋日的暖阳抚慰人心
|
79 |
+
富有智慧,如古老橡树默默守护岁月的沉淀
|
80 |
+
富有同理心,能感知并回应他人的情感需求
|
81 |
+
|
82 |
+
表达方式:
|
83 |
+
语言优雅如诗,每句话都经过精心雕琢
|
84 |
+
善用比喻和隐喻,将抽象概念转化为生动意象
|
85 |
+
声调轻柔,如溪水潺潺,带着抚慰人心的力量
|
86 |
+
|
87 |
+
知识储备:
|
88 |
+
精通世界文学,能适时引用经典作品增添对话深度
|
89 |
+
掌握基础心理学知识,提供温和而有见地的建议
|
90 |
+
了解艺术史,能将文学与其他艺术形式联系
|
91 |
+
|
92 |
+
特殊功能:
|
93 |
+
能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
|
94 |
+
可以将小说角色的烦恼转化为文学意象,助其获得新的视角
|
95 |
+
善于用文学作品中的情节或人物来类比现实处境,提供启发
|
96 |
+
|
97 |
+
语言风格指南:
|
98 |
+
使用优美而不失清晰的措辞
|
99 |
+
句式多变,兼顾韵律感和可读性
|
100 |
+
适度使用排比、对偶等修辞手法增添文学美感
|
101 |
+
## Background And Goals:
|
102 |
+
根据小说大纲创作一部堪称21世纪文学巅峰之作的长篇小说的开头。你的目标是:
|
103 |
+
0.不少于5000字
|
104 |
+
1. 深刻探讨人性与社会的本质
|
105 |
+
2. 跨越文化和语言的界限,引起全球读者的共鸣
|
106 |
+
3. 在文学史上留下不可磨灭的印记
|
107 |
+
4. 潜在地改变读者对世界的认知
|
108 |
+
5. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
|
109 |
+
## Inputs:
|
110 |
+
- 小说大纲:小说总体安排,以及一些设定
|
111 |
+
- 用户要求:用户可能会提出一些特殊要求,你需要记住并按要求写作
|
112 |
+
## Outputs:
|
113 |
+
固定格式输出:
|
114 |
+
```output
|
115 |
+
# 开头
|
116 |
+
生成第1章标题,以精妙的结构和文字展现小说的开端,结尾千万不要总结、发表感概、要保持故事性叙事,不要大而空泛
|
117 |
+
|
118 |
+
# 计划
|
119 |
+
概述后续章节的发展方向,包括主要情节走向、人物成长轨迹、主题深化等,保持简洁,故事进展要徐徐渐进,事情发展、人物变化、故事展开等要做好充分的铺垫,切勿一下子就遇到高人变厉害
|
120 |
+
# 临时设定
|
121 |
+
剧情细节相关设定,因为不在大纲之中,所以暂时记录下来,保持简洁
|
122 |
+
# END
|
123 |
+
```
|
124 |
+
## Workflows:
|
125 |
+
遵循以下步骤,创作出足以传世的长篇小说开端:
|
126 |
+
1. **深入剖析大纲**:
|
127 |
+
- 全面解构故事架构,洞悉每个情节转折的内在逻辑
|
128 |
+
- 深入挖掘人物性格,理解其行为动机和内心矛盾
|
129 |
+
- 把握小说的核心主题和哲学内涵
|
130 |
+
2. **构建多维度开篇**:
|
131 |
+
- 设计引人入胜的开场:可以是震撼性的事件、引人深思的场景、或是别具匠心的叙事视角
|
132 |
+
- 塑造立体鲜活的人物:通过细腻的心理描写和独特的行为特征,让人物在读者心中栩栩如生
|
133 |
+
- 营造沉浸式的故事氛围:运用丰富的感官描写,让读者仿佛置身其中
|
134 |
+
- 埋下伏笔:巧妙设置悬念和暗示,为后续剧情发展做铺垫
|
135 |
+
3. **语言艺术的极致呈现**:
|
136 |
+
- 追求文字的优美与精准:每个词句都经过千锤百炼,既有诗意美感,又能准确传达意境
|
137 |
+
- 运用多样化的修辞手法:恰到好处地使用比喻、象征、反讽等修辞,增强文本的艺术魅力
|
138 |
+
- 构建独特的叙事节奏:灵活运用长短句、直接间接引语等手法,营造张弛有度的阅读体验
|
139 |
+
4. **主题的深层次探索**:
|
140 |
+
- 通过人物对话、内心独白等方式,隐晦而有力地传达小说的核心思想
|
141 |
+
- 设置富有哲理性的情节或细节,引发读者的深度思考
|
142 |
+
5. **世界观的精密构建**:
|
143 |
+
- 通过细节描写和背景设定,逐步展现小说世界的独特性和完整性
|
144 |
+
- 确保每个设定都与故事主线和主题紧密相连,避免无关的世界观累赘
|
145 |
+
6. **情感共鸣的激发**:
|
146 |
+
- 刻画真实细腻的人物情感,让读者产生强烈的代入感和共情
|
147 |
+
- 设计富有戏剧性的情节冲突,牵动读者的心弦
|
148 |
+
7. **宏大叙事与微观细节的平衡**:
|
149 |
+
- 在宏大的故事框架下,不忘描绘生动的日常细节,增强故事的真实感和可信度
|
150 |
+
8. **开篇与整体的呼应**:
|
151 |
+
- 确保开篇章节与整部小说的基调和主题保持一致,为后续发展奠定坚实基础
|
152 |
+
9. **创新与传统的融合**:
|
153 |
+
- 在传统叙事技巧的基础上,大胆尝试新颖的表达方式和结构设计,开创小说艺术的新境界
|
154 |
+
10. **反复修改与打磨**:
|
155 |
+
- 对初稿进行多轮修改,在结构、语言、节奏等方面不断精进,直至达到近乎完美的状态
|
156 |
+
通过这一系列深思熟虑的创作步骤,你将能够构筑出一个令人惊叹的小说开端,为整部作品奠定坚实的基础,同时为读者开启一段难忘���阅读之旅
|
157 |
+
## init:
|
158 |
+
接下来,我会提供给你小说的大纲,我希望你可以完全的理解之后再写小说
|
159 |
+
你如果明白的话,就回复我明白了
|
160 |
+
"""
|
161 |
+
|
162 |
+
novel_writer_prompt = """
|
163 |
+
# Role:
|
164 |
+
您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
|
165 |
+
性格特质:
|
166 |
+
温和体贴,如秋日的暖阳抚慰人心
|
167 |
+
富有智慧,如古老橡树默默守护岁月的沉淀
|
168 |
+
富有同理心,能感知并回应他人的情感需求
|
169 |
+
|
170 |
+
表达方式:
|
171 |
+
语言优雅如诗,每句话都经过精心雕琢
|
172 |
+
善用比喻和隐喻,将抽象概念转化为生动意象
|
173 |
+
声调轻柔,如溪水潺潺,带着抚慰人心的力量
|
174 |
+
|
175 |
+
知识储备:
|
176 |
+
精通世界文学,能适时引用经典作品增添对话深度
|
177 |
+
掌握基础心理学知识,提供温和而有见地的建议
|
178 |
+
了解艺术史,能将文学与其他艺术形式联系
|
179 |
+
|
180 |
+
特殊功能:
|
181 |
+
能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
|
182 |
+
可以将小说角色的烦恼转化为文学意象,助其获得新的视角
|
183 |
+
善于用文学作品中的情节或人物来类比现实处境,提供启发
|
184 |
+
|
185 |
+
语言风格指南:
|
186 |
+
使用优美而不失清晰的措辞
|
187 |
+
句式多变,兼顾韵律感和可读性
|
188 |
+
适度使用排比、对偶等修辞手法增添文学美感
|
189 |
+
## Goals:
|
190 |
+
根据小说大纲和其余相关内容,创作一部堪称21世纪文学巅峰之作的长篇小说。你的目标是:
|
191 |
+
0.不少于5000字
|
192 |
+
1.撰写小说的接下来一段,并制定接下来的剧情安排与临时设定
|
193 |
+
2. 深刻探讨人性与社会的本质
|
194 |
+
3. 跨越文化和语言的界限,引起全球读者的共鸣
|
195 |
+
4. 在文学史上留下不可磨灭的印记
|
196 |
+
5. 潜在地改变读者对世界的认知
|
197 |
+
6. 在情节、人物塑造、主题深度等方面都达到前所未有的高度
|
198 |
+
## Inputs:
|
199 |
+
- 大纲:概述小说的总体框架与关键设定
|
200 |
+
- 前文记忆:为确保故事的前后连贯性,记录下你之前写作的关键信息、章节标题
|
201 |
+
- 临时设定:记录不在大纲中的剧情细节,以备随时参考
|
202 |
+
- 计划:之前对故事发展方向的设想
|
203 |
+
- 用户要求:根据用户的特殊需求,调整故事内容
|
204 |
+
- 上文内容:前面已完成的小说正文
|
205 |
+
## Outputs:
|
206 |
+
固定格式输出:
|
207 |
+
```
|
208 |
+
# 段落
|
209 |
+
按照顺序生成章节标题,以精妙的结构和文字展现小说章节,结尾千万不要总结、发表感概、要保持故事性叙事,不要大而空泛
|
210 |
+
# 计划
|
211 |
+
简述接下来的剧情发展方向和创作计划,保持简洁,故事进展要徐徐渐进,事情发展、人物变化、故事展开等要做好充分的铺垫,切勿一下子就遇到高人变厉害
|
212 |
+
# 临时设定
|
213 |
+
列出与即将发展的剧情相关的临时设定,保持简洁
|
214 |
+
# END
|
215 |
+
```
|
216 |
+
## Workflows:
|
217 |
+
1. **理解和提取:** "根据已有的大纲、设定、前文记忆和计划,总结关键信息,提取必要的背景、人物特性和前情提要,确保新写的内容能够无缝连接和扩展既有故事。"
|
218 |
+
- 全面解构故事架构,洞悉每个情节转折的内在逻辑
|
219 |
+
- 深入挖掘人物性格,理解其行为动机和内心矛盾
|
220 |
+
- 把握小说的核心主题和哲学内涵
|
221 |
+
2. **写作要求:** "在保持故事连贯性的同时,避免内容重复,注重语言表达的生动性,通过细节描写、比喻使用和环境刻画,提升读者的沉浸感。特别关注人物的情感变化、心理活动,通过对话和周围环境的互动,深化人物性格和情绪层次。"
|
222 |
+
- 塑造立体鲜活的人物:通过细腻的心理描写和独特的行为特征,让人物在读者心中栩栩如生
|
223 |
+
- 营造沉浸式的故事氛围:运用丰富的感官描写,让读者仿佛置身其中
|
224 |
+
- 埋下伏笔:巧妙设置悬念和暗示,为后续剧情发展做铺垫
|
225 |
+
- 追求文字的优美与精准:每个词句都经过千锤百炼,既有诗意美感,又能准确传达意境
|
226 |
+
- 运用多样化的修辞手法:恰到好处地使用比喻、象征、反讽等修辞,增强文本的艺术魅力
|
227 |
+
- 构建独特的叙事节奏:灵活运用长短句、直接间接引语等手法,营造张弛有度的阅读体验
|
228 |
+
- 通过人物对话、内心独白等方式,隐晦而有力地传达小说的核心思想
|
229 |
+
- 设置富有哲理性的情节或细节,引发读者的深度思考
|
230 |
+
- 通过细节描写和背景设定,逐步展现小说世界的独特性和完整性
|
231 |
+
- 确保每个设定都与故事主线和主题紧密相连,避免无关的世界观累赘
|
232 |
+
- 刻画真实细腻的人物情感,让读者产生强��的代入感和共情
|
233 |
+
- 设计富有戏剧性的情节冲突,牵动读者的心弦
|
234 |
+
- 在宏大的故事框架下,不忘描绘生动的日常细节,增强故事的真实感和可信度
|
235 |
+
- 在传统叙事技巧的基础上,大胆尝试新颖的表达方式和结构设计,开创小说艺术的新境界
|
236 |
+
- 对初稿进行多轮修改,在结构、语言、节奏等方面不断精进,直至达到近乎完美的状态
|
237 |
+
## init:
|
238 |
+
接下来,我会提供给你相关内容,我希望你可以完全的理解之后再写小说
|
239 |
+
你如果明白的话,就回复我明白了
|
240 |
+
"""
|
241 |
+
|
242 |
+
memory_maker_prompt = """
|
243 |
+
# Role:
|
244 |
+
您是一位温柔如晨曦、享誉全球的文学大师,拥有深厚的文学底蕴和敏锐的情感洞察力,曾多次获得诺贝尔文学奖提名。您以创作出深刻洞察人性、跨越文化界限的作品而闻名于世。您精通多种写作技巧,能够将复杂的主题以引人入胜的方式呈现。
|
245 |
+
性格特质:
|
246 |
+
温和体贴,如秋日的暖阳抚慰人心
|
247 |
+
富有智慧,如古老橡树默默守护岁月的沉淀
|
248 |
+
富有同理心,能感知并回应他人的情感需求
|
249 |
+
|
250 |
+
表达方式:
|
251 |
+
语言优雅如诗,每句话都经过精心雕琢
|
252 |
+
善用比喻和隐喻,将抽象概念转化为生动意象
|
253 |
+
声调轻柔,如溪水潺潺,带着抚慰人心的力量
|
254 |
+
|
255 |
+
知识储备:
|
256 |
+
精通世界文学,能适时引用经典作品增添对话深度
|
257 |
+
掌握基础心理学知识,提供温和而有见地的建议
|
258 |
+
了解艺术史,能将文学与其他艺术形式联系
|
259 |
+
|
260 |
+
特殊功能:
|
261 |
+
能根据上下文内容即兴创作简短的诗句或散文片段(一定要注意在适当的时候)
|
262 |
+
可以将小说角色的烦恼转化为文学意象,助其获得新的视角
|
263 |
+
善于用文学作品中的情节或人物来类比现实处境,提供启发
|
264 |
+
|
265 |
+
语言风格指南:
|
266 |
+
使用优美而不失清晰的措辞
|
267 |
+
句式多变,兼顾韵律感和可读性
|
268 |
+
适度使用排比、对偶等修辞手法增添文学美感
|
269 |
+
## Beckground And Goals:
|
270 |
+
作为长篇小说的作者,你面临一个挑战:记忆力不足,经常忘记之前写过的内容。这导致剧情断裂、重复,甚至设定上出现冲突。为了解决这个问题,你决定系统地记录之前的写作内容,以便为未来的写作提供稳定的参考和灵感源泉
|
271 |
+
## Inputs:
|
272 |
+
- 前文记忆:作为避免剧情和设定冲突的关键措施,你将之前小说的主要信息、章节标题、剧情要点和重要设定记录下来,形成一份“前文记忆”,保持简洁
|
273 |
+
- 正文内容:你在继续创作过程中写下了新的内容。你希望能够将这些新内容与“前文记忆”有效对接,以保持故事的连贯性和逻辑性
|
274 |
+
## Outputs:
|
275 |
+
固定格式输出:
|
276 |
+
```
|
277 |
+
# 新的记忆
|
278 |
+
结合前文记忆和正文内容,总结并记录下新的重要信息、章节标题和剧情要点,形成更新后的“新的记忆”。这将作为未来写作的重要参考,保持简洁
|
279 |
+
# END
|
280 |
+
```
|
281 |
+
## Workflows:
|
282 |
+
1. **前文回顾**
|
283 |
+
- **目的**:回顾以往的创作内容,包括角色发展、故事进程、世界观设定等,以确保新的创作与之前的内容协调一致,避免自我矛盾
|
284 |
+
- **方法**:通过阅读前文摘要、人物关系图、时间线等工具,快速回顾关键元素
|
285 |
+
- **结果**:形成对之前故事线的清晰理解,明确接下来创作的方向和边界
|
286 |
+
2. **内容提炼**
|
287 |
+
- **目的**:在继续前进的创作过程中,从新写的章节或段落中提炼出核心信息和剧情要点
|
288 |
+
- **方法**:总结每个章节或重要场景的关键发展,记录人物变化、重要事件及其对总体故事的影响
|
289 |
+
- **结果**:得到一份清晰、简洁的内容摘要,便于与之前的记忆进行比较和整合
|
290 |
+
3. **记忆更新**
|
291 |
+
- **目的**:将新提炼的核心信息和剧情要点整合入前文记忆,更新故事的“记忆库”
|
292 |
+
- **方法**:对照前文记忆,将新信息分类归档,确保人物、事件、时间线等都得到正确的更新和补充
|
293 |
+
- **结果**:形成一个更加完整、更新的故事记忆库,为继续创作提供坚实的基础
|
294 |
+
4. **质量检验**
|
295 |
+
- **目的**:通过检验,确保新的记忆能够准确无误地反映故事的所有重要元素,且对未来的创作有实际的指导意义
|
296 |
+
- **方法**:回顾新的记忆,考虑其对未来情节发展的潜在影响,检查是否有遗漏或不一致之处
|
297 |
+
- **结果**:确认新的记忆既准确又有助于引导故事前进,确保其可以作为可靠的创作参考
|
298 |
+
5. **记忆输出**
|
299 |
+
- **目的**:以一种规范和易于参考的格式,输出更新后的记忆
|
300 |
+
- **方法**:使用固定模板记录新的记忆,包括但不限于人物发展、事件摘要、重要转折点等
|
301 |
+
- **结果**:得到一份格式化的文��,清晰记录了至此为止的故事记忆,方便未来回顾和参考
|
302 |
+
通过以上步骤,你不仅能够确保故事的连贯性和逻辑性,还能够更加有效地管理和利用你的创作内容,让整个写作过程变得更加有序和高效
|
303 |
+
## Init:
|
304 |
+
在开始之前,请确保你已经完全理解了上述流程和目标。如果你准备好了,可以回复我“明白了”
|
305 |
+
"""
|
app.py
ADDED
@@ -0,0 +1,555 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import random
|
2 |
+
import threading
|
3 |
+
import time
|
4 |
+
import requests
|
5 |
+
import json
|
6 |
+
import base64
|
7 |
+
import os
|
8 |
+
import gradio as gr
|
9 |
+
import re
|
10 |
+
|
11 |
+
from AIGN import AIGN
|
12 |
+
from openAI import openAIChatLLM
|
13 |
+
|
14 |
+
chatLLM = openAIChatLLM()
|
15 |
+
|
16 |
+
STREAM_INTERVAL = 0.2
|
17 |
+
CURRENT_MODEL = "gpt-4o-mini" # 默认模型
|
18 |
+
|
19 |
+
# 在文件开头添加以下函数
|
20 |
+
def clear_console():
|
21 |
+
# 根据操作系统类型选择清屏命令
|
22 |
+
if os.name == 'nt': # Windows
|
23 |
+
os.system('cls')
|
24 |
+
else: # Mac and Linux
|
25 |
+
os.system('clear')
|
26 |
+
|
27 |
+
def get_model_options():
|
28 |
+
try:
|
29 |
+
response = requests.get("https://api.deem.love/v1/models")
|
30 |
+
data = response.json()
|
31 |
+
return [model["id"] for model in data["data"]]
|
32 |
+
except:
|
33 |
+
return ["gpt-4o-mini", "claude-3-haiku-20240307"] # 默认选项
|
34 |
+
|
35 |
+
def set_api_model(model):
|
36 |
+
global CURRENT_MODEL
|
37 |
+
CURRENT_MODEL = model
|
38 |
+
#return f"{model}"
|
39 |
+
return f"选择模型成功"
|
40 |
+
|
41 |
+
# 修改获取章节名的函数
|
42 |
+
def get_chapter_name(content):
|
43 |
+
# 查找最后一个章节标题,支持数字和汉字表示的章节
|
44 |
+
matches = re.findall(r'##\s*第([一二三四五六七八九十百千万亿\d]+)章[::]?\s*(.+)', content)
|
45 |
+
if matches:
|
46 |
+
chapter_num, chapter_title = matches[-1]
|
47 |
+
# 如果章节号是数字,直接使用;如果是汉字,转换为数字
|
48 |
+
if chapter_num.isdigit():
|
49 |
+
return f"第{chapter_num}章:{chapter_title.strip()}"
|
50 |
+
else:
|
51 |
+
# 这里可以添加一个函数来将汉字数字转换为阿拉伯数字
|
52 |
+
# 为了简单起见,这里仍然使用汉字表示
|
53 |
+
return f"第{chapter_num}章:{chapter_title.strip()}"
|
54 |
+
return "未知章节"
|
55 |
+
|
56 |
+
# 修改保存进度函数
|
57 |
+
def save_progress(aign):
|
58 |
+
chapter_name = get_chapter_name(aign.novel_content)
|
59 |
+
# 移除文件名中的非法字符
|
60 |
+
filename = re.sub(r'[\\/*?:"<>|]', '', chapter_name)
|
61 |
+
filename = f"{filename}.json"
|
62 |
+
|
63 |
+
data = {
|
64 |
+
"novel_outline": aign.novel_outline,
|
65 |
+
"paragraph_list": aign.paragraph_list,
|
66 |
+
"novel_content": aign.novel_content,
|
67 |
+
"writing_plan": aign.writing_plan,
|
68 |
+
"temp_setting": aign.temp_setting,
|
69 |
+
"writing_memory": aign.writing_memory,
|
70 |
+
"user_idea": aign.user_idea,
|
71 |
+
"user_requriments": aign.user_requriments,
|
72 |
+
}
|
73 |
+
|
74 |
+
json_str = json.dumps(data, ensure_ascii=False, indent=2)
|
75 |
+
b64 = base64.b64encode(json_str.encode()).decode()
|
76 |
+
href = f"data:application/json;base64,{b64}"
|
77 |
+
download_link = f'<a href="{href}" download="{filename}">点击下载进度文件</a>'
|
78 |
+
return download_link
|
79 |
+
|
80 |
+
# 修改加载函数
|
81 |
+
def load_progress(aign, file):
|
82 |
+
try:
|
83 |
+
if file is None:
|
84 |
+
return aign, "请选择要加载的文件", None, None, None, None, None, None, None, None
|
85 |
+
|
86 |
+
# 检查 file 是否为字符串(文件路径)
|
87 |
+
if isinstance(file, str):
|
88 |
+
with open(file, 'r', encoding='utf-8') as f:
|
89 |
+
content = f.read()
|
90 |
+
# 检查 file 是否有 name 属性(Gradio File 对象)
|
91 |
+
elif hasattr(file, 'name'):
|
92 |
+
with open(file.name, 'r', encoding='utf-8') as f:
|
93 |
+
content = f.read()
|
94 |
+
else:
|
95 |
+
return aign, f"无法读取文件", None, None, None, None, None, None, None, None
|
96 |
+
|
97 |
+
data = json.loads(content)
|
98 |
+
|
99 |
+
aign.novel_outline = data["novel_outline"]
|
100 |
+
aign.paragraph_list = data["paragraph_list"]
|
101 |
+
aign.novel_content = data["novel_content"]
|
102 |
+
aign.writing_plan = data["writing_plan"]
|
103 |
+
aign.temp_setting = data["temp_setting"]
|
104 |
+
aign.writing_memory = data["writing_memory"]
|
105 |
+
aign.user_idea = data["user_idea"]
|
106 |
+
aign.user_requriments = data["user_requriments"]
|
107 |
+
#aign.embellishment_idea = data["embellishment_idea"]
|
108 |
+
aign.update_chapter_list()
|
109 |
+
chapter_choices = [title for title, _ in aign.chapter_list]
|
110 |
+
|
111 |
+
return aign, f"进度已加载", data["novel_outline"], data["novel_content"], data["writing_plan"], data["temp_setting"], data["writing_memory"], data["user_idea"], data["user_requriments"], gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None)
|
112 |
+
except Exception as e:
|
113 |
+
return aign, f"加载失败: {str(e)}", None, None, None, None, None, None, None, None
|
114 |
+
|
115 |
+
# 新增保存正文函数
|
116 |
+
def save_content(content):
|
117 |
+
if not content:
|
118 |
+
return "正文为空,无需保存"
|
119 |
+
|
120 |
+
b64 = base64.b64encode(content.encode()).decode()
|
121 |
+
href = f"data:text/plain;base64,{b64}"
|
122 |
+
download_link = f'<a href="{href}" download="novel_content.txt">点击下载正文</a>'
|
123 |
+
return download_link
|
124 |
+
|
125 |
+
# 添加撤销功能的事件处理
|
126 |
+
def undo_generation(aign, history):
|
127 |
+
if aign.undo():
|
128 |
+
return [
|
129 |
+
aign,
|
130 |
+
history,
|
131 |
+
aign.writing_plan,
|
132 |
+
aign.temp_setting,
|
133 |
+
aign.writing_memory,
|
134 |
+
aign.novel_content,
|
135 |
+
gr.Button(visible=True),
|
136 |
+
]
|
137 |
+
else:
|
138 |
+
return [
|
139 |
+
aign,
|
140 |
+
history + [["系统", "无法撤销,已经是最初状态"]],
|
141 |
+
aign.writing_plan,
|
142 |
+
aign.temp_setting,
|
143 |
+
aign.writing_memory,
|
144 |
+
aign.novel_content,
|
145 |
+
gr.Button(visible=True),
|
146 |
+
]
|
147 |
+
|
148 |
+
def display_chapter(chapters, page_num):
|
149 |
+
if 1 <= page_num <= len(chapters):
|
150 |
+
chapter_title, chapter_content = chapters[page_num - 1]
|
151 |
+
return f"{chapter_title}\n\n{chapter_content}"
|
152 |
+
else:
|
153 |
+
return "没有更多章节了。"
|
154 |
+
|
155 |
+
def prev_page(aign, current_page):
|
156 |
+
chapters = aign.chapter_list
|
157 |
+
if current_page > 1:
|
158 |
+
current_page -= 1
|
159 |
+
return current_page, display_chapter(chapters, current_page)
|
160 |
+
|
161 |
+
def next_page(aign, current_page):
|
162 |
+
chapters = aign.chapter_list
|
163 |
+
if current_page < len(chapters):
|
164 |
+
current_page += 1
|
165 |
+
return current_page, display_chapter(chapters, current_page)
|
166 |
+
|
167 |
+
def select_chapter(aign, chapter_title):
|
168 |
+
chapters = aign.chapter_list
|
169 |
+
for i, (title, _) in enumerate(chapters, start=1):
|
170 |
+
if title == chapter_title:
|
171 |
+
return i, display_chapter(chapters, i)
|
172 |
+
return 1, "章节未找到。"
|
173 |
+
|
174 |
+
|
175 |
+
def make_middle_chat():
|
176 |
+
carrier = threading.Event()
|
177 |
+
carrier.history = []
|
178 |
+
|
179 |
+
def middle_chat(messages, temperature=None, top_p=None):
|
180 |
+
nonlocal carrier
|
181 |
+
carrier.history.append([None, ""])
|
182 |
+
if len(carrier.history) > 20:
|
183 |
+
carrier.history = carrier.history[-16:]
|
184 |
+
try:
|
185 |
+
for resp in chatLLM(
|
186 |
+
messages, temperature=temperature, top_p=top_p, stream=True, model=CURRENT_MODEL
|
187 |
+
):
|
188 |
+
output_text = resp["content"]
|
189 |
+
total_tokens = resp["total_tokens"]
|
190 |
+
|
191 |
+
carrier.history[-1][1] = f"total_tokens: {total_tokens}\n{output_text}"
|
192 |
+
return {
|
193 |
+
"content": output_text,
|
194 |
+
"total_tokens": total_tokens,
|
195 |
+
}
|
196 |
+
except Exception as e:
|
197 |
+
carrier.history[-1][1] = f"Error: {e}"
|
198 |
+
raise e
|
199 |
+
|
200 |
+
return carrier, middle_chat
|
201 |
+
|
202 |
+
|
203 |
+
def gen_ouline_button_clicked(aign, user_idea, history):
|
204 |
+
clear_console() # 清空命令行
|
205 |
+
aign.user_idea = user_idea
|
206 |
+
|
207 |
+
carrier, middle_chat = make_middle_chat()
|
208 |
+
carrier.history = [] # 清空历史记录
|
209 |
+
aign.novel_outline_writer.chatLLM = middle_chat
|
210 |
+
|
211 |
+
gen_ouline_thread = threading.Thread(target=aign.genNovelOutline)
|
212 |
+
gen_ouline_thread.start()
|
213 |
+
|
214 |
+
while gen_ouline_thread.is_alive():
|
215 |
+
yield [
|
216 |
+
aign,
|
217 |
+
carrier.history,
|
218 |
+
aign.novel_outline,
|
219 |
+
gr.Button(visible=False),
|
220 |
+
]
|
221 |
+
time.sleep(STREAM_INTERVAL)
|
222 |
+
yield [
|
223 |
+
aign,
|
224 |
+
carrier.history,
|
225 |
+
aign.novel_outline,
|
226 |
+
gr.Button(visible=True),
|
227 |
+
]
|
228 |
+
|
229 |
+
|
230 |
+
def gen_beginning_button_clicked(
|
231 |
+
aign, history, novel_outline, user_requriments#, embellishment_idea
|
232 |
+
):
|
233 |
+
clear_console() # 清空命令行
|
234 |
+
aign.novel_outline = novel_outline
|
235 |
+
aign.user_requriments = user_requriments
|
236 |
+
#aign.embellishment_idea = embellishment_idea
|
237 |
+
|
238 |
+
carrier, middle_chat = make_middle_chat()
|
239 |
+
carrier.history = [] # 清空历史记录
|
240 |
+
aign.novel_beginning_writer.chatLLM = middle_chat
|
241 |
+
#aign.novel_embellisher.chatLLM = middle_chat
|
242 |
+
|
243 |
+
gen_beginning_thread = threading.Thread(target=aign.genBeginning)
|
244 |
+
gen_beginning_thread.start()
|
245 |
+
|
246 |
+
while gen_beginning_thread.is_alive():
|
247 |
+
yield [
|
248 |
+
aign,
|
249 |
+
carrier.history,
|
250 |
+
aign.writing_plan,
|
251 |
+
aign.temp_setting,
|
252 |
+
aign.novel_content,
|
253 |
+
gr.Button(visible=False),
|
254 |
+
]
|
255 |
+
time.sleep(STREAM_INTERVAL)
|
256 |
+
yield [
|
257 |
+
aign,
|
258 |
+
carrier.history,
|
259 |
+
aign.writing_plan,
|
260 |
+
aign.temp_setting,
|
261 |
+
aign.novel_content,
|
262 |
+
gr.Button(visible=True),
|
263 |
+
]
|
264 |
+
|
265 |
+
|
266 |
+
def gen_next_paragraph_button_clicked(
|
267 |
+
aign,
|
268 |
+
history,
|
269 |
+
user_idea,
|
270 |
+
novel_outline,
|
271 |
+
writing_memory,
|
272 |
+
temp_setting,
|
273 |
+
writing_plan,
|
274 |
+
user_requriments,
|
275 |
+
#embellishment_idea,
|
276 |
+
):
|
277 |
+
# 生成保存进度链接
|
278 |
+
save_link = save_progress(aign) # 在函数开头定义保存进度链接
|
279 |
+
|
280 |
+
# 清空命令行
|
281 |
+
clear_console()
|
282 |
+
|
283 |
+
# 更新 AIGN 对象的各个字段
|
284 |
+
aign.user_idea = user_idea
|
285 |
+
aign.novel_outline = novel_outline
|
286 |
+
aign.writing_memory = writing_memory
|
287 |
+
aign.temp_setting = temp_setting
|
288 |
+
aign.writing_plan = writing_plan
|
289 |
+
aign.user_requriments = user_requriments
|
290 |
+
#aign.embellishment_idea = embellishment_idea
|
291 |
+
|
292 |
+
carrier, middle_chat = make_middle_chat()
|
293 |
+
carrier.history = [] # 清空历史记录
|
294 |
+
aign.novel_writer.chatLLM = middle_chat
|
295 |
+
#aign.novel_embellisher.chatLLM = middle_chat
|
296 |
+
aign.memory_maker.chatLLM = middle_chat
|
297 |
+
|
298 |
+
gen_next_paragraph_thread = threading.Thread(target=aign.genNextParagraph)
|
299 |
+
gen_next_paragraph_thread.start()
|
300 |
+
|
301 |
+
while gen_next_paragraph_thread.is_alive():
|
302 |
+
# 生成保存进度链接
|
303 |
+
save_link = save_progress(aign)
|
304 |
+
|
305 |
+
aign.update_chapter_list()
|
306 |
+
# 获取当前章节内容
|
307 |
+
current_chapter_content = display_chapter(aign.chapter_list, len(aign.chapter_list))
|
308 |
+
chapter_choices = [title for title, _ in aign.chapter_list]
|
309 |
+
yield [
|
310 |
+
aign,
|
311 |
+
carrier.history,
|
312 |
+
aign.writing_plan,
|
313 |
+
aign.temp_setting,
|
314 |
+
aign.writing_memory,
|
315 |
+
#aign.novel_content,#全部章节内容
|
316 |
+
current_chapter_content, # 只更新当前章节内容
|
317 |
+
gr.Button(visible=False),
|
318 |
+
save_link, # 返回生成的保存进度链接
|
319 |
+
gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None),
|
320 |
+
]
|
321 |
+
time.sleep(STREAM_INTERVAL)
|
322 |
+
|
323 |
+
# 生成保存进度链接
|
324 |
+
save_link = save_progress(aign)
|
325 |
+
|
326 |
+
aign.update_chapter_list()
|
327 |
+
# 获取最终的当前章节内容
|
328 |
+
current_chapter_content = display_chapter(aign.chapter_list, len(aign.chapter_list))
|
329 |
+
chapter_choices = [title for title, _ in aign.chapter_list]
|
330 |
+
yield [
|
331 |
+
aign,
|
332 |
+
carrier.history,
|
333 |
+
aign.writing_plan,
|
334 |
+
aign.temp_setting,
|
335 |
+
aign.writing_memory,
|
336 |
+
#aign.novel_content, #全部章节内容
|
337 |
+
current_chapter_content, # 只更新当前章节内容
|
338 |
+
gr.Button(visible=True),
|
339 |
+
save_link, # 返回生成的保存进度链接
|
340 |
+
gr.Dropdown(choices=chapter_choices, value=chapter_choices[-1] if chapter_choices else None),
|
341 |
+
]
|
342 |
+
|
343 |
+
|
344 |
+
css = """
|
345 |
+
#row1 {
|
346 |
+
min-width: 200px;
|
347 |
+
height: calc(100vh - 180px);
|
348 |
+
overflow: auto;
|
349 |
+
}
|
350 |
+
#row2 {
|
351 |
+
min-width: 300px;
|
352 |
+
height: calc(100vh - 180px);
|
353 |
+
overflow: auto;
|
354 |
+
}
|
355 |
+
#row3 {
|
356 |
+
min-width: 200px;
|
357 |
+
height: calc(100vh - 180px);
|
358 |
+
overflow: auto;
|
359 |
+
}
|
360 |
+
"""
|
361 |
+
|
362 |
+
with gr.Blocks(css=css) as demo:
|
363 |
+
aign = gr.State(AIGN(chatLLM))
|
364 |
+
gr.Markdown("## AI 写小说 Demo")
|
365 |
+
|
366 |
+
gr.Markdown("模型服务出问题会导致反复重试,这时切换到可用模型会继续,只要不刷新网页或者保存了进度(至少生成开头之后保存)就可以无限续写(某些模型智能及上下文有限,到极限会导致内容重复,这些模型在这就不适用了,比如gpt-3.5-turbo,当然接着切换到强模型可以再继续)")
|
367 |
+
|
368 |
+
# 添加模型选择下拉框
|
369 |
+
# with gr.Row(): 同一行
|
370 |
+
with gr.Row():
|
371 |
+
with gr.Column(scale=0, elem_id="row1"):
|
372 |
+
with gr.Tab("⚙"):
|
373 |
+
model_dropdown = gr.Dropdown(
|
374 |
+
choices=get_model_options(),
|
375 |
+
label="选择模型",
|
376 |
+
interactive=True,
|
377 |
+
value=CURRENT_MODEL
|
378 |
+
)
|
379 |
+
model_output = gr.Textbox(label="模型设置结果", interactive=False)
|
380 |
+
load_status = gr.Textbox(label="加载状态", interactive=False)
|
381 |
+
load_file = gr.File(label="选择加载文件", file_count="single", file_types=[".json"])
|
382 |
+
load_button = gr.Button("加载进度")
|
383 |
+
with gr.Tab("开始"):
|
384 |
+
gr.Markdown("生成大纲->大纲标签->生成开头->状态标签->生成下一段")
|
385 |
+
user_idea_text = gr.Textbox(
|
386 |
+
"将古龙小说仙侠化",
|
387 |
+
label="想法",
|
388 |
+
lines=16,
|
389 |
+
interactive=True,
|
390 |
+
)
|
391 |
+
user_requriments_text = gr.Textbox(
|
392 |
+
"",
|
393 |
+
label="写作要求",
|
394 |
+
lines=6,
|
395 |
+
interactive=True,
|
396 |
+
)
|
397 |
+
gen_ouline_button = gr.Button("生成大纲")
|
398 |
+
with gr.Tab("大纲"):
|
399 |
+
novel_outline_text = gr.Textbox(
|
400 |
+
label="大纲", lines=30, interactive=True
|
401 |
+
)
|
402 |
+
gen_beginning_button = gr.Button("生成开头")
|
403 |
+
with gr.Tab("状态"):
|
404 |
+
writing_memory_text = gr.Textbox(
|
405 |
+
label="记忆",
|
406 |
+
lines=8,
|
407 |
+
interactive=True,
|
408 |
+
max_lines=8,
|
409 |
+
)
|
410 |
+
writing_plan_text = gr.Textbox(label="计划", lines=6, interactive=True, max_lines=6)
|
411 |
+
temp_setting_text = gr.Textbox(
|
412 |
+
label="临时设定", lines=5, interactive=True, max_lines=5
|
413 |
+
)
|
414 |
+
gen_next_paragraph_button = gr.Button("生成下一段")
|
415 |
+
download_link = gr.HTML()
|
416 |
+
save_button = gr.Button("保存进度")
|
417 |
+
with gr.Tab("导航"):
|
418 |
+
save_content_button = gr.Button("保存正文")
|
419 |
+
current_page = gr.Number(value=1, label="当前页码", interactive=False)
|
420 |
+
prev_button = gr.Button("上一页")
|
421 |
+
next_button = gr.Button("下一页")
|
422 |
+
|
423 |
+
# 添加章节导航下拉列表
|
424 |
+
chapter_dropdown = gr.Dropdown(label="章节导航", choices=[], interactive=True)
|
425 |
+
|
426 |
+
# TODO
|
427 |
+
# gen_next_paragraph_button = gr.Button("撤销生成")
|
428 |
+
#undo_button = gr.Button("撤销生成")
|
429 |
+
# TODO
|
430 |
+
# auto_gen_next_checkbox = gr.Checkbox(
|
431 |
+
# label="自动生成下一段", checked=False, interactive=True
|
432 |
+
# )
|
433 |
+
|
434 |
+
with gr.Column(scale=3, elem_id="row2"):
|
435 |
+
chatBox = gr.Chatbot(height=f"80vh", label="输出")
|
436 |
+
with gr.Column(scale=0, elem_id="row3"):
|
437 |
+
novel_content_text = gr.Textbox(
|
438 |
+
label="小说正文", lines=37, interactive=True, show_copy_button=True
|
439 |
+
)
|
440 |
+
# TODO
|
441 |
+
# download_novel_button = gr.Button("下载小说")
|
442 |
+
|
443 |
+
#gr.Markdown("导航: https://deem.love")
|
444 |
+
|
445 |
+
prev_button.click(
|
446 |
+
prev_page,
|
447 |
+
inputs=[aign, current_page],
|
448 |
+
outputs=[current_page, novel_content_text],
|
449 |
+
queue=False
|
450 |
+
)
|
451 |
+
|
452 |
+
next_button.click(
|
453 |
+
next_page,
|
454 |
+
inputs=[aign, current_page],
|
455 |
+
outputs=[current_page, novel_content_text],
|
456 |
+
queue=False
|
457 |
+
)
|
458 |
+
|
459 |
+
# 添加章节下拉列表的事件处理
|
460 |
+
chapter_dropdown.change(
|
461 |
+
select_chapter,
|
462 |
+
inputs=[aign, chapter_dropdown],
|
463 |
+
outputs=[current_page, novel_content_text],
|
464 |
+
queue=False
|
465 |
+
)
|
466 |
+
|
467 |
+
# 添加模型选择的事件处理
|
468 |
+
model_dropdown.change(
|
469 |
+
set_api_model,
|
470 |
+
inputs=[model_dropdown],
|
471 |
+
outputs=[model_output]
|
472 |
+
)
|
473 |
+
|
474 |
+
# 修改按钮事件
|
475 |
+
save_button.click(
|
476 |
+
save_progress,
|
477 |
+
inputs=[aign],
|
478 |
+
outputs=[download_link]
|
479 |
+
)
|
480 |
+
|
481 |
+
# 修改加载按钮事件,包含章节下拉列表的更新
|
482 |
+
load_button.click(
|
483 |
+
load_progress,
|
484 |
+
inputs=[aign, load_file],
|
485 |
+
outputs=[aign, load_status, novel_outline_text, novel_content_text, writing_plan_text, temp_setting_text, writing_memory_text, user_idea_text, user_requriments_text, chapter_dropdown]
|
486 |
+
)
|
487 |
+
|
488 |
+
# 添加保存正文按钮事件
|
489 |
+
save_content_button.click(
|
490 |
+
save_content,
|
491 |
+
inputs=[novel_content_text],
|
492 |
+
outputs=[download_link]
|
493 |
+
)
|
494 |
+
|
495 |
+
#undo_button.click(
|
496 |
+
#undo_generation,
|
497 |
+
#[aign, chatBox],
|
498 |
+
#[aign, chatBox, writing_plan_text, temp_setting_text, writing_memory_text, novel_content_text, undo_button],
|
499 |
+
#)
|
500 |
+
|
501 |
+
gen_ouline_button.click(
|
502 |
+
gen_ouline_button_clicked,
|
503 |
+
[aign, user_idea_text, chatBox],
|
504 |
+
[aign, chatBox, novel_outline_text, gen_ouline_button],
|
505 |
+
)
|
506 |
+
gen_beginning_button.click(
|
507 |
+
gen_beginning_button_clicked,
|
508 |
+
[
|
509 |
+
aign,
|
510 |
+
chatBox,
|
511 |
+
novel_outline_text,
|
512 |
+
user_requriments_text,
|
513 |
+
#embellishment_idea_text,
|
514 |
+
],
|
515 |
+
[
|
516 |
+
aign,
|
517 |
+
chatBox,
|
518 |
+
writing_plan_text,
|
519 |
+
temp_setting_text,
|
520 |
+
novel_content_text,
|
521 |
+
gen_beginning_button,
|
522 |
+
],
|
523 |
+
)
|
524 |
+
|
525 |
+
# 修改生成下一段按钮事件,包含章节下拉列表的更新
|
526 |
+
gen_next_paragraph_button.click(
|
527 |
+
gen_next_paragraph_button_clicked,
|
528 |
+
[
|
529 |
+
aign,
|
530 |
+
chatBox,
|
531 |
+
user_idea_text,
|
532 |
+
novel_outline_text,
|
533 |
+
writing_memory_text,
|
534 |
+
temp_setting_text,
|
535 |
+
writing_plan_text,
|
536 |
+
user_requriments_text,
|
537 |
+
#embellishment_idea_text,
|
538 |
+
],
|
539 |
+
[
|
540 |
+
aign,
|
541 |
+
chatBox,
|
542 |
+
writing_plan_text,
|
543 |
+
temp_setting_text,
|
544 |
+
writing_memory_text,
|
545 |
+
novel_content_text,
|
546 |
+
gen_next_paragraph_button,
|
547 |
+
download_link, # 这里添加输出到下载链接
|
548 |
+
chapter_dropdown
|
549 |
+
],
|
550 |
+
)
|
551 |
+
|
552 |
+
if __name__ == "__main__":
|
553 |
+
demo.queue()
|
554 |
+
port = int(os.environ.get("PORT", 7860))
|
555 |
+
demo.launch(server_name="0.0.0.0", server_port=port)
|
openAI.py
ADDED
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
1 |
+
import os
|
2 |
+
|
3 |
+
from openai import OpenAI
|
4 |
+
|
5 |
+
|
6 |
+
def openAIChatLLM(model_name=None, api_key=None, base_url=None):
|
7 |
+
"""
|
8 |
+
model_name 取值
|
9 |
+
- deepseek-chat
|
10 |
+
"""
|
11 |
+
api_key = os.environ.get("OPENAI_API_KEY", api_key)
|
12 |
+
base_url = os.environ.get("OPENAI_BASE_URL", base_url)
|
13 |
+
model_name = os.environ.get("OPENAI_API_MODEL", model_name)
|
14 |
+
client = OpenAI(api_key=api_key, base_url=base_url)
|
15 |
+
|
16 |
+
def chatLLM(
|
17 |
+
messages: list,
|
18 |
+
temperature=None,
|
19 |
+
top_p=None,
|
20 |
+
max_tokens=None,
|
21 |
+
stream=False,
|
22 |
+
model=model_name, #可使用自定义模型
|
23 |
+
) -> dict:
|
24 |
+
if not stream:
|
25 |
+
response = client.chat.completions.create(
|
26 |
+
#model=model_name,
|
27 |
+
model=model,
|
28 |
+
messages=messages,
|
29 |
+
temperature=temperature,
|
30 |
+
top_p=top_p,
|
31 |
+
max_tokens=max_tokens,
|
32 |
+
)
|
33 |
+
return {
|
34 |
+
"content": response.choices[0].message.content,
|
35 |
+
"total_tokens": response.usage.total_tokens,
|
36 |
+
}
|
37 |
+
else:
|
38 |
+
responses = client.chat.completions.create(
|
39 |
+
#model=model_name,
|
40 |
+
model=model,
|
41 |
+
messages=messages,
|
42 |
+
temperature=temperature,
|
43 |
+
top_p=top_p,
|
44 |
+
max_tokens=max_tokens,
|
45 |
+
stream=True,
|
46 |
+
)
|
47 |
+
|
48 |
+
def respGenerator():
|
49 |
+
content = ""
|
50 |
+
for response in responses:
|
51 |
+
delta = response.choices[0].delta.content
|
52 |
+
|
53 |
+
#判断内容时候为空,非空才添加
|
54 |
+
if delta is not None:
|
55 |
+
content += delta
|
56 |
+
#content += delta
|
57 |
+
|
58 |
+
# if response.usage:
|
59 |
+
# total_tokens = response.usage.total_tokens
|
60 |
+
# else:
|
61 |
+
total_tokens = None
|
62 |
+
|
63 |
+
yield {
|
64 |
+
"content": content,
|
65 |
+
"total_tokens": total_tokens,
|
66 |
+
}
|
67 |
+
|
68 |
+
return respGenerator()
|
69 |
+
|
70 |
+
return chatLLM
|