import gradio as gr import os import json from typing import TypedDict, List, Dict, Any, Optional from langgraph.graph import StateGraph, START, END # from langchain_openai import ChatOpenAI from langchain_core.messages import HumanMessage, AIMessage # from llama_index.llms.huggingface_api import HuggingFaceInferenceAPI from huggingface_hub import InferenceClient class EmailState(TypedDict): # 正在处理的电子邮件 email: Dict[str, Any] # 包含主题、发件人、正文等。 # 分析与决策 is_spam: Optional[bool] spam_reason: Optional[str] email_category: Optional[str] # 响应生成 draft_response: Optional[str] # 处理元数据 messages: List[Dict[str, Any]] # 跟踪与 LLM 的对话以进行分析 # Initialize our LLM # model = ChatOpenAI(temperature=0) # model = HuggingFaceInferenceAPI(model_name="Qwen/Qwen2.5-Coder-32B-Instruct") hf_token=os.getenv("hf_token") model = InferenceClient( model="Qwen/Qwen2.5-Coder-32B-Instruct", token=hf_token, timeout=30 ) def query_llm(prompt: str): response = model.chat.completions.create(messages=[ { "role": "user", "content": prompt }],temperature=0) return response.choices[0].message def read_email(state: EmailState): """Alfred reads and logs the incoming email""" email = state["email"] # 在这里我们可能会做一些初步的预处理 print(f"Alfred is processing an email from {email['sender']} with subject: {email['subject']}") # 这里不需要更改状态 return {} def classify_email(state: EmailState): """Alfred uses an LLM to determine if the email is spam or legitimate""" email = state["email"] # 为 LLM 准备提示 prompt = f""" As Alfred the butler, analyze this email and determine if it is spam or legitimate. Email: From: {email['sender']} Subject: {email['subject']} Body: {email['body']} First, determine if this email is spam. If it is spam, explain why. If it is legitimate, categorize it (inquiry, complaint, thank you, etc.). Please answer strictly in the following format: Email category: inquiry, complaint, thank you, spam, etc. Is spam: Reason for spam: """ # Call the LLM # messages = [HumanMessage(content=prompt)] response = query_llm(prompt) # 解析响应的简单逻辑(在实际应用中,您需要更强大的解析) response_text = response.content.lower() # print(f"test response_text:{response_text}") is_spam = "is spam: yes" in response_text # print(f"test is_spam:{is_spam and "reasoning:" in response_text}") # print(f"test reasoning:{response_text.split("reasoning:")[1].strip()}") # 如果是垃圾邮件,请提取原因,输出的格式可能不同,需要根据提示词固定格式。 spam_reason = None spam_reason_keyword="reason for spam:" if is_spam and spam_reason_keyword in response_text: spam_reason = response_text.split(spam_reason_keyword)[1].strip() # print(f"test spam_reason:{spam_reason}") # 确定类别是否合法 email_category = None if not is_spam: categories = ["inquiry", "complaint", "thank you", "request", "information"] for category in categories: if category in response_text: email_category = category break # 更新消息以进行追踪 new_messages = state.get("messages", []) + [ {"role": "user", "content": prompt}, {"role": "assistant", "content": response.content} ] # 返回状态更新 return { "is_spam": is_spam, "spam_reason": spam_reason, "email_category": email_category, "messages": new_messages } def route_email(state: EmailState) -> str: """Determine the next step based on spam classification""" # print(f"route_email,state={state}") if state["is_spam"]: return "spam" else: return "legitimate" def handle_spam(state: EmailState): """Alfred discards spam email with a note""" # print(f"handle_spam,state={state}") # print(f"is_spam:{state.get("is_spam","none")}") print(f"Alfred has marked the email as spam. Reason: {state.get("spam_reason","none")}") print("The email has been moved to the spam folder.") # 我们已处理完这封电子邮件 return {} def draft_response(state: EmailState): """Alfred drafts a preliminary response for legitimate emails""" email = state["email"] category = state["email_category"] or "general" # 为 LLM 准备提示词 prompt = f""" As Alfred the butler, draft a polite preliminary response to this email. Email: From: {email['sender']} Subject: {email['subject']} Body: {email['body']} This email has been categorized as: {category} Draft a brief, professional response that Mr. Hugg can review and personalize before sending. """ # Call the LLM # messages = [HumanMessage(content=prompt)] response = query_llm(prompt) # 更新消息以进行追踪 new_messages = state.get("messages", []) + [ {"role": "user", "content": prompt}, {"role": "assistant", "content": response.content} ] # 返回状态更新 return { "draft_response": response.content, "messages": new_messages } def notify_mr_hugg(state: EmailState): """Alfred notifies Mr. Hugg about the email and presents the draft response""" email = state["email"] print("\n" + "="*50) print(f"Sir, you've received an email from {email['sender']}.") print(f"Subject: {email['subject']}") print(f"Category: {state['email_category']}") print("\nI've prepared a draft response for your review:") print("-"*50) print(state["draft_response"]) print("="*50 + "\n") # 我们已处理完这封电子邮件 return {} # 创建 graph email_graph = StateGraph(EmailState) # 添加 nodes email_graph.add_node("read_email", read_email) email_graph.add_node("classify_email", classify_email) email_graph.add_node("handle_spam", handle_spam) email_graph.add_node("draft_response", draft_response) email_graph.add_node("notify_mr_hugg", notify_mr_hugg) # 添加 edges - 定义流程 email_graph.add_edge(START, "read_email") email_graph.add_edge("read_email", "classify_email") # 从 classify_email 添加条件分支 email_graph.add_conditional_edges( "classify_email", route_email, { "spam": "handle_spam", "legitimate": "draft_response" } ) # 添加最后的 edges email_graph.add_edge("handle_spam", END) email_graph.add_edge("draft_response", "notify_mr_hugg") email_graph.add_edge("notify_mr_hugg", END) # 编译 graph compiled_graph = email_graph.compile() # 合法电子邮件示例 legitimate_email = { "sender": "john.smith@example.com", "subject": "Question about your services", "body": "Dear Mr. Hugg, I was referred to you by a colleague and I'm interested in learning more about your consulting services. Could we schedule a call next week? Best regards, John Smith" } # 垃圾邮件示例 spam_email = { "sender": "winner@lottery-intl.com", "subject": "YOU HAVE WON $5,000,000!!!", "body": "CONGRATULATIONS! You have been selected as the winner of our international lottery! To claim your $5,000,000 prize, please send us your bank details and a processing fee of $100." } def classify_email(json_input): email = json.loads(json_input) result = compiled_graph.invoke({ "email": email, "is_spam": None, "spam_reason": None, "email_category": None, "draft_response": None, "messages": [] }) return f"is_spam:{result.get("is_spam")}\nspam_reason:{result.get("spam_reason")}" # ===================== 核心处理函数(模拟邮件分析) ===================== def classify_email(sender: str, subject: str, body: str) -> tuple[str, str]: if sender.strip() == "" or subject.strip() == "" or body.strip() == "" : return "true", "sender、subject、subject为空" email = { "sender": sender, "subject": subject, "body": body } result = compiled_graph.invoke({ "email": email, "is_spam": None, "spam_reason": None, "email_category": None, "draft_response": None, "messages": [] }) return result.get("is_spam"), result.get("spam_reason") # ===================== Gradio 表单界面构建 ===================== with gr.Blocks(title="邮件分析表单", theme=gr.themes.Soft()) as demo: # 标题说明 gr.Markdown("### 📧 邮件垃圾信息分析表单") gr.Markdown("请输入以下3项信息,提交后自动分析邮件是否为垃圾邮件") # 表单区域:3个文本输入框 with gr.Row(): # 行布局,可选:改为 gr.Column() 垂直布局 with gr.Column(scale=1): sender_input = gr.Textbox( label="1. 发件人邮箱地址", placeholder="john.smith@gmail.com", lines=1, max_lines=1 ) with gr.Column(scale=2): subject_input = gr.Textbox( label="2. 邮件主题", placeholder="预约会议", lines=1, max_lines=1 ) # 第三个文本框:多行输入(邮件内容) content_input = gr.Textbox( label="3. 邮件内容", placeholder="我想预约下周二的咨询会议", lines=5, max_lines=10 ) # 提交按钮 submit_btn = gr.Button("📤 提交分析", variant="primary", size="lg") # 分割线 # gr.Divider() # 结果展示区域(结构化输出) gr.Markdown("### 📝 分析结果") with gr.Row(): is_spam_output = gr.Textbox(label="是否垃圾邮件", interactive=False) reason_output = gr.Textbox(label="判定原因", lines=3, interactive=False) # 绑定按钮与处理函数 submit_btn.click( fn=classify_email, inputs=[sender_input, subject_input, content_input], # 3个输入框 outputs=[is_spam_output, reason_output] # 2个输出框 ) # 重置按钮(可选) reset_btn = gr.Button("🔄 清空输入") reset_btn.click( fn=lambda: ("", "", "", "", "", ""), # 重置所有输入输出 inputs=[], outputs=[sender_input, subject_input, content_input, is_spam_output, reason_output] ) # ===================== 启动界面 ===================== if __name__ == "__main__": # server_name="0.0.0.0" 允许局域网访问,server_port 自定义端口 demo.launch() # demo = gr.Interface(fn=classify_email, inputs="text", outputs="text") # demo.launch()