HelenaXH commited on
Commit
2738430
·
verified ·
1 Parent(s): b68190e

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +395 -370
app.py CHANGED
@@ -2,9 +2,9 @@ import gradio as gr
2
  from openai import OpenAI
3
  import os
4
 
5
- # ============================ 用户信息结构 ============================
6
  user_profile = {
7
- "mode": None, # 只在回答第一个问题时设置(是/否)
8
  "specific_career": None,
9
  "bg_info": None,
10
  "work_value": None,
@@ -12,33 +12,33 @@ user_profile = {
12
  "dream_day": None,
13
  "forward_direction_choice": None,
14
 
15
- # Forward模式下更多状态
16
- "recommended_directions": [], # 3个大方向
17
- "selected_direction": None, # 学生最终选定的大方向
18
- "recommended_jobs": [], # 3个具体职业
19
- "final_choice": None # 学生最终选定的具体职业
20
  }
21
 
22
- # ============================ 基础问题 ============================
23
  base_questions = [
24
- ("mode", "你是否有心仪的职业方向?\n- 如果有,请回复''Backward模式)\n- 如果没有,请回复''Forward模式)")
25
  ]
26
 
27
- # ============================ Forward 模式问题 ============================
28
  forward_additional_questions = [
29
- ("bg_info", """那么接下来我会需要你提供一些你的资料,并分享一些你的性格和喜好。请先告诉我,你的学校、年级、专业,和主修课程是什么?如果能提供你的选课表或resume就更好啦。"""),
30
- ("work_value", """非常好!那你认为"工作"的意义是什么?你觉得一份理想的工作,应该带来哪些价值或满足感?(例如:帮助他人、赚大钱、自由时间、个人成长、创意空间等)"""),
31
- ("personality_summary", "用几句话总结你的性格:比如外向/内向?喜欢挑战?注重细节?讨厌重复吗?"),
32
- ("dream_day", "再描述一下你理想工作的一天是什么样:在哪工作?做什么?和谁合作?忙还是闲?更自由还是更有秩序?")
33
  ]
34
 
35
- # ============================ Backward 模式问题 ============================
36
  backward_additional_questions = [
37
- ("specific_career", "请具体描述你想要从事的职业方向。"),
38
- ("bg_info", "请提供你的学校、年级、专业以及已有的实习或项目经历,以便更好地帮你规划如何达成目标。")
39
  ]
40
 
41
- # ============================ 状态控制变量 ============================
42
  current_q_index = 0
43
  questions = base_questions[:]
44
  in_forward_flow = False
@@ -53,15 +53,15 @@ forward_deep_dive_done = False
53
  roadmap_offered = False
54
  roadmap_done = False
55
 
56
- direction_chosen = False # 是否已经选定大方向
57
- jobs_recommended = False # 是否已给出3个具体职业
58
- job_chosen = False # 是否选定了具体职业
59
 
60
- # ========== 用于 A/B/C 专项问题的标记 ==========
61
- post_career_detail_asked = False # 是否已询问用户要看A/B/C
62
- post_career_detail_done = False # 是否已回答完A/B/C
63
 
64
- # ============================ 模型设置 ============================
65
  model_default = "gpt-4o"
66
  token_default = 2000
67
  temp_default = 0.7
@@ -69,30 +69,30 @@ top_p_default = 0.95
69
 
70
 
71
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72
- # 动态函数:根据用户选择的 A/B/C,让 OpenAI 生成相应分析
73
  def backward_strategy_plan(specific_career, bg_info):
74
  prompt = f"""
75
- 你是一位专业的职业规划顾问。
76
- 现在有一位学生,他的目标职业是:{specific_career}
77
- 他的当前背景是:{bg_info}
78
-
79
- 请根据这个学生的背景和目标,帮他做一个清晰的达成路径规划,内容要包括:
80
- 1. 是否需要考虑转专业或辅修?
81
- 2. 是否需要补哪些课?课程关键词有哪些?
82
- 3. 推荐的证书/考试(如CFA等)和学习建议
83
- 4. 推荐的实习方向(基于他的背景)
84
- 5. 如何利用本校资源(career center、club、networking等)
85
- 6. 如果他背景不匹配(比如机械去金融),如何填补Gap?
86
-
87
- 请使用 Markdown 分段格式,不少于300字,内容务必紧扣他目前的背景和目标。
88
  """
89
  try:
90
  api_key = os.environ.get("API_TOKEN")
91
  if not api_key:
92
- return "错误:API_TOKEN 未设置"
93
  client = OpenAI(api_key=api_key)
94
  msgs = [
95
- {"role": "system", "content": "你是一位经验丰富的职业顾问,擅长根据用户背景制定路径规划"},
96
  {"role": "user", "content": prompt}
97
  ]
98
  resp = client.chat.completions.create(
@@ -105,9 +105,11 @@ def backward_strategy_plan(specific_career, bg_info):
105
  )
106
  return resp.choices[0].message.content
107
  except Exception as e:
108
- return f"生成职业路径建议时出错: {str(e)}"
 
109
 
110
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
111
  def answer_abc_questions(selected, bg_info, wv, ps, dd):
112
  desired_parts = []
113
  if "a" in selected.lower():
@@ -118,31 +120,46 @@ def answer_abc_questions(selected, bg_info, wv, ps, dd):
118
  desired_parts.append("C")
119
 
120
  if not desired_parts:
121
- return "好的,你暂时不需要查看A/B/C的专项信息。"
122
 
123
  prompt_text = f"""
124
- 你是一位专业的职业规划顾问。以下是学生背景信息,请根据他选择的模块(A/B/C)给出详细分析和建议。
125
- 学生背景:
126
- - 学术背景: {bg_info}
127
- - 工作意义: {wv}
128
- - 性格: {ps}
129
- - 理想工作: {dd}
130
-
131
- 学生目前想要的额外信息模块:{', '.join(desired_parts)}
132
- 请分别按顺序为每个模块写一段分析和建议,可以使用Markdown分段形式。
133
- (A: 专业/选课方向 & 求职入门建议部分的回答请根据学生的学术背景信息分析学生已经有的能力,然后根据其选择的职业方向分析该学生未来的选课应该注重什么课程方向,提示学生课程名称和关键词)
134
- ('B: 本校资源利用建议'部分的回答请你从3个方面回答学生,包括1.如何利用学校的career service;2.如何关注学校与职业准备和证书考试相关的club、组织和平台;3.学生如何与学校的及其所在专业的校友取得联系和networking技巧)
135
- ('C: 实习 & Experience积累'部分的回答请你从申请internship的平台、达成目前职业目标需要的internship方向,申请需要的资料和准备技巧、通过networking获得实习或者学生工作经历的策略)
136
-
137
- ��只回答用户所选模块,不要回答未选的模块。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
138
  """
139
  try:
140
  api_key = os.environ.get("API_TOKEN")
141
  if not api_key:
142
- return "错误:API_TOKEN 未设置"
143
  client = OpenAI(api_key=api_key)
144
  msgs = [
145
- {"role": "system", "content": "你是一位专业职业顾问,会针对A/B/C进行分析。"},
146
  {"role": "user", "content": prompt_text}
147
  ]
148
  resp = client.chat.completions.create(
@@ -155,35 +172,39 @@ def answer_abc_questions(selected, bg_info, wv, ps, dd):
155
  )
156
  return resp.choices[0].message.content
157
  except Exception as e:
158
- return f"生成A/B/C专项信息时出错: {str(e)}"
159
 
160
 
161
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 职业详细介绍 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
162
  def do_selected_career_detail(selected_career, bg_info, wv, ps, dd):
163
  prompt = f"""
164
- 你是一位专业的职业规划顾问。
165
- 学生最终选择的具体职业: {selected_career}
166
- 学生背景: {bg_info}
167
- 工作意义: {wv}
168
- 性格: {ps}
169
- 理想工作: {dd}
170
- 请写一份更详细的职业说明,至少包含:
171
- 1. 行业平均薪资(初级/中级/高级)
172
- 2. 工作环境(远程/混合/办公室,团队规模等)
173
- 3. 晋升难度(需要什么资历或条件)
174
- 4. 关键技能和考证
175
- 5. 日常工作节奏
176
- 6. 行业前景
177
- 用分段Markdown格式,字数不少于300字。
 
 
 
 
178
  """
179
  try:
180
  api_key = os.environ.get("API_TOKEN")
181
  if not api_key:
182
- return "错误:API_TOKEN 未设置"
183
  client = OpenAI(api_key=api_key)
184
 
185
  msgs = [
186
- {"role": "system", "content": "你是一位专业职业顾问,会为用户选定的具体职业提供详细的分析。"},
187
  {"role": "user", "content": prompt}
188
  ]
189
  resp = client.chat.completions.create(
@@ -196,42 +217,46 @@ def do_selected_career_detail(selected_career, bg_info, wv, ps, dd):
196
  )
197
  return resp.choices[0].message.content
198
  except Exception as e:
199
- return f"生成职业详细信息时出错: {str(e)}"
200
 
201
 
202
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 时间轴式规划 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
203
  def do_time_roadmap():
204
  direction = user_profile.get("final_choice") or user_profile.get("selected_direction") or \
205
  user_profile.get("forward_direction_choice") or user_profile.get("specific_career")
206
- bg_info = user_profile["bg_info"] or "未知专业"
207
- wv = user_profile["work_value"] or "暂无"
208
- ps = user_profile["personality_summary"] or "暂无"
209
- dd = user_profile["dream_day"] or "暂无"
210
 
211
  prompt_roadmap = f"""
212
- 学生信息:
213
- - 学术背景:{bg_info}
214
- - 职业方向/具体职业:{direction}
215
- - 工作价值:{wv}
216
- - 性格:{ps}
217
- - 理想工作:{dd}
218
- 请你使用时间轴方式,给出一个职业路线图,如:
219
- 📍 大二暑假: [申请什么实习,参加什么活动,考什么证书]
220
- 🎓 Year 3: [需要选的课/要做的项目]
221
- 💼 大三暑假: [申请什么实习、参加什么活动,考什么证书]
222
- 📄 毕业前1年到6个月: [考什么证书、准备什么申请工作或者申请研究生的材料]
223
- 🚀 毕业后: [目标岗位、如何申请]
224
- 请用Markdown分段写作,结合学生当前背景合理推断要点,写得具体些。
 
 
 
 
225
  """
226
  try:
227
  api_key = os.environ.get("API_TOKEN")
228
  if not api_key:
229
- return "错误:API_TOKEN 未设置"
230
  client = OpenAI(api_key=api_key)
231
 
232
  msgs = [
233
- {"role":"system","content":"你是一位专业的职业规划顾问,会生成时间轴式路线图"},
234
- {"role":"user","content": prompt_roadmap}
235
  ]
236
  resp = client.chat.completions.create(
237
  model=model_default,
@@ -243,39 +268,41 @@ def do_time_roadmap():
243
  )
244
  return resp.choices[0].message.content
245
  except Exception as e:
246
- return f"生成路线图时出错: {str(e)}"
247
 
248
 
249
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 推荐具体职业 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
250
  def recommend_3jobs_for_direction(direction, bg_info, wv, ps, dd):
251
  prompt = f"""
252
- 你是一位专业的职业规划顾问。
253
- 学生选定的大方向:{direction}
254
- 学生背景:{bg_info}
255
- 工作意义:{wv}
256
- 性格:{ps}
257
- 理想工作:{dd}
258
- 请为该方向推荐3个更具体的职业岗位,并分点说明:
259
- 1) 为什么适合这个大方向
260
- 2) 典型工作职责
261
- 3) 技能/证书要求
262
- 4) 未来发展前景
263
- 最后以这样的格式结束(不要在前面重复这些选项):
264
-
265
- 请选择你的理想职业:
266
- 1. [职业名称1]
267
- 2. [职业名称2]
268
- 3. [职业名称3]
269
- 如都不满意,可输入'换'。
 
 
270
  """
271
  try:
272
  api_key = os.environ.get("API_TOKEN")
273
  if not api_key:
274
- return "错误:API_TOKEN 未设置"
275
  client = OpenAI(api_key=api_key)
276
 
277
  msgs = [
278
- {"role": "system", "content": "你是一位专业职业顾问,会根据用户选定的大方向再推荐3个具体职业。"},
279
  {"role": "user", "content": prompt}
280
  ]
281
  resp = client.chat.completions.create(
@@ -288,65 +315,69 @@ def recommend_3jobs_for_direction(direction, bg_info, wv, ps, dd):
288
  )
289
  output_text = resp.choices[0].message.content
290
 
291
- # 从回复中尝试解析职业名称 - 简单示例实现
292
  import re
293
  job_pattern = r'(\d+)\.\s+\[?(.*?)\]?(?:\n|$)'
294
  job_matches = re.findall(job_pattern, output_text)
295
  job_list = [match[1].strip() for match in job_matches]
296
-
297
- # 如果没有找到合适的匹配,创建默认值
298
  if len(job_list) < 3:
299
  job_list = [
300
- f"{direction} - 职业A",
301
- f"{direction} - 职业B",
302
- f"{direction} - 职业C"
303
  ]
304
-
305
- user_profile["recommended_jobs"] = job_list[:3] # 确保只取前3
306
-
307
- # 设置为已经推荐过职业
308
  global jobs_recommended
309
  jobs_recommended = True
310
-
311
  return output_text
312
  except Exception as e:
313
- return f"推荐具体职业时出错: {str(e)}"
314
 
315
 
316
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 推荐大方向 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
317
  def recommend_3directions():
318
- bg_info = user_profile["bg_info"] or "未知"
319
- wv = user_profile["work_value"] or ""
320
- ps = user_profile["personality_summary"] or "不详"
321
- dd = user_profile["dream_day"] or ""
322
 
323
  rec_prompt = f"""
324
- 请根据以下信息,写一份条理清晰的人物画像+3个大方向,其中称呼对面用"你"不要用"他/她":
325
- - 学术背景: {bg_info}
326
- - 工作意义: {wv}
327
- - 性格: {ps}
328
- - 理想工作: {dd}
329
- 分两部分:
330
- [一、人物画像]:1) 学术背景 2) 价值观 3) 性格 4) 理想工作
331
-
332
- [二、推荐3个大方向]:每个方向写1~2段文字分析,并说明为什么适合学生。
333
-
334
- 最后结束时只有这样的选项(不要在前面重复这些选项):
335
-
336
- 请选择你的职业方向:
337
- 1. [方向名称1]
338
- 2. [方向名称2]
339
- 3. [方向名称3]
340
- 如都不满意,可输入'换'。
 
 
 
 
341
  """
342
  try:
343
  api_key = os.environ.get("API_TOKEN")
344
  if not api_key:
345
- return "错误:API_TOKEN 未设置"
346
  client = OpenAI(api_key=api_key)
347
  msgs = [
348
- {"role":"system","content":"你是一位职业顾问,擅长根据用户资料推荐3大方向"},
349
- {"role":"user","content": rec_prompt}
350
  ]
351
  resp = client.chat.completions.create(
352
  model=model_default,
@@ -358,27 +389,24 @@ def recommend_3directions():
358
  )
359
  output_text = resp.choices[0].message.content
360
 
361
- # 从回复中尝试解析方向名称 - 简单示例实现
362
  import re
363
  dir_pattern = r'(\d+)\.\s+\[?(.*?)\]?(?:\n|$)'
364
  dir_matches = re.findall(dir_pattern, output_text)
365
  dir_list = [match[1].strip() for match in dir_matches]
366
-
367
- # 如果没有找到合适的匹配,创建默认值
368
  if len(dir_list) < 3:
369
- dir_list = [
370
- "方向1",
371
- "方向2",
372
- "方向3"
373
- ]
374
-
375
- user_profile["recommended_directions"] = dir_list[:3] # 确保只取前3个
376
  return output_text
377
  except Exception as e:
378
- return f"多方向推荐出错: {str(e)}"
379
 
380
 
381
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 生成系统提示 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 
382
  def generate_system_prompt():
383
  mode = user_profile["mode"]
384
  sc = user_profile["specific_career"]
@@ -388,39 +416,40 @@ def generate_system_prompt():
388
  dd = user_profile["dream_day"]
389
  fwd = user_profile["forward_direction_choice"]
390
 
391
- if mode == "是":
392
  return f"""
393
- 你是一位专业的职业规划顾问,使用Backward Design方法帮助学生达成他们的职业目标。
394
- 目标职业: {sc}
395
- 背景信息: {bg}
396
- 请提供:
397
- 1. 该职业的简要分析
398
- 2. 排名前列的组织或公司
399
- 3. 所需技能和能力
400
- 4. 职业发展路径
401
- 5. 学术/课程建议、技能培养、资源使用、实习规划、简历优化等
402
- 最后请用Markdown格式输出职业路线图:
403
- 📍 现在 🎓 学习建议 💼 实践建议 📄 认证建议 → 🚀 求职建议
 
404
  """
405
- else:
406
  return f"""
407
- 你是一位专业的职业规划顾问,使用Forward Design方法帮助学生探索合适的职业路径。
408
- 学生背景: {bg}
409
- 工作意义: {wv}
410
- 性格总结: {ps}
411
- 理想工作: {dd}
412
- 学生选择方向: {fwd}
413
- 请输出:
414
- 1. 学生的优势、性格、价值观分析
415
- 2. 推荐3个具体职业,并说明日常工作内容、适配性、要求、准备路径
416
- 3. 最后输出职业路线图:
417
- 📍 现在 → 🎓 学习建议 → 💼 实践建议 → 📄 认证建议 → 🚀 求职建议
418
  """
419
 
420
 
421
- # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 主对话逻辑函数 ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
422
  def predict(message, history):
423
- # ========= 在函数开头,提前声明所有 global =========
424
  global current_q_index, questions
425
  global in_forward_flow, in_backward_flow
426
  global forward_index, backward_index
@@ -432,7 +461,7 @@ def predict(message, history):
432
  global direction_chosen, jobs_recommended, job_chosen
433
  global post_career_detail_asked, post_career_detail_done
434
 
435
- # ======================== 初始化 =========================
436
  if not history:
437
  current_q_index = 0
438
  questions[:] = base_questions
@@ -457,35 +486,32 @@ def predict(message, history):
457
  post_career_detail_asked = False
458
  post_career_detail_done = False
459
 
460
- # ======================== 先处理基础问题 =========================
461
  if 0 < current_q_index <= len(questions):
462
  key = questions[current_q_index - 1][0]
463
-
464
 
465
  if key == "mode" and current_q_index == 1:
466
- ans = message.strip()
467
- user_profile["mode"] = ans
468
- questions[:] = [] # 清空基础问答流程
469
- if "是" in ans:
470
- in_backward_flow = True
471
- else:
472
- in_forward_flow = True
473
-
474
 
475
- if current_q_index < len(questions)and not in_forward_flow and not in_backward_flow:
476
  nxt = questions[current_q_index][1]
477
  current_q_index += 1
478
  return nxt
479
 
480
-
481
 
482
 
483
- # ======================== 分模式处理 =========================
484
- mode = user_profile.get("mode") or ""
485
 
486
- # ~~~~~~~~~ Backward模式 ~~~~~~~~~
487
- if mode == "是":
488
- if in_backward_flow and not backward_done:
489
  if backward_index > 0 and backward_index <= len(backward_additional_questions):
490
  prev_key = backward_additional_questions[backward_index - 1][0]
491
  user_profile[prev_key] = message.strip()
@@ -493,9 +519,9 @@ def predict(message, history):
493
  if backward_index < len(backward_additional_questions):
494
  k, prompt_text = backward_additional_questions[backward_index]
495
  backward_index += 1
496
- return prompt_text # ✅ 正确:缩进在 if 块内
497
  else:
498
- # 已完成背景收集,生成路径分析
499
  strategy = backward_strategy_plan(
500
  specific_career=user_profile.get("specific_career"),
501
  bg_info=user_profile.get("bg_info")
@@ -505,37 +531,36 @@ def predict(message, history):
505
  forward_deep_dive_done = True
506
  post_career_detail_asked = True
507
 
508
- return strategy + "\n\n我还可以针对以下三方面提供更深入的分析建议:" \
509
- "\n- A:专业/选课 & 求职方向" \
510
- "\n- B:本校资源利用建议" \
511
- "\n- C:实习 & networking 积累" \
512
- "\n如果你想查看其中一个或多个,请输入 A / B / C / AB / BC / AC / ABC" \
513
- "\n如果都不需要,请回复'不需要'"
514
 
515
-
516
- # 处理 A/B/C 答疑
517
  elif post_career_detail_asked and not post_career_detail_done:
518
  user_choice = message.strip().lower()
519
  if user_choice in ["不需要", "no", "n"]:
520
  post_career_detail_done = True
521
  roadmap_offered = True
522
- return "好的,不查看这三个专项。那需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
523
  else:
524
  abc_text = answer_abc_questions(
525
  selected=user_choice,
526
  bg_info=user_profile.get("bg_info", ""),
527
- wv=user_profile.get("work_value", "未提供"),
528
- ps=user_profile.get("personality_summary", "未提供"),
529
- dd=user_profile.get("dream_day", "未提供")
530
  )
531
  post_career_detail_done = True
532
  roadmap_offered = True
533
- return abc_text + "\n\n以上是你所选的A/B/C专项信息。需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
534
 
535
- # 路线图询问
536
  elif job_chosen and not roadmap_offered and not roadmap_done:
537
  roadmap_offered = True
538
- return "需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
539
 
540
  elif roadmap_offered and not roadmap_done:
541
  ans = message.strip().lower()
@@ -546,19 +571,19 @@ def predict(message, history):
546
  else:
547
  roadmap_done = True
548
  backward_done = True
549
- return "好的,不生成时间轴,本次规划结束。"
550
 
551
- # Backward 最终总结
552
  if backward_done:
553
  try:
554
  api_key = os.environ.get("API_TOKEN")
555
  if not api_key:
556
- return "错误:API_TOKEN 未设置"
557
  client = OpenAI(api_key=api_key)
558
  sprompt = generate_system_prompt()
559
  msgs = [
560
  {"role": "system", "content": sprompt},
561
- {"role": "user", "content": f"以下是资料: {user_profile}. 若需要更多咨询可再次输入"}
562
  ]
563
  resp = client.chat.completions.create(
564
  model=model_default,
@@ -570,165 +595,165 @@ def predict(message, history):
570
  )
571
  return resp.choices[0].message.content
572
  except Exception as e:
573
- return f"发生错误: {str(e)}"
574
-
575
-
576
- # ~~~~~~~~~ Forward模式 ~~~~~~~~~
577
- else:
578
- # 前期处理:收集信息和推荐大方向
579
- if in_forward_flow and not direction_chosen:
580
- # 收集 4个基础问题
581
- if forward_index > 0 and forward_index <= len(forward_additional_questions):
582
- prev_key = forward_additional_questions[forward_index - 1][0]
583
- user_profile[prev_key] = message.strip()
584
 
585
- if forward_index < len(forward_additional_questions):
586
- k, prompt_text = forward_additional_questions[forward_index]
587
- forward_index += 1
588
- return prompt_text
589
 
590
- # 问完 4个问题 -> 推荐3方向
591
- elif not forward_recommendation_given:
592
- forward_recommendation_given = True
593
- return recommend_3directions()
594
 
595
- # 等待用户选 1/2/3 or
596
- else:
597
- choice = message.strip().lower()
598
- if choice in ["1","2","3"]:
599
- idx = int(choice) - 1
600
- if idx < len(user_profile["recommended_directions"]):
601
- sel_dir = user_profile["recommended_directions"][idx]
602
- user_profile["selected_direction"] = sel_dir
603
- direction_chosen = True
604
- # 不在这里设置jobs_recommended=True,而是在函数内部设置
605
- return recommend_3jobs_for_direction(
606
- direction=sel_dir,
607
- bg_info=user_profile["bg_info"] or "",
608
- wv=user_profile["work_value"] or "",
609
- ps=user_profile["personality_summary"] or "",
610
- dd=user_profile["dream_day"] or ""
611
- )
612
- else:
613
- return "无效选项,请重新输入1/2/3或'换'"
614
- elif choice == "换":
615
- recommendation_round += 1
616
- if recommendation_round > 2:
617
- forward_done = True
618
- return "已多次换方向,结束推荐。"
619
- else:
620
- return recommend_3directions()
621
- else:
622
- return "请回复1/2/3选择方向,或输入'换'来重新推荐"
623
-
624
- # 已推荐具体职业 -> 等待用户选择
625
- elif direction_chosen and jobs_recommended and not job_chosen:
626
  choice = message.strip().lower()
627
- if choice in ["1","2","3"]:
628
  idx = int(choice) - 1
629
- if idx < len(user_profile["recommended_jobs"]):
630
- final_job = user_profile["recommended_jobs"][idx]
631
- user_profile["final_choice"] = final_job
632
- job_chosen = True
633
-
634
- detail_msg = do_selected_career_detail(
635
- selected_career=final_job,
636
  bg_info=user_profile["bg_info"] or "",
637
  wv=user_profile["work_value"] or "",
638
  ps=user_profile["personality_summary"] or "",
639
  dd=user_profile["dream_day"] or ""
640
  )
641
- forward_deep_dive_done = True
642
-
643
- # **询问 A/B/C 专项**
644
- post_career_detail_asked = True
645
-
646
- return detail_msg + "\n\n我还可以针对以下三方面提供更深入的分析建议:" \
647
- "\n- A:专业/选课 & 求职方向" \
648
- "\n- B:本校资源利用建议" \
649
- "\n- C:实习 & networking 积累" \
650
- "\n如果你想查看其中一个或多个,请输入 A / B / C / AB / BC / AC / ABC" \
651
- "\n如果都不需要,请回复'不需要'。"
652
  else:
653
- return "无效的选项,请重新输入1/2/3''"
654
  elif choice == "换":
655
  recommendation_round += 1
656
  if recommendation_round > 2:
657
  forward_done = True
658
- return "已多次换职业,结束推荐。"
659
  else:
660
- sel_dir = user_profile.get("selected_direction") or "未知方向"
661
- return recommend_3jobs_for_direction(
662
- direction=sel_dir,
663
- bg_info=user_profile["bg_info"] or "",
664
- wv=user_profile["work_value"] or "",
665
- ps=user_profile["personality_summary"] or "",
666
- dd=user_profile["dream_day"] or ""
667
- )
668
- else:
669
- return "请回复1/2/3选择具体职业,或输入'换'来重新推荐"
670
-
671
- # 处理 A/B/C 答疑
672
- elif post_career_detail_asked and not post_career_detail_done:
673
- user_choice = message.strip().lower()
674
- if user_choice in ["不需要", "no", "n"]:
675
- post_career_detail_done = True
676
- roadmap_offered = True
677
- return "好的,不查看这三个专项。那需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
678
  else:
679
- abc_text = answer_abc_questions(
680
- selected=user_choice,
681
- bg_info=user_profile["bg_info"],
682
- wv=user_profile["work_value"],
683
- ps=user_profile["personality_summary"],
684
- dd=user_profile["dream_day"]
 
 
 
 
 
 
 
 
 
 
 
 
685
  )
686
- post_career_detail_done = True
687
- roadmap_offered = True
688
- return abc_text + "\n\n以上是你所选的A/B/C专项信息。需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
689
 
690
- # 已选定具体职业 -> 问是否需要时间轴
691
- elif job_chosen and not roadmap_offered and not roadmap_done:
692
- roadmap_offered = True
693
- return "需要一个时间轴式职业路线图吗?如需,则回复【要】,否则回复【不要】。"
694
 
695
- elif roadmap_offered and not roadmap_done:
696
- ans = message.strip().lower()
697
- if ans in ["要", "yes", "y"]:
698
- roadmap_done = True
699
- forward_done = True
700
- return do_time_roadmap()
701
  else:
702
- roadmap_done = True
 
 
 
703
  forward_done = True
704
- return "好的,不生成时间轴,本次规划结束。"
705
-
706
- # 如果 forward_done,就给个收尾
707
- if forward_done:
708
- try:
709
- api_key = os.environ.get("API_TOKEN")
710
- if not api_key:
711
- return "错误:API_TOKEN 未设置"
712
- client = OpenAI(api_key=api_key)
713
- sprompt = generate_system_prompt()
714
- msgs = [
715
- {"role":"system","content":sprompt},
716
- {"role":"user","content": f"以下是资料: {user_profile}. 若需要更多咨询可再次输入"}
717
- ]
718
- resp = client.chat.completions.create(
719
- model=model_default,
720
- messages=msgs,
721
- max_tokens=token_default,
722
- temperature=temp_default,
723
- top_p=top_p_default,
724
- stream=False
725
  )
726
- return resp.choices[0].message.content
727
- except Exception as e:
728
- return f"发生错误: {str(e)}"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
729
 
730
- # 兜底
731
- return "信息收集完毕,如还未得到最终回复,请再输入任意文字继续。"
732
 
733
  # ============================ Gradio UI ============================
734
  import gradio as gr
 
2
  from openai import OpenAI
3
  import os
4
 
5
+ # ============================ User Information Structure ============================
6
  user_profile = {
7
+ "mode": None, # Only set when answering the first question (yes/no)
8
  "specific_career": None,
9
  "bg_info": None,
10
  "work_value": None,
 
12
  "dream_day": None,
13
  "forward_direction_choice": None,
14
 
15
+ # Additional state for Forward mode
16
+ "recommended_directions": [], # 3 general career directions
17
+ "selected_direction": None, # The final general direction selected by the user
18
+ "recommended_jobs": [], # 3 specific job recommendations
19
+ "final_choice": None # The final specific job selected by the user
20
  }
21
 
22
+ # ============================ Base Question ============================
23
  base_questions = [
24
+ ("mode", "Do you already have a preferred career path?\n- If yes, please reply 'Yes' (Backward mode)\n- If no, please reply 'No' (Forward mode)")
25
  ]
26
 
27
+ # ============================ Forward Mode Questions ============================
28
  forward_additional_questions = [
29
+ ("bg_info", """To get started, please tell me a bit about your academic background and interests. What's your school, year, major, and core courses? If you have a transcript or resume, that would be even better!"""),
30
+ ("work_value", """Great! What do you think is the meaning or value of 'work'? What should an ideal job provide for you? (e.g., helping others, high income, free time, personal growth, creative space, etc.)"""),
31
+ ("personality_summary", "Briefly describe your personality: Are you introverted or extroverted? Do you enjoy challenges? Detail-oriented? Do you dislike repetitive work?"),
32
+ ("dream_day", "Now describe what your ideal workday looks like: Where are you working? What are you doing? Who are you collaborating with? Is it busy or relaxed? More freedom or more structure?")
33
  ]
34
 
35
+ # ============================ Backward Mode Questions ============================
36
  backward_additional_questions = [
37
+ ("specific_career", "Please describe the specific career path you want to pursue."),
38
+ ("bg_info", "Please provide your school, year, major, and any internship or project experience you already have, so I can help you build a clearer path to your goal.")
39
  ]
40
 
41
+ # ============================ State Control Variables ============================
42
  current_q_index = 0
43
  questions = base_questions[:]
44
  in_forward_flow = False
 
53
  roadmap_offered = False
54
  roadmap_done = False
55
 
56
+ direction_chosen = False # Whether a general direction has been selected
57
+ jobs_recommended = False # Whether 3 specific job options have been recommended
58
+ job_chosen = False # Whether the final job has been selected
59
 
60
+ # ========== Flags for A/B/C Deep Dive Questions ==========
61
+ post_career_detail_asked = False # Whether the user has been asked about A/B/C sections
62
+ post_career_detail_done = False # Whether A/B/C responses are complete
63
 
64
+ # ============================ Model Configuration ============================
65
  model_default = "gpt-4o"
66
  token_default = 2000
67
  temp_default = 0.7
 
69
 
70
 
71
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72
+ # Dynamic Function: Generate a personalized career roadmap based on A/B/C analysis
73
  def backward_strategy_plan(specific_career, bg_info):
74
  prompt = f"""
75
+ You are a professional career planning advisor.
76
+ A student has the target career: {specific_career}
77
+ Their current background is: {bg_info}
78
+
79
+ Please help the student build a clear plan to reach their goal, including:
80
+ 1. Should they consider changing majors or taking a minor?
81
+ 2. Are there any courses they should take? What are the key course topics?
82
+ 3. Recommended certificates/exams (e.g., CFA) and study tips
83
+ 4. Suggested internship directions (based on their background)
84
+ 5. How to make use of university resources (career center, clubs, networking, etc.)
85
+ 6. If their background is mismatched (e.g., mechanical engineering to finance), how to bridge the gap?
86
+
87
+ Use Markdown formatting in sections, with a minimum of 300 words. The content must align closely with the student's current background and goals.
88
  """
89
  try:
90
  api_key = os.environ.get("API_TOKEN")
91
  if not api_key:
92
+ return "Error: API_TOKEN is not set"
93
  client = OpenAI(api_key=api_key)
94
  msgs = [
95
+ {"role": "system", "content": "You are an experienced career advisor, good at creating roadmaps based on user background."},
96
  {"role": "user", "content": prompt}
97
  ]
98
  resp = client.chat.completions.create(
 
105
  )
106
  return resp.choices[0].message.content
107
  except Exception as e:
108
+ return f"Error generating career roadmap: {str(e)}"
109
+
110
 
111
  # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
112
+ # Dynamic function: Generate detailed analysis based on selected A/B/C modules
113
  def answer_abc_questions(selected, bg_info, wv, ps, dd):
114
  desired_parts = []
115
  if "a" in selected.lower():
 
120
  desired_parts.append("C")
121
 
122
  if not desired_parts:
123
+ return "Alright, you chose not to view the A/B/C modules for now."
124
 
125
  prompt_text = f"""
126
+ You are a professional career planning advisor. Below is the student's background information. Please provide detailed analysis and suggestions based on the selected module(s): A, B, or C.
127
+
128
+ Student Profile:
129
+ - Academic Background: {bg_info}
130
+ - Work Value: {wv}
131
+ - Personality: {ps}
132
+ - Ideal Workday: {dd}
133
+
134
+ The student wants insights on the following module(s): {', '.join(desired_parts)}
135
+ Please write a separate section for each selected module in order, using Markdown formatting.
136
+
137
+ (A: Course Selection & Career Entry)
138
+ - Analyze the student’s current academic background and what capabilities they already have.
139
+ - Based on their intended career, recommend what types of courses they should focus on.
140
+ - Suggest course names and keywords to guide their course planning.
141
+
142
+ (B: School Resource Utilization)
143
+ - Provide 3 types of suggestions:
144
+ 1. How to utilize the university's career services.
145
+ 2. How to get involved with career-related clubs, organizations, and platforms.
146
+ 3. How to network with alumni in their field and improve networking skills.
147
+
148
+ (C: Internships & Experience Building)
149
+ - Suggest platforms to find internships.
150
+ - Recommend internship directions aligned with the student’s career goal.
151
+ - Provide tips for preparing materials and applying.
152
+ - Strategies for gaining experience through networking, internships, or campus jobs.
153
+
154
+ Only include the modules selected by the student. Do not generate content for unselected modules.
155
  """
156
  try:
157
  api_key = os.environ.get("API_TOKEN")
158
  if not api_key:
159
+ return "Error: API_TOKEN is not set."
160
  client = OpenAI(api_key=api_key)
161
  msgs = [
162
+ {"role": "system", "content": "You are a professional career advisor who provides module-specific (A/B/C) analysis and suggestions."},
163
  {"role": "user", "content": prompt_text}
164
  ]
165
  resp = client.chat.completions.create(
 
172
  )
173
  return resp.choices[0].message.content
174
  except Exception as e:
175
+ return f"Error generating A/B/C insights: {str(e)}"
176
 
177
 
178
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Career Detail Generation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
179
  def do_selected_career_detail(selected_career, bg_info, wv, ps, dd):
180
  prompt = f"""
181
+ You are a professional career planning advisor.
182
+
183
+ The student has selected the following specific career: {selected_career}
184
+ Student Profile:
185
+ - Academic Background: {bg_info}
186
+ - Work Value: {wv}
187
+ - Personality: {ps}
188
+ - Ideal Workday: {dd}
189
+
190
+ Please write a detailed description of this career including at least the following:
191
+ 1. Average salary (entry-level / mid-level / senior)
192
+ 2. Work environment (remote/hybrid/in-office, team size, etc.)
193
+ 3. Promotion difficulty (what qualifications or milestones are needed)
194
+ 4. Key skills and certifications required
195
+ 5. Typical work rhythm (fast-paced or routine?)
196
+ 6. Industry outlook
197
+
198
+ Please use Markdown formatting in separate sections, with a minimum of 300 words.
199
  """
200
  try:
201
  api_key = os.environ.get("API_TOKEN")
202
  if not api_key:
203
+ return "Error: API_TOKEN is not set."
204
  client = OpenAI(api_key=api_key)
205
 
206
  msgs = [
207
+ {"role": "system", "content": "You are a professional career advisor who provides in-depth career descriptions."},
208
  {"role": "user", "content": prompt}
209
  ]
210
  resp = client.chat.completions.create(
 
217
  )
218
  return resp.choices[0].message.content
219
  except Exception as e:
220
+ return f"Error generating detailed career info: {str(e)}"
221
 
222
 
223
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Timeline-Based Career Roadmap ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
224
  def do_time_roadmap():
225
  direction = user_profile.get("final_choice") or user_profile.get("selected_direction") or \
226
  user_profile.get("forward_direction_choice") or user_profile.get("specific_career")
227
+ bg_info = user_profile["bg_info"] or "Unknown Major"
228
+ wv = user_profile["work_value"] or "Not provided"
229
+ ps = user_profile["personality_summary"] or "Not provided"
230
+ dd = user_profile["dream_day"] or "Not provided"
231
 
232
  prompt_roadmap = f"""
233
+ Student Profile:
234
+ - Academic Background: {bg_info}
235
+ - Career Direction / Specific Role: {direction}
236
+ - Work Values: {wv}
237
+ - Personality: {ps}
238
+ - Ideal Workday: {dd}
239
+
240
+ Please create a timeline-style career roadmap based on the student's background and goal.
241
+ Use the following structure as a reference:
242
+
243
+ 📍 Summer after Year 2: [What internship to apply for, what activities to participate in, what certifications to pursue]
244
+ 🎓 Year 3: [Recommended courses, key projects or academic goals]
245
+ 💼 Summer after Year 3: [Target internships, certifications, personal/professional development plans]
246
+ 📄 6–12 Months Before Graduation: [Certifications to complete, job/grad school application materials to prepare]
247
+ 🚀 Post-Graduation: [Target roles, how to apply, next steps]
248
+
249
+ Please write the roadmap using Markdown formatting and tailor your suggestions specifically to the student's profile.
250
  """
251
  try:
252
  api_key = os.environ.get("API_TOKEN")
253
  if not api_key:
254
+ return "Error: API_TOKEN is not set."
255
  client = OpenAI(api_key=api_key)
256
 
257
  msgs = [
258
+ {"role": "system", "content": "You are a professional career advisor who generates timeline-based career roadmaps."},
259
+ {"role": "user", "content": prompt_roadmap}
260
  ]
261
  resp = client.chat.completions.create(
262
  model=model_default,
 
268
  )
269
  return resp.choices[0].message.content
270
  except Exception as e:
271
+ return f"Error generating career roadmap: {str(e)}"
272
 
273
 
274
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Recommend Specific Careers ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
275
  def recommend_3jobs_for_direction(direction, bg_info, wv, ps, dd):
276
  prompt = f"""
277
+ You are a professional career advisor.
278
+ The student has selected the following general career direction: {direction}
279
+ Student background: {bg_info}
280
+ Work values: {wv}
281
+ Personality: {ps}
282
+ Ideal workday: {dd}
283
+
284
+ Please recommend 3 specific job roles related to this direction, and provide details for each:
285
+ 1) Why this job fits the chosen direction
286
+ 2) Typical responsibilities
287
+ 3) Required skills and certifications
288
+ 4) Future career prospects
289
+
290
+ End your response with this format (do not repeat this format earlier in the message):
291
+
292
+ Please select your ideal job:
293
+ 1. [Job Title 1]
294
+ 2. [Job Title 2]
295
+ 3. [Job Title 3]
296
+ If none of these suit you, enter 'change'.
297
  """
298
  try:
299
  api_key = os.environ.get("API_TOKEN")
300
  if not api_key:
301
+ return "Error: API_TOKEN not set"
302
  client = OpenAI(api_key=api_key)
303
 
304
  msgs = [
305
+ {"role": "system", "content": "You are a professional career advisor, recommending 3 specific jobs based on the student's chosen direction."},
306
  {"role": "user", "content": prompt}
307
  ]
308
  resp = client.chat.completions.create(
 
315
  )
316
  output_text = resp.choices[0].message.content
317
 
318
+ # Try to extract job titles from the response
319
  import re
320
  job_pattern = r'(\d+)\.\s+\[?(.*?)\]?(?:\n|$)'
321
  job_matches = re.findall(job_pattern, output_text)
322
  job_list = [match[1].strip() for match in job_matches]
323
+
324
+ # If parsing fails, provide fallback options
325
  if len(job_list) < 3:
326
  job_list = [
327
+ f"{direction} - Job A",
328
+ f"{direction} - Job B",
329
+ f"{direction} - Job C"
330
  ]
331
+
332
+ user_profile["recommended_jobs"] = job_list[:3] # Ensure only the first 3 are used
333
+
334
+ # Mark that jobs have been recommended
335
  global jobs_recommended
336
  jobs_recommended = True
337
+
338
  return output_text
339
  except Exception as e:
340
+ return f"Error recommending specific jobs: {str(e)}"
341
 
342
 
343
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Recommend General Directions ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
344
  def recommend_3directions():
345
+ bg_info = user_profile["bg_info"] or "Unknown"
346
+ wv = user_profile["work_value"] or "Not provided"
347
+ ps = user_profile["personality_summary"] or "Not provided"
348
+ dd = user_profile["dream_day"] or "Not provided"
349
 
350
  rec_prompt = f"""
351
+ Based on the following information, write a structured student profile and recommend 3 general career directions.
352
+ Address the student as "you" (not he/she).
353
+
354
+ - Academic Background: {bg_info}
355
+ - Work Values: {wv}
356
+ - Personality: {ps}
357
+ - Ideal Workday: {dd}
358
+
359
+ Two parts required:
360
+
361
+ [1. Student Profile]: Briefly summarize their academic background, core values, personality, and career expectations.
362
+
363
+ [2. Recommend 3 General Directions]: For each direction, write 1–2 paragraphs explaining why it's a good fit for the student.
364
+
365
+ End with only the following format (do not repeat it earlier):
366
+
367
+ Please choose your career direction:
368
+ 1. [Direction Name 1]
369
+ 2. [Direction Name 2]
370
+ 3. [Direction Name 3]
371
+ If none are suitable, type 'change'.
372
  """
373
  try:
374
  api_key = os.environ.get("API_TOKEN")
375
  if not api_key:
376
+ return "Error: API_TOKEN not set"
377
  client = OpenAI(api_key=api_key)
378
  msgs = [
379
+ {"role": "system", "content": "You are a career advisor who recommends 3 general directions based on the user's profile."},
380
+ {"role": "user", "content": rec_prompt}
381
  ]
382
  resp = client.chat.completions.create(
383
  model=model_default,
 
389
  )
390
  output_text = resp.choices[0].message.content
391
 
392
+ # Try to extract direction names from the response
393
  import re
394
  dir_pattern = r'(\d+)\.\s+\[?(.*?)\]?(?:\n|$)'
395
  dir_matches = re.findall(dir_pattern, output_text)
396
  dir_list = [match[1].strip() for match in dir_matches]
397
+
398
+ # Fallback if parsing fails
399
  if len(dir_list) < 3:
400
+ dir_list = ["Direction 1", "Direction 2", "Direction 3"]
401
+
402
+ user_profile["recommended_directions"] = dir_list[:3]
 
 
 
 
403
  return output_text
404
  except Exception as e:
405
+ return f"Error recommending general directions: {str(e)}"
406
 
407
 
408
+
409
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Generate System Prompt ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
410
  def generate_system_prompt():
411
  mode = user_profile["mode"]
412
  sc = user_profile["specific_career"]
 
416
  dd = user_profile["dream_day"]
417
  fwd = user_profile["forward_direction_choice"]
418
 
419
+ if mode == "是": # Backward Mode
420
  return f"""
421
+ You are a professional career advisor using the Backward Design method to help students achieve their career goals.
422
+ Target career: {sc}
423
+ Background information: {bg}
424
+ Please provide:
425
+ 1. A brief analysis of the target career
426
+ 2. Top companies or organizations in this field
427
+ 3. Required skills and qualifications
428
+ 4. Career development path
429
+ 5. Academic/course suggestions, skill development, use of resources, internship planning, resume tips, etc.
430
+
431
+ Finally, output a career roadmap in Markdown format like this:
432
+ 📍 Now → 🎓 Learning Suggestions → 💼 Practice Suggestions → 📄 Certification Advice → 🚀 Job Search Suggestions
433
  """
434
+ else: # Forward Mode
435
  return f"""
436
+ You are a professional career advisor using the Forward Design method to help students explore suitable career paths.
437
+ Student background: {bg}
438
+ Work values: {wv}
439
+ Personality summary: {ps}
440
+ Ideal workday: {dd}
441
+ Student's selected direction: {fwd}
442
+ Please provide:
443
+ 1. An analysis of the student’s strengths, personality traits, and values
444
+ 2. 3 recommended specific jobs, including daily work content, fit, requirements, and preparation pathway
445
+ 3. A final career roadmap:
446
+ 📍 Now → 🎓 Learning Suggestions → 💼 Practice Suggestions → 📄 Certification Advice → 🚀 Job Search Suggestions
447
  """
448
 
449
 
450
+ # ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ Main Dialogue Logic Function ~~~~~~~~~~~~~~~~~~~~~~~~~~~~
451
  def predict(message, history):
452
+ # ========= Declare all global variables at the beginning =========
453
  global current_q_index, questions
454
  global in_forward_flow, in_backward_flow
455
  global forward_index, backward_index
 
461
  global direction_chosen, jobs_recommended, job_chosen
462
  global post_career_detail_asked, post_career_detail_done
463
 
464
+ # ======================== Initialization =========================
465
  if not history:
466
  current_q_index = 0
467
  questions[:] = base_questions
 
486
  post_career_detail_asked = False
487
  post_career_detail_done = False
488
 
489
+ # ======================== First handle base questions =========================
490
  if 0 < current_q_index <= len(questions):
491
  key = questions[current_q_index - 1][0]
 
492
 
493
  if key == "mode" and current_q_index == 1:
494
+ ans = message.strip()
495
+ user_profile["mode"] = ans
496
+ questions[:] = [] # Clear base questions
497
+ if "是" in ans:
498
+ in_backward_flow = True
499
+ else:
500
+ in_forward_flow = True
 
501
 
502
+ if current_q_index < len(questions) and not in_forward_flow and not in_backward_flow:
503
  nxt = questions[current_q_index][1]
504
  current_q_index += 1
505
  return nxt
506
 
 
507
 
508
 
509
+ # ======================== Mode-based Flow Control =========================
510
+ mode = user_profile.get("mode") or ""
511
 
512
+ # ~~~~~~~~~ Backward Mode ~~~~~~~~~
513
+ if mode == "是":
514
+ if in_backward_flow and not backward_done:
515
  if backward_index > 0 and backward_index <= len(backward_additional_questions):
516
  prev_key = backward_additional_questions[backward_index - 1][0]
517
  user_profile[prev_key] = message.strip()
 
519
  if backward_index < len(backward_additional_questions):
520
  k, prompt_text = backward_additional_questions[backward_index]
521
  backward_index += 1
522
+ return prompt_text # ✅ Correct: properly indented inside the `if` block
523
  else:
524
+ # All background info collected, generate career path strategy
525
  strategy = backward_strategy_plan(
526
  specific_career=user_profile.get("specific_career"),
527
  bg_info=user_profile.get("bg_info")
 
531
  forward_deep_dive_done = True
532
  post_career_detail_asked = True
533
 
534
+ return strategy + "\n\nI can also provide in-depth suggestions for the following three aspects:" \
535
+ "\n- A: Major/Course Planning & Job Entry Advice" \
536
+ "\n- B: Making Use of Campus Resources" \
537
+ "\n- C: Internship & Networking Strategy" \
538
+ "\nIf you'd like to view one or more, please type A / B / C / AB / BC / AC / ABC" \
539
+ "\nIf not needed, just reply with 'No'."
540
 
541
+ # Handle A/B/C deep-dive answers
 
542
  elif post_career_detail_asked and not post_career_detail_done:
543
  user_choice = message.strip().lower()
544
  if user_choice in ["不需要", "no", "n"]:
545
  post_career_detail_done = True
546
  roadmap_offered = True
547
+ return "Understood. Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
548
  else:
549
  abc_text = answer_abc_questions(
550
  selected=user_choice,
551
  bg_info=user_profile.get("bg_info", ""),
552
+ wv=user_profile.get("work_value", "Not provided"),
553
+ ps=user_profile.get("personality_summary", "Not provided"),
554
+ dd=user_profile.get("dream_day", "Not provided")
555
  )
556
  post_career_detail_done = True
557
  roadmap_offered = True
558
+ return abc_text + "\n\nAbove is your selected A/B/C section. Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
559
 
560
+ # Ask if a roadmap is needed
561
  elif job_chosen and not roadmap_offered and not roadmap_done:
562
  roadmap_offered = True
563
+ return "Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
564
 
565
  elif roadmap_offered and not roadmap_done:
566
  ans = message.strip().lower()
 
571
  else:
572
  roadmap_done = True
573
  backward_done = True
574
+ return "Alright, no roadmap generated. This concludes the planning session."
575
 
576
+ # Final summary for Backward Mode
577
  if backward_done:
578
  try:
579
  api_key = os.environ.get("API_TOKEN")
580
  if not api_key:
581
+ return "Error: API_TOKEN is not set"
582
  client = OpenAI(api_key=api_key)
583
  sprompt = generate_system_prompt()
584
  msgs = [
585
  {"role": "system", "content": sprompt},
586
+ {"role": "user", "content": f"Here is the user profile: {user_profile}. Feel free to ask more questions if needed."}
587
  ]
588
  resp = client.chat.completions.create(
589
  model=model_default,
 
595
  )
596
  return resp.choices[0].message.content
597
  except Exception as e:
598
+ return f"An error occurred while generating summary: {str(e)}"
599
+
600
+ # ~~~~~~~~~ Forward Mode ~~~~~~~~~
601
+ else:
602
+ # Step 1: Collect user information and recommend 3 career directions
603
+ if in_forward_flow and not direction_chosen:
604
+ # Collect the 4 basic background questions
605
+ if forward_index > 0 and forward_index <= len(forward_additional_questions):
606
+ prev_key = forward_additional_questions[forward_index - 1][0]
607
+ user_profile[prev_key] = message.strip()
 
608
 
609
+ if forward_index < len(forward_additional_questions):
610
+ k, prompt_text = forward_additional_questions[forward_index]
611
+ forward_index += 1
612
+ return prompt_text
613
 
614
+ # After collecting the 4 questions → Recommend 3 directions
615
+ elif not forward_recommendation_given:
616
+ forward_recommendation_given = True
617
+ return recommend_3directions()
618
 
619
+ # Wait for user to choose 1 / 2 / 3 or ask to switch
620
+ else:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
621
  choice = message.strip().lower()
622
+ if choice in ["1", "2", "3"]:
623
  idx = int(choice) - 1
624
+ if idx < len(user_profile["recommended_directions"]):
625
+ sel_dir = user_profile["recommended_directions"][idx]
626
+ user_profile["selected_direction"] = sel_dir
627
+ direction_chosen = True
628
+ # jobs_recommended is set internally inside the function
629
+ return recommend_3jobs_for_direction(
630
+ direction=sel_dir,
631
  bg_info=user_profile["bg_info"] or "",
632
  wv=user_profile["work_value"] or "",
633
  ps=user_profile["personality_summary"] or "",
634
  dd=user_profile["dream_day"] or ""
635
  )
 
 
 
 
 
 
 
 
 
 
 
636
  else:
637
+ return "Invalid option. Please reply with 1/2/3 or 'switch'."
638
  elif choice == "换":
639
  recommendation_round += 1
640
  if recommendation_round > 2:
641
  forward_done = True
642
+ return "You've switched too many times. Ending recommendation."
643
  else:
644
+ return recommend_3directions()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
645
  else:
646
+ return "Please reply with 1/2/3 to select a direction, or 'switch' to change options."
647
+
648
+ # Step 2: Recommend 3 specific job roles and wait for user to choose
649
+ elif direction_chosen and jobs_recommended and not job_chosen:
650
+ choice = message.strip().lower()
651
+ if choice in ["1", "2", "3"]:
652
+ idx = int(choice) - 1
653
+ if idx < len(user_profile["recommended_jobs"]):
654
+ final_job = user_profile["recommended_jobs"][idx]
655
+ user_profile["final_choice"] = final_job
656
+ job_chosen = True
657
+
658
+ detail_msg = do_selected_career_detail(
659
+ selected_career=final_job,
660
+ bg_info=user_profile["bg_info"] or "",
661
+ wv=user_profile["work_value"] or "",
662
+ ps=user_profile["personality_summary"] or "",
663
+ dd=user_profile["dream_day"] or ""
664
  )
665
+ forward_deep_dive_done = True
 
 
666
 
667
+ # Ask if user wants A/B/C section suggestions
668
+ post_career_detail_asked = True
 
 
669
 
670
+ return detail_msg + "\n\nI can also provide in-depth suggestions for the following 3 areas:" \
671
+ "\n- A: Major/Course Planning & Job Entry Advice" \
672
+ "\n- B: Making Use of Campus Resources" \
673
+ "\n- C: Internship & Networking Strategy" \
674
+ "\nIf you'd like to view one or more, please type A / B / C / AB / BC / AC / ABC" \
675
+ "\nIf not needed, just reply with 'no'."
676
  else:
677
+ return "Invalid option. Please reply with 1/2/3 or 'switch'."
678
+ elif choice == "换":
679
+ recommendation_round += 1
680
+ if recommendation_round > 2:
681
  forward_done = True
682
+ return "You've switched too many times. Ending recommendation."
683
+ else:
684
+ sel_dir = user_profile.get("selected_direction") or "Unknown Direction"
685
+ return recommend_3jobs_for_direction(
686
+ direction=sel_dir,
687
+ bg_info=user_profile["bg_info"] or "",
688
+ wv=user_profile["work_value"] or "",
689
+ ps=user_profile["personality_summary"] or "",
690
+ dd=user_profile["dream_day"] or ""
 
 
 
 
 
 
 
 
 
 
 
 
691
  )
692
+ else:
693
+ return "Please reply with 1/2/3 to choose a specific role, or 'switch' to get new ones."
694
+
695
+ # Step 3: Answer user-selected A/B/C questions
696
+ elif post_career_detail_asked and not post_career_detail_done:
697
+ user_choice = message.strip().lower()
698
+ if user_choice in ["不需要", "no", "n"]:
699
+ post_career_detail_done = True
700
+ roadmap_offered = True
701
+ return "Okay, skipping the three detailed sections. Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
702
+ else:
703
+ abc_text = answer_abc_questions(
704
+ selected=user_choice,
705
+ bg_info=user_profile["bg_info"],
706
+ wv=user_profile["work_value"],
707
+ ps=user_profile["personality_summary"],
708
+ dd=user_profile["dream_day"]
709
+ )
710
+ post_career_detail_done = True
711
+ roadmap_offered = True
712
+ return abc_text + "\n\nAbove is the A/B/C section you selected. Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
713
+
714
+ # Step 4: Ask if user wants a timeline roadmap
715
+ elif job_chosen and not roadmap_offered and not roadmap_done:
716
+ roadmap_offered = True
717
+ return "Would you like a timeline-style career roadmap? Reply 'yes' or 'no'."
718
+
719
+ elif roadmap_offered and not roadmap_done:
720
+ ans = message.strip().lower()
721
+ if ans in ["要", "yes", "y"]:
722
+ roadmap_done = True
723
+ forward_done = True
724
+ return do_time_roadmap()
725
+ else:
726
+ roadmap_done = True
727
+ forward_done = True
728
+ return "Okay, no roadmap generated. This concludes the session."
729
+
730
+ # Step 5: Final wrap-up after Forward mode finishes
731
+ if forward_done:
732
+ try:
733
+ api_key = os.environ.get("API_TOKEN")
734
+ if not api_key:
735
+ return "Error: API_TOKEN is not set"
736
+ client = OpenAI(api_key=api_key)
737
+ sprompt = generate_system_prompt()
738
+ msgs = [
739
+ {"role": "system", "content": sprompt},
740
+ {"role": "user", "content": f"Here is the user profile: {user_profile}. Feel free to ask more questions if needed."}
741
+ ]
742
+ resp = client.chat.completions.create(
743
+ model=model_default,
744
+ messages=msgs,
745
+ max_tokens=token_default,
746
+ temperature=temp_default,
747
+ top_p=top_p_default,
748
+ stream=False
749
+ )
750
+ return resp.choices[0].message.content
751
+ except Exception as e:
752
+ return f"An error occurred: {str(e)}"
753
+
754
+ # Fallback response
755
+ return "Information collection is complete. If you haven’t received a final reply, send any message to continue."
756
 
 
 
757
 
758
  # ============================ Gradio UI ============================
759
  import gradio as gr