删除的对话接口
This commit is contained in:
@@ -1,51 +0,0 @@
|
||||
"""求职助手 Agent 对话 AI 引擎
|
||||
|
||||
构造 prompt + 调 LLM + 解析返回。
|
||||
依赖:LLM 枚举、job_agent/prompts、parse_llm_json
|
||||
"""
|
||||
|
||||
from app.ai.job_agent.prompts import SYSTEM_PROMPT
|
||||
from app.ai.model_config import JobAgentModel
|
||||
from app.core.logger import log
|
||||
from app.tool.json_helper import parse_llm_json
|
||||
|
||||
|
||||
async def agent_chat(resume_text: str, message: str, history: list[dict],
|
||||
job_categories: list[str], regions: list[str],
|
||||
industries: list[str]) -> dict:
|
||||
"""求职助手对话
|
||||
1. 构造 system prompt 2. 拼 messages 3. 调 LLM 4. 解析返回
|
||||
"""
|
||||
# 1. 构造 system prompt
|
||||
system_content = SYSTEM_PROMPT.format(
|
||||
resume_text=resume_text,
|
||||
job_categories="、".join(job_categories) if job_categories else "未设置",
|
||||
regions="、".join(regions) if regions else "未设置",
|
||||
industries="、".join(industries) if industries else "未设置",
|
||||
)
|
||||
|
||||
# 2. 拼 messages
|
||||
messages = [("system", system_content)]
|
||||
for msg in history:
|
||||
messages.append((msg["role"], msg["content"]))
|
||||
messages.append(("human", message))
|
||||
|
||||
# 3. 调 LLM
|
||||
try:
|
||||
result = await JobAgentModel.CHAT.ainvoke(messages)
|
||||
raw = result.content
|
||||
except Exception as e:
|
||||
log.error(f"求职助手AI调用失败: {e}")
|
||||
return {"message": "抱歉,我暂时无法回复,请稍后再试。", "tool": None, "toolParams": None}
|
||||
|
||||
# 4. 解析返回
|
||||
try:
|
||||
parsed = parse_llm_json(raw)
|
||||
return {
|
||||
"message": parsed.get("message", ""),
|
||||
"tool": parsed.get("tool"),
|
||||
"toolParams": parsed.get("toolParams"),
|
||||
}
|
||||
except Exception as e:
|
||||
log.warning(f"求职助手AI返回解析失败, raw={raw}, error={e}")
|
||||
return {"message": raw.strip() if raw else "抱歉,我暂时无法回复。", "tool": None, "toolParams": None}
|
||||
@@ -1,41 +1,4 @@
|
||||
"""求职助手 Agent 对话 Prompt 模板 + 岗位简历优化 Prompt"""
|
||||
|
||||
SYSTEM_PROMPT = """你是 OfferPie 求职助手,帮助用户找到合适的工作。
|
||||
|
||||
【用户简历】
|
||||
{resume_text}
|
||||
|
||||
【求职意向】
|
||||
意向岗位:{job_categories}
|
||||
意向城市:{regions}
|
||||
意向行业:{industries}
|
||||
|
||||
【你的能力】
|
||||
1. 回答求职相关问题(面试技巧、简历建议、行业分析等),回复不超过200字
|
||||
2. 当用户想看岗位推荐时,提取用户的偏好描述,调用岗位推荐工具
|
||||
3. 当用户想修改求职偏好/设置时,调用偏好设置工具
|
||||
|
||||
【输出格式】
|
||||
严格返回 JSON,不要其他内容:
|
||||
{{"message":"回复内容","tool":null,"toolParams":null}}
|
||||
|
||||
tool 可选值:
|
||||
- null:普通对话,不触发工具
|
||||
- "recommend":岗位推荐,toolParams 必须包含 {{"preference":"用户偏好描述"}}
|
||||
- "editPreference":调整偏好,toolParams 为 null
|
||||
|
||||
【规则】
|
||||
1. 只聊求职相关话题,其他话题礼貌拒绝
|
||||
2. 回复简洁,不超过200字
|
||||
3. 用户表达想看岗位、推荐岗位、帮我找工作等意图时,从对话中提取偏好描述,返回 recommend
|
||||
4. 用户表达想改设置、调整偏好、修改意向等意图时,返回 editPreference
|
||||
5. 偏好描述要准确概括用户的岗位偏好,如"更偏技术方向的产品岗"、"大厂优先"
|
||||
6. 如果用户没有明确偏好,preference 填"无特殊偏好"
|
||||
7. 触发工具时(tool 非 null),前端会在消息展示后再打开对应面板/列表,message 只做一句简短的"即将..."提示,不要追问"想怎么调整"、"有什么偏好"等引导用户在对话里继续输入的话
|
||||
- 注意工具调用是置后的:message 先展示给用户,面板/列表随后打开,所以用将来时态,不要用"已为你..."这种完成时
|
||||
- recommend 示例:"好的,正在为你匹配合适的岗位"
|
||||
- editPreference 示例:"好的,为你打开偏好设置"
|
||||
"""
|
||||
"""求职助手 Agent 岗位简历优化 Prompt"""
|
||||
|
||||
|
||||
# ===== 岗位简历优化 Prompt =====
|
||||
|
||||
@@ -24,8 +24,6 @@ class SkillGapModel:
|
||||
|
||||
class JobAgentModel:
|
||||
"""求职助手Agent模块"""
|
||||
# 多轮对话:理解用户求职意图,返回结构化回复(message+tool调用)
|
||||
CHAT = LLM.DOUBAO_PRO_32K.create(temperature=0.7)
|
||||
# 岗位简历-summary优化:针对具体岗位JD优化个人概述
|
||||
SUMMARY = LLM.DEEPSEEK_V4_FLASH.create(temperature=0.3)
|
||||
# 岗位简历-经历优化:针对具体岗位JD优化单条经历描述
|
||||
|
||||
@@ -1,30 +1,16 @@
|
||||
"""求职助手 Agent 对话接口"""
|
||||
"""求职助手 Agent 岗位简历优化接口"""
|
||||
|
||||
from fastapi import APIRouter, Depends
|
||||
|
||||
from app.core.auth import func_permission
|
||||
from app.core.context import RequestContext
|
||||
from app.core.database import get_db
|
||||
from app.schemas.job_agent_chat import JobAgentChatParam, OptimizeResumeParam
|
||||
from app.schemas.job_agent_chat import OptimizeResumeParam
|
||||
from app.services.job_agent_chat_service import JobAgentChatService
|
||||
|
||||
router = APIRouter(prefix="/job-agent", tags=["求职助手agent"])
|
||||
|
||||
|
||||
@router.post("/chat", summary="求职助手对话")
|
||||
async def chat(param: JobAgentChatParam):
|
||||
"""求职助手对话,根据用户简历和意向提供求职建议、触发岗位推荐或偏好调整"""
|
||||
user_id = RequestContext.user_id.get()
|
||||
async for session in get_db():
|
||||
service = JobAgentChatService(session)
|
||||
result = await service.chat(
|
||||
user_id, param.resume_id, param.message,
|
||||
[msg.model_dump() for msg in param.history],
|
||||
param.job_categories, param.regions, param.industries,
|
||||
)
|
||||
return result
|
||||
|
||||
|
||||
@router.post("/optimize-resume", summary="针对岗位优化简历")
|
||||
async def optimize_resume(param: OptimizeResumeParam, _: None = Depends(func_permission("resume_custom"))):
|
||||
"""根据目标岗位,AI并发优化简历(summary + 5张子表经历),存Redis并返回"""
|
||||
|
||||
@@ -1,41 +1,11 @@
|
||||
"""求职助手 Agent 对话 Schema
|
||||
"""求职助手 Agent 岗位简历优化 Schema
|
||||
|
||||
请求参数 Param、响应 Dto。
|
||||
请求参数 Param。
|
||||
字段命名使用 camelCase alias,与前端 JSON 对齐。
|
||||
"""
|
||||
|
||||
from typing import Literal
|
||||
|
||||
from pydantic import BaseModel, Field
|
||||
|
||||
ChatRole = Literal["user", "assistant"]
|
||||
|
||||
|
||||
class ChatMessage(BaseModel):
|
||||
role: ChatRole
|
||||
content: str
|
||||
|
||||
|
||||
class JobAgentChatParam(BaseModel):
|
||||
message: str = Field(..., description="用户输入的消息")
|
||||
resume_id: int = Field(..., alias="resumeId", description="简历ID")
|
||||
history: list[ChatMessage] = Field(default_factory=list, description="对话历史")
|
||||
job_categories: list[str] = Field(default_factory=list, alias="jobCategories", description="意向岗位类型名称")
|
||||
regions: list[str] = Field(default_factory=list, alias="regions", description="意向城市名称")
|
||||
industries: list[str] = Field(default_factory=list, alias="industries", description="意向行业名称")
|
||||
|
||||
|
||||
class ToolParams(BaseModel):
|
||||
preference: str = Field(default="", description="用户岗位偏好描述")
|
||||
|
||||
|
||||
class JobAgentChatDto(BaseModel):
|
||||
message: str = Field(..., description="AI回复文本,不超过200字")
|
||||
tool: str | None = Field(default=None, description="前端需执行的工具:recommend / editPreference / null")
|
||||
tool_params: ToolParams | None = Field(default=None, alias="toolParams", description="工具参数")
|
||||
|
||||
model_config = {"populate_by_name": True}
|
||||
|
||||
|
||||
class OptimizeResumeParam(BaseModel):
|
||||
resume_id: int = Field(..., alias="resumeId", description="简历ID")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""求职助手 Agent 对话 + 岗位简历优化 Service
|
||||
"""求职助手 Agent 岗位简历优化 Service
|
||||
|
||||
主要功能:查询简历数据,调用 AI 模块完成对话;针对岗位并发优化简历。
|
||||
依赖:resume_loader(简历统一查询)、customize_resume_store(定制简历存取+构建)、job_agent.chat AI 模块、job_agent.resume_optimizer(岗位简历优化)
|
||||
主要功能:针对岗位并发优化简历。
|
||||
依赖:resume_loader(简历统一查询)、customize_resume_store(定制简历存取+构建)、job_agent.resume_optimizer(岗位简历优化)
|
||||
使用表:bg_user_resume + 5张子表(通过 resume_loader 查询)、bg_job(查岗位)
|
||||
"""
|
||||
|
||||
@@ -12,12 +12,11 @@ import time
|
||||
from sqlalchemy import select
|
||||
from sqlalchemy.ext.asyncio import AsyncSession
|
||||
|
||||
from app.ai.job_agent.chat import agent_chat
|
||||
from app.ai.job_agent.resume_optimizer import optimize_summary, optimize_experience_record
|
||||
from app.core.logger import log
|
||||
from app.models.job import Job
|
||||
from app.schemas.customize_resume import CustomizeResume, Education, Work, Internship, Project, Competition
|
||||
from app.services.resume_loader import ResumeDetail, load_resume_detail
|
||||
from app.services.resume_loader import load_resume_detail
|
||||
from app.services import customize_resume_store
|
||||
|
||||
|
||||
@@ -26,51 +25,6 @@ class JobAgentChatService:
|
||||
def __init__(self, session: AsyncSession):
|
||||
self.session = session
|
||||
|
||||
async def chat(self, user_id: int, resume_id: int, message: str,
|
||||
history: list[dict], job_categories: list[str],
|
||||
regions: list[str], industries: list[str]) -> dict:
|
||||
"""求职助手对话:查简历 → 序列化 → 调 AI 模块"""
|
||||
detail = await load_resume_detail(self.session, resume_id, user_id)
|
||||
resume_text = self._build_resume_text(detail)
|
||||
return await agent_chat(resume_text, message, history, job_categories, regions, industries)
|
||||
|
||||
@staticmethod
|
||||
def _build_resume_text(detail: ResumeDetail) -> str:
|
||||
"""将简历数据序列化为文本供 AI 使用"""
|
||||
resume = detail.resume
|
||||
parts = []
|
||||
if resume.name:
|
||||
parts.append(f"姓名:{resume.name}")
|
||||
if resume.target_position:
|
||||
parts.append(f"目标岗位:{resume.target_position}")
|
||||
if resume.skills:
|
||||
parts.append(f"技能:{'、'.join(resume.skills)}")
|
||||
if resume.certificates:
|
||||
parts.append(f"证书:{'、'.join(resume.certificates)}")
|
||||
if resume.summary:
|
||||
parts.append(f"个人概述:{resume.summary}")
|
||||
if detail.education:
|
||||
parts.append("教育经历:")
|
||||
for r in detail.education:
|
||||
parts.append(f" - {r.school or ''} {r.major or ''} {r.degree or ''}")
|
||||
if detail.work:
|
||||
parts.append("工作经历:")
|
||||
for r in detail.work:
|
||||
parts.append(f" - {r.company_name or ''} {r.position or ''}")
|
||||
if detail.internship:
|
||||
parts.append("实习经历:")
|
||||
for r in detail.internship:
|
||||
parts.append(f" - {r.company_name or ''} {r.position or ''}")
|
||||
if detail.project:
|
||||
parts.append("项目经历:")
|
||||
for r in detail.project:
|
||||
parts.append(f" - {r.project_name or ''} {r.role or ''}")
|
||||
if detail.competition:
|
||||
parts.append("竞赛经历:")
|
||||
for r in detail.competition:
|
||||
parts.append(f" - {r.competition_name or ''} {r.award or ''}")
|
||||
return "\n".join(parts) if parts else "暂无简历信息"
|
||||
|
||||
async def optimize_resume(self, user_id: int, resume_id: int, job_id: int) -> dict:
|
||||
"""针对岗位优化简历:查简历+岗位 → 构建定制简历 → 按单条记录并发AI优化 → 存数据库 → 返回"""
|
||||
# 1. 查简历 + 岗位
|
||||
|
||||
Reference in New Issue
Block a user