用 OpenAI API 给老 Java 系统“续命”:一个佛系 Vim 党的实战记录
上周五晚上,我正躺在工位上刷 GitHub Trending(没错,摸鱼是程序员的基本修养),突然钉钉弹出一条消息:“老板想在咱们后台加个 AI 功能,下周三上线。”
我一口冰美式差点喷出来——这需求来得比线上 P0 故障还猝不及防。
我是那种在公司待了三年多、每天准时打卡、代码写得比注释还清晰的老油条。Vim 是我的 IDE,.vimrc 比 .bashrc 还长。团队里都说我“代码有洁癖”,其实我只是懒得后期维护别人写的天书。但这次……AI?我们还在用 Spring Boot 2.5 + MyBatis 的老古董系统啊!
不过转念一想:跳槽简历上写“集成过 OpenAI API”总比“优化过 SQL 查询”听起来高级点吧?行,干就完了。
为啥不用 LangChain?因为太重了!
一开始,我本能地去搜 “Java OpenAI SDK”。结果发现官方没有!社区倒是有个 OpenAI-Java,但更新慢得像我们公司的审批流程。后来听隔壁组的小年轻说 LangChain for Java 已经出来了,赶紧拉下来试了试。
结果……启动一个最简单的问答链,内存直接飙到 1.2G。我们生产环境 JVM 才给 2G,这玩意儿跑上去怕不是要触发 OOM 被运维大哥半夜电话问候。
佛系吐槽:LangChain 确实强大,抽象层做得漂亮,但对于只想“调个接口”的场景,简直像为了切苹果买了一整套米其林厨房。
于是我决定——自己封装 HTTP 调用。简单、可控、还能塞进我们那套老旧的工具类体系里。
手搓 OpenAI API:HTTP + Jackson 足矣
OpenAI 的 REST API 其实很直白。核心就是 POST 到 https://api.openai.com/v1/chat/completions,带个 JSON body 和 Authorization header。
但在 Java 里,怎么发请求才不显得“原始”?我们项目里本来就有 OkHttp 和 Jackson,那就别引入新依赖了——少一个 jar 包,少一次 Maven 冲突,少一次被测试提 Bug。
先定义个简单的 DTO:
public class ChatMessage {
private String role; // "system", "user", "assistant"
private String content;
// 构造器、getter/setter 略,Vim 自动生成 yyds
}
public class ChatCompletionRequest {
private String model = "gpt-3.5-turbo";
private List<ChatMessage> messages;
private Double temperature = 0.7;
private Integer maxTokens = 512;
// 同上
}
然后写个工具类(名字我都懒得想,就叫 AiHelper):
@Component
public class AiHelper {
private final OkHttpClient client = new OkHttpClient();
private final ObjectMapper objectMapper = new ObjectMapper();
@Value("${openai.api.key}")
private String apiKey;
public String chat(List<ChatMessage> messages) {
try {
ChatCompletionRequest request = new ChatCompletionRequest();
request.setMessages(messages);
RequestBody body = RequestBody.create(
objectMapper.writeValueAsString(request),
MediaType.get("application/json; charset=utf-8")
);
Request req = new Request.Builder()
.url("https://api.openai.com/v1/chat/completions")
.header("Authorization", "Bearer " + apiKey)
.header("Content-Type", "application/json")
.post(body)
.build();
try (Response response = client.newCall(req).execute()) {
if (!response.isSuccessful()) {
throw new RuntimeException("OpenAI API error: " + response.body().string());
}
String responseBody = response.body().string();
// 简化解析,实际建议用 JsonNode 避免强依赖结构
JsonNode root = objectMapper.readTree(responseBody);
return root.get("choices").get(0).get("message").get("content").asText().trim();
}
} catch (Exception e) {
log.error("调用 OpenAI 失败", e);
// 降级返回空字符串 or 抛业务异常,看场景
return "AI 今天心情不好,不想说话...";
}
}
}
看起来是不是平平无奇?但可读性和可维护性才是重点。三个月后另一个同事接手这段代码,一眼就能看懂“哦,就是发了个 HTTP 请求”。
实战场景:自动生成 SQL 注释
我们系统有个痛点:数据库表几十张,字段几百个,但注释全是中文拼音缩写,比如 yhm(用户名)、sjh(手机号)。新来的实习生每次看表都像在玩密室逃脱。
产品经理灵机一动:“能不能让 AI 根据字段名自动猜注释?”
我说:“可以,但别指望它 100% 准确,当参考就行。”
于是搞了个小功能:用户选中一张表,点击“AI 注释建议”,后端把字段列表发给 GPT,让它生成每个字段的可能含义。
Prompt 设计很关键。一开始我写:
请解释以下数据库字段名的含义:yhm, sjh, dzyx
结果 GPT 回:“yhm 可能是‘用户号码’,也可能是‘英文名’;sjh 可能是‘手机号’或‘设备号’……”
太模糊了!后来改成带上下文的 system message:
List<ChatMessage> messages = new ArrayList<>();
messages.add(new ChatMessage("system",
"你是一个资深数据库管理员,熟悉中国互联网常用字段命名规范。" +
"请根据常见业务场景,为以下字段名提供最可能的中文注释。" +
"只返回 JSON 格式:{ \"字段名\": \"注释\" },不要任何其他内容。"));
messages.add(new ChatMessage("user",
"字段列表:[\"yhm\", \"sjh\", \"dzyx\", \"zcsj\"]"));
这次返回:
{"yhm":"用户名","sjh":"手机号","dzyx":"电子邮箱","zcsj":"注册时间"}
完美!直接解析成 Map 塞进前端表格,产品看了直呼“高科技”。
经验之谈:Prompt 不是写一次就完事的。我前后调了 7 版,最后固定成模板。建议把 prompt 也抽成配置文件,方便非技术同学调整。
OpenCode?抱歉,我们还没资格谈这个
说到 OpenCode,我知道这是指开源代码模型(比如 StarCoder、CodeLlama)。但现实很骨感——我们连 GPU 服务器都没有,更别说 fine-tune 一个 15B 参数的模型了。
所以现阶段,用好 OpenAI API 就是性价比最高的“AI 能力”。特别是 gpt-3.5-turbo,便宜($0.001/1k tokens)、快(平均 800ms 响应)、够用。
不过我也偷偷试过本地跑 CodeLlama-7b-quantized,结果在我 16G 内存的 MacBook 上,光加载模型就卡了 3 分钟,推理速度 2 token/s……算了,还是把钱省下来买咖啡吧。
性能 & 成本:别让老板破产
接入 AI 最怕两件事:响应慢 和 账单爆炸。
我们做了三件事控制风险:
- 限流:用 Guava 的
RateLimiter控制每分钟最多 10 次调用,避免测试同学疯狂点击。 - 缓存:相同输入(比如同样的字段列表)缓存 1 小时,Redis 存 JSON 字符串。
- token 监控:在日志里打印每次请求/响应的 token 数,定期分析高频使用场景。
下面是两周内的使用数据:
| 日期 | 请求次数 | 输入 tokens | 输出 tokens | 费用(USD) |
|---|---|---|---|---|
| 2024-04-01 | 128 | 4,200 | 3,800 | $0.008 |
| 2024-04-02 | 95 | 3,100 | 2,900 | $0.006 |
| ... | ... | ... | ... | ... |
| 总计 | 1,842 | 61,200 | 55,300 | $0.116 |
看到没?一个月不到 12 美分。老板看了报表后,居然主动问:“还有别的地方能用 AI 吗?”
LangChain 的正确打开方式
虽然我没在主流程用 LangChain,但它在调试和原型验证阶段帮了大忙。
比如我想试试“先查数据库再让 AI 总结”,用 LangChain 的 RetrievalQA 链几行代码就跑通了。确认逻辑可行后,再手撸轻量版集成到生产系统。
所以我的建议是:
- POC / 快速验证 → 用 LangChain,效率高
- 生产核心链路 → 自己封装,可控性强
写在最后:技术人别把自己逼太紧
说实话,这次接 OpenAI API,我原以为要啃一堆文档、调参、处理异步流式响应……结果三天搞定,还顺手优化了几个工具类。
有时候我们总想着“要用最先进的框架”、“要上最酷的架构”,但解决问题的本质,往往是找到最合适的工具,而不是最炫的工具。
我现在依然每天躺平摸鱼,Vim 里敲着老旧的 Java 代码。但至少,我的简历上可以写一句:“成功将生成式 AI 能力集成至千万级用户系统,成本可控,效果达标。”
下家面试官要是问我 LangChain 和 OpenCode 的区别?我就说:“LangChain 是瑞士军刀,OpenCode 是炼丹炉,而我,是个会用菜刀的厨子。”
毕竟,能跑起来的代码,才是好代码。不是吗?
附:避坑清单
- ❌ 别用
gpt-4做高频功能,贵且慢- ✅
temperature=0适合确定性任务(如格式转换)- ✅ 一定要处理 rate limit(429 错误),加 retry + backoff
- ✅ 敏感数据别传给 OpenAI!合规红线
- ✅ 日志里别打 full request/response,token 泄露等于钱泄露
就这样吧,我要去续杯咖啡了。AI 再强,也得靠人喂数据啊 😴

评论 0