generated from kgod/ai-review-template
feat: builder 简历生成 + 轻度优化 + 简历导入交付副本
自内部仓库剥离深度优化与 RAG 知识库后的交付版本: - Builder 对话式简历生成(FSM + 意图路由 LLM 兜底增强) - 条目级轻度优化:事实覆盖门禁 + STAR/bullet 修复链,功能/简介/成果与技术栈同级保护 - 简历导入:DOCX/PDF 解析、结构归一、手机号脱敏 - PostgreSQL 运行时 + Alembic 迁移链 Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,81 @@
|
||||
"""Grounded target-position recommendations for users who are still exploring."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Any, Protocol
|
||||
|
||||
from pydantic import Field
|
||||
|
||||
from .llm_services import OpenAICompatibleStructuredClient, StrictSchema
|
||||
from .settings import Settings
|
||||
|
||||
|
||||
class PositionSuggestion(StrictSchema):
|
||||
title: str = Field(min_length=1, max_length=32)
|
||||
reason: str = Field(min_length=1, max_length=100)
|
||||
|
||||
|
||||
class TargetPositionSuggestionOutput(StrictSchema):
|
||||
positions: list[PositionSuggestion] = Field(min_length=3, max_length=5)
|
||||
|
||||
|
||||
class TargetPositionSuggester(Protocol):
|
||||
def suggest(self, *, major: str, job_type: str | None, interests: str | None) -> list[dict[str, str]]: ...
|
||||
|
||||
|
||||
class RuleBasedTargetPositionSuggester:
|
||||
def suggest(self, *, major: str, job_type: str | None, interests: str | None) -> list[dict[str, str]]:
|
||||
text = f"{major} {interests or ''}".casefold()
|
||||
if any(token in text for token in ("计算机", "软件", "网络", "data", "人工智能", "ai")):
|
||||
titles = ["后端工程师", "前端工程师", "测试开发工程师", "数据分析师", "产品经理"]
|
||||
elif any(token in text for token in ("设计", "视觉", "艺术", "media")):
|
||||
titles = ["UI/UX 设计师", "视觉设计师", "产品经理", "新媒体运营", "品牌营销专员"]
|
||||
elif any(token in text for token in ("财务", "会计", "金融", "经济")):
|
||||
titles = ["财务分析师", "审计助理", "数据分析师", "商业分析师", "产品运营"]
|
||||
else:
|
||||
titles = ["产品运营", "项目助理", "数据分析师", "市场专员", "客户成功专员"]
|
||||
suffix = "实习岗位" if job_type == "internship" else "校招/社招岗位"
|
||||
return [
|
||||
{"title": title, "reason": f"结合{major}及已填写方向的{suffix}建议"}
|
||||
for title in titles
|
||||
]
|
||||
|
||||
|
||||
class OpenAITargetPositionSuggester:
|
||||
def __init__(self, completion: OpenAICompatibleStructuredClient) -> None:
|
||||
self.completion = completion
|
||||
|
||||
def suggest(self, *, major: str, job_type: str | None, interests: str | None) -> list[dict[str, str]]:
|
||||
output = self.completion.complete(
|
||||
schema=TargetPositionSuggestionOutput,
|
||||
schema_name="target_position_suggestions",
|
||||
system_prompt=(
|
||||
"Recommend 3 to 5 realistic Chinese job titles from the user's major and optional interests. "
|
||||
"These are exploratory suggestions, not facts about the user. "
|
||||
"When interests explicitly name a role or domain, put that exact role/domain first and prioritize its direct adjacent roles; "
|
||||
"do not replace an explicit technical interest such as 后端开发 with unrelated general roles. "
|
||||
"Do not claim skills, experience, qualifications, or hiring outcomes."
|
||||
),
|
||||
payload={"major": major, "job_type": job_type, "interests": interests},
|
||||
)
|
||||
return [item.model_dump() for item in output.positions]
|
||||
|
||||
|
||||
class FallbackTargetPositionSuggester:
|
||||
def __init__(self, primary: TargetPositionSuggester, fallback: TargetPositionSuggester) -> None:
|
||||
self.primary = primary
|
||||
self.fallback = fallback
|
||||
|
||||
def suggest(self, *, major: str, job_type: str | None, interests: str | None) -> list[dict[str, str]]:
|
||||
try:
|
||||
return self.primary.suggest(major=major, job_type=job_type, interests=interests)
|
||||
except Exception:
|
||||
return self.fallback.suggest(major=major, job_type=job_type, interests=interests)
|
||||
|
||||
|
||||
def build_target_position_suggester(settings: Settings, client: Any | None = None) -> TargetPositionSuggester:
|
||||
rules = RuleBasedTargetPositionSuggester()
|
||||
if not settings.use_openai:
|
||||
return rules
|
||||
primary = OpenAITargetPositionSuggester(OpenAICompatibleStructuredClient(settings, client))
|
||||
return FallbackTargetPositionSuggester(primary, rules) if settings.fallback_to_rules else primary
|
||||
Reference in New Issue
Block a user