LPC/app.py

370 lines
13 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import logging
import sys
logging.basicConfig(
level=logging.DEBUG,
format='%(asctime)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('app_debug.log'),
logging.StreamHandler(sys.stdout)
]
)
logger = logging.getLogger(__name__)
from flask import Flask, render_template, request, jsonify, session
from services.deepseek_service import deepseek_service
from config import Config
import uuid
import datetime
app = Flask(__name__)
app.secret_key = "interviewer-secret-key-change-in-production"
# 设置session过期时间为1小时
app.permanent_session_lifetime = datetime.timedelta(hours=1)
# 内存数据库存储面试数据避免session过期导致的"面试不存在"错误
interviews_db = {}
INTERVIEW_PHASES = {
"intro": "自我介绍",
"professional": "专业能力",
"scenario": "情景假设",
"career": "职业规划",
"closing": "面试结束"
}
QUESTION_COUNTS = {
"intro": 2,
"professional": 4,
"scenario": 2,
"career": 1
}
@app.route("/")
def index():
return render_template("index.html")
@app.route("/api/chat", methods=["POST"])
def chat():
data = request.json
user_input = data.get("message", "").strip()
system_type = data.get("system_type", "general_assistant")
conversation_key = data.get("conversation_key", "default")
if not user_input:
return jsonify({"error": "请输入内容"}), 400
if conversation_key not in session:
session[conversation_key] = []
conversation_history = session[conversation_key]
try:
result = deepseek_service.chat(
user_input=user_input,
conversation_history=conversation_history,
system_type=system_type
)
conversation_history.append({"role": "user", "content": user_input})
conversation_history.append(result)
session[conversation_key] = conversation_history[-20:]
return jsonify({"response": result["content"]})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/resume/optimize", methods=["POST"])
def optimize_resume():
data = request.json
resume_content = data.get("resume_content", "").strip()
target_position = data.get("target_position", "").strip()
if not resume_content:
return jsonify({"error": "请提供简历内容"}), 400
try:
result = deepseek_service.optimize_resume(resume_content, target_position)
return jsonify(result)
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/api/interview/start", methods=["POST"])
def start_interview():
data = request.json
job_position = data.get("job_position", "").strip()
difficulty = data.get("difficulty", "intermediate")
if not job_position:
return jsonify({"error": "请选择目标岗位"}), 400
interview_id = str(uuid.uuid4())
interview_data = {
"job_position": job_position,
"difficulty": difficulty,
"current_phase": "intro",
"question_count": 0,
"conversation_history": [],
"is_active": True
}
session[f"interview_{interview_id}"] = interview_data
interviews_db[interview_id] = interview_data
first_question = deepseek_service.generate_interview_question(
job_position=job_position,
difficulty=difficulty,
phase="intro"
)
session[f"interview_{interview_id}"]["conversation_history"].append({
"role": "assistant",
"content": f"你好!我是面试官,现在开始针对{job_position}岗位的面试。\n\n{first_question}"
})
return jsonify({
"interview_id": interview_id,
"job_position": job_position,
"difficulty": difficulty,
"question": first_question,
"phase": "intro"
})
@app.route("/api/interview/answer", methods=["POST"])
def answer_question():
data = request.json
interview_id = data.get("interview_id", "").strip()
user_answer = data.get("answer", "").strip()
request_feedback = data.get("request_feedback", False)
if not interview_id:
return jsonify({"error": "无效的面试ID"}), 400
# 首先从内存数据库查找面试数据
if interview_id not in interviews_db:
# 如果内存数据库中没有再检查session
interview_key = f"interview_{interview_id}"
if interview_key not in session:
return jsonify({"error": "面试不存在或已结束"}), 400
# 如果session中有同步到内存数据库
interviews_db[interview_id] = session[interview_key]
interview_data = interviews_db[interview_id]
if not interview_data["is_active"]:
return jsonify({"error": "面试已结束"}), 400
if not user_answer:
return jsonify({"error": "请输入你的回答"}), 400
conversation_history = interview_data["conversation_history"]
if request_feedback:
last_question = ""
logger.debug(f"查找最后一个问题,对话历史长度:{len(conversation_history)}")
for msg in reversed(conversation_history):
logger.debug(f"检查消息:角色={msg['role']}, 内容={msg['content'][:50]}...")
if msg["role"] == "assistant" and ("" in msg["content"] or "?" in msg["content"]):
last_question = msg["content"]
logger.debug(f"找到最后一个问题:{last_question[:50]}...")
break
if last_question:
try:
feedback = deepseek_service.chat_with_feedback(
user_input=last_question,
user_answer=user_answer,
conversation_history=conversation_history[:-1]
)
conversation_history.append({"role": "user", "content": user_answer})
conversation_history.append(feedback)
return jsonify({
"feedback": feedback["content"],
"ended": False
})
except Exception as e:
logger.error(f"生成反馈失败:{str(e)}", exc_info=True)
return jsonify({"error": f"生成反馈失败:{str(e)}"}), 500
else:
logger.warning("没有找到最后一个问题")
return jsonify({"error": "没有找到相关问题"}), 400
conversation_history.append({"role": "user", "content": user_answer})
interview_data["question_count"] += 1
current_phase = interview_data["current_phase"]
phase_order = ["intro", "professional", "scenario", "career", "closing"]
current_index = phase_order.index(current_phase)
questions_in_phase = QUESTION_COUNTS.get(current_phase, 1)
next_question = None
if interview_data["question_count"] >= questions_in_phase:
if current_index < len(phase_order) - 1:
next_phase = phase_order[current_index + 1]
interview_data["current_phase"] = next_phase
interview_data["question_count"] = 0
if next_phase == "closing":
conversation_history.append({
"role": "assistant",
"content": "面试结束!感谢你的参与。点击下方按钮获取本次面试的详细反馈。"
})
interview_data["is_active"] = False
return jsonify({
"question": None,
"feedback": "面试结束",
"ended": True,
"conversation_history": conversation_history[-10:]
})
else:
phase_names = {
"intro": "自我介绍",
"professional": "专业能力",
"scenario": "情景假设",
"career": "职业规划"
}
try:
next_question = deepseek_service.generate_interview_question(
job_position=interview_data["job_position"],
difficulty=interview_data["difficulty"],
conversation_history=conversation_history,
phase=next_phase
)
conversation_history.append({
"role": "assistant",
"content": f"{phase_names.get(next_phase, next_phase)}阶段)\n\n{next_question}"
})
except Exception as e:
return jsonify({"error": f"生成问题失败:{str(e)}"}), 500
else:
try:
next_question = deepseek_service.generate_interview_question(
job_position=interview_data["job_position"],
difficulty=interview_data["difficulty"],
conversation_history=conversation_history,
phase=current_phase
)
conversation_history.append({"role": "assistant", "content": next_question})
except Exception as e:
return jsonify({"error": f"生成问题失败:{str(e)}"}), 500
# 同时更新内存数据库和session中的数据
interviews_db[interview_id] = interview_data
session[f"interview_{interview_id}"] = interview_data
return jsonify({
"question": next_question,
"feedback": None,
"ended": False,
"phase": interview_data["current_phase"]
})
@app.route("/api/interview/feedback", methods=["POST"])
def get_interview_feedback():
logger.info("接收到面试反馈请求")
try:
# 首先确保能够获取请求数据
if not request.is_json:
logger.warning("请求数据不是JSON格式")
return jsonify({"error": "请求数据必须是JSON格式"}), 400
data = request.json
logger.debug(f"请求数据:{data}")
# 检查必要参数
interview_id = data.get("interview_id", "").strip()
conversation_history = data.get("conversation_history", [])
if not interview_id:
logger.warning("面试ID无效")
return jsonify({"error": "无效的面试ID"}), 400
if not conversation_history:
logger.warning("没有提供面试对话历史")
return jsonify({"error": "没有提供面试对话历史"}), 400
# 获取岗位信息
job_position = ""
if interview_id in interviews_db:
job_position = interviews_db[interview_id].get("job_position", "")
logger.debug(f"岗位信息:{job_position}")
logger.debug(f"对话历史长度:{len(conversation_history)}")
# 构建系统提示
system_prompt = """作为一位专业的面试评估专家,请对整场面试进行全面评估。
请提供:
1. 整体表现评分0-100分和评级优秀/良好/一般/需改进)
2. 各轮回答的详细分析(针对每个问题和回答给出具体评价)
3. strengths优势- 列出至少3点
4. areas_for_improvement需要改进的方面- 列出至少3点
5. 具体的准备建议(针对改进点给出可操作的建议)
请用中文回复,格式清晰、结构化,避免过于笼统的描述。"""
# 构建对话文本
conversation_text = "\n\n".join([
f"{'面试官' if msg['role'] == 'assistant' else '候选人'}{msg['content']}"
for msg in conversation_history
])
# 构建用户提示
if job_position:
user_prompt = f"请分析以下针对{job_position}岗位的面试对话并给出综合反馈:\n\n{conversation_text}"
else:
user_prompt = f"请分析以下面试对话并给出综合反馈:\n\n{conversation_text}"
# 构建完整的消息
messages = [
{"role": "system", "content": system_prompt},
{"role": "user", "content": user_prompt}
]
logger.debug("准备调用DeepSeek API生成反馈")
logger.debug(f"API请求消息数量{len(messages)}")
# 调用DeepSeek API
response = deepseek_service._call_api(messages)
logger.debug("DeepSeek API调用成功")
# 处理API响应
if not response or "choices" not in response or not response["choices"]:
logger.error("API响应格式错误缺少choices字段")
return jsonify({"error": "生成反馈失败API响应格式错误"}), 500
feedback = response["choices"][0]["message"]["content"]
logger.info("反馈生成成功")
logger.debug(f"生成的反馈内容长度:{len(feedback)}字符")
logger.debug(f"生成的反馈内容开头:{feedback[:100]}...")
return jsonify({"feedback": feedback})
except Exception as e:
logger.error(f"生成面试反馈失败:{str(e)}", exc_info=True)
# 返回更具体的错误信息
return jsonify({"error": f"生成面试反馈失败:{str(e)}", "details": str(type(e).__name__)}), 500
if __name__ == "__main__":
app.run(
host=Config.APP_HOST,
port=Config.APP_PORT,
debug=False
)