在 AI 人才市场的讨论里,"年薪千万不如估值百亿,AI 大神看不上大厂"这类说法经常出现。它背后的真实信号不是薪酬本身,而是 AI 行业的竞争焦点变了:大家关注的已经不单是"哪个模型参数更大",而是"谁能把模型能力变成稳定、可评估、可交付的软件产品"。对开发者来说,这意味着 AI 大模型应用开发的核心能力,正从"会调 API"转向工程化落地。
这篇文章不讨论薪酬和公司选择,而是拆解 AI 应用开发中最容易被低估、却最能拉开差距的工程环节:AI Agent 的任务编排、Spring AI 在 Java 生态里的集成方式、模型部署的选型、AI 幻觉的排查,以及提示词、上下文和成本三个隐藏抓手。
适合已经写过接口、对 Spring Boot 有一定了解的开发者。读完以后,你会得到一张从概念到代码、从验证到排查的 AI 应用工程实践地图,而不是一堆散落的 API 示例。
1. 先想清楚:AI 应用竞争的是工程能力,不是模型参数
1.1 为什么同样的模型,有人做出 demo,有人做出产品
很多团队拿到的大模型是一样的,甚至用的是同一个 API。差距往往出现在模型之外。观察实际落地项目时,最常看到的五个问题:
- 提示词是写死在业务代码里,还是按模板和场景统一管理?
- 用户输入千奇百怪时,程序能不能保证输出结构稳定?
- 模型答错时,有没有校验、兜底和人工确认?
- 一次请求消耗多少 token、响应有多慢、并发上来会不会打爆成本?
- 线上出问题时,日志和链路能不能支撑快速定位?
这五个问题没有一个是模型本身能回答的,全部是工程问题。大模型 API 提供的是强大的生成能力,但它只是一个"能力引擎"。要让引擎在真实业务里稳定工作,还需要一层完整的工程骨架:接口层把用户输入变成规范请求,提示词层控制模型行为边界,检索层补充最新知识,校验层拦截错误输出,缓存和限流保护成本与稳定性,监控层记录每一次调用的质量。缺少任何一层,应用停留在 demo 阶段非常正常。
所以,判断一个 AI 应用是否能上线,不能只看"模型答得准不准",还要看"模型答错时系统会怎样"。工程化的核心就是在模型能力之外,补上可控性和确定性。
1.2 "AI 大神看不上大厂"背后的技术信号
用"AI 大神"来概括技术能力优秀的人不一定准确,但这类讨论里有一个值得注意的信号:能持续做出好 AI 产品的人,往往更愿意待在自己能定义技术方向、能快速尝试新方案的环境里,而不是固化在成熟流程中维护一套稳定旧系统。这不是价值观对错问题,而是技术成长路径的选择问题。
对普通开发者来说,与其羡慕别人的薪酬或估值,不如把注意力放在可迁移的能力上。目前市面上大量岗位需要的不是发明新模型的人,而是能把现有模型接进业务系统的人。具体来说,就是做好这些事情:把开源模型或商业 API 接入业务系统,设计 Agent 的任务流程,治理 AI 幻觉,优化请求成本,建立评估和回归机制。这些能力不依赖某一家公司,也不绑定某一个模型,换一个场景仍然成立。
后面几节就围绕这条主线展开:不追逐模型前沿,而是把模型当成一个组件,把工程做扎实。
2. AI Agent、Spring AI、模型部署:三个概念要放在一起理解
2.1 AI Agent:让模型从"回答问题"变成"完成任务"
AI Agent(智能体)是目前 AI 应用开发里最热的方向之一。通俗地说,它不是让模型"说一段话",而是让模型根据一个目标,自己拆解步骤、调用工具、检查结果,最后完成任务。
一个典型 Agent 至少包含四部分:
- 规划:拆解任务,决定下一步做什么。
- 记忆:短期记忆保存当前任务上下文,长期记忆来自外部存储或知识库。
- 工具调用:模型通过函数调用访问外部系统,比如查数据库、调接口、发消息。
- 执行与反馈:执行动作后,把结果再交给模型判断,循环直到结束。
工程难度在于:Agent 的自由度越大,越不可控。所以真实项目里不会让 Agent 无限循环,而是会设置最大轮数、工具白名单、输出格式约束和人工确认节点。Spring AI 在 Java 生态里提供的工具,主要就是解决这类工程化诉求,而不是让模型"随便跑"。
2.2 Spring AI:Java 生态接入大模型的一层抽象
Spring AI 是一个面向 AI 应用的 Spring 生态项目。它的目标是提供统一抽象层,让开发者用类似 Spring Boot 的方式接入大模型,而不是为每家模型厂商写一套不同的调用代码。
它解决的问题有几类:
- ChatClient:统一聊天接口,支持 system、user 提示词管理和结构化输出。
- EmbeddingModel:统一向量化接口,方便接入不同向量模型。
- VectorStore:统一向量存储抽象,对接内存、Redis、PGVector、Milvus。
- Tool 调用:通过注解把 Spring Bean 暴露成可被模型调用的工具。
- Advisors:类似过滤器,用来做上下文整理、日志记录、内容改写。
实际项目里,Spring AI 的价值不是让模型能力变强,而是降低集成成本,让团队用熟悉的 Spring 风格开发 AI 功能。需要注意:Spring AI 版本迭代较快,不同版本 API 会有差异,落地前要以官方文档和依赖版本为准,不要照抄旧版本博客。
2.3 模型部署:自建、API 与私有化的取舍
模型部署是 AI 应用落地里很容易被低估的一环。很多团队在测试环境用 API 调得很顺,到了生产环境才发现延迟、成本、合规和可用性全都不一样。
三种常见方式的对比:
| 部署方式 | 优点 | 主要代价 | 适合场景 |
|---|---|---|---|
| 商业 API | 接入快、免运维 | 单次调用成本、数据出域、限流 | 快速验证、数据敏感度不高的场景 |
| 自建推理服务 | 可控成本、数据内部闭环 | GPU 资源、运维复杂、版本更新 | 高频调用、数据隐私要求高 |
| 私有化一体机或混合部署 | 满足合规、离线可用 | 前期投入大、模型更新慢 | 政企、内网、强合规场景 |
自建推理路线里,常见做法是先用量化模型在开发机验证效果,再通过推理框架暴露成兼容接口,业务侧无需改代码。以本地方便验证为例,可以用 Ollama 这类工具快速启动一个模型:
# 本地开发验证,先确认 Ollama 已安装 ollama pull qwen2.5:7b ollama run qwen2.5:7b业务侧配置改成指向本地地址即可:
spring: ai: openai: base-url: http://localhost:11434/v1 api-key: ${LOCAL_AI_KEY}这里要特别强调:不要把"本地能跑通"等同于"生产可以用"。推理服务的吞吐、并发上限、GPU 显存占用和冷启动时间,都需要单独做压测和容量规划。
3. 搭建一个最小可运行的 AI 应用工程骨架
3.1 技术选型:用最小闭环验证全链路
为了演示从"模型调用"到"工程化"的完整链路,这里使用一个最小闭环:Spring Boot 提供接口层,Spring AI 封装模型调用,内存向量存储模拟知识库,最后用结构化输出保证返回结果可控。这个骨架把提示词、检索、输出校验、工具调用串在一起,适合作为学习模板。
示例工程假设:
- 技术栈:Java 17、Spring Boot 3.x、Spring AI。
- 模型来源:兼容接口,开发环境可以是商业 API,也可以是本地推理服务。
- 知识库:内存向量存储,重启后数据清空,生产环境要换成 Redis、PGVector 或 Milvus。
3.2 项目结构与依赖配置
先看最小目录结构:
ai-demo ├── pom.xml └── src/main/java/com/example/ai ├── AiDemoApplication.java ├── controller/AiController.java ├── service/AiChatService.java ├── service/RagService.java ├── config/VectorStoreConfig.java └── tool/DeviceTools.javapom.xml 中引入 Spring AI 相关依赖。版本不写死,以 Maven 中央仓库当前稳定版为准,避免教程里的版本和实际环境不一致。
<dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-model-openai</artifactId> </dependency> <dependency> <groupId>org.springframework.ai</groupId> <artifactId>spring-ai-starter-vector-store</artifactId> </dependency>application.yml 里配置模型地址、密钥和基础参数:
spring: application: name: ai-demo ai: openai: base-url: ${AI_BASE_URL:http://localhost:11434/v1} api-key: ${AI_API_KEY:demo-key} chat: options: model: ${AI_MODEL:qwen2.5:7b} temperature: 0.2 client: max-output-tokens: 800密钥不要写死在配置里,使用环境变量注入。temperature 调低可以降低输出随机性,适合知识问答和结构化输出场景。如果 base-url 指向本地推理服务,api-key 填一个占位值也可以,但生产环境必须走密钥管理。
3.3 核心代码:ChatClient、结构化输出与工具调用
先定义一个最基础的对话服务:
@Service public class AiChatService { private final ChatClient chatClient; public AiChatService(ChatClient.Builder builder) { this.chatClient = builder.build(); } public String ask(String question) { return chatClient.prompt() .system("你是一名资深软件工程师,回答要简洁、准确、结构清晰。") .user(question) .call() .content(); } }很多场景不希望模型返回松散文本,而是希望返回一个可解析的 JSON 对象。可以用 record 定义返回结构:
public record Solution(String title, List<String> steps, double confidence) { }public Solution plan(String problem) { return chatClient.prompt() .system("给出一个可执行的解决方案,并评估置信度。") .user(problem) .entities(Solution.class) .call() .entity(); }这样解析结果就有了类型安全,业务层可以进一步校验和兜底。如果模型连续输出不符合结构,要检查提示词是否足够明确,以及当前模型对结构化输出的支持情况。
工具调用是 Agent 的基础能力。通过注解把业务方法暴露给模型:
@Component public class DeviceTools { @Tool(description = "查询指定设备最近一次在线状态") public String lastOnline(String deviceId) { // 真实项目里这里会查询设备表 return deviceId + " 最近在线时间:2025-01-01 12:00:00"; } }创建 ChatClient 时把工具注册进去:
this.chatClient = ChatClient.builder(chatModel) .defaultTools(new DeviceTools()) .build();模型在回答设备问题时,可以自行决定是否调用这个工具。工程上建议在 user 提示词里明确"先调用工具查询,再基于结果回答",降低模型瞎猜的概率。
3.4 启动验证:输入、输出与异常分支
在 Controller 暴露 HTTP 接口:
@RestController public class AiController { private final AiChatService chatService; private final RagService ragService; public AiController(AiChatService chatService, RagService ragService) { this.chatService = chatService; this.ragService = ragService; } @GetMapping("/chat") public String chat(@RequestParam String question) { return chatService.ask(question); } @GetMapping("/rag") public String rag(@RequestParam String question) { return ragService.answer(question); } }启动后用 curl 验证:
curl "http://localhost:8080/chat?question=什么是缓存穿透"curl "http://localhost:8080/rag?question=数据库连接池的默认连接数是多少"正常结果应该是一段结构清晰的回答。异常时常见现象包括:服务启动失败(密钥或 base-url 配置错误)、请求超时(模型服务不可用)、返回内容为空(模型输出被拦截或内容策略限制)、返回结构不对(结构化输出解析失败)。这些现象在排错章节再展开。
注意:不要只验证程序能启动,还要验证输入、输出、异常分支和日志是否符合预期。AI 应用最容易出问题的地方往往发生在模型返回之后。
4. AI 幻觉不是模型的锅,是工程链路要补的课
4.1 幻觉的类型:事实性幻觉、逻辑幻觉、指令幻觉
AI 幻觉是指模型生成的内容与现实不符、逻辑矛盾或不符合指令要求。常见类型如下:
| 类型 | 表现 | 示例 |
|---|---|---|
| 事实性幻觉 | 编造不存在的事实 | 虚构某公司发布的版本号或日期 |
| 逻辑幻觉 | 推理链条站不住 | 前一步说禁用了缓存,后一步又说缓存生效 |
| 指令幻觉 | 不遵守用户明确约束 | 明确要求只输出 JSON,结果带了 Markdown 代码块 |
幻觉无法彻底消除,但工程上能显著降低。核心思路是:不让模型"自由发挥"它不知道的部分,同时给足可溯源的上下文。
4.2 缓解幻觉的工程手段
第一条是减少回答依赖模型内部记忆。把知识库和当前业务数据通过检索增强(RAG)放到提示词里,让模型优先基于给定上下文作答。RAGService 的最小实现:
@Service public class RagService { private final ChatClient chatClient; private final VectorStore vectorStore; public RagService(ChatClient.Builder builder, VectorStore vectorStore) { this.chatClient = builder.build(); this.vectorStore = vectorStore; } public String answer(String question) { List<Document> docs = vectorStore.similaritySearch( SearchRequest.builder().query(question).topK(3).build()); String context = docs.stream() .map(Document::getContent) .collect(Collectors.joining("\n")); return chatClient.prompt() .system("只基于参考资料回答。资料中没有的信息,直接说明不知道,不要编造。") .user("参考资料:\n" + context + "\n\n问题:" + question) .call() .content(); } }这段代码的关键有两点:一是给模型的上下文必须来自可溯源资料,二是 system 提示词明确限制"不知道就直说",降低编造概率。
第二条是调低温度参数。知识问答、结构化抽取场景建议用 0 到 0.3,别用高温度。高温度适合创意写作,不适合事实回答。
第三条是输出校验与兜底。对结构化输出,校验关键字段是否缺失、数值是否合理,不合法就重试或走默认值。对涉及操作类的 Agent 任务,最后一步增加人工确认或风险校验。
幻觉无法彻底消除,工程目标是把发生概率降到可控范围,并让模型在信息不足时明确告知用户"我不确定"。
4.3 幻觉排查路径
排查幻觉问题,按这个顺序看:
- 输入是否完整。用户问题本身是否模糊,缺少关键约束。
- 上下文是否准确。RAG 检索到的文档是否相关、是否太旧、是否混入噪声。
- 提示词是否明确。是否给了模型编造的空间,是否缺少"不知道就直说"的约束。
- 温度是否合适。事实问答是不是用了过高温度。
- 输出校验是否存在。结构化和数值结果有没有校验层。
实际项目里,幻觉治理不是一次性工作,而是要持续积累"错误案例集"。把用户反馈和评估中发现的问题保存下来,作为提示词优化和检索优化的回归数据。
5. 提示词、上下文与成本:三个隐藏工程抓手
5.1 提示词模板化,而不是散落在代码里
提示词是 AI 应用最重要的"业务配置",但很多时候会被写散在代码里。推荐做法是放到 resources 下按场景管理:
src/main/resources/prompts/ ├── chat-system.txt ├── rag-system.txt └── extract-system.txtSpring 里用 Resource 加载提示词模板:
@Value("classpath:/prompts/rag-system.txt") private Resource ragSystemPrompt;模板文件里写清楚角色、任务、约束和输出格式。测试时只改文件,不用重新编译业务代码。提示词改动要进入版本管理,和代码一起评审、回归。
5.2 上下文窗口是资源,要按预算分配
大模型的上下文窗口不是无限内存。超出窗口长度,会发生截断、遗忘或成本暴增,所以要把上下文当成资源来管理。
三种常见做法:
- 截断:保留最近 N 轮对话,丢弃更早的内容。
- 压缩:对历史消息做摘要,把长对话压缩成要点。
- 检索:只在需要时拉取相关知识片段,而不是每次塞入全量知识库。
Spring AI 的 Advisor 机制可以做这类处理,也可以自己写在调用前的准备函数里。建议所有涉及上下文的逻辑都集中在统一位置,方便后续调优和排查。
5.3 成本治理从请求结构开始
token 成本取决于输入和输出长度。要控制成本,先控制请求结构:
- 系统提示词保持精简,删除不生效的冗余说明。
- 知识检索只传 topK 之后的片段,不要传整篇文档。
- 输出限制 tokens,避免模型生成超长无用内容。
- 对相同或相似请求加缓存,减少重复调用。
- 设置单用户、单接口的限流和配额,防止异常调用打爆账单。
一个简单缓存思路:对问题和检索命中文档的组合做哈希,如果近段时间有相同结果直接返回。要注意缓存后的数据时效问题,业务数据更新频繁的场景要设置合理的过期时间。
6. 从演示到生产:还要补齐部署、监控与回滚
6.1 配置外置与密钥管理
演示项目里配置写在 application.yml,生产环境不能这么干。至少要做到:
- 模型密钥、数据库密码、存储地址全部使用环境变量或配置中心。
- 不同环境用独立配置,禁止把测试环境密钥带到生产。
- 涉及敏感信息的配置进入代码仓库前要做脱敏检查。
如果用的是商业 API,建议在网关或代理层统一管理密钥,不让业务服务直接持有厂商密钥。
6.2 日志、追踪与可观测性
AI 应用的可观测性比普通应用更强调"模型输入输出记录"。排错时不能只看 HTTP 状态码,还要知道模型收到了什么提示词、返回了什么内容、用了多少 token、耗时多久。
生产建议记录以下信息:
| 记录项 | 用途 |
|---|---|
| 用户输入原文 | 判断问题是否模糊 |
| 最终提示词(脱敏后) | 复现模型行为 |
| 模型输出 | 评估输出质量 |
| token 数 | 成本核算 |
| 延迟和重试次数 | 性能优化 |
| 工具调用结果 | 排查 Agent 决策链路 |
注意脱敏,防止用户隐私和业务敏感数据进入日志。
6.3 缓存、限流与回滚策略
生产环境需要三件事:缓存、限流、回滚。缓存降低重复请求的成本和延迟;限流保护模型服务和后端资源;回滚在模型升级、提示词调整效果不符合预期时能迅速恢复。
模型版本本身也要纳入发布流程。模型厂商更新模型、本地推理服务升级权重,都可能改变输出行为。上线前先小流量验证,再逐步放量。一旦发现问题,要能快速切回上一个可用版本。
上线前请自己问一遍:模型调用失败时,用户看到的是什么?有没有兜底提示?如果答案是"空白页",说明工程链路还没走完。
7. AI 应用开发的常见坑与排查清单
7.1 高频踩坑点速查
| 坑点 | 现象 | 原因 | 解决 |
|---|---|---|---|
| 密钥配置错了 | 启动报 401 或认证失败 | 环境变量没生效、写错密钥 | 检查配置来源,使用配置中心或环境变量注入 |
| base-url 指向错误 | 连接超时、404 | 地址多空格、路径不支持 | 确认服务地址和版本路径 |
| 温度过高 | 同一问题多次回答不一致 | 温度参数不适合事实问答 | 降到 0 到 0.3,配合结构约束 |
| 上下文过长 | 请求报错或输出截断 | 超过上下文窗口 | 加截断、压缩或检索 |
| 结构化输出不稳定 | JSON 解析失败 | 模型指令不明确或模型能力不足 | 明确输出格式,用结构化解析并校验 |
| 工具调用失败 | Agent 一直重试或乱答 | 工具方法异常、参数描述不准确 | 检查工具描述和异常处理 |
| 效果上线后变差 | 同一提示词效果突然下降 | 模型版本或行为发生变化 | 建立评估集,小流量验证,准备回滚 |
| 本地能跑生产不能 | 并发高时延迟飙升 | 推理资源不足、没有压测 | 做容量规划和压测,必要时换商业 API |
7.2 上线前检查清单
- 已确认模型来源、版本和调用方式,密钥通过安全方式注入。
- 提示词已模板化,并经过评审。
- 知识库数据已清洗,检索返回内容的时效性和相关性有保障。
- 输出校验已加,结构化结果有兜底逻辑。
- 日志已记录模型输入输出(脱敏后)、token 和耗时。
- 缓存、限流、配额已配置。
- 模型升级或提示词变更具备回滚方案。
- 已压测预期并发,延迟和成本在可接受范围。
7.3 下一步扩展方向
沿着这套工程骨架,可以继续深入的方向:
- RAG 精细化:混合检索、重排序、段落切分策略。
- Agent 进阶:多轮规划、任务队列、人工介入审批。
- 评估体系:建立离线评测集和在线质量监控。
- 统一模型网关:多模型路由、降级、成本分摊。
- Java 生态扩展:关注 Spring AI 版本更新,以及社区的工具调用和 Agent 编排方案。
AI 应用开发的竞争最后会落到工程能力上。对普通开发者来说,最有价值的做法不是盯着"哪些大厂给多少钱",而是把一个 AI 应用从 demo 一步步推到可观测、可回滚、可评估的生产状态。这个过程积累的提示词管理、上下文治理、幻觉排查和成本控制能力,无论未来模型怎么换代,都依然有效。