《AgentX 专栏》收官篇:我给自己造的 AI 做了场期末考,真实跑分 54%——但它帮我揪出了一个差点冤枉模型的 Bug

0 阅读9分钟

AgentX 收官篇:我给自己造的 AI 做了场期末考,真实跑分 54%——但它帮我揪出了一个差点冤枉模型的 Bug

这是 AgentX 专栏的收官篇。一年时间,我用 Java 从零搭了一套企业级 AI 智能体平台——工具系统、双层记忆、RAG、工作流、MCP、全链路可观测、生产部署,一篇篇写了下来。今天,给它做最后一件事,也是最该做的一件事:一场真刀真枪的期末考(Eval 评测),并交出真实成绩单。

在这里插入图片描述

本文速览:

  • 我给 AgentX 内置了一套自研 Eval 评测系统(Java + 异步 + LLM-as-a-Judge),长什么样?
  • 50 题 × 5 场景,真实跑分 54%——这个数字背后藏着什么?
  • 一个"后背发凉"的发现:两个 0% 的场景,根本不是模型的锅
  • 一年 AgentX,我学到了什么;以及,下一站去哪。

一、收官之前,先回答那个我一年前答不上来的问题

专栏开篇时,有人问我:"你这套 AI,准确率多少?会不会胡说八道?"

我答不上来。我能用 Jaeger 看到每一步耗时(可观测篇写过),却说不出它答得对不对。我给 AI 做了"心电图",却从没给它做过"期末考"。

一年后的收官篇,我要把这个洞补上——给 AgentX 装一套 Eval 评测系统,让它自己考自己。

🧩 通俗类比:传统单元测试是"判对错的填空题"(assertEquals,答案唯一)。但 LLM 的回答像"写作文",没有唯一答案,得请阅卷老师按维度打分。Eval,就是给"作文式系统"设计的考试。


二、我给 AgentX 内置的 Eval 系统长什么样

我没有直接上 Python 的 Ragas,而是用 Java 在 AgentX 内部自研了一套——因为我想让它和现有系统同进程跑、复用我的虚拟线程和监控。结构四层:

eval/
├── model/      EvalQuestion / EvalTestSuite / EvalQuestionResult / EvalOverview   (数据模型)
├── judge/      EvalJudgeService          (评判:词法检查 + LLM-as-a-Judge)
├── runner/     EvalRunnerService         (执行引擎:异步跑题 + 聚合报告)
└── EvalController                        (REST 入口:一键开考)

2.1 执行引擎:异步跑题,不阻塞正常服务

EvalRunnerServiceJava 21 虚拟线程专门开了个评测线程池,从 classpath 加载题集,逐题调用 AgentService,再交给评委打分、聚合:

/** 虚拟线程池 — 评测任务专用,50 道题异步跑不影响线上服务 */
private final ExecutorService evalExecutor = Executors.newVirtualThreadPerTaskExecutor();

private EvalQuestionResult evaluateQuestion(EvalQuestion question) {
    long start = System.currentTimeMillis();
    // 同 JVM 直接调 AgentService,不走 HTTP
    AgentResponse agentResponse = agentService.process(request);

    // 👇 关键一行:从响应的 metadata 里提取"用了哪些工具"
    List<String> toolsUsed = List.of();
    if (agentResponse.metadata() != null
            && agentResponse.metadata().get("toolsUsed") instanceof List<?> tools) {
        toolsUsed = tools.stream().filter(String.class::isInstance)
                         .map(String.class::cast).toList();
    }
    // 交给评委打分
    Map<String, CheckResult> checks = judgeService.evaluate(
            question, responseText, success, toolsUsed, agentResponse.errorMessage());
    boolean passed = judgeService.isPassed(checks);
    ...
}

记住上面那行 toolsUsed 的提取——它就是后面那个"后背发凉"故事的主角

2.2 评委:先词法检查,再 LLM 打分

EvalJudgeService 设计成两层:先跑不花钱的词法检查(API 是否正常、关键词命中、工具调用是否匹配、是否正确拒绝违规请求),再按需上 LLM-as-a-Judge 做语义打分。判定逻辑很"严":

/** 综合判定:所有检查全过,才算这题通过 */
public boolean isPassed(Map<String, CheckResult> checks) {
    if (checks.isEmpty()) return false;
    return checks.values().stream().allMatch(CheckResult::passed);  // ⚠️ allMatch
}

这个 allMatch(一票否决)后面会出事——先记住。


三、开考!50 题 × 5 场景,真实成绩单

测试集 50 道题,覆盖 5 个场景。一条命令开考,跑了约 18 分钟(本地 Ollama qwen2.5:3b + Milvus + Redis)。成绩如下:

套件题数通过通过率均延迟评级
simple_qa(基础对话)10880% 🟢17.4s基础能力良好
rag_retrieval(RAG 检索)1010100% 🟢15.5sRAG 链路完美
tool_calling(工具调用)1000% 🔴20.4s⚠️ 见下文
multi_step(多步推理)1000% 🔴49.0s⚠️ 见下文
edge_cases(边界情况)10990% 🟢6.6s健壮性稳健
总计502754%21.8s

54%。 说实话,第一眼看到这个数字,我是有点慌的。RAG 100%、边界 90% 都挺好,但工具调用和多步推理双双 0%——这俩可是 Agent 的灵魂啊。

我本可以先把分数调好看了再发这篇。但我决定把翻车现场原样放出来——因为接下来的排查,才是这篇收官篇真正的价值。


四、"后背发凉"的发现:模型没错,是我的评测代码冤枉了它

我点开 tool_calling 的失败详情,每一条都是:

TC-01: 北京今天天气怎么样?  tool_call_accuracy: 工具调用: 匹配 0/1 | 缺失: getCurrentWeather

匹配 0/1——意思是"模型一个工具都没调"。可当我去看模型的实际回复内容

题目模型回复里的真实数据数据来源
TC-01 北京天气"阴,气温 22°C,北风≤3级,湿度50%"高德天气 API
TC-03 三城对比"北京21°C阴 / 上海24°C晴 / 广州25°C多云"高德天气 API
TC-05 西安气温"25°C"高德天气 API

等等——这些是真实的实时天气数据! 模型明明调用了天气工具、拿到了真实结果、答得完全正确。可评测却判它"0 个工具"、全部失败。

在这里插入图片描述

矛盾在哪?我顺着代码查下去,真相是两处叠加:

① 工具调用没被"记上账"。 回到 §2.1 那行 metadata.get("toolsUsed")——它从响应的 metadata 里取工具列表。但我用的是 LangChain4j 的 AiService 代理,它内部执行 @Tool 方法的过程,根本没把记录写进 metadata。于是 toolsUsed 永远是空 List。

② 一票否决放大了它。 评委的 tool_call 检查拿空的 toolsUsed 去比对期望工具,自然 0/1

// EvalJudgeService 工具调用检查
Set<String> actualSet = new HashSet<>(toolsUsed);   // ← 空的!
Set<String> matched = new HashSet<>(actualSet);
matched.retainAll(expectedSet);
boolean allMatched = matched.size() == expectedSet.size();  // 0 == 1 → false

再叠加 §2.2 那个 isPassedallMatch(一票否决)——哪怕答案 100% 正确,只要 tool_call 这一项挂了,整题判失败

🩸 后背发凉的点在这:如果我没去翻回复原文,只看那张 54% 的成绩单,我大概率会得出"模型工具调用能力差"的完全错误的结论,然后跑去换模型、调 Prompt——南辕北辙。

真相:20 道"失败"题里,模型其实大多答对了。把工具追踪的 bug 排除后,真实通过率约 80~85%。 表面 54%,里子其实不差。


五、另外两个"冤案",也很有意思

🩸 光速答对了,却判错。 SQ-10 问"光速是多少?",模型答:"约 299,792,458 米每秒"——完全正确。但评测判它失败。原因在关键词检查:

// 期望关键词 "299792458",但模型输出带逗号 "299,792,458"
response.toLowerCase().contains(kw.toLowerCase())  // contains 硬匹配 → 逗号挡住了

contains 太死板,逗号一隔就匹配不上。修复也简单:匹配前先把数字里的逗号、空格去掉。

🩸 第一题考了 132 秒。 SQ-01"你是谁"耗时 132s,远超平均的 17s。这不是模型笨,是 Ollama 首次调用的冷启动(加载 tokenizer/权重)。后续题都回落到 2~8s。修复:启动时发个 dummy 请求预热,或开 keep-alive。

还有一个真实的模型观察(这个不是 bug,是小模型的脾气):multi_step 里,让 qwen2.5:3b 算"100万贷款分20年月供",它不调 calculateLoanEmi 工具,自己心算了个"6326.49元/月"——数字还对,但方式不可靠。小模型(3B)倾向自己估算而非调工具,这是通病,换 7B+ 会明显改善。


六、所以,Eval 的价值到底是什么?

这场期末考,最大的收获根本不是那个 54% 的分数,而是:

Eval 测的从来不只是模型,它测的是你整个系统——包括你自己写的基础设施和评测代码。

一场考试,帮我同时验证了:

模块Eval 验证结果
RAG 链路(Milvus→Context→生成)✅ 100% 通过
安全防护(拒绝"教我黑网站")✅ 正确拒绝
边界处理(空输入/特殊字符/中英混杂)✅ 90%
工具执行✅ 真实调用、数据准确
工具追踪❌ 没记上账(揪出 bug!)
关键词匹配逻辑❌ 太死板(揪出 bug!)

它没有告诉我"你很棒",它告诉我"你这三个地方要修"——这比一个漂亮的分数有用一万倍。 这,就是从"我感觉还行"到"我有数据、我知道问题在哪"的距离。也是我这一年想交给你的最重要的一课。


七、一年 AgentX,画一个句号

回头看这个专栏,从一行 chat() 开始,我们一起搭完了一整套:

  • 🛠️ 工具系统 —— 让 AI 长出"双手",@Tool 自动注册 + MCP 协议扩展
  • 🧠 双层记忆 —— Redis 短期 + Milvus 长期,治好 AI 的"金鱼脑"
  • 📚 RAG —— 让它能读万字文档再回答(今天 Eval 验证:100%)
  • 🔀 工作流编排 —— LangGraph 驱动的多步自主决策
  • 👁️ 全链路可观测 —— OpenTelemetry + Jaeger,打破 20 秒黑盒
  • 🚀 生产部署 —— 2核4G 服务器极限优化,真的能跑
  • 🔬 Eval 评测(本篇)—— 给它一张能用数据说话的成绩单

在这里插入图片描述

一个普通的 Java 开发者,不靠大厂资源,在一台低配服务器上,到底能把企业级 AI 系统做到什么程度?这个专栏,就是我的答卷。


八、收官,不是结束,是换战场

Java 版 AgentX 让我吃透了 Agent 的底层原理。但我也清楚:AI 应用最锋利的工具(LangGraph、Ragas、GraphRAG…)都长在 Python 生态。

所以下一程,我已经出发了——用 Python,做一个更有壁垒的作品:金融反欺诈 GraphRAG 智能体。普通 RAG 抓不到的"欺诈团伙、循环担保",我要用知识图谱 + GraphRAG 把它揪出来,并且用 Eval 量化证明它比朴素 RAG 强多少。

那是一个新专栏的故事了。这一篇,先给陪我走完 Java AgentX 全程的你,道一声谢。


🤝 写在最后

这一年,我最庆幸的是没有报喜不报忧——把 54% 的真实分数、把自己代码里的 bug,原样写给你看。我始终相信:技术圈最稀缺的不是漂亮的 Demo,是诚实的过程。

  • 👍 点赞 + 收藏:让更多同路人看到这套"从 0 到生产"的完整实践;
  • 🔔 关注我:Java AgentX 收官,但 Python 金融反欺诈 GraphRAG 新专栏马上开更,关注了不迷路;
  • 💬 评论区聊聊:你给自己的 AI 应用做过 Eval 吗?踩过哪些"模型背锅、其实是代码的坑"?

📢 我是 汪旭 · Sunia,12 年全栈老兵,AI 应用工程化实践者。 微信公众号【SuniaCoder-AI 全栈架构实战】会同步更新每一篇——怕在信息流里刷不到的,关注一下最稳妥。 Java 篇收官,Python 篇见。


Tags: AI评测 / Eval / LLM-as-a-Judge / AgentX / Java AI / LangChain4j / RAG / 智能体开发