classifyEmail / app.py
wanggenfu's picture
Update app.py
01e2dc9 verified
Raw History Blame Contribute Delete
10.9 kB
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()