找回密码
 立即注册
首页 业界区 安全 langchain4j 学习系列(7)-文本分类

langchain4j 学习系列(7)-文本分类

胥望雅 昨天 22:25
继续我们的langchain4j学习之旅,很多“智能客服”之类的AI应用,“问题分类”是非常重要的功能之一。比如:客人进来咨询问题,得判断出客人的问题是“订单相关”(比如:我要取消订单),还是“支付相关”(比如:我要退款),还是“投诉相关”(比如:你们的服务太差了,我要投诉到相关部门)。识别出对应分类后,就可以交给相应的流程(或细分的sub agent)做进一步处理。
langchain4j 提供了2种分类方法:
一、基于LLM的语义理解
1.1 定义分类枚举
  1.     enum CustomerServiceCategory {
  2.         PRODUCT("产品相关"),
  3.         ORDER("订单相关"),
  4.         ACCOUNT("账户相关"),
  5.         MEMBER("会员相关"),
  6.         PAYMENT("支付相关"),
  7.         OTHERS("其它问题");
  8.         @Getter
  9.         private final String desc;
  10.         CustomerServiceCategory(String desc) {
  11.             this.desc = desc;
  12.         }
  13.     }
复制代码
1.2 定义AIService的接口
  1.     interface CustomerServiceCategoryClassifier {
  2.         @UserMessage("将客人遇到的问题【{{text}}】归类")
  3.         CustomerServiceCategory classify(String text);
  4.     }
复制代码
1.3 测试
  1.     @GetMapping(value = "/classify", produces = MediaType.APPLICATION_JSON_VALUE)
  2.     public ResponseEntity<String> classify(@RequestParam String query) {
  3.         try {
  4.             CustomerServiceCategoryClassifier customerServiceClassifier = AiServices.create(CustomerServiceCategoryClassifier.class, ollamaChatModel);
  5.             CustomerServiceCategory classify = customerServiceClassifier.classify(query);
  6.             return ResponseEntity.ok(classify.getDesc());
  7.         } catch (Exception e) {
  8.             log.error("classify", e);
  9.             return ResponseEntity.ok("{"error":"classify error: " + e.getMessage() + ""}");
  10.         }
  11.     }
复制代码
效果:
1.png

2.png

观察日志的话,能看到与LLM的交互:
  1. 2025-12-09T21:00:57.280+08:00  INFO 5948 --- [langchain4j-study] [io-8080-exec-10] d.l.http.client.log.LoggingHttpClient    : HTTP request:
  2. - method: POST
  3. - url: http://localhost:11434/api/chat
  4. - headers: [Content-Type: application/json]
  5. - body: {
  6.   "model" : "deepseek-v3.1:671b-cloud",
  7.   "messages" : [ {
  8.     "role" : "user",
  9.     "content" : "将客人遇到的问题【我的退款为什么还没到账?】归类\nYou must answer strictly with one of these enums:\nPRODUCT\nORDER\nACCOUNT\nMEMBER\nPAYMENT\nOTHERS"
  10.   } ],
  11.   "options" : {
  12.     "stop" : [ ]
  13.   },
  14.   "stream" : false,
  15.   "tools" : [ ]
  16. }
复制代码
这种方法的准确性完全取决于模型能力的强弱,大多数情况下是OK的,但有些case可能会不太符合我们的预期,比如:
3.png

一些互联网平台,对于“积分”可能会归到【会员相关】(比如:积分累积到了一定程度,可以兑换成高等级会员),这时候就可以考虑第2种方法。
  
二、基于向量数据库的相似度计算
2.1 先梳理已知问题的归类
  1.     Map<CustomerServiceCategory, List<String>> getExamples() {
  2.         Map<CustomerServiceCategory, List<String>> examples = new HashMap<>();
  3.         examples.put(PRODUCT, asList(
  4.                 "怎么没有产品说明书?",
  5.                 "产品的保修期过了怎么办?",
  6.                 "我新买的东西用了一周就坏了?",
  7.                 "产品无法使用?"
  8.         ));
  9.         examples.put(ORDER, asList(
  10.                 "我的订单现在到哪里了?",
  11.                 "能给我一个快递单号吗?",
  12.                 "我怎么知道我的订单已经发货了?",
  13.                 "我可以更改配送方式吗?",
  14.                 "你们提供次日达服务吗?",
  15.                 "可以选择到店自提吗?",
  16.                 "我的订单什么时候能到?",
  17.                 "为什么我的配送延迟了?",
  18.                 "我可以指定配送日期吗?",
  19.                 "已经过了预计送达日期,我的订单在哪里?",
  20.                 "如果出现延迟,我会收到通知吗?",
  21.                 "天气原因会导致配送延迟多久?",
  22.                 "我收到了订单,但少了一件商品。",
  23.                 "包裹送达时是空的。",
  24.                 "我收到的商品错了,该怎么办?",
  25.                 "我所有的商品会同时送达吗?",
  26.                 "为什么我只收到部分订单商品?",
  27.                 "剩下的商品能更快送达吗?"
  28.         ));
  29.         examples.put(PAYMENT, asList(
  30.                 "能用支付宝吗?",
  31.                 "微信支付可以吗?",
  32.                 "支持信用卡吗?",
  33.                 "付款时我遇到一个错误",
  34.                 "可以通过银行转账付款吗?",
  35.                 "为什么我的付款被拒绝了",
  36.                 "可以给我发送上一笔订单的发票吗?",
  37.                 "发票会自动发送到我的电子邮箱吗?",
  38.                 "如何申请退款?"
  39.         ));
  40.         examples.put(MEMBER, asList(
  41.                 "我的会员等级变低了?",
  42.                 "我的积分过期了?",
  43.                 "我的优惠券不能用了?",
  44.                 "能给我发个支付95折优惠券吗?",
  45.                 "满减优惠券当次消费就能用吗?",
  46.                 "能送100点优惠积分吗?"
  47.         ));
  48.         examples.put(ACCOUNT, asList(
  49.                 "如何注销账号?",
  50.                 "我的密码过期了?",
  51.                 "为什么无法登录",
  52.                 "登录时手机收不到验证码",
  53.                 "如果更换账号绑定的手机号"
  54.         ));
  55.         examples.put(OTHERS, asList(
  56.                 "厂商的联系电话是多少?",
  57.                 "如何加盟?",
  58.                 "你们公司还招人吗?"
  59.         ));
  60.         return examples;
  61.     }
复制代码
2.2 使用embedding模型分类
  1.     @GetMapping(value = "/classify/embed", produces = MediaType.APPLICATION_JSON_VALUE)
  2.     public ResponseEntity<String> classifyEmbed(@RequestParam String query) {
  3.         try {
  4.             TextClassifier<CustomerServiceCategory> classifier = new EmbeddingModelTextClassifier<>(ollamaEmbeddingModel, getExamples());
  5.             List<CustomerServiceCategory> categories = classifier.classify(query);
  6.             String result = "";
  7.             if (!CollectionUtils.isEmpty(categories)) {
  8.                 CustomerServiceCategory classify = categories.get(0);
  9.                 result += classify.getDesc() + ",";
  10.             }
  11.             return ResponseEntity.ok(StringUtils.trimTrailingCharacter(result, ','));
  12.         } catch (Exception e) {
  13.             log.error("classify", e);
  14.             return ResponseEntity.ok("{"error":"classify error: " + e.getMessage() + ""}");
  15.         }
  16.     }
复制代码
4.png

刚才的case符合预期了,但该方法有缺陷也十分明显,如果梳理的已知问题分类不够全面,会出现误判,比如:
5.png

这个问题不在事先准备好的问题列表中,计算出来的结果就差强人意了。实际应用中,如何取舍看业务场景,或者二者结合使用(比如:2个方法都跑一遍,看是否一致,然后再根据一定策略做处理。或者先用方法1,先做一轮分类,将结果人工复检后,用于完善方法2中的分类列表)
 
文中示例代码:GitHub - yjmyzz/langchain4j-study at day07

来源:程序园用户自行投稿发布,如果侵权,请联系站长删除
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!

相关推荐

您需要登录后才可以回帖 登录 | 立即注册