目录

一、前言

二、代码审查中的问题

1. 🔴严重:多模态分析提示词从来没正确注入过

2. 🟡三个中等隐患

3. 🟢 六个小问题

三、联调中发现的六项前端问题

1. 圆环只画了半圈

2. 检测按钮永远灰色

3. 置信度算错了

4. 热门标签无效

5. 知识库入口打不开

6. 视频选择页语法错误

四、当前能跑通的功能


一、前言

        第五篇博客记录了前后端三阶段适配改造:Phase A路由对齐、Phase B视频异步化、Phase C流式输出。改造完成后,后端17个接口全部就绪,前端7个文件对齐完毕,mvn test 24个用例全部通过。从任何技术指标看,这个项目"已经完成了"。

        但功能能跑和代码没问题,是两回事。在做完所有功能后,我指示DeepSeek V4 Pro做了一件事—逐文件通读后端和前端代码并进行了前后端的测试。结果发现了从严重到轻微的十项问题,加上后续前端联调中暴露的六项,总共十六项。

        有些Bug属于"代码看似正确但功能完全失效"——比如多模态分析中音视频检测数据从来没被注入到提示词里。有些属于"数据对但显示错"——比如置信度算偏、圆环画短、按钮不亮。本文就记录这些隐藏的问题。

二、代码审查中的问题

我按严重程度分了三级:🔴严重(影响核心功能)、🟡中等(有隐患但不会立刻崩溃)、🟢轻微(防御性优化)。

1. 🔴严重:多模态分析提示词从来没正确注入过

这是我发现的最大的一个问题。multimodal_analysis.txt模板里写着这样的占位符:

【音频检测结果】
伪造概率:{audio.fakeProbability}
置信度:{audio.confidence}
伪造类型:{audio.fakeType}

而 LLMService 在调用时传的参数是:

Map.of("audio", audioResult, "video", videoResult)

PromptLoader的做法是遍历Map,对每个entry做 String.replace("{" + key + "}", value)。它会尝试把 "{audio}" 替换为audioResulttoString()结果。但模板里写的是{audio.fakeProbability}——这个字符串根本不包含"{audio}"这个子串,replace匹配不到。

后果很直接:多模态分析接口内LLM收到的提示词中,音视频检测结果区域是空的。它只能看到用户手写的文本描述,完全不知道音频检测出了90%伪造概率或视频检测出了换脸类型。这个功能从写好那天起就没正常工作过...

我的修复方案是扩展 PromptLoader,新增一个正则{([a-zA-Z]+)\.([a-zA-Z]+)}来匹配点分隔占位符。匹配到{audio.fakeProbability}后,用反射调用audioResult.getFakeProbability()获取字段值:

private String resolveField(Object obj, String fieldName) {
    String getterName = "get" + Character.toUpperCase(fieldName.charAt(0))
                     + fieldName.substring(1);
    Method getter = obj.getClass().getMethod(getterName);
    Object value = getter.invoke(obj);
    return value != null ? value.toString() : "";
}

这样{audio.fakeProbability}getFakeProbability()"0.95"→正确注入。原有的简单占位符{text}{knowledge}不受影响,仍走原来的replace逻辑。

2. 🟡三个中等隐患

DetectionTaskManager 内存泄漏

ConcurrentHashMap存储任务对象,但complete()fail()后从不remove。每次视频检测残留一个对象,短时间不会出事,但如果答辩演示时反复测试,可能OOM。可以加一个daemon清理线程,complete/fail后延迟30秒自动 remove。

LLMService流式调用用了裸线程

原代码new Thread(() -> { ... }).start(),绕过Spring的线程池管理。JVM关闭时这些线程不会被中断,可能卡住退出。可改用@Async注解,通过applicationContext.getBean获取代理调用executeStreamCall

detection.js的finally块在视频异步检测时提前清除了isDetecting

异步检测走watchVideoTask分支后return,finally执行setData({isDetecting: false})——但此时异步任务还在跑,页面状态却被标记为"未在检测中",可能导致后续的canDetect判断错误放行。所以加 videoAsync 标志位,异步模式下跳过finally清除。

3. 🟢 六个小问题

上传的临时文件从没被删除过

DetectionControllerdetectAudiodetectVideo加上了finally块调deleteQuietly,检测完成后自动清理。异步视频检测在异步任务的finally中也进行清理。

detection.js在devMode=true时仍会发起真实的HTTP请求(虽然 catch 里有 mock 兜底)

改为在try块最前面判断devMode,true时直接走generateMockResult()返回,不发网络请求。

LLMService手写的JSON序列化方法原先只转义了反斜杠和双引号

没转义换行、回车、制表符。虽然正常prompt内容不含这些字符,但可能防御性不足。补全\n → \\n\r → \\r\t → \\t

模拟结束接口endSimulation原本只是调了一句analyzeText("请分析对话")

没有传入剧本名和对话内容,LLM只能泛泛回复。改为接收完整消息历史和scriptId,拼成带上下文的prompt。

@Async原来没指定线程池

Spring Boot 会默认用 SimpleAsyncTaskExecutor——一个任务创一个线程,无限增长。新增AsyncConfig,配置ThreadPoolTaskExecutor:核心2线程、最大4、队列10、shutdown等待60秒。

RestTemplate的重试拦截器对所有请求都生效,包括 SSE 流式长连接

如果流式请求被429或5xx触发重试,会导致重复发送。我让intercept开头判断URI是否含"stream""sse",是则直接执行不重试。

三、联调中发现的六项前端问题

修完后端Bug后开始实际联调,前端又冒出来六个问题(虽然不是我干的)。

1. 圆环只画了半圈

结果页置信度圆环用的是纯CSS border方案——给圆加border-top-colorborder-right-colorrotate(-90deg)。这个方案最多画180度,所以即使伪造概率99%也只显示半圈红。

改为conic-gradient方案:外层ring-bg用锥形渐变从12点方向按百分比填充颜色,内层ring-fill用卡片背景色做圆形挖空。0到100%都能正确显示。

2. 检测按钮永远灰色

WXML 里{{!canDetect}}绑定的是data.canDetect字段,但代码里canDetect是个方法。小程序的模板取值取不到方法,拿到undefined!undefined永远是true,按钮一直禁用。

我把canDetect从方法改为data字段,新增updateCanDetect()在所有输入变化点调用——选文件、输入文字、切Tab、检测开始和完成——让按钮状态始终与实际条件同步。

3. 置信度算错了

normalizeResult中文本检测的置信度公式是 Math.min(1, riskProb + 0.1)。当riskProb = 0.95 时结果为1,显示100%。但"95% 确定是伪造"和"100% 确定"差了一个量级。

改为Math.max(riskProb, 1 - riskProb)。95%伪造就是95%,95%真实也是95%,取较大的一方,直观且正确。(还是感觉哪里不对劲、后面再改吧)

4. 热门标签无效

知识库页点击"冒充公检法"标签无筛选结果。原因:后端KnowledgeItem的标签是"公检法"“电话诈骗"这种短词,前端标签是完整的"冒充公检法”,单向contains匹配——"公检法"在"冒充公检法"内为true,但tag.toLowerCase().includes("冒充公检法")为false。

改为双向匹配:搜索词包含标签或标签包含搜索词都算命中。同时加了category名匹配作为 fallback。

5. 知识库入口打不开

主页"更多服务"的"防骗知识库"用wx.navigateTo跳转,但知识库页面在底部TabBar中。navigateTo不能跳 TabBar 页面。改为wx.switchTab

6. 视频选择页语法错误

在一次批量替换时漏掉了if(res)块的闭合括号,导致detection.js整个解析失败。补回即可。

四、当前能跑通的功能

修复完成后,我们在本地完成了联调验证。

不依赖Flask即可运行的功能:

  • 知识库GET /api/knowledge加载5条数据,搜索和分类筛选正常
  • 模拟对话:选择4种剧本 → 对话 → AI 逐字SSE流式回复 → ≥8轮后自动结束分析
  • 文本检测:输入话术 → LLM 分析 → 弹出风险等级 + 可疑点 + 建议
  • 结果页conic-gradient圆环360度正确显示,置信度计算修正

需要 Flask 启动的功能:

  • 音频检测(Flask 5001 端口)
  • 视频检测 + 异步进度推送(Flask 5002 端口)

后端mvn test 24/24通过,Postman验证/api/llm/analyze/api/llm/scam/chat/stream通过。代码已推送到Gitee。


前端界面有点丑...

Logo

欢迎加入DeepSeek 技术社区。在这里,你可以找到志同道合的朋友,共同探索AI技术的奥秘。

更多推荐