feat: initialize resume agent with OfferPai sync

This commit is contained in:
Codex
2026-08-05 20:20:18 +08:00
commit 61ec750031
197 changed files with 33291 additions and 0 deletions
@@ -0,0 +1,114 @@
"""Rule predicates for Builder messages (legacy keyword routing, kept as fallback)."""
from __future__ import annotations
import re
from typing import Any
from .constants import (
GAP_PROMPTS,
IDENTITY_CHANGE_TERMS,
MAX_GAP_DIMENSIONS,
MAX_GAPS_PER_TURN,
SECTION_GAP_DIMENSIONS,
SECTION_KEYWORDS,
)
def _requests_new_entry(content: str) -> bool:
normalized = content.casefold()
return any(token in normalized for token in ("新增", "新建", "再添加", "再补充", "另一段", "另一个", "第二段", "写一段"))
def _is_revision_instruction(content: str) -> bool:
normalized = re.sub(r"[\s,,。;;!?]", "", content.casefold())
correction_terms = ("不要跳过", "没有跳过", "没说要跳过", "没有说要跳过", "不是这个意思", "保留原文", "保留这段", "不要删除", "不要删", "无需跳过")
return any(term in normalized for term in correction_terms)
def _requested_section(content: str) -> str | None:
normalized = content.casefold()
if not any(token in normalized for token in ("补充", "新增", "添加", "新建", "写一段")):
return None
return next((kind for kind, tokens in SECTION_KEYWORDS.items() if any(token in normalized for token in tokens)), None)
def _looks_like_recent_continuation(state: dict[str, Any], content: str) -> bool:
if not isinstance(state.get("last_confirmed_entry"), dict):
return False
normalized = re.sub(r"[,。!!?\s]", "", content.casefold())
if len(normalized) < 4:
return False
if normalized in {"可以", "好的", "继续", "没问题", "谢谢", "知道了"}:
return False
continuation_terms = ("对了", "", "另外", "前面", "之前", "补充", "获得", "拿过", "拿到")
return any(term in normalized for term in continuation_terms) and not any(
token in normalized for token in ("新增", "新建", "写一段", "另一段", "别的经历")
)
def _matching_entries(resume_content: dict[str, Any], content: str) -> list[tuple[dict[str, Any], dict[str, Any]]]:
normalized = content.casefold()
if not any(token in normalized for token in ("修改", "编辑", "调整", "补充")):
return []
all_entries = [(section, entry) for section in resume_content.get("sections") or [] if isinstance(section, dict) for entry in section.get("items") or [] if isinstance(entry, dict)]
named = [
pair for pair in all_entries
if any(str(pair[1].get(key) or "").strip().casefold() in normalized for key in ("company", "project_name", "school", "organization", "position", "role") if str(pair[1].get(key) or "").strip())
]
if named:
return named
kinds = [kind for kind, tokens in SECTION_KEYWORDS.items() if any(token in normalized for token in tokens)]
return [pair for pair in all_entries if str(pair[0].get("kind") or "") in kinds]
def _entry_by_id(resume_content: dict[str, Any], entry_id: str) -> tuple[dict[str, Any], dict[str, Any]] | None:
for section in resume_content.get("sections") or []:
if not isinstance(section, dict):
continue
for entry in section.get("items") or []:
if isinstance(entry, dict) and entry.get("id") == entry_id:
return section, entry
return None
def _requests_identity_change(content: str) -> bool:
normalized = content.casefold()
return any(token in normalized for token in ("", "修改", "变更", "")) and any(term in normalized for term in IDENTITY_CHANGE_TERMS)
def _is_no_information_reply(content: str) -> bool:
normalized = re.sub(r"[\s,,。;;!?]", "", content.casefold())
return normalized in {
"没有", "", "", "暂无", "没了", "没有了", "不清楚", "不确定",
"跳过", "先跳过", "跳过吧", "暂时跳过", "略过", "不用了", "先不用", "不需要", "暂时不用", "以后再说", "再说吧",
}
def _dimension_present(dimension: str, entry: dict[str, Any]) -> bool:
# Identity fields such as dates are not evidence of an experience outcome or scale.
text = str(entry.get("description") or "").casefold()
patterns = {
"academic_result": r"gpa|均分|成绩|绩点|排名|top\s*\d+|前\s*\d+|奖学金|荣誉|获奖|奖项",
"practice_evidence": r"课程项目|课程设计|项目|竞赛|实验室|实践|实训|研究|论文",
"contribution_method": r"负责|主导|参与|设计|开发|实现|搭建|分析|调研|协调|测试|维护|优化|使用|通过|python|sql|java|vue|react|excel",
"delivery_or_outcome": r"交付|上线|发布|落地|完成|产出|验收|结果|成果|提升|降低|减少|增长|获得|达成",
"scale_or_metric": r"\d|百分比|%|人|次|天|周|月|小时|万元|万|千|覆盖|规模|效率|质量",
"responsibility_execution": r"负责|主导|参与|组织|策划|执行|协调|运营|宣传|招募|管理",
"scale_or_result": r"\d|人|次|场|覆盖|规模|参与|报名|增长|完成|结果|成果|获奖",
}
return bool(re.search(patterns[dimension], text, flags=re.IGNORECASE))
def _next_gap_dimensions(section: str, entry: dict[str, Any], state: dict[str, Any]) -> list[str]:
asked = set(state["asked"])
skipped = set(state["skipped"])
if len(asked) >= MAX_GAP_DIMENSIONS:
return []
candidates = [dimension for dimension in SECTION_GAP_DIMENSIONS.get(section, ()) if dimension not in skipped and not _dimension_present(dimension, entry)]
remaining_capacity = MAX_GAP_DIMENSIONS - len(asked)
return candidates[: min(MAX_GAPS_PER_TURN, remaining_capacity)]
def _gap_prompt(dimensions: list[str]) -> str:
return "\n".join(GAP_PROMPTS[dimension] for dimension in dimensions)