Update app.py
Browse files
app.py
CHANGED
|
@@ -21,11 +21,8 @@ base_questions = [
|
|
| 21 |
# ============================ Forward 模式问题 ============================
|
| 22 |
forward_additional_questions = [
|
| 23 |
("bg_info", """那么接下来我会需要你提供一些你的资料,并分享一些你的性格和喜好。请先告诉我,你的学校、年级、专业,和主修课程是什么?如果能提供你的选课表或resume就更好啦。"""),
|
| 24 |
-
|
| 25 |
("work_value", """非常好!那你认为“工作”的意义是什么?你觉得一份理想的工作,应该带来哪些价值或满足感?(例如:帮助他人、赚大钱、自由时间、个人成长、创意空间等)"""),
|
| 26 |
-
|
| 27 |
("personality_summary", "用几句话总结你的性格:比如外向/内向?喜欢挑战?注重细节?讨厌重复吗?"),
|
| 28 |
-
|
| 29 |
("dream_day", "再描述一下你理想工作的一天是什么样:在哪工作?做什么?和谁合作?忙还是闲?更自由还是更有秩序?")
|
| 30 |
]
|
| 31 |
|
|
@@ -46,7 +43,6 @@ forward_done = False
|
|
| 46 |
backward_done = False
|
| 47 |
forward_recommendation_given = False
|
| 48 |
|
| 49 |
-
# 新增:多次换方向、深度分析、路线图
|
| 50 |
recommendation_round = 0
|
| 51 |
forward_deep_dive_done = False
|
| 52 |
roadmap_offered = False
|
|
@@ -105,7 +101,7 @@ def do_time_roadmap():
|
|
| 105 |
except Exception as e:
|
| 106 |
return f"生成路线图时出错: {str(e)}"
|
| 107 |
|
| 108 |
-
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 深度分析
|
| 109 |
def do_deep_analysis():
|
| 110 |
direction = user_profile["forward_direction_choice"] or "尚未选择"
|
| 111 |
bg_info = user_profile["bg_info"] or "未知背景"
|
|
@@ -161,7 +157,7 @@ def recommend_3directions():
|
|
| 161 |
rec_prompt = f"""
|
| 162 |
请根据以下信息,写一份条理清晰的人物画像+3个大方向,并结尾让学生选1~3或'换':
|
| 163 |
- 学术背景: {bg_info}
|
| 164 |
-
- 工作
|
| 165 |
- 性格: {ps}
|
| 166 |
- 理想工作: {dd}
|
| 167 |
分两部分:
|
|
@@ -204,7 +200,7 @@ def generate_system_prompt():
|
|
| 204 |
dd = user_profile["dream_day"]
|
| 205 |
fwd = user_profile["forward_direction_choice"]
|
| 206 |
|
| 207 |
-
if mode == "是":
|
| 208 |
return f"""
|
| 209 |
你是一位专业的职业规划顾问,使用Backward Design方法帮助学生达成他们的职业目标。
|
| 210 |
目标职业: {sc}
|
|
@@ -265,15 +261,11 @@ def predict(message, history):
|
|
| 265 |
# ===== 如果在base_questions环节 =====
|
| 266 |
if 0 < current_q_index <= len(questions):
|
| 267 |
key = questions[current_q_index - 1][0]
|
| 268 |
-
# 仅当 key == "mode" 且我们还在问第1题 时, 才存回答到 user_profile["mode"]
|
| 269 |
if key == "mode" and current_q_index == 1:
|
| 270 |
-
|
| 271 |
-
user_profile["mode"] = message.strip()
|
| 272 |
else:
|
| 273 |
-
# 其他base问题(目前没别的), 可以存到 user_profile 也行
|
| 274 |
user_profile[key] = message.strip()
|
| 275 |
|
| 276 |
-
# 如果 base_questions 没问完 -> 问下一道
|
| 277 |
if current_q_index < len(questions):
|
| 278 |
nxt = questions[current_q_index][1]
|
| 279 |
current_q_index += 1
|
|
@@ -281,7 +273,7 @@ def predict(message, history):
|
|
| 281 |
|
| 282 |
mode = user_profile.get("mode") or ""
|
| 283 |
|
| 284 |
-
# ~~~~~~~~~~~~ Backward
|
| 285 |
if mode == "是":
|
| 286 |
if in_backward_flow and not backward_done:
|
| 287 |
if backward_index > 0 and backward_index <= len(backward_additional_questions):
|
|
@@ -296,15 +288,14 @@ def predict(message, history):
|
|
| 296 |
backward_done = True
|
| 297 |
|
| 298 |
if backward_done:
|
| 299 |
-
# 生成backward结果
|
| 300 |
-
import_text = generate_system_prompt()
|
| 301 |
try:
|
| 302 |
api_key = os.environ.get("API_TOKEN")
|
| 303 |
if not api_key:
|
| 304 |
return "错误:API_TOKEN 未设置"
|
| 305 |
client = OpenAI(api_key=api_key)
|
|
|
|
| 306 |
msgs = [
|
| 307 |
-
{"role":"system","content":
|
| 308 |
{"role":"user","content": f"请根据{user_profile}给出最终规划"}
|
| 309 |
]
|
| 310 |
resp = client.chat.completions.create(
|
|
@@ -319,25 +310,21 @@ def predict(message, history):
|
|
| 319 |
except Exception as e:
|
| 320 |
return f"发生错误: {str(e)}"
|
| 321 |
|
| 322 |
-
# ~~~~~~~~~~~~ Forward
|
| 323 |
else:
|
| 324 |
if in_forward_flow and not forward_done and not forward_deep_dive_done and not roadmap_done:
|
| 325 |
-
# 存储上一回答(不影响mode)
|
| 326 |
if forward_index > 0 and forward_index <= len(forward_additional_questions):
|
| 327 |
prev_key = forward_additional_questions[forward_index - 1][0]
|
| 328 |
user_profile[prev_key] = message.strip()
|
| 329 |
|
| 330 |
-
# 如果还有forward附加问题没问完
|
| 331 |
if forward_index < len(forward_additional_questions):
|
| 332 |
k, prompt_text = forward_additional_questions[forward_index]
|
| 333 |
forward_index += 1
|
| 334 |
return prompt_text
|
| 335 |
-
# 问完4个问题 -> 推荐3方向
|
| 336 |
elif not forward_recommendation_given:
|
| 337 |
forward_recommendation_given = True
|
| 338 |
return recommend_3directions()
|
| 339 |
else:
|
| 340 |
-
# 用户选1/2/3或 '换'
|
| 341 |
choice = message.strip().lower()
|
| 342 |
if choice in ["1","2","3"]:
|
| 343 |
user_profile["forward_direction_choice"] = choice
|
|
@@ -347,19 +334,17 @@ def predict(message, history):
|
|
| 347 |
recommendation_round += 1
|
| 348 |
if recommendation_round > 2:
|
| 349 |
forward_done = True
|
| 350 |
-
return "已多次换方向,
|
| 351 |
else:
|
| 352 |
return recommend_3directions()
|
| 353 |
else:
|
| 354 |
-
return "请回复1/2/3选择方向,或输入'换'来重
|
| 355 |
|
| 356 |
elif forward_deep_dive_done and not roadmap_offered:
|
| 357 |
-
# 深度分析完 -> 问路线图
|
| 358 |
roadmap_offered = True
|
| 359 |
-
return "需要一个时间轴式
|
| 360 |
|
| 361 |
elif roadmap_offered and not roadmap_done:
|
| 362 |
-
# 学生答 “是” or “否”
|
| 363 |
ans = message.strip().lower()
|
| 364 |
if ans == "是":
|
| 365 |
roadmap_done = True
|
|
@@ -368,19 +353,18 @@ def predict(message, history):
|
|
| 368 |
else:
|
| 369 |
roadmap_done = True
|
| 370 |
forward_done = True
|
| 371 |
-
return "好的,不生成
|
| 372 |
|
| 373 |
if forward_done:
|
| 374 |
-
# 最终输出
|
| 375 |
-
sprompt = generate_system_prompt()
|
| 376 |
try:
|
| 377 |
api_key = os.environ.get("API_TOKEN")
|
| 378 |
if not api_key:
|
| 379 |
return "错误:API_TOKEN 未设置"
|
| 380 |
client = OpenAI(api_key=api_key)
|
|
|
|
| 381 |
msgs = [
|
| 382 |
{"role":"system","content":sprompt},
|
| 383 |
-
{"role":"user","content": f"
|
| 384 |
]
|
| 385 |
resp = client.chat.completions.create(
|
| 386 |
model=model_default,
|
|
@@ -394,8 +378,7 @@ def predict(message, history):
|
|
| 394 |
except Exception as e:
|
| 395 |
return f"发生错误: {str(e)}"
|
| 396 |
|
| 397 |
-
return "信息收集完毕,
|
| 398 |
-
|
| 399 |
|
| 400 |
# ============================ Gradio UI ============================
|
| 401 |
with gr.Blocks(css="""
|
|
@@ -455,19 +438,20 @@ footer {
|
|
| 455 |
<img src='https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExMmFucGwxbmNsd3J5NXV0Y282NXNtMzNsZW5jMm4wNWh6c2dqbXIwdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l41m18LjqpzxUr2WA/giphy.gif' width='200' style='border-radius: 12px; box-shadow: 0 0 10px #ec4899;'>
|
| 456 |
<div style='text-align: left;'>
|
| 457 |
<h1 style='color:white; font-size: 36px; margin-bottom: 6px;'>🎓 多步职业规划助手</h1>
|
| 458 |
-
<p style='font-size: 18px; font-weight:bold; color:#ec4899; margin-top: 0;'>Forward三步:推荐大方向→深度分析→可选时间轴
|
| 459 |
</div>
|
| 460 |
</div>
|
| 461 |
""")
|
| 462 |
-
|
| 463 |
-
|
| 464 |
-
|
| 465 |
-
|
| 466 |
-
|
| 467 |
-
|
| 468 |
-
|
| 469 |
-
|
| 470 |
-
|
|
|
|
| 471 |
with gr.Row():
|
| 472 |
with gr.Column(scale=8):
|
| 473 |
msg = gr.Textbox(placeholder="请在这���输入你的回答...", show_label=False, container=False)
|
|
@@ -476,25 +460,25 @@ footer {
|
|
| 476 |
|
| 477 |
with gr.Row():
|
| 478 |
reset_btn = gr.Button("🔄 重新开始")
|
| 479 |
-
|
| 480 |
-
def add_user_message(m, h):
|
| 481 |
-
h = h or []
|
| 482 |
-
h.append({"role":"user","content":m})
|
| 483 |
-
return "", h
|
| 484 |
|
| 485 |
-
def
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
return h
|
| 490 |
|
| 491 |
-
|
| 492 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 493 |
|
| 494 |
-
|
| 495 |
-
|
|
|
|
|
|
|
| 496 |
|
| 497 |
-
def
|
| 498 |
global current_q_index, questions
|
| 499 |
global in_forward_flow, in_backward_flow
|
| 500 |
global forward_index, backward_index
|
|
@@ -509,21 +493,23 @@ footer {
|
|
| 509 |
for k in user_profile:
|
| 510 |
user_profile[k] = None
|
| 511 |
|
| 512 |
-
in_forward_flow =
|
| 513 |
-
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
| 515 |
forward_recommendation_given = False
|
| 516 |
recommendation_round = 0
|
| 517 |
forward_deep_dive_done = False
|
| 518 |
roadmap_offered = False
|
| 519 |
roadmap_done = False
|
| 520 |
-
return []
|
| 521 |
-
|
| 522 |
-
reset.click(reset_all, outputs=chatbot)
|
| 523 |
|
| 524 |
-
|
| 525 |
-
|
| 526 |
|
| 527 |
-
|
|
|
|
| 528 |
|
| 529 |
-
demo.
|
|
|
|
|
|
| 21 |
# ============================ Forward 模式问题 ============================
|
| 22 |
forward_additional_questions = [
|
| 23 |
("bg_info", """那么接下来我会需要你提供一些你的资料,并分享一些你的性格和喜好。请先告诉我,你的学校、年级、专业,和主修课程是什么?如果能提供你的选课表或resume就更好啦。"""),
|
|
|
|
| 24 |
("work_value", """非常好!那你认为“工作”的意义是什么?你觉得一份理想的工作,应该带来哪些价值或满足感?(例如:帮助他人、赚大钱、自由时间、个人成长、创意空间等)"""),
|
|
|
|
| 25 |
("personality_summary", "用几句话总结你的性格:比如外向/内向?喜欢挑战?注重细节?讨厌重复吗?"),
|
|
|
|
| 26 |
("dream_day", "再描述一下你理想工作的一天是什么样:在哪工作?做什么?和谁合作?忙还是闲?更自由还是更有秩序?")
|
| 27 |
]
|
| 28 |
|
|
|
|
| 43 |
backward_done = False
|
| 44 |
forward_recommendation_given = False
|
| 45 |
|
|
|
|
| 46 |
recommendation_round = 0
|
| 47 |
forward_deep_dive_done = False
|
| 48 |
roadmap_offered = False
|
|
|
|
| 101 |
except Exception as e:
|
| 102 |
return f"生成路线图时出错: {str(e)}"
|
| 103 |
|
| 104 |
+
# ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 深度分析函数 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| 105 |
def do_deep_analysis():
|
| 106 |
direction = user_profile["forward_direction_choice"] or "尚未选择"
|
| 107 |
bg_info = user_profile["bg_info"] or "未知背景"
|
|
|
|
| 157 |
rec_prompt = f"""
|
| 158 |
请根据以下信息,写一份条理清晰的人物画像+3个大方向,并结尾让学生选1~3或'换':
|
| 159 |
- 学术背景: {bg_info}
|
| 160 |
+
- 工作意义: {wv}
|
| 161 |
- 性格: {ps}
|
| 162 |
- 理想工作: {dd}
|
| 163 |
分两部分:
|
|
|
|
| 200 |
dd = user_profile["dream_day"]
|
| 201 |
fwd = user_profile["forward_direction_choice"]
|
| 202 |
|
| 203 |
+
if mode == "是":
|
| 204 |
return f"""
|
| 205 |
你是一位专业的职业规划顾问,使用Backward Design方法帮助学生达成他们的职业目标。
|
| 206 |
目标职业: {sc}
|
|
|
|
| 261 |
# ===== 如果在base_questions环节 =====
|
| 262 |
if 0 < current_q_index <= len(questions):
|
| 263 |
key = questions[current_q_index - 1][0]
|
|
|
|
| 264 |
if key == "mode" and current_q_index == 1:
|
| 265 |
+
user_profile["mode"] = message.strip() # 只这里写 mode
|
|
|
|
| 266 |
else:
|
|
|
|
| 267 |
user_profile[key] = message.strip()
|
| 268 |
|
|
|
|
| 269 |
if current_q_index < len(questions):
|
| 270 |
nxt = questions[current_q_index][1]
|
| 271 |
current_q_index += 1
|
|
|
|
| 273 |
|
| 274 |
mode = user_profile.get("mode") or ""
|
| 275 |
|
| 276 |
+
# ~~~~~~~~~~~~ Backward模式 ~~~~~~~~~~~~
|
| 277 |
if mode == "是":
|
| 278 |
if in_backward_flow and not backward_done:
|
| 279 |
if backward_index > 0 and backward_index <= len(backward_additional_questions):
|
|
|
|
| 288 |
backward_done = True
|
| 289 |
|
| 290 |
if backward_done:
|
|
|
|
|
|
|
| 291 |
try:
|
| 292 |
api_key = os.environ.get("API_TOKEN")
|
| 293 |
if not api_key:
|
| 294 |
return "错误:API_TOKEN 未设置"
|
| 295 |
client = OpenAI(api_key=api_key)
|
| 296 |
+
sprompt = generate_system_prompt()
|
| 297 |
msgs = [
|
| 298 |
+
{"role":"system","content": sprompt},
|
| 299 |
{"role":"user","content": f"请根据{user_profile}给出最终规划"}
|
| 300 |
]
|
| 301 |
resp = client.chat.completions.create(
|
|
|
|
| 310 |
except Exception as e:
|
| 311 |
return f"发生错误: {str(e)}"
|
| 312 |
|
| 313 |
+
# ~~~~~~~~~~~~ Forward模式 ~~~~~~~~~~~~
|
| 314 |
else:
|
| 315 |
if in_forward_flow and not forward_done and not forward_deep_dive_done and not roadmap_done:
|
|
|
|
| 316 |
if forward_index > 0 and forward_index <= len(forward_additional_questions):
|
| 317 |
prev_key = forward_additional_questions[forward_index - 1][0]
|
| 318 |
user_profile[prev_key] = message.strip()
|
| 319 |
|
|
|
|
| 320 |
if forward_index < len(forward_additional_questions):
|
| 321 |
k, prompt_text = forward_additional_questions[forward_index]
|
| 322 |
forward_index += 1
|
| 323 |
return prompt_text
|
|
|
|
| 324 |
elif not forward_recommendation_given:
|
| 325 |
forward_recommendation_given = True
|
| 326 |
return recommend_3directions()
|
| 327 |
else:
|
|
|
|
| 328 |
choice = message.strip().lower()
|
| 329 |
if choice in ["1","2","3"]:
|
| 330 |
user_profile["forward_direction_choice"] = choice
|
|
|
|
| 334 |
recommendation_round += 1
|
| 335 |
if recommendation_round > 2:
|
| 336 |
forward_done = True
|
| 337 |
+
return "已多次换方向,结束推荐了~"
|
| 338 |
else:
|
| 339 |
return recommend_3directions()
|
| 340 |
else:
|
| 341 |
+
return "请回复1/2/3选择方向,或输入'换'来重选"
|
| 342 |
|
| 343 |
elif forward_deep_dive_done and not roadmap_offered:
|
|
|
|
| 344 |
roadmap_offered = True
|
| 345 |
+
return "需要一个时间轴式职业路线图吗?如果是,请回复“是”,否则回复“否”。"
|
| 346 |
|
| 347 |
elif roadmap_offered and not roadmap_done:
|
|
|
|
| 348 |
ans = message.strip().lower()
|
| 349 |
if ans == "是":
|
| 350 |
roadmap_done = True
|
|
|
|
| 353 |
else:
|
| 354 |
roadmap_done = True
|
| 355 |
forward_done = True
|
| 356 |
+
return "好的,不生成时间轴,本次就到这里。"
|
| 357 |
|
| 358 |
if forward_done:
|
|
|
|
|
|
|
| 359 |
try:
|
| 360 |
api_key = os.environ.get("API_TOKEN")
|
| 361 |
if not api_key:
|
| 362 |
return "错误:API_TOKEN 未设置"
|
| 363 |
client = OpenAI(api_key=api_key)
|
| 364 |
+
sprompt = generate_system_prompt()
|
| 365 |
msgs = [
|
| 366 |
{"role":"system","content":sprompt},
|
| 367 |
+
{"role":"user","content": f"这是最终资料: {user_profile}, 如果还需要帮助请提问。"}
|
| 368 |
]
|
| 369 |
resp = client.chat.completions.create(
|
| 370 |
model=model_default,
|
|
|
|
| 378 |
except Exception as e:
|
| 379 |
return f"发生错误: {str(e)}"
|
| 380 |
|
| 381 |
+
return "信息收集完毕,如果尚未得到最终回复,请再输入任意文字继续。"
|
|
|
|
| 382 |
|
| 383 |
# ============================ Gradio UI ============================
|
| 384 |
with gr.Blocks(css="""
|
|
|
|
| 438 |
<img src='https://media3.giphy.com/media/v1.Y2lkPTc5MGI3NjExMmFucGwxbmNsd3J5NXV0Y282NXNtMzNsZW5jMm4wNWh6c2dqbXIwdiZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/l41m18LjqpzxUr2WA/giphy.gif' width='200' style='border-radius: 12px; box-shadow: 0 0 10px #ec4899;'>
|
| 439 |
<div style='text-align: left;'>
|
| 440 |
<h1 style='color:white; font-size: 36px; margin-bottom: 6px;'>🎓 多步职业规划助手</h1>
|
| 441 |
+
<p style='font-size: 18px; font-weight:bold; color:#ec4899; margin-top: 0;'>Forward三步:推荐大方向→深度分析→可选时间轴</p>
|
| 442 |
</div>
|
| 443 |
</div>
|
| 444 |
""")
|
| 445 |
+
gr.Markdown("""
|
| 446 |
+
**Forward / Backward** 多轮交互;Forward可换方向+深度分析+时间轴;Backward直接问目标职业+背景
|
| 447 |
+
""")
|
| 448 |
+
|
| 449 |
+
chatbot = gr.Chatbot(
|
| 450 |
+
height=500,
|
| 451 |
+
show_label=False,
|
| 452 |
+
show_copy_button=True,
|
| 453 |
+
type="messages"
|
| 454 |
+
)
|
| 455 |
with gr.Row():
|
| 456 |
with gr.Column(scale=8):
|
| 457 |
msg = gr.Textbox(placeholder="请在这���输入你的回答...", show_label=False, container=False)
|
|
|
|
| 460 |
|
| 461 |
with gr.Row():
|
| 462 |
reset_btn = gr.Button("🔄 重新开始")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 463 |
|
| 464 |
+
def add_message(m, hist):
|
| 465 |
+
hist = hist or []
|
| 466 |
+
hist.append({"role":"user","content":m})
|
| 467 |
+
return "", hist
|
|
|
|
| 468 |
|
| 469 |
+
def bot_response(hist):
|
| 470 |
+
hist = hist or []
|
| 471 |
+
user_m = hist[-1]["content"]
|
| 472 |
+
bot_m = predict(user_m, hist[:-1])
|
| 473 |
+
hist.append({"role":"assistant","content":bot_m})
|
| 474 |
+
return hist
|
| 475 |
|
| 476 |
+
submit_btn.click(add_message, [msg, chatbot], [msg, chatbot]) \
|
| 477 |
+
.then(bot_response, chatbot, chatbot)
|
| 478 |
+
msg.submit(add_message, [msg, chatbot], [msg, chatbot]) \
|
| 479 |
+
.then(bot_response, chatbot, chatbot)
|
| 480 |
|
| 481 |
+
def reset_state():
|
| 482 |
global current_q_index, questions
|
| 483 |
global in_forward_flow, in_backward_flow
|
| 484 |
global forward_index, backward_index
|
|
|
|
| 493 |
for k in user_profile:
|
| 494 |
user_profile[k] = None
|
| 495 |
|
| 496 |
+
in_forward_flow = False
|
| 497 |
+
in_backward_flow = False
|
| 498 |
+
forward_index = 0
|
| 499 |
+
backward_index = 0
|
| 500 |
+
forward_done = False
|
| 501 |
+
backward_done = False
|
| 502 |
forward_recommendation_given = False
|
| 503 |
recommendation_round = 0
|
| 504 |
forward_deep_dive_done = False
|
| 505 |
roadmap_offered = False
|
| 506 |
roadmap_done = False
|
|
|
|
|
|
|
|
|
|
| 507 |
|
| 508 |
+
return []
|
| 509 |
+
reset_btn.click(reset_state, outputs=chatbot)
|
| 510 |
|
| 511 |
+
def auto_first():
|
| 512 |
+
return [{"role": "assistant", "content": questions[0][1]}]
|
| 513 |
|
| 514 |
+
demo.load(auto_first, outputs=chatbot)
|
| 515 |
+
demo.launch()
|