快速学会 Java 实现意图识别:从规则匹配到 BiLSTM 分类器

在 Agent 开发中,意图识别是绕不开的第一关。用户说了一句话,系统要先搞清楚他到底想干什么,才能把请求路由到正确的处理流程。

很多人一提到意图识别,第一反应是调大模型。但对于 50 种左右的意图规模,纯 LLM 方案延迟高、成本高、输出不稳定,其实并不是最优解。

本文用 Java 从零实现一套意图识别方案,从最简单的规则匹配,到 DeepLearning4J 训练的 BiLSTM 分类器,再到接入 LangGraph4j 工作流,帮你快速落地一个生产可用的意图识别模块。

一、为什么 Java 也能做好意图识别

Java 生态在 NLP 和深度学习领域并不弱。Stanford CoreNLP、OpenNLP 提供了经典的文本处理能力,DeepLearning4J 是 Java 原生的深度学习框架,可以直接训练和推理 LSTM、BiLSTM 等序列模型,不需要依赖 Python 服务。

对于意图识别这种典型的文本分类任务,Java 完全可以从训练到部署一条龙搞定。

二、整体方案设计

针对 50 种意图的规模,推荐采用三级流水线架构:

复制代码
用户输入 → 第一层:关键词规则匹配(毫秒级)
         → 未命中 → 第二层:BiLSTM 分类器(~30ms)
         → 置信度低 → 第三层:大模型兜底
         → 输出:意图 + 置信度

第一层用规则覆盖高频固定意图,延迟不到 1ms,准确率接近 100%。第二层用 BiLSTM 处理语义相似但表述不同的情况,延迟约 30ms。第三层只在置信度很低时调用大模型兜底,大幅降低整体成本。

三、第一层:关键词规则匹配

规则匹配是最简单也最高效的方式,适合表述固定的高频意图。

java 复制代码
public class RuleBasedIntentRecognizer {

    public String classify(String text) {
        if (text.matches(".*(查|看).*(订单|物流).*")) {
            return "query_order";
        } else if (text.matches(".*(退|取消).*(订单|服务).*")) {
            return "cancel_order";
        } else if (text.matches(".*(天气|温度|冷|热).*")) {
            return "weather";
        }
        return "unknown";
    }
}

规则匹配的优势是零成本、零延迟、完全可控。缺点是覆盖有限,用户换个说法就可能识别不了,所以需要第二层来兜底。

四、第二层:BiLSTM 分类器

这是本文的核心部分。我们用 DeepLearning4J 训练一个 BiLSTM 模型,对规则匹配未命中的文本做语义分类。

4.1 引入依赖
xml 复制代码
<dependency>
    <groupId>org.deeplearning4j</groupId>
    <artifactId>deeplearning4j-core</artifactId>
    <version>1.0.0-M2.1</version>
</dependency>
<dependency>
    <groupId>org.nd4j</groupId>
    <artifactId>nd4j-native-platform</artifactId>
    <version>1.0.0-M2.1</version>
</dependency>
4.2 准备训练数据

准备一个 CSV 文件,格式为 text,intent,每个意图建议 50~100 条样本:

csv 复制代码
帮我查一下订单,query_order
我的快递到哪了,query_order
物流信息查一下,query_order
我要取消订单,cancel_order
帮我退掉这个服务,cancel_order
今天天气怎么样,weather
明天冷不冷,weather
4.3 构建 BiLSTM 模型

BiLSTM 的核心思路是:正向 LSTM 从左到右读文本,反向 LSTM 从右到左读文本,两者拼接后能同时捕捉上下文信息,比单向 LSTM 效果更好。

java 复制代码
MultiLayerConfiguration conf = new NeuralNetConfiguration.Builder()
    .seed(42)
    .updater(new Adam(0.001))
    .weightInit(WeightInit.XAVIER)
    .gradientNormalization(GradientNormalization.ClipElementWiseAbsoluteValue)
    .gradientNormalizationThreshold(1.0)
    .list()
    .layer(0, new LSTM.Builder()
        .nIn(WORD_VECTOR_SIZE).nOut(HIDDEN_SIZE)
        .activation(Activation.TANH).build())
    .layer(1, new LSTM.Builder()
        .nIn(HIDDEN_SIZE).nOut(HIDDEN_SIZE)
        .activation(Activation.TANH).build())
    .layer(2, new RnnOutputLayer.Builder()
        .nIn(HIDDEN_SIZE).nOut(numClasses)
        .activation(Activation.SOFTMAX)
        .lossFunction(LossFunctions.LossFunction.MCXENT)
        .build())
    .build();

MultiLayerNetwork model = new MultiLayerNetwork(conf);
model.init();
4.4 文本预处理

将文本转为模型能接受的 INDArray 格式,形状为 [batchSize, wordVectorSize, timeSteps]

java 复制代码
private INDArray textToFeatures(String text) {
    String[] tokens = text.split("");
    INDArray features = Nd4j.zeros(1, WORD_VECTOR_SIZE, MAX_SEQ_LEN);

    for (int t = 0; t < Math.min(tokens.length, MAX_SEQ_LEN); t++) {
        int wordIdx = wordToIndex.getOrDefault(tokens[t], 0);
        for (int d = 0; d < WORD_VECTOR_SIZE; d++) {
            features.putScalar(0, d, t, (wordIdx * (d + 1)) % 100 / 100.0);
        }
    }
    return features;
}

实际项目中建议将这里的伪词向量替换为预训练词向量(如腾讯 AI Lab 中文词向量),效果能提升 10%~15%。

4.5 训练与推理

训练完成后将模型保存为 .zip 文件,推理时加载即可:

java 复制代码
// 训练
model.fit(dataSet);
ModelSerializer.writeModel(model, "intent_model.zip", true);

// 推理
MultiLayerNetwork model = ModelSerializer.restoreMultiLayerNetwork("intent_model.zip", true);
INDArray output = model.output(textToFeatures("帮我看看快递到哪了"));
int predictedIdx = Nd4j.argMax(output, 1).getInt(0);
double confidence = output.getDouble(0, predictedIdx);

五、接入 LangGraph4j 工作流

意图识别的最终目的是路由。在 LangGraph4j 中,意图识别作为工作流的第一个节点,识别完成后通过条件边路由到不同的处理节点:

复制代码
START → 意图识别节点 → 条件路由 → 订单处理 / 天气查询 / 兜底 → END
java 复制代码
var stateGraph = new StateGraph<>(IntentState.SCHEMA, IntentState::new)
    .addNode("classify", node_async(new IntentClassifyNode()))
    .addNode("order_handler", node_async(new OrderHandlerNode()))
    .addNode("weather_handler", node_async(new WeatherHandlerNode()))
    .addNode("fallback", node_async(new FallbackNode()))
    .addEdge(START, "classify")
    .addConditionalEdges("classify", state -> {
        String intent = state.intent();
        return switch (intent) {
            case "query_order", "cancel_order" -> "order_handler";
            case "weather" -> "weather_handler";
            default -> "fallback";
        };
    })
    .addEdge("order_handler", END)
    .addEdge("weather_handler", END)
    .addEdge("fallback", END);

这样,用户输入进入工作流后,先经过意图识别节点判断意图,再自动路由到对应的处理节点,整个流程清晰可控。

六、关键超参数参考

超参数 建议值 说明
词向量维度 100 实际项目建议用预训练词向量
LSTM 隐藏层大小 128 50 个意图建议 128~256
最大序列长度 30 根据平均句长调整
学习率 0.001 配合 Adam 优化器
训练轮数 20 观察 loss 不再下降即可停止
批大小 32 数据量小可用 16

七、预期效果

  • 训练数据:50 个意图 × 50~100 条样本
  • 训练时间:CPU 上约 1~5 分钟
  • 推理延迟:<30ms(单次推理)
  • 整体准确率:90%+(数据质量好的情况下)

八、总结

意图识别本质上是一个分类问题,不需要每次都动用大模型。Java 生态下,规则匹配 + DeepLearning4J 的 BiLSTM 分类器就能覆盖绝大多数场景,延迟低、成本低、完全可控。

关键思路是分层处理:规则解决确定性高的问题,模型解决语义相似的问题,大模型只在兜底时使用。这样既保证了速度,又保证了覆盖率。

希望这篇文章能帮你快速上手 Java 意图识别,如果有 50 种左右的意图需要处理,不妨试试这套方案。

相关推荐
wear工程师1 小时前
接口超时别只调大 timeout:Java HTTP 客户端其实有 3 段等待
java·http
william_yangshun1 小时前
招投标应答Agent实战:从2周人工赶工到48小时自动成稿,中标率+25%
人工智能
小小龙学IT1 小时前
simdjson:利用 SIMD 指令实现 GB/s 级 JSON 解析的 C++ 开源库
开发语言·c++·json
AndrewHZ1 小时前
【LLM技术全景】RAG 从原理到实战——检索增强生成完整指南
人工智能·深度学习·算法·llm·检索增强·生成式模型·rag
ydyd202604211 小时前
设备OEE怎么提升?数据采集+分析优化的完整方案
java·服务器·前端
A_nanda1 小时前
c#WPF开发常见问题
开发语言·c#·wpf
爆写加倍1 小时前
2026年3款视频转文字软件测评技术升级让转写整理更准更省心
人工智能·ai
樊小肆1 小时前
DeepSeeker-Code源码导读02-runAgent主循环
人工智能·agent
Hello.Reader1 小时前
REALITY 技术深度解析从 TLS 伪装、抗主动探测到 VLESS + XTLS Vision 完整实践
开发语言·php