学习如何通过将客服邮件处理流程分解为离散步骤来使用 Spring AI Alibaba Graph 构建智能工作流。
Spring AI Alibaba Graph 可以改变您构建智能代理的思维方式。使用 Graph 构建代理时,您将首先把它分解为称为**节点(nodes)的离散步骤。然后,描述每个节点的不同决策和转换。最后,通过一个共享的状态(state)**将节点连接起来,每个节点都可以读取和写入该状态。在本教程中,我们将指导您完成使用 Spring AI Alibaba Graph 构建客服邮件处理代理的思维过程。
假设您需要构建一个处理客服邮件的 AI 代理。产品团队给出了以下需求:
代理应该:
- 读取客户邮件
- 按紧急程度和主题分类
- 搜索相关文档以回答问题
- 起草适当的回复
- 将复杂问题上报给人工客服
- 在需要时安排后续跟进
需要处理的示例场景:
- 简单产品问题:”如何重置我的密码?”
- Bug 报告:”导出功能在选择 PDF 格式时崩溃”
- 紧急账单问题:”我的订阅被重复扣费了!”
- 功能请求:”能在移动应用中添加暗黑模式吗?”
- 复杂技术问题:”我们的 API 集成间歇性失败,返回 504 错误”
要在 Spring AI Alibaba Graph 中实现代理,通常遵循以下五个步骤。
首先识别流程中的不同步骤。每个步骤将成为一个节点(执行特定任务的函数)。然后勾画这些步骤如何相互连接。
箭头显示可能的路径,但具体选择哪条路径的决策发生在每个节点内部。
现在您已经识别了工作流中的组件,让我们了解每个节点需要做什么:
- 读取邮件:提取和解析邮件内容
- 分类意图:使用 LLM 对紧急程度和主题进行分类,然后路由到适当的操作
- 文档搜索:查询知识库以获取相关信息
- Bug跟踪:在跟踪系统中创建或更新问题
- 起草回复:生成适当的响应
- 人工审核:上报给人工客服进行审批或处理
- 发送回复:发送邮件响应
提示:注意有些节点会决定接下来去哪里(分类意图、起草回复、人工审核),而其他节点总是进入相同的下一步(读取邮件总是进入分类意图,文档搜索总是进入起草回复)。
对于图中的每个节点,确定它代表什么类型的操作以及它需要什么上下文才能正常工作。
LLM 步骤
当步骤需要理解、分析、生成文本或做出推理决策时:
分类意图节点
- 静态上下文(提示):分类类别、紧急程度定义、响应格式
- 动态上下文(来自状态):邮件内容、发件人信息
- 期望结果:确定路由的结构化分类
起草回复节点
- 静态上下文(提示):语气指南、公司政策、响应模板
- 动态上下文(来自状态):分类结果、搜索结果、客户历史
- 期望结果:准备好审核的专业邮件响应
数据步骤
当步骤需要从外部源检索信息时:
文档搜索节点
- 参数:从意图和主题构建的查询
- 错误处理:捕获异常并存储错误信息,继续执行流程
- 缓存:可以缓存常见查询以减少 API 调用(本示例中未实现)
客户历史查询
- 参数:来自状态的客户邮箱或 ID
- 错误处理:如果不可用则回退到基本信息(本示例中未实现)
- 缓存:是,使用生存时间来平衡新鲜度和性能(本示例中未实现)
操作步骤
当步骤需要执行外部操作时:
发送回复节点
- 何时执行:批准后(人工或自动)
- 错误处理:让异常冒泡以进行调试(本示例中未实现重试逻辑)
- 不应缓存:每次发送都是唯一操作
Bug跟踪节点
- 何时执行:当意图是”bug”时总是执行
- 错误处理:直接执行,不丢失 bug 报告至关重要(本示例中未实现重试逻辑)
- 返回:要包含在响应中的票据 ID
用户输入步骤
当步骤需要人工干预时:
人工审核节点
- 决策上下文:原始邮件、草稿响应、紧急程度、分类
- 预期输入格式:批准布尔值加上可选的编辑响应
- 何时触发:高紧急程度、复杂问题或质量问题
状态是图中所有节点可访问的共享记忆。可以把它想象成代理用来跟踪在工作过程中学到和决定的一切的笔记本。
什么应该放在状态中?
对每条数据问自己这些问题:
包含在状态中
是否需要跨步骤持久化? 如果是,它就应该放在状态中。
不要存储
能从其他数据派生吗? 如果是,在需要时计算它,而不是存储在状态中。
对于我们的邮件代理,我们需要跟踪:
- 原始邮件和发件人信息(无法重建这些)
- 分类结果(多个下游节点需要)
- 搜索结果和客户数据(重新获取成本高)
- 草稿响应(需要在审核过程中持久化)
- 执行元数据(用于调试和恢复)
保持状态原始,按需格式化提示
重要原则:您的状态应该存储原始数据,而不是格式化的文本。在需要时在节点内格式化提示。
这种分离意味着:
- 不同节点可以根据需要以不同方式格式化相同数据
- 您可以更改提示模板而无需修改状态模式
- 调试更清晰 – 您可以确切看到每个节点收到了什么数据
- 您的代理可以演进而不会破坏现有状态
让我们定义状态:
import com.alibaba.cloud.ai.graph.KeyStrategy;
import com.alibaba.cloud.ai.graph.KeyStrategyFactory;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.alibaba.cloud.ai.graph.state.strategy.AppendStrategy;
import java.util.Map;
import java.util.HashMap;
// 邮件分类结构
public static class EmailClassification {
private String intent; // "question", "bug", "billing", "feature", "complex"
private String urgency; // "low", "medium", "high", "critical"
private String topic;
private String summary;
public EmailClassification() {
}
public EmailClassification(String intent, String urgency, String topic, String summary) {
this.intent = intent;
this.urgency = urgency;
this.topic = topic;
this.summary = summary;
}
public String getIntent() {
return intent;
}
public void setIntent(String intent) {
this.intent = intent;
}
public String getUrgency() {
return urgency;
}
public void setUrgency(String urgency) {
this.urgency = urgency;
}
public String getTopic() {
return topic;
}
public void setTopic(String topic) {
this.topic = topic;
}
public String getSummary() {
return summary;
}
public void setSummary(String summary) {
this.summary = summary;
}
@Override
public String toString() {
return String.format("EmailClassification{intent='%s', urgency='%s', topic='%s', summary='%s'}",
intent, urgency, topic, summary);
}
}
// 配置状态键策略
public static KeyStrategyFactory createKeyStrategyFactory() {
return () -> {
HashMap<String, KeyStrategy> strategies = new HashMap<>();
strategies.put("email_content", new ReplaceStrategy());
strategies.put("sender_email", new ReplaceStrategy());
strategies.put("email_id", new ReplaceStrategy());
strategies.put("classification", new ReplaceStrategy());
strategies.put("search_results", new ReplaceStrategy());
strategies.put("customer_history", new ReplaceStrategy());
strategies.put("draft_response", new ReplaceStrategy());
strategies.put("messages", new AppendStrategy());
strategies.put("next_node", new ReplaceStrategy());
strategies.put("status", new ReplaceStrategy());
strategies.put("review_data", new ReplaceStrategy());
return strategies;
};
}
注意状态只包含原始数据 – 没有提示模板、没有格式化字符串、没有指令。分类输出直接从 LLM 存储为单个对象。
现在我们将每个步骤实现为一个函数。Spring AI Alibaba Graph 中的节点就是一个接受当前状态并返回更新的 Java 函数。
本文整理自 Spring AI Alibaba 官方文档《Graph 工作流编排指南》:https://java2ai.com/docs/frameworks/graph-core/quick-start

AI项目博客






