代码跑通了,权限却漏了:Java 转大模型,别只盯着 Prompt 调优
如果你正准备往大模型方向转,《做过Java的人学大模型,哪些经验可以直接迁移?》这类问题别只看热度。更重要的是判断自己该补哪块能力,以及怎么证明你真的会。
摘要
很多从 Java 后端转过来的朋友,问我同一个问题:“怎么让 Agent 在业务里真正跑起来?”
我的回答通常有点泼冷水:别急着优化 Prompt,先搞定权限控制和可观测性。
为什么?因为我在带几个初级 AI 工程师时看到过太多这样的场景:Demo 里的 RAG(检索增强生成)或 Agent 逻辑丝滑无比,用户问什么答什么,甚至能自主调用 API 完成复杂任务。但一旦尝试接入内部系统,或者稍微增加一点并发量,问题就来了——模型幻觉导致它调用了不该调用的接口,或者因为缺乏日志追踪,根本不知道是检索错了、模型选错了还是代码执行崩了。
对于 Java 程序员来说,你们最大的优势不是懂 Transformer 架构,而是工程化思维。大模型应用正在从“玩具 Demo”阶段进入“生产可用”阶段,这个分水岭的标志就是:谁先解决了权限隔离、日志审计和失败兜底,谁才能真正接盘企业级需求。
目录
- Java 开发者的“降维打击”:工程基建的迁移
- 补齐短板:从“确定性强类型”到“概率性弱类型”
- 实战对照:从 Demo 到生产的关键一跃
- 项目练习建议:不要造轮子,要造“难用的轮子”
- 面试准备:面试官到底想听什么?
- 总结
Java 开发者的“降维打击”:工程基建的迁移
不要轻视 Java 后端那些看似枯燥的经验。在大模型应用开发中,以下能力是直接可用的:
1. 领域驱动设计(DDD)意识:大模型应用本质上也是软件系统。你需要清晰地划分边界,比如“意图识别层”、“工具调用层”和“业务逻辑层”。这和你拆分 Service 层、DAO 层的思路是一模一样的。
2. 配置管理与环境变量:Prompt 模板、模型参数、API Key,这些都不应该硬编码。Spring Boot 的application.yml习惯可以完美迁移到 LangChain4j 或 Spring AI 的配置体系中。
3. 异常处理与重试机制:LLM 的输出是不确定的。Java 里你对 HTTP 超时、数据库锁的处理逻辑,同样适用于应对 LLM 的 Timeout 或结构化解析失败。
取舍建议:如果你之前主要写 CRUD,现在需要刻意练习“非确定性逻辑”的处理。传统的后端追求强一致性,而 AI 应用往往需要容忍一定程度的模糊性,并通过后续步骤进行校验。
补齐短板:从“确定性强类型”到“概率性弱类型”
Java 是静态强类型语言,编译器帮你挡住了一半的错误。Python 和大模型生态是动态弱类型的,且输出具有概率性。你需要补齐的技能点并不深奥,但很关键:
- JSON Schema 约束:这是连接 Java 严谨性和 LLM 自由性的桥梁。不要指望模型每次都输出完美的 JSON。在代码中强制定义输出结构,并使用库(如 Jackson 配合自定义反序列化器,或 LangChain4j 的
@ToolReturn)进行校验和修正。 - 向量数据库基础:不需要成为算法专家,但要懂 Embedding 的本质。理解为什么“语义相似度”不等于“关键词匹配”,以及如何处理向量存储的更新策略(增量 vs 全量)。
- Prompt 工程的结构化:不要只会写“你是一个助手”。要学会使用 XML 标签包裹上下文,明确角色、任务、约束和输出格式。
实战对照:从 Demo 到生产的关键一跃
让我们看一个具体的例子。假设我们要做一个“查询员工考勤并请假”的 Agent。
Demo 阶段(常见写法)
// 伪代码:典型的简单 Agent public String handleRequest(String userQuery) { // 1. 直接调用 LLM ChatLanguageModel model = new OpenAiChatModel(apiKey); // 2. 简单的提示词 String prompt = "用户说:" + userQuery + "\n请帮他查考勤。"; // 3. 返回结果 return model.chat(prompt); }这个代码在本地跑通没问题。但在生产环境中,它有两个致命缺陷:
1. 权限黑洞:模型可能直接拼接 SQL 去查数据库,或者调用内部未公开的接口。
2. 不可观测:如果模型返回了错误信息,你只知道“错了”,不知道是 Prompt 不对,还是检索到的考勤数据有问题。
生产阶段(Spring AI / LangChain4j 风格)
引入 Spring AI 后,我们利用其内置的工具调用(Tool Calling)机制和声明式安全框架。
@Service public class AttendanceAgentService { private final ChatClient chatClient; public AttendanceAgentService(ChatClient.Builder builder) { this.chatClient = builder.build(); } /** * 核心改进点 1:使用 @Tool 注解明确工具边界 * 核心改进点 2:返回值严格限制为 DTO,禁止直接暴露原始文本 */ @Tool(description = "查询指定员工的考勤记录") public List<AttendanceRecord> queryAttendance(@Param("employeeId") String employeeId, @Param("startDate") LocalDate start) { // 这里应该加入 Spring Security 的鉴权检查 // if (!hasPermission(employeeId)) throw new AccessDeniedException(); // 调用底层服务... return attendanceRepository.findByEmployeeIdAndStartDate(employeeId, start); } public String processAttendanceRequest(String userId, String query) { // 核心改进点 3:结构化响应 + 日志追踪 ResponseMetadata metadata = chatClient.prompt() .user(query) .system("你是一个考勤助手。只能使用提供的工具查询数据。") .call() .chatResponse(); // 获取元数据用于日志记录 // 解析结果,确保符合 JSON Schema return parseAndValidateResult(metadata); } }关键差异分析:
1. 显式权限控制:在@Tool的方法内部,你可以像写普通 Controller 一样加@PreAuthorize。这样,无论模型多么“聪明”,它都无法绕过代码层面的权限校验。
2. 可观测性:通过捕获ResponseMetadata,你可以记录每一次调用的 Token 消耗、延迟、以及关键的 Trace ID。这是后续排查模型幻觉的根本依据。
项目练习建议:不要造轮子,要造“难用的轮子”
为了准备面试或实战,我建议你做一个完整的“带权限控制的请假审批 Agent”。
技术要求:
1. 使用 Spring AI 或 LangChain4j 构建基础 Agent。
2. 实现两个工具:checkBalance(查看年假余额)和submitRequest(提交申请)。
3. 难点植入:
* 模拟一次“模型幻觉”:当用户输入非法参数时,模型尝试调用不存在的工具,你需要编写拦截器捕获并友好提示。
* 实现“人工审核”环节:当请假天数超过 3 天,Agent 不应自动提交,而是生成草稿进入人工审批队列。这体现了对业务边界的理解。
简历亮点:
不要只写“使用了 LangChain4j”。要写“设计了基于工具调用的权限隔离方案,通过声明式注解实现工具级细粒度控制,解决了 LLM 越权访问潜在风险”。
面试准备:面试官到底想听什么?
当面试官问你“从 Java 转大模型,你觉得最大的挑战是什么?”时,不要回答“数学不好”或“英文文献看不懂”。
推荐回答逻辑:
1. 思维转变:从“流程确定性”转向“概率性控制”。以前靠单元测试覆盖 100% 分支,现在靠结构化输出约束和后置校验。
2. 真正跑起来:强调你对“可观测性”的重视。提到你如何设计日志体系来追踪 Prompt 版本、Embedding 质量和模型响应延迟。
3. 安全边界:举例说明你如何通过 Tool Calling 机制将 LLM 的能力限制在预定义的 API 范围内,防止 Prompt Injection 导致的越权操作。
总结
Java 转大模型开发,不是让你去重造一个 LLM,而是让你用成熟的工程体系去驾驭这个不确定的黑盒。
记住这三句话:
1.Prompt 只是入口,权限才是底线。
2.日志不是记录,是调试幻觉的唯一线索。
3.不要追求模型的“聪明”,要追求系统的“稳健”。
在这个行业里,懂模型原理的人很多,但能把模型塞进生产环境、稳住线上事故的人很少。你的 Java 背景,正是填补这块空缺的最佳武器。
资料展示
下面是我整理的AI大模型学习资料和工具包预览,适合收藏后按主题逐步学习。
如果你想看完整资料目录,可以在评论区留言「资料」;也欢迎告诉我你更关注AI大模型里的哪类内容。