大家好,我是小悟。
一、真实场景痛点描述
公司日均收到300+封客户邮件,涵盖:
- 技术问题报修(如“API调用返回500错误”)
- 账单咨询(如“我被多扣了$49”)
- 销售询价(如“团队版多少钱?”)
- 账号权限问题(如“无法邀请成员”)
痛点
- 响应慢:客服人工分类+初回复平均耗时15分钟/封
- 错漏多:紧急技术问题被埋没在账单邮件中
- 人力浪费:初级客服70%时间在处理“模板可回答”的问题
- 无统一标准:同样的问题不同人回复质量差异大
二、解决方案:邮件处理Agent架构
我们设计一个大模型驱动的多步Agent,自动完成:
- 读取未读邮件
- 分类(紧急技术 > 账单 > 销售 > 账号 > 其他)
- 提取关键实体(订单号、错误码、版本)
- 根据类别调用对应“回复生成器”
- 将草稿存入CRM待人工一键发送
技术栈
- LangChain(Agent编排)
- GPT-4(推理与生成)
- IMAPClient(收邮件)
- Chroma(历史案例向量检索,可选)
三、详细步骤与代码实现
步骤1:定义邮件处理工具集
# tools.py
from langchain.tools import BaseTool
from typing import Type, Optional
from pydantic import BaseModel, Field
import re
class ClassifyEmailInput(BaseModel):
subject: str = Field(description="邮件主题")
body: str = Field(description="邮件正文")
class ClassifyEmailTool(BaseTool):
name = "classify_email"
description = "根据主题和正文判断邮件类别,返回: tech_urgent, billing, sales, account, other"
args_schema: Type[BaseModel] = ClassifyEmailInput
def _run(self, subject: str, body: str) -> str:
text = f"{subject} {body}".lower()
if any(k in text for k in ["error", "500", "crash", "down", "not working"]):
return "tech_urgent"
if any(k in text for k in ["bill", "charge", "invoice", "refund", "$"]):
return "billing"
if any(k in text for k in ["pricing", "buy", "quote", "team plan"]):
return "sales"
if any(k in text for k in ["login", "invite", "access", "role"]):
return "account"
return "other"
class ExtractEntitiesInput(BaseModel):
text: str = Field(description="邮件全文")
class ExtractEntitiesTool(BaseTool):
name = "extract_entities"
description = "提取订单号(ORD-数字)、错误码(数字)、邮箱地址"
args_schema: Type[BaseModel] = ExtractEntitiesInput
def _run(self, text: str) -> dict:
order_id = re.search(r'ORD-(\d+)', text)
error_code = re.search(r'error\s*code[:\s]*(\d+)', text, re.I)
email = re.search(r'[\w\.-]+@[\w\.-]+\.\w+', text)
return {
"order_id": order_id.group(0) if order_id else None,
"error_code": error_code.group(1) if error_code else None,
"customer_email": email.group(0) if email else None
}
步骤2:构建回复生成器(带知识库)
# reply_generators.py
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
import os
llm = ChatOpenAI(model="gpt-4", temperature=0.3)
# 技术问题专用prompt(可嫁接内部知识库)
tech_prompt = ChatPromptTemplate.from_messages([
("system", """你是SaaS公司的技术支持专家。根据用户描述的错误,给出:
1. 可能原因分析(2-3条)
2. 排查步骤(具体命令或操作)
3. 如需要日志,说明要哪些字段
保持专业但友好,不超过150字。"""),
("human", "问题描述:{error_desc}\n提取到错误码:{error_code}")
])
billing_prompt = ChatPromptTemplate.from_messages([
("system", "你是客服主管,处理账单咨询。先表达理解,然后引导用户提供订单号或邮箱以便核查。禁止承诺退款金额。"),
("human", "用户说:{billing_text}")
])
def gen_tech_reply(error_desc, error_code):
chain = tech_prompt | llm
return chain.invoke({"error_desc": error_desc, "error_code": error_code}).content
def gen_billing_reply(billing_text):
chain = billing_prompt | llm
return chain.invoke({"billing_text": billing_text}).content
def gen_sales_reply(question):
return f"感谢您对定价的关注。当前团队版 $199/月,含10个席位。如需定制报价,请提供大致人数和功能需求。\n\n原问题:{question}"
def gen_account_reply(issue):
return f"账号问题已收到。请您尝试:1) 登出后重新登录 2) 主账号在设置-成员中重新发送邀请。如仍失败,请提供您的账号邮箱和错误截图。\n\n{issue}"
步骤3:Agent主控(ReAct模式)
# mail_agent.py
from langchain.agents import Tool, AgentExecutor, LLMSingleActionAgent
from langchain.agents import AgentOutputParser
from langchain.prompts import StringPromptTemplate
from langchain.chains import LLMChain
from typing import List, Union
import imaplib
import email
from email.header import decode_header
# 连接邮箱(示例用IMAP)
def fetch_unread_emails(imap_server, username, password):
mail = imaplib.IMAP4_SSL(imap_server)
mail.login(username, password)
mail.select("INBOX")
status, ids = mail.search(None, "UNSEEN")
emails = []
for num in ids[0].split():
status, msg_data = mail.fetch(num, "(RFC822)")
msg = email.message_from_bytes(msg_data[0][1])
subject = decode_header(msg["Subject"])[0][0]
if isinstance(subject, bytes):
subject = subject.decode()
body = ""
if msg.is_multipart():
for part in msg.walk():
if part.get_content_type() == "text/plain":
body = part.get_payload(decode=True).decode()
break
else:
body = msg.get_payload(decode=True).decode()
emails.append({"id": num, "subject": subject, "body": body})
return emails
# Agent的prompt模板
class MailAgentPrompt(StringPromptTemplate):
template = """你是一个邮件处理专家。你有以下工具:
{tools}
当前任务邮件:
主题:{subject}
正文:{body}
请按以下步骤思考:
1. 先分类(调用classify_email)
2. 提取实体(调用extract_entities)
3. 根据类别选择回复策略:
- tech_urgent → 生成技术回复
- billing → 生成账单回复
- sales → 生成销售回复
- account → 生成账号回复
- other → 转人工
输出格式:
Thought: 我需要做什么
Action: 工具名
Action Input: {{参数}}
... (可重复)
Final Answer: 最终的回复草稿 + 建议的下一步(人工发送/需补充信息)
开始!
{agent_scratchpad}"""
def format(self, **kwargs) -> str:
kwargs["tools"] = "\n".join([f"- {t.name}: {t.description}" for t in kwargs["tools"]])
return self.template.format(**kwargs)
# 注册工具
from tools import ClassifyEmailTool, ExtractEntitiesTool
classify = ClassifyEmailTool()
extract = ExtractEntitiesTool()
tools = [
Tool(name="classify_email", func=classify._run, description=classify.description),
Tool(name="extract_entities", func=extract._run, description=extract.description)
]
# 初始化Agent
prompt = MailAgentPrompt()
llm_chain = LLMChain(llm=llm, prompt=prompt)
agent = LLMSingleActionAgent(
llm_chain=llm_chain,
output_parser=AgentOutputParser(),
stop=["\nObservation:"],
allowed_tools=[t.name for t in tools]
)
executor = AgentExecutor.from_agent_and_tools(agent=agent, tools=tools, verbose=True)
步骤4:主循环 + 人工审核接口
# main.py
def process_email(email_item, agent_executor):
raw_input = f"Subject: {email_item['subject']}\nBody: {email_item['body']}"
result = agent_executor.run(raw_input)
# 简单后处理:如果是tech_urgent 且包含error_code,标记高优先级
if "tech_urgent" in result and re.search(r'error_code', raw_input):
priority = "HIGH"
else:
priority = "NORMAL"
# 存储草稿到数据库(示意)
draft_record = {
"original_email_id": email_item["id"],
"agent_reply": result,
"priority": priority,
"human_reviewed": False
}
# 实际开发存入PostgreSQL
return draft_record
def main():
# 配置
IMAP_SERVER = "imap.example.com"
USER = "support@yourcompany.com"
PWD = "your_app_password"
unread = fetch_unread_emails(IMAP_SERVER, USER, PWD)
for email_item in unread:
draft = process_email(email_item, executor)
# 将草稿写入客服系统dashboard,等人点击“发送”
print(f"草稿已生成:{draft['agent_reply'][:100]}...")
print(f"处理完成,共 {len(unread)} 封")
if __name__ == "__main__":
main()
四、效果与总结
实际运行结果(灰度测试2周数据)
| 指标 | 人工处理 | Agent辅助后 |
|---|---|---|
| 平均首响时间 | 15分钟 | 1.8分钟 |
| 技术类误分类率 | 12% | 4% |
| 客服人均日处理量 | 40封 | 120封 |
| 客户满意度(CSAT) | 82% | 89% |
关键成功点
- 工具分层清晰:分类→实体提取→回复生成,每个工具可独立迭代。后期我们把
extract_entities从正则升级为BERT小模型,准确率从88%提到96%。 - 保留人工在环:Agent只生成草稿,不自动发送。客服审核+一键发送,既安全又提效。
- Prompt带结构化输出:强制Agent输出
Final Answer包含草稿和下一步动作,避免自由发挥。
常见坑与避坑指南
- 邮件正文截断:长邮件要分块,否则超出GPT上下文。解决方案:先保留全文但切片给
extract_entities。 - 分类过拟合关键词:“bill”出现在“Bill Gates reported error”会被误归为账单。改进:加入否定规则或使用微调的分类模型。
- 隐私合规:切勿将用户邮箱、订单号明文存入LangChain的debug日志。我们加了
logging_filter脱敏。
后续演进
目前已将该Agent集成到Zendesk,实现了:
- 当Agent置信度>0.9且类别为“账单/销售”时,自动发送(无需审核)
- 当为“技术紧急”时,除了生成草稿,还会自动@值班工程师到Slack
- 每周从人工修正的数据中微调一个本地小模型,逐步减少对GPT-4的调用(成本降70%)
结论:一个设计良好、工具职责单一的AI Agent,在客服邮件处理这类半结构化、高频次场景下,能切实降低重复劳动,提升响应速度与质量。关键在于不追求全自动,而是做人机协同的智能副驾。
谢谢你看我的文章,既然看到这里了,如果觉得不错,随手点个赞、转发、在看三连吧,感谢感谢。那我们,下次再见。
您的一键三连,是我更新的最大动力,谢谢
山水有相逢,来日皆可期,谢谢阅读,我们再会
我手中的金箍棒,上能通天,下能探海