Chan Meng
commited on
Commit
·
7822cb8
1
Parent(s):
88f8e60
Init
Browse files- .gitignore +1 -0
- app.py +356 -87
- 互动式故事生成器开发规范文档.md +101 -0
.gitignore
CHANGED
@@ -1,2 +1,3 @@
|
|
1 |
|
2 |
/venv
|
|
|
|
1 |
|
2 |
/venv
|
3 |
+
.env
|
app.py
CHANGED
@@ -13,9 +13,46 @@ load_dotenv()
|
|
13 |
logging.basicConfig(level=logging.INFO)
|
14 |
logger = logging.getLogger(__name__)
|
15 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
16 |
# 初始化故事生成器的系统提示
|
17 |
-
|
18 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
19 |
|
20 |
STORY_STYLES = [
|
21 |
"奇幻",
|
@@ -41,131 +78,342 @@ def create_client() -> InferenceClient:
|
|
41 |
def generate_story(
|
42 |
scene: str,
|
43 |
style: str,
|
44 |
-
|
|
|
|
|
45 |
temperature: float = 0.7,
|
46 |
max_tokens: int = 512,
|
47 |
top_p: float = 0.95,
|
48 |
) -> Generator[str, None, None]:
|
|
|
|
|
|
|
49 |
if history is None:
|
50 |
history = []
|
51 |
|
52 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
53 |
|
54 |
messages = [
|
55 |
{"role": "system", "content": STORY_SYSTEM_PROMPT},
|
56 |
-
{"role": "user", "content":
|
57 |
]
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
58 |
|
59 |
-
|
60 |
-
|
|
|
|
|
|
|
|
|
|
|
61 |
|
62 |
-
|
63 |
-
|
|
|
|
|
|
|
|
|
64 |
|
65 |
-
|
66 |
-
|
67 |
-
|
68 |
-
|
69 |
-
|
70 |
-
max_tokens=max_tokens,
|
71 |
-
stream=True,
|
72 |
-
temperature=temperature,
|
73 |
-
top_p=top_p,
|
74 |
-
):
|
75 |
-
if hasattr(message.choices[0].delta, 'content'):
|
76 |
-
token = message.choices[0].delta.content
|
77 |
-
if token is not None:
|
78 |
-
response += token
|
79 |
-
yield response
|
80 |
-
break
|
81 |
-
except Exception as e:
|
82 |
-
retries += 1
|
83 |
-
logger.error(f"生成故事时发生错误 (尝试 {retries}/{MAX_RETRIES}): {str(e)}")
|
84 |
-
if retries < MAX_RETRIES:
|
85 |
-
time.sleep(RETRY_DELAY)
|
86 |
-
else:
|
87 |
-
yield f"抱歉,生成故事时遇到了问题:{str(e)}\n请稍后重试。"
|
88 |
|
89 |
def create_demo():
|
90 |
-
with gr.Blocks() as demo:
|
91 |
-
gr.Markdown(
|
92 |
-
|
93 |
-
|
94 |
-
|
95 |
-
|
96 |
-
|
97 |
-
label="选择故事风格"
|
98 |
-
)
|
99 |
-
scene_input = gr.Textbox(
|
100 |
-
lines=3,
|
101 |
-
placeholder="请输入一个场景或角色描述...",
|
102 |
-
label="场景描述"
|
103 |
)
|
104 |
|
105 |
-
|
106 |
-
|
107 |
-
|
108 |
-
|
109 |
-
|
110 |
-
|
111 |
-
|
112 |
-
|
113 |
-
|
114 |
-
|
115 |
-
|
116 |
-
|
117 |
-
|
118 |
-
|
119 |
-
|
120 |
-
|
121 |
-
|
122 |
-
|
123 |
-
|
124 |
-
|
125 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
126 |
|
127 |
-
|
128 |
-
|
129 |
-
|
|
|
|
|
|
|
|
|
|
|
130 |
)
|
131 |
-
status_msg = gr.Markdown("")
|
132 |
|
133 |
-
|
134 |
-
|
|
|
|
|
|
|
|
|
135 |
|
|
|
136 |
def user_input(user_message, history):
|
|
|
|
|
|
|
|
|
|
|
|
|
137 |
if history is None:
|
138 |
history = []
|
139 |
-
history.append(
|
140 |
return "", history
|
141 |
-
|
142 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
143 |
try:
|
144 |
-
|
145 |
-
|
|
|
146 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
147 |
for text in generate_story(
|
148 |
-
|
149 |
style,
|
150 |
-
|
|
|
|
|
151 |
temperature,
|
152 |
max_tokens,
|
153 |
top_p
|
154 |
):
|
155 |
-
|
|
|
156 |
yield history
|
|
|
157 |
except Exception as e:
|
158 |
logger.error(f"处理响应时发生错误: {str(e)}")
|
159 |
-
|
|
|
160 |
yield history
|
|
|
|
|
|
|
|
|
|
|
161 |
|
|
|
162 |
scene_input.submit(
|
163 |
user_input,
|
164 |
[scene_input, chatbot],
|
165 |
[scene_input, chatbot]
|
166 |
).then(
|
167 |
bot_response,
|
168 |
-
[chatbot, style_select, temperature, max_tokens, top_p],
|
169 |
chatbot
|
170 |
)
|
171 |
|
@@ -175,13 +423,10 @@ def create_demo():
|
|
175 |
[scene_input, chatbot]
|
176 |
).then(
|
177 |
bot_response,
|
178 |
-
[chatbot, style_select, temperature, max_tokens, top_p],
|
179 |
chatbot
|
180 |
)
|
181 |
|
182 |
-
def clear_chat():
|
183 |
-
return [], ""
|
184 |
-
|
185 |
clear_btn.click(
|
186 |
clear_chat,
|
187 |
None,
|
@@ -190,10 +435,34 @@ def create_demo():
|
|
190 |
|
191 |
return demo
|
192 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
193 |
if __name__ == "__main__":
|
194 |
demo = create_demo()
|
195 |
demo.queue().launch(
|
196 |
-
server_name="0.0.0.0",
|
197 |
server_port=7860,
|
198 |
share=False
|
199 |
)
|
|
|
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 |
+
STORY_SYSTEM_PROMPT = """你是一个专业的故事生成器。你的任务是根据用户提供的设定和实时输入,生成连贯且引人入胜的故事。
|
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 |
"奇幻",
|
|
|
78 |
def generate_story(
|
79 |
scene: str,
|
80 |
style: str,
|
81 |
+
theme: str,
|
82 |
+
character_desc: str,
|
83 |
+
history: list = None,
|
84 |
temperature: float = 0.7,
|
85 |
max_tokens: int = 512,
|
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 |
+
风格:{style}
|
118 |
+
主题:{theme}
|
119 |
+
角色:{character_desc}
|
120 |
+
初始场景:{scene}
|
121 |
+
|
122 |
+
请从这个场景开始,展开故事的开端。注意为后续发展留下铺垫。
|
123 |
+
"""
|
124 |
+
else:
|
125 |
+
# 后续生成,侧重情节延续
|
126 |
+
prompt = f"""
|
127 |
+
{context_summary}
|
128 |
+
|
129 |
+
故事设定提醒:
|
130 |
+
- 风格:{style}
|
131 |
+
- 主题:{theme}
|
132 |
+
- 主要角色:{character_desc}
|
133 |
+
|
134 |
+
用户新的输入:{scene}
|
135 |
+
|
136 |
+
请基于以上已发生的情节和用户新的输入,自然地继续发展故事。注意:
|
137 |
+
1. 新的发展必须与之前的情节保持连贯
|
138 |
+
2. 合理化用户提供的新元素
|
139 |
+
3. 注意人物性格的一致性
|
140 |
+
4. 为后续发展留下可能性
|
141 |
+
|
142 |
+
继续讲述:
|
143 |
+
"""
|
144 |
|
145 |
messages = [
|
146 |
{"role": "system", "content": STORY_SYSTEM_PROMPT},
|
147 |
+
{"role": "user", "content": prompt}
|
148 |
]
|
149 |
+
|
150 |
+
try:
|
151 |
+
client = create_client()
|
152 |
+
response = ""
|
153 |
+
|
154 |
+
for message in client.chat_completion(
|
155 |
+
messages,
|
156 |
+
max_tokens=max_tokens,
|
157 |
+
stream=True,
|
158 |
+
temperature=temperature,
|
159 |
+
top_p=top_p,
|
160 |
+
):
|
161 |
+
if hasattr(message.choices[0].delta, 'content'):
|
162 |
+
token = message.choices[0].delta.content
|
163 |
+
if token is not None:
|
164 |
+
response += token
|
165 |
+
yield response
|
166 |
+
except Exception as e:
|
167 |
+
logger.error(f"生成故事时发生错误: {str(e)}")
|
168 |
+
yield f"抱歉,生成故事时遇到了问题:{str(e)}\n请稍后重试。"
|
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: 这里可以添加更复杂的NLP处理来提取关键信息
|
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 |
+
让AI为您创造独特的故事体验。您可以选择故事风格、主题,添加角色设定,
|
206 |
+
然后描述一个场景开始您的故事。与AI互动来继续发展故事情节!
|
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("✨ 开始故事", variant="primary", scale=2)
|
257 |
+
clear_btn = gr.Button("🗑️ 清除对话", scale=1)
|
258 |
+
save_btn = gr.Button("💾 保存故事", scale=1)
|
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():
|
275 |
+
temperature = gr.Slider(
|
276 |
+
minimum=0.1,
|
277 |
+
maximum=2.0,
|
278 |
+
value=0.7,
|
279 |
+
step=0.1,
|
280 |
+
label="创意度(Temperature)",
|
281 |
+
info="较高的值会让故事更有创意但可能不够连贯"
|
282 |
+
)
|
283 |
+
|
284 |
+
max_tokens = gr.Slider(
|
285 |
+
minimum=64,
|
286 |
+
maximum=1024,
|
287 |
+
value=512,
|
288 |
+
step=64,
|
289 |
+
label="最大生成长度",
|
290 |
+
info="控制每次生成的文本长度"
|
291 |
+
)
|
292 |
+
|
293 |
+
top_p = gr.Slider(
|
294 |
+
minimum=0.1,
|
295 |
+
maximum=1.0,
|
296 |
+
value=0.95,
|
297 |
+
step=0.05,
|
298 |
+
label="采样范围(Top-p)",
|
299 |
+
info="控制词语选择的多样性"
|
300 |
+
)
|
301 |
+
|
302 |
+
# 帮助信息
|
303 |
+
with gr.Accordion("📖 使用帮助", open=False):
|
304 |
+
gr.Markdown(
|
305 |
+
"""
|
306 |
+
## 如何使用故事生成器
|
307 |
+
1. 选择故事风格和主题来确定故事的整体基调
|
308 |
+
2. 选择预设角色模板或自定义角色描述
|
309 |
+
3. 描述故事发生的场景和环境
|
310 |
+
4. 点击"开始故事"生成开篇
|
311 |
+
5. 继续输入内容与AI交互,推进故事发展
|
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 |
+
|
330 |
+
character_select.change(
|
331 |
+
update_character_desc,
|
332 |
+
character_select,
|
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: 聊天历史记录 [(user_msg, bot_msg), ...]
|
350 |
+
"""
|
351 |
if history is None:
|
352 |
history = []
|
353 |
+
history.append([user_message, None]) # 添加用户消息,bot消息暂时为None
|
354 |
return "", history
|
355 |
+
|
356 |
+
# AI响应处理
|
357 |
+
def bot_response(history, style, theme, character_desc, temperature, max_tokens, top_p):
|
358 |
+
"""
|
359 |
+
生成AI响应
|
360 |
+
Args:
|
361 |
+
history: 聊天历史记录 [(user_msg, bot_msg), ...]
|
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 |
+
# 转换历史记录格式以传递给generate_story
|
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,
|
386 |
style,
|
387 |
+
theme,
|
388 |
+
character_desc,
|
389 |
+
message_history,
|
390 |
temperature,
|
391 |
max_tokens,
|
392 |
top_p
|
393 |
):
|
394 |
+
current_response = text
|
395 |
+
history[-1][1] = current_response # 更新最后一条消息的bot回复
|
396 |
yield history
|
397 |
+
|
398 |
except Exception as e:
|
399 |
logger.error(f"处理响应时发生错误: {str(e)}")
|
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],
|
413 |
[scene_input, chatbot]
|
414 |
).then(
|
415 |
bot_response,
|
416 |
+
[chatbot, style_select, theme_select, character_desc, temperature, max_tokens, top_p],
|
417 |
chatbot
|
418 |
)
|
419 |
|
|
|
423 |
[scene_input, chatbot]
|
424 |
).then(
|
425 |
bot_response,
|
426 |
+
[chatbot, style_select, theme_select, character_desc, temperature, max_tokens, top_p],
|
427 |
chatbot
|
428 |
)
|
429 |
|
|
|
|
|
|
|
430 |
clear_btn.click(
|
431 |
clear_chat,
|
432 |
None,
|
|
|
435 |
|
436 |
return 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"
|
446 |
+
|
447 |
+
os.makedirs("stories", exist_ok=True)
|
448 |
+
|
449 |
+
try:
|
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"用户: {user_msg}\n")
|
454 |
+
if bot_msg:
|
455 |
+
f.write(f"AI: {bot_msg}\n\n")
|
456 |
+
return f"故事已保存至 {filename}"
|
457 |
+
except Exception as e:
|
458 |
+
return f"保存失败: {str(e)}"
|
459 |
+
|
460 |
+
|
461 |
+
|
462 |
if __name__ == "__main__":
|
463 |
demo = create_demo()
|
464 |
demo.queue().launch(
|
465 |
+
# server_name="0.0.0.0",
|
466 |
server_port=7860,
|
467 |
share=False
|
468 |
)
|
互动式故事生成器开发规范文档.md
ADDED
@@ -0,0 +1,101 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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变体。
|