Chan Meng
commited on
Commit
·
d750212
1
Parent(s):
a66d76b
update
Browse files- app.py +151 -162
- 互动式故事生成器开发规范文档.md +0 -101
app.py
CHANGED
@@ -6,61 +6,58 @@ import logging
|
|
6 |
import os
|
7 |
from dotenv import load_dotenv
|
8 |
|
9 |
-
#
|
10 |
load_dotenv()
|
11 |
|
12 |
-
#
|
13 |
logging.basicConfig(level=logging.INFO)
|
14 |
logger = logging.getLogger(__name__)
|
15 |
|
16 |
|
17 |
STORY_THEMES = [
|
18 |
-
"
|
19 |
-
"
|
20 |
-
"
|
21 |
-
"
|
22 |
-
"
|
23 |
-
"
|
24 |
]
|
25 |
|
26 |
CHARACTER_TEMPLATES = {
|
27 |
-
"
|
28 |
-
"
|
29 |
-
"
|
30 |
-
"
|
31 |
-
"
|
32 |
}
|
33 |
|
|
|
|
|
34 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
35 |
|
36 |
-
|
|
|
|
|
|
|
|
|
37 |
|
38 |
-
|
39 |
-
|
40 |
-
关键要求:
|
41 |
-
1. 故事必须具有连续性,每次回应都要基于之前的所有情节发展
|
42 |
-
2. 认真分析对话历史,保持人物性格、情节走向的一致性
|
43 |
-
3. 当用户补充新的细节或提供新的发展方向时,自然地将其整合到现有故事中
|
44 |
-
4. 注意因果关系,确保每个情节的发生都有合理的铺垫和解释
|
45 |
-
5. 通过环境描写、人物对话等手法,让故事更加生动
|
46 |
-
6. 在故事发展的关键节点,可以给出一些暗示,引导用户参与情节推进
|
47 |
-
|
48 |
-
你不应该:
|
49 |
-
1. 重新开始新的故事
|
50 |
-
2. 忽视之前提到的重要情节或细节
|
51 |
-
3. 生成与已建立设定相矛盾的内容
|
52 |
-
4. 突兀地引入未经铺垫的重大转折
|
53 |
-
|
54 |
-
请记住:你正在创作一个持续发展的故事,而不是独立的片段。"""
|
55 |
|
56 |
|
57 |
STORY_STYLES = [
|
58 |
-
"
|
59 |
-
"
|
60 |
-
"
|
61 |
-
"
|
62 |
-
"
|
63 |
-
"
|
64 |
]
|
65 |
|
66 |
MAX_RETRIES = 3
|
@@ -69,7 +66,7 @@ RETRY_DELAY = 2
|
|
69 |
def create_client() -> InferenceClient:
|
70 |
hf_token = os.getenv('HF_TOKEN')
|
71 |
if not hf_token:
|
72 |
-
raise ValueError("HF_TOKEN
|
73 |
return InferenceClient(
|
74 |
"HuggingFaceH4/zephyr-7b-beta",
|
75 |
token=hf_token
|
@@ -86,60 +83,60 @@ def generate_story(
|
|
86 |
top_p: float = 0.95,
|
87 |
) -> Generator[str, None, None]:
|
88 |
"""
|
89 |
-
|
90 |
"""
|
91 |
if history is None:
|
92 |
history = []
|
93 |
|
94 |
-
#
|
95 |
context_summary = ""
|
96 |
story_content = []
|
97 |
|
98 |
-
#
|
99 |
for msg in history:
|
100 |
if msg["role"] == "assistant":
|
101 |
story_content.append(msg["content"])
|
102 |
|
103 |
if story_content:
|
104 |
context_summary = "\n".join([
|
105 |
-
"
|
106 |
"---",
|
107 |
"\n".join(story_content),
|
108 |
"---"
|
109 |
])
|
110 |
|
111 |
-
#
|
112 |
if not history:
|
113 |
-
#
|
114 |
prompt = f"""
|
115 |
-
|
116 |
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
|
122 |
-
|
123 |
"""
|
124 |
else:
|
125 |
-
#
|
126 |
prompt = f"""
|
127 |
{context_summary}
|
128 |
|
129 |
-
|
130 |
-
-
|
131 |
-
-
|
132 |
-
-
|
133 |
|
134 |
-
|
135 |
|
136 |
-
|
137 |
-
1.
|
138 |
-
2.
|
139 |
-
3.
|
140 |
-
4.
|
141 |
|
142 |
-
|
143 |
"""
|
144 |
|
145 |
messages = [
|
@@ -164,111 +161,106 @@ def generate_story(
|
|
164 |
response += token
|
165 |
yield response
|
166 |
except Exception as e:
|
167 |
-
logger.error(f"
|
168 |
-
yield f"
|
169 |
-
|
170 |
-
|
171 |
|
172 |
def summarize_story_context(history: list) -> str:
|
173 |
"""
|
174 |
-
|
175 |
"""
|
176 |
if not history:
|
177 |
return ""
|
178 |
|
179 |
summary_parts = []
|
180 |
key_elements = {
|
181 |
-
"characters": set(), #
|
182 |
-
"locations": set(), #
|
183 |
-
"events": [], #
|
184 |
-
"objects": set() #
|
185 |
}
|
186 |
|
187 |
for msg in history:
|
188 |
content = msg.get("content", "")
|
189 |
-
# TODO:
|
190 |
-
#
|
191 |
if content:
|
192 |
summary_parts.append(content)
|
193 |
|
194 |
return "\n".join(summary_parts)
|
195 |
|
196 |
-
|
197 |
-
|
198 |
-
# 创建故事生成器界面
|
199 |
-
|
200 |
def create_demo():
|
201 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
202 |
gr.Markdown(
|
203 |
"""
|
204 |
-
# 🎭
|
205 |
-
|
206 |
-
|
207 |
"""
|
208 |
)
|
209 |
|
210 |
with gr.Tabs():
|
211 |
-
#
|
212 |
-
with gr.Tab("✍️
|
213 |
with gr.Row(equal_height=True):
|
214 |
-
#
|
215 |
with gr.Column(scale=1):
|
216 |
with gr.Group():
|
217 |
style_select = gr.Dropdown(
|
218 |
choices=STORY_STYLES,
|
219 |
-
value="
|
220 |
-
label="
|
221 |
-
info="
|
222 |
)
|
223 |
|
224 |
theme_select = gr.Dropdown(
|
225 |
choices=STORY_THEMES,
|
226 |
-
value="
|
227 |
-
label="
|
228 |
-
info="
|
229 |
)
|
230 |
|
231 |
with gr.Group():
|
232 |
-
gr.Markdown("### 👤
|
233 |
character_select = gr.Dropdown(
|
234 |
choices=list(CHARACTER_TEMPLATES.keys()),
|
235 |
-
value="
|
236 |
-
label="
|
237 |
-
info="
|
238 |
)
|
239 |
|
240 |
character_desc = gr.Textbox(
|
241 |
lines=3,
|
242 |
-
value=CHARACTER_TEMPLATES["
|
243 |
-
label="
|
244 |
-
info="
|
245 |
)
|
246 |
|
247 |
with gr.Group():
|
248 |
scene_input = gr.Textbox(
|
249 |
lines=3,
|
250 |
-
placeholder="
|
251 |
-
label="
|
252 |
-
info="
|
253 |
)
|
254 |
|
255 |
with gr.Row():
|
256 |
-
submit_btn = gr.Button("✨
|
257 |
-
clear_btn = gr.Button("🗑️
|
258 |
-
save_btn = gr.Button("💾
|
259 |
|
260 |
-
#
|
261 |
with gr.Column(scale=2):
|
262 |
chatbot = gr.Chatbot(
|
263 |
-
label="
|
264 |
height=600,
|
265 |
show_label=True
|
266 |
)
|
267 |
|
268 |
status_msg = gr.Markdown("")
|
269 |
|
270 |
-
#
|
271 |
-
with gr.Tab("⚙️
|
272 |
with gr.Group():
|
273 |
with gr.Row():
|
274 |
with gr.Column():
|
@@ -277,8 +269,8 @@ def create_demo():
|
|
277 |
maximum=2.0,
|
278 |
value=0.7,
|
279 |
step=0.1,
|
280 |
-
label="
|
281 |
-
info="
|
282 |
)
|
283 |
|
284 |
max_tokens = gr.Slider(
|
@@ -286,8 +278,8 @@ def create_demo():
|
|
286 |
maximum=1024,
|
287 |
value=512,
|
288 |
step=64,
|
289 |
-
label="
|
290 |
-
info="
|
291 |
)
|
292 |
|
293 |
top_p = gr.Slider(
|
@@ -295,35 +287,35 @@ def create_demo():
|
|
295 |
maximum=1.0,
|
296 |
value=0.95,
|
297 |
step=0.05,
|
298 |
-
label="
|
299 |
-
info="
|
300 |
)
|
301 |
|
302 |
-
#
|
303 |
-
with gr.Accordion("📖
|
304 |
gr.Markdown(
|
305 |
"""
|
306 |
-
##
|
307 |
-
1.
|
308 |
-
2.
|
309 |
-
3.
|
310 |
-
4.
|
311 |
-
5.
|
312 |
|
313 |
-
##
|
314 |
-
-
|
315 |
-
-
|
316 |
-
-
|
317 |
-
-
|
318 |
|
319 |
-
##
|
320 |
-
-
|
321 |
-
-
|
322 |
-
-
|
323 |
"""
|
324 |
)
|
325 |
|
326 |
-
#
|
327 |
def update_character_desc(template):
|
328 |
return CHARACTER_TEMPLATES[template]
|
329 |
|
@@ -333,53 +325,52 @@ def create_demo():
|
|
333 |
character_desc
|
334 |
)
|
335 |
|
336 |
-
#
|
337 |
save_btn.click(
|
338 |
save_story,
|
339 |
chatbot,
|
340 |
status_msg,
|
341 |
)
|
342 |
|
343 |
-
#
|
344 |
def user_input(user_message, history):
|
345 |
"""
|
346 |
-
|
347 |
Args:
|
348 |
-
user_message:
|
349 |
-
history:
|
350 |
"""
|
351 |
if history is None:
|
352 |
history = []
|
353 |
-
history.append([user_message, None]) #
|
354 |
return "", history
|
355 |
|
356 |
-
# AI
|
357 |
def bot_response(history, style, theme, character_desc, temperature, max_tokens, top_p):
|
358 |
"""
|
359 |
-
|
360 |
Args:
|
361 |
-
history:
|
362 |
-
style:
|
363 |
-
theme:
|
364 |
-
character_desc:
|
365 |
-
temperature:
|
366 |
-
max_tokens:
|
367 |
-
top_p:
|
368 |
"""
|
369 |
try:
|
370 |
-
|
371 |
-
# 获取用户的最后一条消息
|
372 |
user_message = history[-1][0]
|
373 |
|
374 |
-
#
|
375 |
message_history = []
|
376 |
-
for user_msg, bot_msg in history[:-1]: #
|
377 |
if user_msg:
|
378 |
message_history.append({"role": "user", "content": user_msg})
|
379 |
if bot_msg:
|
380 |
message_history.append({"role": "assistant", "content": bot_msg})
|
381 |
|
382 |
-
#
|
383 |
current_response = ""
|
384 |
for text in generate_story(
|
385 |
user_message,
|
@@ -392,21 +383,21 @@ def create_demo():
|
|
392 |
top_p
|
393 |
):
|
394 |
current_response = text
|
395 |
-
history[-1][1] = current_response #
|
396 |
yield history
|
397 |
|
398 |
except Exception as e:
|
399 |
-
logger.error(f"
|
400 |
-
error_msg = f"
|
401 |
history[-1][1] = error_msg
|
402 |
yield history
|
403 |
|
404 |
|
405 |
-
#
|
406 |
def clear_chat():
|
407 |
return [], ""
|
408 |
|
409 |
-
#
|
410 |
scene_input.submit(
|
411 |
user_input,
|
412 |
[scene_input, chatbot],
|
@@ -437,9 +428,9 @@ def create_demo():
|
|
437 |
|
438 |
|
439 |
def save_story(chatbot):
|
440 |
-
"""
|
441 |
if not chatbot:
|
442 |
-
return "
|
443 |
|
444 |
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
445 |
filename = f"stories/story_{timestamp}.txt"
|
@@ -450,14 +441,12 @@ def save_story(chatbot):
|
|
450 |
with open(filename, "w", encoding="utf-8") as f:
|
451 |
for user_msg, bot_msg in chatbot:
|
452 |
if user_msg:
|
453 |
-
f.write(f"
|
454 |
if bot_msg:
|
455 |
f.write(f"AI: {bot_msg}\n\n")
|
456 |
-
return f"
|
457 |
except Exception as e:
|
458 |
-
return f"
|
459 |
-
|
460 |
-
|
461 |
|
462 |
if __name__ == "__main__":
|
463 |
demo = create_demo()
|
|
|
6 |
import os
|
7 |
from dotenv import load_dotenv
|
8 |
|
9 |
+
# Load environment variables
|
10 |
load_dotenv()
|
11 |
|
12 |
+
# Set up logging
|
13 |
logging.basicConfig(level=logging.INFO)
|
14 |
logger = logging.getLogger(__name__)
|
15 |
|
16 |
|
17 |
STORY_THEMES = [
|
18 |
+
"Adventure",
|
19 |
+
"Mystery",
|
20 |
+
"Romance",
|
21 |
+
"Historical",
|
22 |
+
"Slice of Life",
|
23 |
+
"Fairy Tale"
|
24 |
]
|
25 |
|
26 |
CHARACTER_TEMPLATES = {
|
27 |
+
"Adventurer": "A brave and fearless explorer who loves adventure and challenges.",
|
28 |
+
"Detective": "A keen and observant detective skilled in observation and deduction.",
|
29 |
+
"Artist": "A creative artist with unique perspectives on beauty.",
|
30 |
+
"Scientist": "A curious scientist dedicated to exploring the unknown.",
|
31 |
+
"Ordinary Person": "An ordinary person with a rich inner world."
|
32 |
}
|
33 |
|
34 |
+
# Initialize story generator system prompt
|
35 |
+
STORY_SYSTEM_PROMPT = """You are a professional story generator. Your task is to generate coherent and engaging stories based on user settings and real-time input.
|
36 |
|
37 |
+
Key requirements:
|
38 |
+
1. The story must maintain continuity, with each response building upon all previous plot developments
|
39 |
+
2. Carefully analyze dialogue history to maintain consistency in character personalities and plot progression
|
40 |
+
3. Naturally integrate new details or development directions when provided by the user
|
41 |
+
4. Pay attention to cause and effect, ensuring each plot point has reasonable setup and explanation
|
42 |
+
5. Make the story more vivid through environmental descriptions and character dialogues
|
43 |
+
6. At key story points, provide hints to guide user participation in plot progression
|
44 |
|
45 |
+
You should not:
|
46 |
+
1. Start a new story
|
47 |
+
2. Ignore previously mentioned important plots or details
|
48 |
+
3. Generate content that contradicts established settings
|
49 |
+
4. Introduce major turns without proper setup
|
50 |
|
51 |
+
Remember: You are creating an ongoing story, not independent fragments."""
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
52 |
|
53 |
|
54 |
STORY_STYLES = [
|
55 |
+
"Fantasy",
|
56 |
+
"Science Fiction",
|
57 |
+
"Mystery",
|
58 |
+
"Adventure",
|
59 |
+
"Romance",
|
60 |
+
"Horror"
|
61 |
]
|
62 |
|
63 |
MAX_RETRIES = 3
|
|
|
66 |
def create_client() -> InferenceClient:
|
67 |
hf_token = os.getenv('HF_TOKEN')
|
68 |
if not hf_token:
|
69 |
+
raise ValueError("HF_TOKEN environment variable not set")
|
70 |
return InferenceClient(
|
71 |
"HuggingFaceH4/zephyr-7b-beta",
|
72 |
token=hf_token
|
|
|
83 |
top_p: float = 0.95,
|
84 |
) -> Generator[str, None, None]:
|
85 |
"""
|
86 |
+
Generate continuous story plot
|
87 |
"""
|
88 |
if history is None:
|
89 |
history = []
|
90 |
|
91 |
+
# Build context summary
|
92 |
context_summary = ""
|
93 |
story_content = []
|
94 |
|
95 |
+
# Extract previous story content
|
96 |
for msg in history:
|
97 |
if msg["role"] == "assistant":
|
98 |
story_content.append(msg["content"])
|
99 |
|
100 |
if story_content:
|
101 |
context_summary = "\n".join([
|
102 |
+
"Previously in the story:",
|
103 |
"---",
|
104 |
"\n".join(story_content),
|
105 |
"---"
|
106 |
])
|
107 |
|
108 |
+
# Use different prompt templates based on whether there's history
|
109 |
if not history:
|
110 |
+
# First generation, use complete settings
|
111 |
prompt = f"""
|
112 |
+
Please start a story based on the following settings:
|
113 |
|
114 |
+
Style: {style}
|
115 |
+
Theme: {theme}
|
116 |
+
Character: {character_desc}
|
117 |
+
Initial Scene: {scene}
|
118 |
|
119 |
+
Please begin from this scene and set up the story's opening. Leave room for future developments.
|
120 |
"""
|
121 |
else:
|
122 |
+
# Subsequent generation, focus on plot continuation
|
123 |
prompt = f"""
|
124 |
{context_summary}
|
125 |
|
126 |
+
Story settings reminder:
|
127 |
+
- Style: {style}
|
128 |
+
- Theme: {theme}
|
129 |
+
- Main Character: {character_desc}
|
130 |
|
131 |
+
User's new input: {scene}
|
132 |
|
133 |
+
Please continue the story based on the previous plot and user's new input. Note:
|
134 |
+
1. New developments must maintain continuity with previous plot
|
135 |
+
2. Rationalize new elements provided by the user
|
136 |
+
3. Maintain consistency in character personalities
|
137 |
+
4. Leave possibilities for future developments
|
138 |
|
139 |
+
Continue the story:
|
140 |
"""
|
141 |
|
142 |
messages = [
|
|
|
161 |
response += token
|
162 |
yield response
|
163 |
except Exception as e:
|
164 |
+
logger.error(f"Error occurred while generating story: {str(e)}")
|
165 |
+
yield f"Sorry, encountered an error while generating the story: {str(e)}\nPlease try again later."
|
|
|
|
|
166 |
|
167 |
def summarize_story_context(history: list) -> str:
|
168 |
"""
|
169 |
+
Summarize current story context for generation assistance
|
170 |
"""
|
171 |
if not history:
|
172 |
return ""
|
173 |
|
174 |
summary_parts = []
|
175 |
key_elements = {
|
176 |
+
"characters": set(), # Characters appeared
|
177 |
+
"locations": set(), # Scene locations
|
178 |
+
"events": [], # Key events
|
179 |
+
"objects": set() # Important items
|
180 |
}
|
181 |
|
182 |
for msg in history:
|
183 |
content = msg.get("content", "")
|
184 |
+
# TODO: More complex NLP processing can be added here to extract key information
|
185 |
+
# Currently using simple text accumulation
|
186 |
if content:
|
187 |
summary_parts.append(content)
|
188 |
|
189 |
return "\n".join(summary_parts)
|
190 |
|
191 |
+
# Create story generator interface
|
|
|
|
|
|
|
192 |
def create_demo():
|
193 |
with gr.Blocks(theme=gr.themes.Soft()) as demo:
|
194 |
gr.Markdown(
|
195 |
"""
|
196 |
+
# 🎭 Interactive Story Generator
|
197 |
+
Let AI create a unique storytelling experience for you. Choose your story style, theme, add character settings,
|
198 |
+
then describe a scene to start your story. Interact with AI to continue developing the plot!
|
199 |
"""
|
200 |
)
|
201 |
|
202 |
with gr.Tabs():
|
203 |
+
# Story Creation Tab
|
204 |
+
with gr.Tab("✍️ Story Creation"):
|
205 |
with gr.Row(equal_height=True):
|
206 |
+
# Left Control Panel
|
207 |
with gr.Column(scale=1):
|
208 |
with gr.Group():
|
209 |
style_select = gr.Dropdown(
|
210 |
choices=STORY_STYLES,
|
211 |
+
value="Fantasy",
|
212 |
+
label="Choose Story Style",
|
213 |
+
info="Select an overall style to define the story's tone"
|
214 |
)
|
215 |
|
216 |
theme_select = gr.Dropdown(
|
217 |
choices=STORY_THEMES,
|
218 |
+
value="Adventure",
|
219 |
+
label="Choose Story Theme",
|
220 |
+
info="Select the main thematic elements to focus on"
|
221 |
)
|
222 |
|
223 |
with gr.Group():
|
224 |
+
gr.Markdown("### 👤 Character Settings")
|
225 |
character_select = gr.Dropdown(
|
226 |
choices=list(CHARACTER_TEMPLATES.keys()),
|
227 |
+
value="Adventurer",
|
228 |
+
label="Select Character Template",
|
229 |
+
info="Choose a preset character type or customize description"
|
230 |
)
|
231 |
|
232 |
character_desc = gr.Textbox(
|
233 |
lines=3,
|
234 |
+
value=CHARACTER_TEMPLATES["Adventurer"],
|
235 |
+
label="Character Description",
|
236 |
+
info="Describe character's personality, background, traits, etc."
|
237 |
)
|
238 |
|
239 |
with gr.Group():
|
240 |
scene_input = gr.Textbox(
|
241 |
lines=3,
|
242 |
+
placeholder="Describe the scene, environment, time, etc. here...",
|
243 |
+
label="Scene Description",
|
244 |
+
info="Detailed scene description will make the story more vivid"
|
245 |
)
|
246 |
|
247 |
with gr.Row():
|
248 |
+
submit_btn = gr.Button("✨ Start Story", variant="primary", scale=2)
|
249 |
+
clear_btn = gr.Button("🗑️ Clear Chat", scale=1)
|
250 |
+
save_btn = gr.Button("💾 Save Story", scale=1)
|
251 |
|
252 |
+
# Right Chat Area
|
253 |
with gr.Column(scale=2):
|
254 |
chatbot = gr.Chatbot(
|
255 |
+
label="Story Dialogue",
|
256 |
height=600,
|
257 |
show_label=True
|
258 |
)
|
259 |
|
260 |
status_msg = gr.Markdown("")
|
261 |
|
262 |
+
# Settings Tab
|
263 |
+
with gr.Tab("⚙️ Advanced Settings"):
|
264 |
with gr.Group():
|
265 |
with gr.Row():
|
266 |
with gr.Column():
|
|
|
269 |
maximum=2.0,
|
270 |
value=0.7,
|
271 |
step=0.1,
|
272 |
+
label="Creativity (Temperature)",
|
273 |
+
info="Higher values make story more creative but potentially less coherent"
|
274 |
)
|
275 |
|
276 |
max_tokens = gr.Slider(
|
|
|
278 |
maximum=1024,
|
279 |
value=512,
|
280 |
step=64,
|
281 |
+
label="Maximum Generation Length",
|
282 |
+
info="Control the length of each generated text"
|
283 |
)
|
284 |
|
285 |
top_p = gr.Slider(
|
|
|
287 |
maximum=1.0,
|
288 |
value=0.95,
|
289 |
step=0.05,
|
290 |
+
label="Sampling Range (Top-p)",
|
291 |
+
info="Control the diversity of word choice"
|
292 |
)
|
293 |
|
294 |
+
# Help Information
|
295 |
+
with gr.Accordion("📖 Usage Guide", open=False):
|
296 |
gr.Markdown(
|
297 |
"""
|
298 |
+
## How to Use the Story Generator
|
299 |
+
1. Choose story style and theme to set the overall tone
|
300 |
+
2. Select a preset character template or customize character description
|
301 |
+
3. Describe the story's scene and environment
|
302 |
+
4. Click "Start Story" to generate the opening
|
303 |
+
5. Continue inputting content to interact with AI and advance the story
|
304 |
|
305 |
+
## Tips
|
306 |
+
- Detailed scene and character descriptions will make the generated story richer
|
307 |
+
- Use the "Save Story" function to save memorable story plots
|
308 |
+
- Adjust parameters in settings to affect story creativity and coherence
|
309 |
+
- Use "Clear Chat" to start over if you're not satisfied with the plot
|
310 |
|
311 |
+
## Parameter Explanation
|
312 |
+
- Creativity: Controls the story's creativity level, higher values increase creativity
|
313 |
+
- Sampling Range: Controls vocabulary richness, higher values increase word diversity
|
314 |
+
- Maximum Length: Controls the length of each generated text
|
315 |
"""
|
316 |
)
|
317 |
|
318 |
+
# Update character description
|
319 |
def update_character_desc(template):
|
320 |
return CHARACTER_TEMPLATES[template]
|
321 |
|
|
|
325 |
character_desc
|
326 |
)
|
327 |
|
328 |
+
# Save story dialogue
|
329 |
save_btn.click(
|
330 |
save_story,
|
331 |
chatbot,
|
332 |
status_msg,
|
333 |
)
|
334 |
|
335 |
+
# User input processing
|
336 |
def user_input(user_message, history):
|
337 |
"""
|
338 |
+
Process user input
|
339 |
Args:
|
340 |
+
user_message: User's input message
|
341 |
+
history: Chat history [(user_msg, bot_msg), ...]
|
342 |
"""
|
343 |
if history is None:
|
344 |
history = []
|
345 |
+
history.append([user_message, None]) # Add user message, bot message temporarily None
|
346 |
return "", history
|
347 |
|
348 |
+
# AI response processing
|
349 |
def bot_response(history, style, theme, character_desc, temperature, max_tokens, top_p):
|
350 |
"""
|
351 |
+
Generate AI response
|
352 |
Args:
|
353 |
+
history: Chat history [(user_msg, bot_msg), ...]
|
354 |
+
style: Story style
|
355 |
+
theme: Story theme
|
356 |
+
character_desc: Character description
|
357 |
+
temperature: Generation parameter
|
358 |
+
max_tokens: Generation parameter
|
359 |
+
top_p: Generation parameter
|
360 |
"""
|
361 |
try:
|
362 |
+
# Get user's last message
|
|
|
363 |
user_message = history[-1][0]
|
364 |
|
365 |
+
# Convert history format for generate_story
|
366 |
message_history = []
|
367 |
+
for user_msg, bot_msg in history[:-1]: # Excluding the last one
|
368 |
if user_msg:
|
369 |
message_history.append({"role": "user", "content": user_msg})
|
370 |
if bot_msg:
|
371 |
message_history.append({"role": "assistant", "content": bot_msg})
|
372 |
|
373 |
+
# Start generating story
|
374 |
current_response = ""
|
375 |
for text in generate_story(
|
376 |
user_message,
|
|
|
383 |
top_p
|
384 |
):
|
385 |
current_response = text
|
386 |
+
history[-1][1] = current_response # Update bot reply for the last message
|
387 |
yield history
|
388 |
|
389 |
except Exception as e:
|
390 |
+
logger.error(f"Error occurred while processing response: {str(e)}")
|
391 |
+
error_msg = f"Sorry, encountered an error while generating the story. Please try again later."
|
392 |
history[-1][1] = error_msg
|
393 |
yield history
|
394 |
|
395 |
|
396 |
+
# Clear chat
|
397 |
def clear_chat():
|
398 |
return [], ""
|
399 |
|
400 |
+
# Bind events
|
401 |
scene_input.submit(
|
402 |
user_input,
|
403 |
[scene_input, chatbot],
|
|
|
428 |
|
429 |
|
430 |
def save_story(chatbot):
|
431 |
+
"""Save story dialogue record"""
|
432 |
if not chatbot:
|
433 |
+
return "Story is empty, cannot save"
|
434 |
|
435 |
timestamp = time.strftime("%Y%m%d_%H%M%S")
|
436 |
filename = f"stories/story_{timestamp}.txt"
|
|
|
441 |
with open(filename, "w", encoding="utf-8") as f:
|
442 |
for user_msg, bot_msg in chatbot:
|
443 |
if user_msg:
|
444 |
+
f.write(f"User: {user_msg}\n")
|
445 |
if bot_msg:
|
446 |
f.write(f"AI: {bot_msg}\n\n")
|
447 |
+
return f"Story has been saved to {filename}"
|
448 |
except Exception as e:
|
449 |
+
return f"Save failed: {str(e)}"
|
|
|
|
|
450 |
|
451 |
if __name__ == "__main__":
|
452 |
demo = create_demo()
|
互动式故事生成器开发规范文档.md
DELETED
@@ -1,101 +0,0 @@
|
|
1 |
-
# 互动式故事生成器 - 开发规范文档 (使用Meta Llama)
|
2 |
-
|
3 |
-
## 1. 项目概述
|
4 |
-
创建一个基于AI的互动式故事生成器,使用Meta Llama模型,允许用户提供初始场景或角色,然后与AI进行对话式交互来发展故事情节。该项目将部署在Hugging Face Space上。
|
5 |
-
|
6 |
-
## 2. 技术栈
|
7 |
-
- Python 3.8+
|
8 |
-
- Gradio (用于创建Web界面)
|
9 |
-
- Hugging Face Transformers (用于访问和使用Llama模型)
|
10 |
-
- PyTorch (作为Transformers的后端)
|
11 |
-
|
12 |
-
## 3. 开发阶段
|
13 |
-
|
14 |
-
### 阶段1: 基本功能实现
|
15 |
-
|
16 |
-
#### 1.1 设置项目环境
|
17 |
-
- 创建`requirements.txt`文件,包含必要的依赖
|
18 |
-
- 设置虚拟环境
|
19 |
-
|
20 |
-
#### 1.2 配置Llama模型
|
21 |
-
- 使用Hugging Face Transformers库加载Llama模型
|
22 |
-
- 设置适当的模型参数(如温度、top-k等)
|
23 |
-
|
24 |
-
#### 1.3 实现基本的故事生成
|
25 |
-
- 创建`app.py`文件
|
26 |
-
- 导入必要的库和Llama模型
|
27 |
-
- 实现使用Llama的文本生成函数
|
28 |
-
|
29 |
-
#### 1.4 创建基本的Gradio界面
|
30 |
-
- 设计输入框供用户输入初始场景
|
31 |
-
- 添加"生成故事"按钮
|
32 |
-
- 创建输出区域显示生成的故事
|
33 |
-
|
34 |
-
#### 1.5 整合模型和界面
|
35 |
-
- 将Llama文本生成函数与Gradio界面连接
|
36 |
-
- 测试基本功能
|
37 |
-
|
38 |
-
### 阶段2: 增强交互性
|
39 |
-
|
40 |
-
#### 2.1 实现对话式交互
|
41 |
-
- 修改Llama生成函数,支持接收用户的后续输入
|
42 |
-
- 更新Gradio界面,添加对话历史显示
|
43 |
-
- 实现回合制的故事发展机制
|
44 |
-
|
45 |
-
#### 2.2 添加故事风格选择
|
46 |
-
- 创建预定义的故事风格列表(如科幻、奇幻、悬疑等)
|
47 |
-
- 在界面中添加风格选择下拉菜单
|
48 |
-
- 修改Llama生成函数,考虑所选风格
|
49 |
-
|
50 |
-
#### 2.3 角色管理
|
51 |
-
- 添加角色创建功能
|
52 |
-
- 在故事生成过程中整合角色信息到Llama输入
|
53 |
-
|
54 |
-
### 阶段3: 高级功能
|
55 |
-
|
56 |
-
#### 3.1 故事分支
|
57 |
-
- 使用Llama生成多个可能的故事发展方向
|
58 |
-
- 允许用户选择喜欢的方向继续发展
|
59 |
-
|
60 |
-
#### 3.2 保存和加载功能
|
61 |
-
- 实现将生成的故事保存为文件的功能
|
62 |
-
- 添加加载已保存故事的选项
|
63 |
-
|
64 |
-
#### 3.3 上下文管理
|
65 |
-
- 实现有效的上下文管理,确保Llama模型能够理解长期上下文
|
66 |
-
|
67 |
-
## 4. 优化Llama模型使用
|
68 |
-
- 实现高效的模型缓存机制
|
69 |
-
- 优化推理速度,考虑使用量化技术
|
70 |
-
- 实现批处理以提高效率
|
71 |
-
|
72 |
-
## 5. Hugging Face Space部署准备
|
73 |
-
- 确保`requirements.txt`包含所有必要的依赖
|
74 |
-
- 创建`README.md`文件,提供项目描述和使用说明
|
75 |
-
- 配置Hugging Face Space的环境变量(如需要)
|
76 |
-
|
77 |
-
## 6. 测试
|
78 |
-
- 为每个主要功能编写单元测试
|
79 |
-
- 进行用户体验测试,收集反馈
|
80 |
-
- 测试在Hugging Face Space环境中的性能
|
81 |
-
|
82 |
-
## 7. 部署
|
83 |
-
- 在Hugging Face Space上部署应用
|
84 |
-
- 确保模型和所有必要文件都正确上传
|
85 |
-
- 测试部署后的应用功能
|
86 |
-
|
87 |
-
## 8. 后续优化
|
88 |
-
- 基于用户反馈进行界面优化
|
89 |
-
- 探索使用Llama模型的最新版本或变体
|
90 |
-
- 考虑添加多语言支持
|
91 |
-
- 优化模型响应时间和资源使用
|
92 |
-
|
93 |
-
## 9. 文档和用户指南
|
94 |
-
- 编写详细的项目文档,包括技术细节和架构说明
|
95 |
-
- 创建用户指南,解释如何使用互动式故事生成器
|
96 |
-
- 在Hugging Face Space项目页面上提供清晰的使用说明
|
97 |
-
|
98 |
-
注意:
|
99 |
-
1. 在开发过程中,请确保遵循Python的PEP 8编码规范,并为函数和类添加适当的文档字符串。
|
100 |
-
2. 使用Llama模型时,请确保遵守相关的使用条款和许可协议。
|
101 |
-
3. 考虑到Hugging Face Space的资源限制,可能需要对模型进行优化或选择较小的Llama变体。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|