diff --git a/src/ai_client.py b/src/ai_client.py index 8fb7cab..7e6fc0d 100644 --- a/src/ai_client.py +++ b/src/ai_client.py @@ -93,7 +93,8 @@ MORE_REPLIES_PROMPT_SHORT = """你是一个邮件回复助手。根据以下邮 {"text": "回复内容"}, {"text": "回复内容"}, {"text": "回复内容"} - ] + ], + "comparison": "用中文简要说明这三个回复的区别和适用场景,帮助用户选择。例如:回复1偏确认型,回复2带感谢,回复3更详细。" } 每条回复控制在 50 字以内。 只返回 JSON,不要包含任何其他文字。""" @@ -113,7 +114,8 @@ MORE_REPLIES_PROMPT_FULL = """你是一个邮件回复助手。根据以下邮 {"text": "回复内容"}, {"text": "回复内容"}, {"text": "回复内容"} - ] + ], + "comparison": "用中文简要说明这三个回复的区别和适用场景,帮助用户选择。例如:回复1偏正式确认,回复2表达感谢并跟进,回复3提供详细方案。" } 每条回复控制在 300 字以内。 只返回 JSON,不要包含任何其他文字。""" @@ -141,7 +143,10 @@ def generate_more_replies(ai_cfg: AIConfig, sender: str, subject: str, body: str "response_format": {"type": "json_object"}, } 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: diff --git a/src/tg_bot.py b/src/tg_bot.py index 165d721..ca056e2 100644 --- a/src/tg_bot.py +++ b/src/tg_bot.py @@ -272,6 +272,17 @@ def _tg_req(bot_token: str, method: str, payload: dict) -> dict: 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 ───────────────────────────────────── 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 style = conv.get("reply_style", "short") try: - new_suggestions = generate_more_replies( + result = generate_more_replies( user.ai, ctx["sender"], ctx["subject"], ctx["original_body"], conv.get("all_suggestions", []), style=style, ) + new_suggestions = result["suggestions"] + comparison = result.get("comparison", "") except Exception as e: send_text(bot_token, str(chat_id), f"生成失败: {e}") return if new_suggestions: conv["ai_suggestions"] = new_suggestions conv["all_suggestions"].extend(new_suggestions) + conv["comparison"] = comparison save_conversation(chat_id, conv) 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): @@ -303,10 +317,12 @@ def _do_ai_hint(bot_token: str, cfg: Config, chat_id: int, conv: dict, ctx: dict return style = conv.get("reply_style", "short") try: - new_suggestions = generate_more_replies( + result = generate_more_replies( user.ai, ctx["sender"], ctx["subject"], ctx["original_body"], conv.get("all_suggestions", []), user_hint=hint, style=style, ) + new_suggestions = result["suggestions"] + comparison = result.get("comparison", "") except Exception as e: send_text(bot_token, str(chat_id), f"生成失败: {e}") 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: conv["ai_suggestions"] = new_suggestions conv["all_suggestions"].extend(new_suggestions) + conv["comparison"] = comparison conv["state"] = "ai_reply_selection" conv.pop("prompt_msg_id", None) save_conversation(chat_id, conv) 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): @@ -404,9 +421,12 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict): return sel_text = suggestions[idx]["text"] 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) send_text(bot_token, str(chat_id), f"✅ 回复已发送") delete_conversation(chat_id) + # 恢复摘要视图 can_reply = ctx.get("can_reply", True) is_promotion = ctx.get("summary_data", {}).get("category") == "promotion" links = ctx.get("summary_data", {}).get("links", []) @@ -430,11 +450,12 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict): # 用代码块显示,点击可复制(MarkdownV2) escaped = sel_text.replace("\\", "\\\\").replace("`", "\\`") - _tg_req(bot_token, "sendMessage", { + copy_result = _tg_req(bot_token, "sendMessage", { "chat_id": str(chat_id), "text": f"*建议回复(点击代码块复制):*\n`{escaped}`", "parse_mode": "MarkdownV2", }) + copy_msg_id = copy_result["result"]["message_id"] # 进入 awaiting_reply 状态,用户可编辑后发送 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["prompt_msg_id"] = prompt_msg_id conv["draft_text"] = sel_text + conv["extra_msg_ids"] = [copy_msg_id] save_conversation(chat_id, conv) logger.info(" 建议已展示,等待用户编辑发送") @@ -475,6 +497,9 @@ def _handle_callback(bot_token: str, cfg: Config, cb: dict): logger.info(" 等待用户输入 AI 提示") elif action == CALLBACK_CANCEL: + conv = load_conversation(chat_id) + if conv: + _cleanup_conv_messages(bot_token, chat_id, conv) delete_conversation(chat_id) # 清除回复建议缓存,重新选择时会重新生成 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) if 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(" 上下文已丢失,清理") return delete_message(bot_token, chat_id_str, user_msg_id) if 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", []) @@ -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, - suggestions: list): - text = "*AI 建议回复,请选择:*\n\n" + suggestions: list, comparison: str = ""): + text = "" + if comparison: + text = f"*💡 三个回复的区别:*\n{_escape(comparison)}\n\n" + text += "*AI 建议回复,请选择:*" for i, s in enumerate(suggestions, 1): text += f"{i}\\. {_escape(s['text'])}\n" _tg_req(bot_token, "editMessageText", {