早期 Agent 珍贵录像
项目 url:https://gitee.com/hazhenyu/mgterp.git
自然语言转化为订单的流程如下:
首先用户输入 input-> 判断用户输入的问题是不是和 erp 有关系的
如何判断用户输入的问题是不是和 erp 有关系的:简单的字符串匹配即可。比如
1 | String[] generalKnowledgeKeywords = { |
如果包含上面的 generalKnowledgeKeywords 或者是 questionPatterns 但是不包含 erpTerms 则说明和 ERP 无关。比如用户问”什么是苹果”,这时候就是典型的与 ERP 无关的,这时候我们就需要走对话模式了。
对话模式(handleConversation)就是普通的调用 deepseek 的 API。只不过添加了以下过程:
- 添加一些提示词,比如”你是 xxx,一个 xxx,用户问你,你应该友好的回答。”
- 然后把这些全部打包发送给 deepseek 并获取 ds 的回复。
- 使用失败重传机制。只有三次都发送失败之后才会显示失败。
如果和 ERP 有关则走下面这条路:
首先对用户的对话进行意图识别分析(analyzeIntent):
- 传入用户的 input。
- 添加 prompt。示例如下:主要是告诉他的身份,以及怎么识别,识别的类别等等。
1 | 你是智能意图识别专家。分析用户输入,判断其真实意图。 |
- 将用户的 input 和 prompt 一同发送给 ds。接收 ds 返回的 json。如果这个时候 AI 检测失败了就需要使用兜底的检测方法,这个检测方法依旧是字符串匹配。根据下面这些字符串进行匹配检测之后最后确定指令的类型(纯指令/纯对话/混合)
1 | // ERP指令关键词 - 更全面的业务关键词列表 |
- 得到返回的 json 之后,构造并返回意图识别结果对象。提取其中的
intent_type来进行判断
1 | ds返回的json |
- 纯对话(handleConversation)上面已经说了,接下来先说纯指令。
- 纯指令(handleCommand):依旧是换汤不换药。把上一个 AI 返回的 json 中的 command 提取出来。这里是
向北京科技有限公司采购二十个苹果,每个苹果五块钱。然后添加提示词:
1 | 你是智能ERP指令解析器。从用户输入中提取信息,转换为标准JSON。 |
- 最后得到 json 格式如下:
1 | { |
- 如果 json 格式有误就需要修复一下:
1 | // 解析并验证JSON指令 |
现在开始判断命令的类型:
如果是创建订单 create_order:
则给这个 node 添加上会话 ID 确保上下文能够共享
1
2
3
4
5
6
7
8// 为订单创建操作添加会话ID,确保上下文能够共享
if ("create_order".equals(action)) {
// 生成或使用现有的会话ID
String sessionId = generateSessionId(input);
((com.fasterxml.jackson.databind.node.ObjectNode) commandNode)
.put("session_id", sessionId);
System.out.println("🔗 设置会话ID: " + sessionId);
}如果是删除订单 delete_order 则需要获取 order_id 并给用户返回确定要删除订单的信息。
接下来就是执行命令:
首先就是根据指令来选择类型
1
2
3
4
5
6
7
8
9
10
11return switch (action) {
case "create_order" -> handleCreateOrder(root, sessionId);
case "delete_order" -> handleDeleteOrder(root);
case "query_order" -> handleQueryOrder(root);
case "confirm_order" -> handleConfirmOrder(root);
case "query_sales" -> handleQuerySales(root);
case "query_inventory" -> handleQueryInventory(root);
case "analyze_finance" -> handleAnalyzeFinance(root);
case "analyze_order" -> handleAnalyzeOrder(root);
default -> "❓ 未知操作类型:" + action + "\n\n💡 支持的操作:\n• create_order (创建订单)\n• query_order (查询订单)\n• delete_order (删除订单)\n• confirm_order (确认订单)\n• query_sales (销售查询)\n• query_inventory (库存查询)\n• analyze_finance (财务分析)\n• analyze_order (订单分析)";
};比如 handleCreateOrder()里面就是根据 sessionId 来判断整个上下文。(获取最近的一次聊天的时间和现在如果超过五分钟就不算完整,如果没超过就算完整)。如果完整的话则直接去创建订单。
创建订单就是先创建一个订单对象。然后从 root 里面获取到供应商,如果没有获取到,则把 original_input 拿出来和正则表达式做匹配来判断。正则表达式如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41String[] patterns = {
// 🆕 优先检查:从XX处/那里购买的模式 (采购订单)
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*那里", // 从哈振宇那里
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*这里", // 从张三这里
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*处", // 从李四处
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*买", // 从王五买
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*购买", // 从张三购买
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*采购", // 从供应商采购
"从\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*进", // 从供应商进
"向\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*买", // 向厂家买
"向\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*购买", // 向供应商购买
// 销售给XX的模式
"卖给了?\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 卖给张三 / 卖给了张三(非贪婪匹配)
"售给\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 售给李四
"发给\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 发给王五
"交付给\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 交付给客户
"出售给\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 出售给张三
"卖了.*给\\s*([\\u4e00-\\u9fa5a-zA-Z]+?)(?:\\s|$|[\\d一二三四五六七八九十])", // 卖了XX给张三
// 基础创建模式
"为\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*创建", // 为张三创建
"给\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*创建", // 给张三创建
"帮\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*创建", // 帮张三创建
"为\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*下", // 为张三下单
"给\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*下", // 给张三下单
"帮\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*买", // 帮张三买
// 标准格式
"客户[::]?\\s*([\\u4e00-\\u9fa5a-zA-Z]+)", // 客户:张三
"供应商[::]?\\s*([\\u4e00-\\u9fa5a-zA-Z]+)", // 供应商:张三
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*的订单", // 张三的订单
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*要", // 张三要
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*订购", // 张三订购
// 灵活的中文表达模式
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*说", // 张三说
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*需要", // 李四需要
"([\\u4e00-\\u9fa5a-zA-Z]+)\\s*想要", // 王五想要
"和\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*", // 和张三
"跟\\s*([\\u4e00-\\u9fa5a-zA-Z]+)\\s*" // 跟李四
};判断之后提取出供应商/客户的名字。然后把相关的信息插入到 order 对象里面。如果没有供应商/客户的名字则需要问用户了。
接着创建商品和订单相关联的信息,这里就纯手动关联了。
创建完商品和订单的关联之后调用:
1
Order savedOrder = orderService.createOrder(order, goodsList);
创建订单的步骤就是前缀+时间戳。如果是销售出去则要判断库存。买入则不需要判断。最后插入这条数据并设置类型为 PENDING。
接着把相关的信息返回给前端。告诉用户订单创建成功,单号是 xxx 什么的。
混合模式就是纯指令+对话,这里就不多讲了。
学习客户的偏好:
- 首先就是更新客户的经常买的商品,并且更新偏好价格(加权平均)。按照加权平均:70%历史价格 + 30%新价格的模式来修改。同时把这个修改添加到数据库里面。
智能识别别名:
写一些常见的错误:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20if ("customer".equals(fieldType)) {
// 客户名常见错误
corrections.put("冯天一", "冯天祎");
corrections.put("张3", "张三");
corrections.put("李4", "李四");
corrections.put("老张", "张三");
corrections.put("小李", "李四");
corrections.put("小王", "王五");
} else if ("product".equals(fieldType)) {
// 商品名常见错误和简写
corrections.put("苹果🍎", "苹果");
corrections.put("apple", "苹果");
corrections.put("water", "水");
corrections.put("🍎", "苹果");
corrections.put("🍌", "香蕉");
corrections.put("🍊", "橙子");
corrections.put("💧", "水");
corrections.put("饮用水", "水");
corrections.put("矿泉水", "水");
}根据映射来查询
(这里可能就涉及到query重写)
系统架构和组件:
- AI 控制器(AIController):REST API 接口层,接收前端请求并调用 AI 服务
- AI 服务接口(AIService):定义 AI 处理逻辑的接口
- AI 服务实现(AIServiceImpl):实现 AI 服务接口,处理意图识别、指令解析和对话模式
- 外部 AI 服务(DeepSeekAIService):与 DeepSeek API 通信,执行各种 AI 任务
整个系统采用分层架构,各组件职责明确:
- AIController 负责 HTTP 请求处理和响应
- AIServiceImpl 负责业务逻辑处理,包括意图识别、指令解析和执行
- DeepSeekAIService 负责与外部 AI 模型通信
错误处理和重试机制:
- 系统实现了多层错误处理和重试机制:
- 意图识别失败时,使用基于关键词的备用分析方法
- JSON 解析失败时,尝试修复格式问题
- AI 服务调用失败时,使用指数退避重试机制(最多 3 次)
- 网络超时时,提供本地分析作为备用方案
- 指令执行失败时,尝试降级为普通对话模式
- 系统实现了多层错误处理和重试机制:
会话管理和上下文保持:
- 系统使用会话 ID 来维护订单创建的上下文
- 会话超时时间为 5 分钟
- 同一会话内的操作可以共享上下文信息
- 过期会话会被自动清理
安全和确认机制:
- 对于危险操作(如删除订单)需要用户确认
- 系统会生成简洁明了的确认消息
- 只有在用户明确确认后才会执行危险操作
整个 sout 如下:
1 | 🎯 处理用户输入: 给我向北京科技有限公司采购二十个苹果,每个苹果五块钱 (已确认: false) |
