Files
resume-agent/backend/app/builder_conversation/summary_regen.py
T

96 lines
4.2 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.
"""Builder 个人总结再生入口:finish 按钮与对话指令统一走"显式请求即重生成"。
agent 侧只在总结缺失或 stale 时才生成(避免自动覆盖用户手工文本);用户的显式
请求必须先把现有总结标记为 stale,让既有闸门放行。对话路径无法直接写简历
add_message 不合并 resume_content),所以走"生成候选 → ChoiceChips 确认 →
组件事件写入"的既有 Builder 模式。
"""
from __future__ import annotations
import re
from typing import Any
from ..fsm import FSMError, Transition, assistant_turn, component
from ..models import ComposerMode, Stage
from ..resume_document import mark_profile_summary_stale, set_generated_profile_summary
from .constants import u
from .state import _set_stream_phases, ensure_builder_state
SUMMARY_APPLY_MODULE = "builder_summary_apply"
_REGEN_VERB = re.compile(r"(重新|再次|再来|重写|更新|刷新|换|再).{0,6}总结")
_ASK_GENERATE = re.compile(r"(?:帮我|请|我要|我想|给我).{0,6}生成.{0,4}总结|^生成.{0,4}总结")
def requests_summary_regen(content: str) -> bool:
""""重新生成个人总结"类指令;提供总结原文或陈述事实的消息不算。"""
normalized = re.sub(r"[\s,,。;;!?]", "", content.casefold())
if "总结" not in normalized or "总结是" in normalized:
return False
return bool(_REGEN_VERB.search(normalized) or _ASK_GENERATE.search(normalized))
def finish_transition(profile: dict[str, Any], resume_content: dict[str, Any]) -> Transition:
""""完成并生成个人总结":已有总结也强制重生成(先标记 stale 放行闸门)。"""
_set_stream_phases(profile, "saving")
return Transition(
Stage.BUILDER_CONVERSATION,
profile,
assistant_turn(
u("好的,已根据当前简历预览信息生成个人总结,内容仍可在右侧预览中编辑。"),
[],
mode=ComposerMode.CHAT,
),
resume_content=mark_profile_summary_stale(resume_content),
generate_profile_summary=True,
)
def summary_regen_turn(agent: Any, profile: dict[str, Any], resume_content: dict[str, Any]) -> Transition:
"""对话"重新生成个人总结":立即生成候选文本,确认后经组件事件写入简历。"""
try:
proposal = agent.profile_summary_generator.generate(resume_content)
except Exception:
return Transition(
Stage.BUILDER_CONVERSATION,
profile,
assistant_turn(u("个人总结生成失败,请稍后重试。"), [], mode=ComposerMode.CHAT),
)
ensure_builder_state(profile)["pending_summary_proposal"] = proposal
card = component(
"ChoiceChips",
module=SUMMARY_APPLY_MODULE,
options=[
{"value": "apply", "label": u("写入简历")},
{"value": "dismiss", "label": u("暂不写入")},
],
)
return Transition(
Stage.BUILDER_CONVERSATION,
profile,
assistant_turn(u("好的,已根据当前简历内容重新生成个人总结:\n") + proposal, [card], mode=ComposerMode.CHAT),
)
def apply_summary_proposal(
profile: dict[str, Any], action: str, payload: dict[str, Any], resume_content: dict[str, Any]
) -> Transition:
"""确认卡事件:apply 写入候选总结(随组件事件合并进简历),否则丢弃。"""
if action != "select":
raise FSMError("invalid_builder_choice", "Choose whether to apply the summary", status_code=422)
proposal = str(ensure_builder_state(profile).pop("pending_summary_proposal", "") or "").strip()
if str(payload.get("value") or "") != "apply" or not proposal:
return Transition(
Stage.BUILDER_CONVERSATION,
profile,
assistant_turn(u("好的,保留当前个人总结。"), [], mode=ComposerMode.CHAT),
)
content = set_generated_profile_summary(mark_profile_summary_stale(resume_content), proposal, replace_stale=True)
return Transition(
Stage.BUILDER_CONVERSATION,
profile,
assistant_turn(u("已写入新的个人总结,仍可在右侧预览中编辑。"), [], mode=ComposerMode.CHAT),
lifecycle="confirmed",
resume_content=content,
)