feat: cleanup reply flow messages + AI comparison explanation - Track extra_msg_ids in conversation for code block messages - _cleanup_conv_messages: delete all intermediate messages on cancel/send - Cancel/send/edit all clean up extra messages before restoring summary view - AI prompts now return 'comparison' field with Chinese explanation of differences - _show_ai_suggestions displays comparison text above button list - generate_more_replies returns {suggestions, comparison}
This commit is contained in:
@@ -93,7 +93,8 @@ MORE_REPLIES_PROMPT_SHORT = """你是一个邮件回复助手。根据以下邮
|
|||||||
{"text": "回复内容"},
|
{"text": "回复内容"},
|
||||||
{"text": "回复内容"},
|
{"text": "回复内容"},
|
||||||
{"text": "回复内容"}
|
{"text": "回复内容"}
|
||||||
]
|
],
|
||||||
|
"comparison": "用中文简要说明这三个回复的区别和适用场景,帮助用户选择。例如:回复1偏确认型,回复2带感谢,回复3更详细。"
|
||||||
}
|
}
|
||||||
每条回复控制在 50 字以内。
|
每条回复控制在 50 字以内。
|
||||||
只返回 JSON,不要包含任何其他文字。"""
|
只返回 JSON,不要包含任何其他文字。"""
|
||||||
@@ -113,7 +114,8 @@ MORE_REPLIES_PROMPT_FULL = """你是一个邮件回复助手。根据以下邮
|
|||||||
{"text": "回复内容"},
|
{"text": "回复内容"},
|
||||||
{"text": "回复内容"},
|
{"text": "回复内容"},
|
||||||
{"text": "回复内容"}
|
{"text": "回复内容"}
|
||||||
]
|
],
|
||||||
|
"comparison": "用中文简要说明这三个回复的区别和适用场景,帮助用户选择。例如:回复1偏正式确认,回复2表达感谢并跟进,回复3提供详细方案。"
|
||||||
}
|
}
|
||||||
每条回复控制在 300 字以内。
|
每条回复控制在 300 字以内。
|
||||||
只返回 JSON,不要包含任何其他文字。"""
|
只返回 JSON,不要包含任何其他文字。"""
|
||||||
@@ -141,7 +143,10 @@ def generate_more_replies(ai_cfg: AIConfig, sender: str, subject: str, body: str
|
|||||||
"response_format": {"type": "json_object"},
|
"response_format": {"type": "json_object"},
|
||||||
}
|
}
|
||||||
result = _call_deepseek(ai_cfg, payload)
|
result = _call_deepseek(ai_cfg, payload)
|
||||||
return result.get("suggestions", [])
|
return {
|
||||||
|
"suggestions": result.get("suggestions", []),
|
||||||
|
"comparison": result.get("comparison", ""),
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def _call_deepseek(ai_cfg: AIConfig, payload: dict) -> dict:
|
def _call_deepseek(ai_cfg: AIConfig, payload: dict) -> dict:
|
||||||
|
|||||||
@@ -272,6 +272,17 @@ def _tg_req(bot_token: str, method: str, payload: dict) -> dict:
|
|||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
def _cleanup_conv_messages(bot_token: str, chat_id: int, conv: dict):
|
||||||
|
"""清理回复流程中产生的所有中间消息"""
|
||||||
|
chat_id_str = str(chat_id)
|
||||||
|
for key in ("prompt_msg_id",):
|
||||||
|
mid = conv.get(key)
|
||||||
|
if mid:
|
||||||
|
delete_message(bot_token, chat_id_str, mid)
|
||||||
|
for mid in conv.get("extra_msg_ids", []):
|
||||||
|
delete_message(bot_token, chat_id_str, mid)
|
||||||
|
|
||||||
|
|
||||||
# ── Update handling ─────────────────────────────────────
|
# ── Update handling ─────────────────────────────────────
|
||||||
|
|
||||||
def _do_regen(bot_token: str, cfg: Config, chat_id: int, msg_id: int, ctx: dict, conv: dict):
|
def _do_regen(bot_token: str, cfg: Config, chat_id: int, msg_id: int, ctx: dict, conv: dict):
|
||||||
@@ -281,19 +292,22 @@ def _do_regen(bot_token: str, cfg: Config, chat_id: int, msg_id: int, ctx: dict,
|
|||||||
return
|
return
|
||||||
style = conv.get("reply_style", "short")
|
style = conv.get("reply_style", "short")
|
||||||
try:
|
try:
|
||||||
new_suggestions = generate_more_replies(
|
result = generate_more_replies(
|
||||||
user.ai, ctx["sender"], ctx["subject"], ctx["original_body"],
|
user.ai, ctx["sender"], ctx["subject"], ctx["original_body"],
|
||||||
conv.get("all_suggestions", []), style=style,
|
conv.get("all_suggestions", []), style=style,
|
||||||
)
|
)
|
||||||
|
new_suggestions = result["suggestions"]
|
||||||
|
comparison = result.get("comparison", "")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
send_text(bot_token, str(chat_id), f"生成失败: {e}")
|
send_text(bot_token, str(chat_id), f"生成失败: {e}")
|
||||||
return
|
return
|
||||||
if new_suggestions:
|
if new_suggestions:
|
||||||
conv["ai_suggestions"] = new_suggestions
|
conv["ai_suggestions"] = new_suggestions
|
||||||
conv["all_suggestions"].extend(new_suggestions)
|
conv["all_suggestions"].extend(new_suggestions)
|
||||||
|
conv["comparison"] = comparison
|
||||||
save_conversation(chat_id, conv)
|
save_conversation(chat_id, conv)
|
||||||
logger.info("[ai_regen] 新生成 %d 条回复 (style=%s)", len(new_suggestions), style)
|
logger.info("[ai_regen] 新生成 %d 条回复 (style=%s)", len(new_suggestions), style)
|
||||||
_show_ai_suggestions(bot_token, chat_id, msg_id, new_suggestions)
|
_show_ai_suggestions(bot_token, chat_id, msg_id, new_suggestions, comparison)
|
||||||
|
|
||||||
|
|
||||||
def _do_ai_hint(bot_token: str, cfg: Config, chat_id: int, conv: dict, ctx: dict, hint: str):
|
def _do_ai_hint(bot_token: str, cfg: Config, chat_id: int, conv: dict, ctx: dict, hint: str):
|
||||||
@@ -303,10 +317,12 @@ def _do_ai_hint(bot_token: str, cfg: Config, chat_id: int, conv: dict, ctx: dict
|
|||||||
return
|
return
|
||||||
style = conv.get("reply_style", "short")
|
style = conv.get("reply_style", "short")
|
||||||
try:
|
try:
|
||||||
new_suggestions = generate_more_replies(
|
result = generate_more_replies(
|
||||||
user.ai, ctx["sender"], ctx["subject"], ctx["original_body"],
|
user.ai, ctx["sender"], ctx["subject"], ctx["original_body"],
|
||||||
conv.get("all_suggestions", []), user_hint=hint, style=style,
|
conv.get("all_suggestions", []), user_hint=hint, style=style,
|
||||||
)
|
)
|
||||||
|
new_suggestions = result["suggestions"]
|
||||||
|
comparison = result.get("comparison", "")
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
send_text(bot_token, str(chat_id), f"生成失败: {e}")
|
send_text(bot_token, str(chat_id), f"生成失败: {e}")
|
||||||
delete_conversation(chat_id)
|
delete_conversation(chat_id)
|
||||||
@@ -314,11 +330,12 @@ def _do_ai_hint(bot_token: str, cfg: Config, chat_id: int, conv: dict, ctx: dict
|
|||||||
if new_suggestions:
|
if new_suggestions:
|
||||||
conv["ai_suggestions"] = new_suggestions
|
conv["ai_suggestions"] = new_suggestions
|
||||||
conv["all_suggestions"].extend(new_suggestions)
|
conv["all_suggestions"].extend(new_suggestions)
|
||||||
|
conv["comparison"] = comparison
|
||||||
conv["state"] = "ai_reply_selection"
|
conv["state"] = "ai_reply_selection"
|
||||||
conv.pop("prompt_msg_id", None)
|
conv.pop("prompt_msg_id", None)
|
||||||
save_conversation(chat_id, conv)
|
save_conversation(chat_id, conv)
|
||||||
logger.info("[ai_hint] 根据提示生成 %d 条新回复", len(new_suggestions))
|
logger.info("[ai_hint] 根据提示生成 %d 条新回复", len(new_suggestions))
|
||||||
_show_ai_suggestions(bot_token, chat_id, conv["summary_msg_id"], new_suggestions)
|
_show_ai_suggestions(bot_token, chat_id, conv["summary_msg_id"], new_suggestions, comparison)
|
||||||
|
|
||||||
|
|
||||||
def _handle_update(bot_token: str, cfg: Config, update: dict):
|
def _handle_update(bot_token: str, cfg: Config, update: dict):
|
||||||
@@ -404,9 +421,12 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict):
|
|||||||
return
|
return
|
||||||
sel_text = suggestions[idx]["text"]
|
sel_text = suggestions[idx]["text"]
|
||||||
logger.info(" 直接发送回复 #%d: %s", idx, sel_text[:60])
|
logger.info(" 直接发送回复 #%d: %s", idx, sel_text[:60])
|
||||||
|
# 清理中间消息
|
||||||
|
_cleanup_conv_messages(bot_token, chat_id, conv)
|
||||||
_do_send_reply(bot_token, cfg, chat_id, msg_id, ctx, sel_text)
|
_do_send_reply(bot_token, cfg, chat_id, msg_id, ctx, sel_text)
|
||||||
send_text(bot_token, str(chat_id), f"✅ 回复已发送")
|
send_text(bot_token, str(chat_id), f"✅ 回复已发送")
|
||||||
delete_conversation(chat_id)
|
delete_conversation(chat_id)
|
||||||
|
# 恢复摘要视图
|
||||||
can_reply = ctx.get("can_reply", True)
|
can_reply = ctx.get("can_reply", True)
|
||||||
is_promotion = ctx.get("summary_data", {}).get("category") == "promotion"
|
is_promotion = ctx.get("summary_data", {}).get("category") == "promotion"
|
||||||
links = ctx.get("summary_data", {}).get("links", [])
|
links = ctx.get("summary_data", {}).get("links", [])
|
||||||
@@ -430,11 +450,12 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict):
|
|||||||
|
|
||||||
# 用代码块显示,点击可复制(MarkdownV2)
|
# 用代码块显示,点击可复制(MarkdownV2)
|
||||||
escaped = sel_text.replace("\\", "\\\\").replace("`", "\\`")
|
escaped = sel_text.replace("\\", "\\\\").replace("`", "\\`")
|
||||||
_tg_req(bot_token, "sendMessage", {
|
copy_result = _tg_req(bot_token, "sendMessage", {
|
||||||
"chat_id": str(chat_id),
|
"chat_id": str(chat_id),
|
||||||
"text": f"*建议回复(点击代码块复制):*\n`{escaped}`",
|
"text": f"*建议回复(点击代码块复制):*\n`{escaped}`",
|
||||||
"parse_mode": "MarkdownV2",
|
"parse_mode": "MarkdownV2",
|
||||||
})
|
})
|
||||||
|
copy_msg_id = copy_result["result"]["message_id"]
|
||||||
|
|
||||||
# 进入 awaiting_reply 状态,用户可编辑后发送
|
# 进入 awaiting_reply 状态,用户可编辑后发送
|
||||||
prompt_msg_id = send_text(
|
prompt_msg_id = send_text(
|
||||||
@@ -446,6 +467,7 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict):
|
|||||||
conv["summary_msg_id"] = msg_id
|
conv["summary_msg_id"] = msg_id
|
||||||
conv["prompt_msg_id"] = prompt_msg_id
|
conv["prompt_msg_id"] = prompt_msg_id
|
||||||
conv["draft_text"] = sel_text
|
conv["draft_text"] = sel_text
|
||||||
|
conv["extra_msg_ids"] = [copy_msg_id]
|
||||||
save_conversation(chat_id, conv)
|
save_conversation(chat_id, conv)
|
||||||
logger.info(" 建议已展示,等待用户编辑发送")
|
logger.info(" 建议已展示,等待用户编辑发送")
|
||||||
|
|
||||||
@@ -475,6 +497,9 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict):
|
|||||||
logger.info(" 等待用户输入 AI 提示")
|
logger.info(" 等待用户输入 AI 提示")
|
||||||
|
|
||||||
elif action == CALLBACK_CANCEL:
|
elif action == CALLBACK_CANCEL:
|
||||||
|
conv = load_conversation(chat_id)
|
||||||
|
if conv:
|
||||||
|
_cleanup_conv_messages(bot_token, chat_id, conv)
|
||||||
delete_conversation(chat_id)
|
delete_conversation(chat_id)
|
||||||
# 清除回复建议缓存,重新选择时会重新生成
|
# 清除回复建议缓存,重新选择时会重新生成
|
||||||
if ctx:
|
if ctx:
|
||||||
@@ -549,12 +574,17 @@ def _handle_message(bot_token: str, cfg: Config, msg: dict):
|
|||||||
delete_message(bot_token, chat_id_str, user_msg_id)
|
delete_message(bot_token, chat_id_str, user_msg_id)
|
||||||
if prompt_msg_id:
|
if prompt_msg_id:
|
||||||
delete_message(bot_token, chat_id_str, prompt_msg_id)
|
delete_message(bot_token, chat_id_str, prompt_msg_id)
|
||||||
|
for mid in conv.get("extra_msg_ids", []):
|
||||||
|
delete_message(bot_token, chat_id_str, mid)
|
||||||
logger.info(" 上下文已丢失,清理")
|
logger.info(" 上下文已丢失,清理")
|
||||||
return
|
return
|
||||||
|
|
||||||
delete_message(bot_token, chat_id_str, user_msg_id)
|
delete_message(bot_token, chat_id_str, user_msg_id)
|
||||||
if prompt_msg_id:
|
if prompt_msg_id:
|
||||||
delete_message(bot_token, chat_id_str, prompt_msg_id)
|
delete_message(bot_token, chat_id_str, prompt_msg_id)
|
||||||
|
# 清理 extra 消息(代码块等)
|
||||||
|
for mid in conv.get("extra_msg_ids", []):
|
||||||
|
delete_message(bot_token, chat_id_str, mid)
|
||||||
|
|
||||||
# 合并累积的附件
|
# 合并累积的附件
|
||||||
all_attachments = conv.get("pending_attachments", [])
|
all_attachments = conv.get("pending_attachments", [])
|
||||||
@@ -590,8 +620,11 @@ def _handle_message(bot_token: str, cfg: Config, msg: dict):
|
|||||||
|
|
||||||
|
|
||||||
def _show_ai_suggestions(bot_token: str, chat_id: int, msg_id: int,
|
def _show_ai_suggestions(bot_token: str, chat_id: int, msg_id: int,
|
||||||
suggestions: list):
|
suggestions: list, comparison: str = ""):
|
||||||
text = "*AI 建议回复,请选择:*\n\n"
|
text = ""
|
||||||
|
if comparison:
|
||||||
|
text = f"*💡 三个回复的区别:*\n{_escape(comparison)}\n\n"
|
||||||
|
text += "*AI 建议回复,请选择:*"
|
||||||
for i, s in enumerate(suggestions, 1):
|
for i, s in enumerate(suggestions, 1):
|
||||||
text += f"{i}\\. {_escape(s['text'])}\n"
|
text += f"{i}\\. {_escape(s['text'])}\n"
|
||||||
_tg_req(bot_token, "editMessageText", {
|
_tg_req(bot_token, "editMessageText", {
|
||||||
|
|||||||
Reference in New Issue
Block a user