女朋友说我听不懂她的话,写个情感分析AI试试

1,450 阅读8分钟

引言

大家好,我是石小石~


有女朋友的同学一定会遇到这样一个问题,经常被女朋友嫌弃听不到他们的话,最后两个人闹得不愉快。 这不,这几天因为和女朋友沟通出了问题,把我拉黑了。哎,女人心,海底针啊!

于是,我突发奇想,为什么不尝试用技术的方式分析情感,看看我的表达是否足够贴近她的期待呢?于是,我决定自己开发一个简单的“女友情感分析工具”,通过AI技术,分析女朋友言语中的情感,判断她开心、平静还是生气!

这样,我岂不要走向人生巅峰?

实现目标

这篇文章,我将完整展示如何实现这个情感分析工具,包括从前端 Vue 3 技术栈、后端 Koa 服务到 AI接口调用,一步步实现整个流程。最终的效果大致如下:

如图,大致效果就是:用户可以输入一段话,点击按钮分析后,工具会返回情感倾向的概率数据,并用柱状图展示分析结果。

技术方案

整个项目的核心其实非常简单,就是调用AI接口分析用户输入的语言,分析后将特定的数据返回给前端,前端做响应的数据处理展示。

市场上AI大模型有很多,国产的如豆包文心一言通义千问kimi 等。考虑到免费、SDK调用简单等因素,本文使用Kimi的月之暗Moonshot模型。关于Kimi的api调用,本文不做过多的赘述,请参考我的其他文章

整个项目分为前端与后端两部分,前端部分用于提交用户输入的数据,与后端进行交互,请求文本分析接口,最终以以柱状图形式展示情感分析结果。后端部分是项目的核心,采用Koa 搭建服务,提供一个接口(调用kimi的模型),最终将数据以特定的json形式返回给前端。

技术实现

后端实现

搭建基础 Koa 服务

我们从最基础的 Koa 框架入手,实现一个最简单的服务。首先通过npm i koa安装依赖并初始化项目,然后,创建 app.js 文件,写入如下代码:

// 引入 Koa 框架依赖
const Koa = require("koa");

// 创建 Koa 实例
const app = new Koa();

// 启动服务器监听3000端口
app.listen(3000, () => {
  console.log("服务已启动,监听 http://localhost:3000");
});

创建访问路由

const Koa = require("koa");
const Router = require("@koa/router");
const cors = require("koa2-cors"); // 引入 CORS 中间件

const app = new Koa();
const router = new Router();

// 添加跨域支持
app.use(cors());

// 定义情感分析的 HTTP 接口
router.post("/analyze", async (ctx) => {
  const { text } = ctx.request.body;
  // 逻辑处理
  ctx.body = "这是要返回给前端的数据";
});

// 启用解析 JSON 请求体的中间件
app.use(require("koa-bodyparser")());
app.use(router.routes());
app.use(router.allowedMethods());

// 启动 HTTP 服务器
app.listen(3000, () => {
  console.log("服务已启动,监听 http://localhost:3000");
});

上面的代码定义了一个用于情感分析的 POST 接口,提供给前端得调用路径是http://localhost:3000/analyze"。

为了提高代码兼容性,我们同时启用了跨域请求支持和解析 JSON 请求体的中间件:

  • 默认 Koa 并不直接支持路由功能,我们引入@koa/router 用来管理不同 URL 地址与其响应逻辑的绑定。
  • koa2-cors 是 Koa 框架中用于支持跨域请求的中间件。

在上面的接口中,我们通过ctx.body 设置服务端响应内容发送给前端,我们现在只需要根据用户输入的值,调用AI接口,设置ctx.body 的值即可。

集成 kimi AI模型配置

const Koa = require("koa");
const Router = require("@koa/router");
const cors = require("koa2-cors"); // 引入 CORS 中间件
const OpenAI = require("openai");

const app = new Koa();
const router = new Router();

// 添加跨域支持
app.use(cors());

// 配置 Moonshot AI 客户端
const client = new OpenAI({
  apiKey: "xxx", // 替换成你的 API Key
  baseURL: "https://api.moonshot.cn/v1",
});

// 定义情感分析的 HTTP 接口
router.post("/analyze", async (ctx) => {
  const { text } = ctx.request.body;

  if (!text) {
    ctx.status = 400;
    ctx.body = { error: "未提供输入文本" };
    return;
  }

  try {
    // 调用 Moonshot API
    const completion = await client.chat.completions.create({
      model: "moonshot-v1-8k",
      messages: [
        {
          role: "system",
          content:
            "你是一个情感分析模型,用于根据用户输入的文本分析情感,返回一个 JSON 对象,包含三个键值对,分别是 positive_prob, neutral_prob, negative_prob,分别代表正面情感的概率,中性情感的概率,负面情感的概率,概率值的范围是 0-1,概率值越高,情感越倾向于对应的情感类型,请严格按照 JSON 格式返回结果,不要添加任何额外的文本。",
        },
        {
          role: "user",
          content: text,
        },
      ],
      temperature: 0.3,
    });

    // 检查 API 返回数据并解析
    const response = completion?.choices?.[0]?.message?.content;
    if (!response) {
      throw new Error("未能从 API 返回有效数据");
    }

    let sentimentResult = {
      positive_prob: 0,
      neutral_prob: 0,
      negative_prob: 0,
    };

    try {
      // 尝试解析 JSON
      sentimentResult = JSON.parse(response);
    } catch (error) {
      console.error("解析 API 返回数据失败:", error);
      ctx.status = 500;
      ctx.body = { error: "数据解析失败" };
      return;
    }

    // 正常返回数据给前端
    ctx.status = 200;
    ctx.body = sentimentResult;
  } catch (error) {
    console.error("API 调用出错:", error.message);
    ctx.status = 500;
    ctx.body = { error: "服务器错误" };
  }
});

// 启用解析 JSON 请求体的中间件
app.use(require("koa-bodyparser")());
app.use(router.routes());
app.use(router.allowedMethods());

// 启动 HTTP 服务器
app.listen(3000, () => {
  console.log("服务已启动,监听 http://localhost:3000");
});

上述代码其实非常简单,核心就是配置 Moonshot AI 客户端,返回特定结构的数据。

const client = new OpenAI({
  apiKey: "xxx", // 替换成你的 API Key
  baseURL: "https://api.moonshot.cn/v1",
});

上面的代码是kimi的官方配置示例,包括client.chat.completions.create的调用及参数,如果你想具体了解,可以参考我其他文章中的教程

注意,上述代码中,我们通在message中定义了下面的预设值

{
    role: "system",
    content:
      "你是一个情感分析模型,用于根据用户输入的文本分析情感,返回一个 JSON 对象,包含三个键值对,分别是 positive_prob, neutral_prob, negative_prob,分别代表正面情感的概率,中性情感的概率,负面情感的概率,概率值的范围是 0-1,概率值越高,情感越倾向于对应的情感类型,请严格按照 JSON 格式返回结果,不要添加任何额外的文本。",
  }

从而获得了我们想要的数据类型,最终在返回给前端。

启动服务

至此,我们的后端接口服务就完成了,我们在终端中运行服务

node app.js

然后前端就可以访问了。

前段实现

前端得逻辑就非常简单了,我们只需要画一个输入框,提价数据给后端,然后用Eacharts渲染处理对应的结果即可。

我们先实现最基本的css

<template>
  <div class="analyzer">
    <h1>女友情感分析工具</h1>
    <textarea
      v-model="textInput"
      placeholder="请输入待分析的文本..."
      rows="3"
    ></textarea>
    <button @click="analyzeSentiment" :disabled="loading">
      {{ loading ? "分析中..." : "开始分析" }}
    </button>

    <div v-if="error" class="error">{{ error }}</div>
    <div v-show="sentimentData" class="result">
      <div ref="chart" style="width: 600px; height: 300px"></div>
    </div>
  </div>
</template>

<script setup>
import { ref, watch, onMounted } from "vue";
import axios from "axios";

// 状态管理
const textInput = ref("");
const loading = ref(false);
const error = ref("");
const sentimentData = ref(null);

// 调用后端接口分析文本情感
const analyzeSentiment = async () => {
  
};
</script>

<style scoped>
.analyzer {
  max-width: 600px;
  margin: 50px auto;
  text-align: center;
}

textarea {
  width: 100%;
  padding: 10px;
  margin-top: 20px;
  margin-bottom: 20px;
}

button {
  padding: 10px 20px;
}

.error {
  color: red;
  margin-top: 10px;
}

.result {
  width: 600px;
  margin-top: 40px;
}
</style>

然后调用后端接口并使用echarts渲染处理好的数据

<script setup>
import { ref, watch, onMounted } from "vue";
import axios from "axios";
import * as echarts from "echarts";

// 状态管理
const textInput = ref("");
const loading = ref(false);
const error = ref("");
const sentimentData = ref(null);
const chart = ref(null);

// 调用后端接口分析文本情感
const analyzeSentiment = async () => {
  if (!textInput.value.trim()) {
    error.value = "请输入文本!";
    return;
  }

  loading.value = true;
  error.value = "";
  try {
    const response = await axios.post("http://localhost:3000/analyze", {
      text: textInput.value.trim(),
    });

    sentimentData.value = response.data; // 保存返回数据
    console.log("sentimentData: ", sentimentData.value);
  } catch (err) {
    console.error(err);
    error.value = "分析失败,请稍后再试!";
  } finally {
    loading.value = false;
  }
};

// 渲染分析结果图表
const renderChart = () => {
  // if (!chart.value || !sentimentData.value) return;
  console.log("echarts: ", echarts);
  console.log("chart.value: ", chart.value);

  console.log(11);
  const chartInstance = echarts.init(chart.value);
  const option = {
    title: {
      text: "女友情感分析结果",
      left: "center",
    },
    tooltip: {},
    xAxis: {
      type: "category",
      data: ["开心", "平静", "生气"],
    },
    yAxis: {
      type: "value",
    },
    series: [
      {
        name: "概率值",
        type: "bar",
        data: [
          sentimentData.value?.positive_prob || 0,
          sentimentData.value?.neutral_prob || 0,
          sentimentData.value?.negative_prob || 0,
        ],
        itemStyle: {
          color: ({ dataIndex }) => {
            return ["#73C9E6", "#FFC107", "#FF5252"][dataIndex];
          },
        },
      },
    ],
  };

  chartInstance.setOption(option);
};

// 监听 sentimentData 变化,渲染图表
watch(sentimentData, (newVal) => {
  if (newVal) {
    renderChart();
  }
});

// 确保 DOM 加载完成后初始化 ECharts
onMounted(() => {
  renderChart();
});
</script>

大功告成!试试最终效果

不错,看着挺可以!我去对线去

悲伤的后记

哄了好久,终于和女朋女和好了。晚上,朋友喊我出去喝酒。为了试探下女友的态度,我把女友的回复粘贴到了AI里,准备根据分析结果决定要不要去!

看到结果,我半信半疑,只好继续试探的问女朋友,那我去了?女朋友一直很开心的告诉我可以。


后来,我就真去了,再后来,就没有然后了,女朋友把我电话也删除了。


AI不可信啊,兄弟们!越想越气,垃圾程序直接删除