
先问各位一个问题你有没有觉得很多 AI 工具刚上线时特别好用提示词稍微写一写就能出惊人效果可用着用着要么开始频繁限制次数要么输出质量明显下滑要么界面里塞满了用不上的“增值功能”如果你也有类似体感那这篇文章想和你聊的话题就比较对味了。最近我在看 Cory Doctorow 关于AI 与“平台腐化”Enshittification时代的演讲视频时相关讨论并不只是停留在互联网平台吐槽上它其实能直接解释今天 AI 工程实践里的很多怪现象为什么大模型 API 越来越贵、为什么有些 Agent 应用刚开始演示很好但上线后不可控、为什么开源模型和私有化部署突然成为刚需。这篇文章我会从三个层面展开先讲清楚 Enshittification 在 AI 时代的具体表现和底层逻辑再把它映射到 AI 应用开发、模型选型、数据闭环和平台依赖这些真实工程问题上最后给出我在实际项目中总结的一套“反腐化”工程实践包括本地化部署思路、以 Spring AI 为例的完整集成代码、模型评估与降本方案以及开发者可以长期保持主动权的方法。内容会偏工程视角但不会一上来就堆术语。无论你是刚接触大模型应用开发的新手还是已经在做 Agent、RAG、模型微调的进阶开发者相信都能从里面找到可以直接上手的内容。1. 背景与核心概念1.1 什么是 EnshittificationEnshittification 是 Cory Doctorow 提出的一个互联网平台分析概念中文互联网经常译作“平台腐化”“劣化”或“烂掉化”。它描述的是一条平台生命周期曲线早期平台对用户足够好用各种补贴、免费功能、优质内容吸引大量用户。中期用户规模起来后平台开始对商家或供给端示好吸引更多供给。晚期平台两边都牢牢掌握在自己手里用户和供给端都很难迁移于是平台开始把价值一点点榨取给自己用户体验持续劣化。放到 AI 领域来看这种逻辑其实体现得相当明显。比如早期某大模型开放平台给开发者大量免费 token、低延迟、高额度等开发者的应用真正跑起来后开始调整限流策略、提高价格、收缩免费额度、把更多能力封装成更高价的套餐。开发者本来没有精力频繁切换底层模型只能默默承受成本上升。这个迁移成本就是平台“腐化”的空间。关键点Enshittification 的核心不是“平台变坏了”而是“平台掌握了足够大的切换成本从而可以主动降低产品质量而不流失用户”。1.2 AI 工程中的腐化风险体现把 Enshittification 套用到 AI 工程实践中至少能看到四个常见风险点风险类型具体表现对开发者的影响API 依赖风险模型 API 调价、限流、版本下架应用成本失控需要频繁改代码数据闭环风险使用平台 API 时数据回流给平台业务数据变成平台训练语料丧失数据主权模型黑箱风险提示词和参数可能被平台端动态调整输出不稳定难以复现实验结果生态锁定风险工具链绑定特定云厂商或平台迁移成本高议价能力弱所以今天再讨论 AI 工程不能只关注模型效果和算力还要关注平台锁定、成本治理、数据主权和可迁移性。这也是为什么开源模型、私有化部署、本地知识库这些方向越来越热的真实原因。1.3 为什么开发者需要关注这个话题可能有人觉得平台腐化是商业层面的事跟我一个写代码的有啥关系关系很大。因为 AI 应用天然是“API 胶水应用”大多数产品都建立在外部模型能力之上。如果你从一开始不把“平台切换成本”“数据闭环”“成本控制”纳入架构设计等到线上业务跑量之后再想调整就会发现自己已经被套牢了。这篇文章后面给的工程方案本质上就是在帮你构建一个“低切换成本、高可迁移性、数据可控”的 AI 应用底座。这套思路无论你现在用 OpenAI、国内大模型、开源模型还是云厂商模型都适用。2. 环境准备与版本说明纸上谈兵没有意义下面直接进入实操。这一节先把环境说清楚避免后续代码跑不通时不知道是哪里的问题。2.1 基础环境本文示例以 Java 技术栈为主因为 Spring AI 的生态越来越成熟适合后端开发者快速接入。示例环境如下组件版本/说明JDKJDK 17 或以上Spring Boot 3.x 要求Spring Boot3.2.xSpring AI1.0.0-M6 或更新版本版本迭代较快按需调整Maven3.8模型 API示例以 OpenAI 兼容接口为例可切换到其他模型IDEIntelliJ IDEA / VS Code / Eclipse 均可说明Spring AI 的版本迭代速度很快API 有可能会调整。本文示例代码给出的是核心思路你实际使用时如果遇到类名或方法名差异建议先查看当前版本的官方更新日志。2.2 为什么用 Spring AISpring AI 是 Spring 官方推出的 AI 应用开发框架定位类似于 LangChain 在 Python 生态中的角色。它会帮你屏蔽掉不同大模型 API 的差异提供统一的 ChatClient、EmbeddingModel、VectorStore 等接口。换句话说Spring AI 本身就是一种“降低平台切换成本”的工程手段。你今天用 OpenAI明天换国产模型只需要调整依赖和配置不需要重写业务代码。2.3 示例项目结构ai-enshittification-demo/ ├── pom.xml ├── src/main/java/com/example/aidemo/ │ ├── AiDemoApplication.java │ ├── controller/ │ │ └── ChatController.java │ ├── service/ │ │ └── ChatService.java │ └── config/ │ └── AiModelConfig.java └── src/main/resources/ └── application.yml这个项目结构非常简单但这个演示真正的意义不在于代码本身而在于它展示了一套可替换模型后端的架构思路这也是对抗平台锁定和模型腐化的基础能力。3. 核心问题拆解AI 平台腐化的根本原因写代码之前先把几个关键概念拆透。只有理解了这些问题你才能在架构设计中提前做防护。3.1 切换成本与模型供应商锁定今天的大模型应用大多通过 API 方式调用模型能力。供应商会尽力让开发者“用起来舒服但走起来难”提供 SDK、示例代码、最佳实践降低接入成本。提供独家能力比如某些特殊参数、内部工具、独有的 embedding 能力。数据进入平台后很难完整导出到其他平台。社区生态和知识沉淀绑定特定平台。这些因素叠加在一起就形成了很强的迁移阻力。工程解法在你和模型供应商之间加一层抽象。不要直接在业务代码里调用某个厂商的 SDK而是通过统一接口调用。Spring AI 就是这种抽象层的实现之一。3.2 数据回流与数据主权很多开发者忽略了一个问题当你调用模型 API 时你的 prompt、上下文、用户数据都会发送到模型服务商的服务器上。如果只是在开发阶段测试问题不大。但在生产环境处理企业敏感数据时这就是重大风险。有些平台条款明确写了会用数据改进模型有些平台虽然没有明说但数据出域本身就有合规风险。工程解法建立数据分级机制。可以出域的数据走云端 API敏感数据走私有化部署模型。再配合本地向量数据库做知识库核心资产不出内网。3.3 服务质量劣化与模型版本漂移还有一个经常被忽略的问题大模型 API 背后的模型版本可能不是你测试时的版本。平台可以在不通知的情况下更换模型、调整温度参数逻辑、修改内容安全策略。你上周测试效果很好但上线后效果可能完全不同。这在行业内被称为模型版本漂移。工程解法在应用层记录每次调用的模型版本、参数、耗时、输出摘要建立评估基线。发现问题时可以快速对比当前调用与历史基线之间的差异。3.4 成本失控API 计费模型中的隐性问题很多 AI 应用的算账逻辑是开发阶段觉得单次调用没多少钱但上线后发现量一大API 费用就成了最大的运营成本。更麻烦的是 prompt 越长、上下文越大成本成倍增加。使用 RAG检索增强生成方案时大量文本被塞进上下文每次调用都是真金白银。工程解法构建 token 用量追踪机制对不同业务线的 token 消耗做监控同时做上下文裁剪、知识库检索优化、缓存命中率提升。这些手段都能在不牺牲效果的前提下降低成本。4. 完整实战案例构建一个低锁定风险的 AI 应用接下来我们完成一个真实的 Java 项目基于 Spring AI 构建一个可切换模型后端、支持流式输出、带有成本监控的智能问答应用。这个项目会完整展示“反平台腐化”架构的核心思路。4.1 创建项目结构先创建一个 Maven 项目目录结构如下ai-enshittification-demo/ ├── pom.xml └── src/main/ ├── java/com/example/aidemo/ │ ├── AiDemoApplication.java │ ├── controller/ChatController.java │ ├── service/ChatService.java │ └── config/ModelConfig.java └── resources/application.yml4.2 添加依赖在pom.xml中添加 Spring Web 和 Spring AI OpenAI 相关依赖?xml version1.0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd modelVersion4.0.0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version3.2.5/version relativePath/ /parent groupIdcom.example/groupId artifactIdai-enshittification-demo/artifactId version0.0.1-SNAPSHOT/version nameai-enshittification-demo/name descriptionDemo project for building AI application with low switching cost/description properties java.version17/java.version spring-ai.version1.0.0-M6/spring-ai.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId version${spring-ai.version}/version /dependency /dependencies dependencyManagement dependencies dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-bom/artifactId version${spring-ai.version}/version typepom/type scopeimport/scope /dependency /dependencies /dependencyManagement build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build repositories repository idspring-milestones/id nameSpring Milestones/name urlhttps://repo.spring.io/milestone/url snapshots enabledfalse/enabled /snapshots /repository /repositories /project注意Spring AI 的里程碑版本可能不会同步到 Maven 中央仓库所以要添加 Spring Milestones 仓库。如果你的项目使用的是已发布稳定版可以将repositories节点去掉。4.3 编写配置文件在application.yml中配置模型参数和 API Keyserver: port: 8080 spring: ai: openai: # 这里填入你的 API Key不要硬编码在项目里建议使用环境变量 api-key: ${OPENAI_API_KEY:your-api-key-here} base-url: ${OPENAI_BASE_URL:https://api.openai.com} chat: options: model: gpt-4o-mini temperature: 0.7 max-tokens: 1024配置说明api-key使用${OPENAI_API_KEY}环境变量注入避免把密钥提交到代码仓库。base-url这是非常关键的一项。Spring AI 默认兼容 OpenAI 接口所以你只需要改这个 base-url就能切换到其他 OpenAI 兼容的服务或开源模型网关而不需要改代码。model指定默认模型。不同厂商的模型命名不一样按需调整。temperature控制输出随机性。需要稳定输出的场景建议设置为 0.2 以下。4.4 编写启动类创建主启动类package com.example.aidemo; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; SpringBootApplication public class AiDemoApplication { public static void main(String[] args) { SpringApplication.run(AiDemoApplication.class, args); } }4.5 编写 ChatService这是业务核心层。我在这个 Service 里刻意做了一层封装没有直接用 Spring AI 的 ChatClient 写死在 Controller 里这样后续调整模型策略时只需要修改 Service 层。package com.example.aidemo.service; import org.springframework.ai.chat.ChatResponse; import org.springframework.ai.chat.messages.UserMessage; import org.springframework.ai.chat.prompt.Prompt; import org.springframework.ai.openai.OpenAiChatClient; import org.springframework.ai.openai.OpenAiChatOptions; import org.springframework.stereotype.Service; /** * 聊天服务统一封装模型调用逻辑方便切换底层模型 */ Service public class ChatService { private final OpenAiChatClient chatClient; public ChatService(OpenAiChatClient chatClient) { this.chatClient chatClient; } /** * 同步调用模型 * * param userMessage 用户输入内容 * param modelName 要使用的模型名称可以为 null则使用默认配置 * return 模型回复文本 */ public String chat(String userMessage, String modelName) { // 构造 prompt这里还可以追加 system prompt Prompt prompt buildPrompt(userMessage, modelName); ChatResponse response chatClient.call(prompt); if (response null || response.getResult() null) { return 抱歉模型未返回有效结果。; } return response.getResult().getOutput().getContent(); } private Prompt buildPrompt(String userMessage, String modelName) { OpenAiChatOptions.Builder optionsBuilder OpenAiChatOptions.builder(); if (modelName ! null !modelName.isBlank()) { optionsBuilder.withModel(modelName); } UserMessage userMsg new UserMessage(userMessage); return new Prompt(userMsg, optionsBuilder.build()); } }这段代码的核心价值在于ChatService可以灵活切换模型而不影响 Controller 和前端。当某个模型服务商出现价格上涨、质量下降或限流时你只需要在这里改模型名或配置甚至实现一个“多模型路由策略”而不需要修改 Controller。4.6 编写 Controller写一个简单的 REST Controller 提供接口package com.example.aidemo.controller; import com.example.aidemo.service.ChatService; import org.springframework.web.bind.annotation.*; import java.util.Map; RestController RequestMapping(/api/chat) public class ChatController { private final ChatService chatService; public ChatController(ChatService chatService) { this.chatService chatService; } /** * 同步聊天接口 * POST /api/chat * Body: {message: 你好请介绍一下你自己, model: gpt-4o-mini} */ PostMapping public MapString, String chat(RequestBody ChatRequest request) { String reply chatService.chat(request.message(), request.model()); return Map.of(reply, reply); } public record ChatRequest(String message, String model) { } }4.7 运行与验证在项目根目录执行mvn spring-boot:run启动成功后用 curl 测试接口curl -X POST http://localhost:8080/api/chat \ -H Content-Type: application/json \ -d {message: 你好请用一句话介绍 Spring AI, model: gpt-4o-mini}预期结果会返回一个 JSON其中reply字段是模型的回复内容。4.8 扩展添加模型切换策略如果你想真正实现“模型可迁移”可以再进一步把模型选择做成配置文件驱动。比如在application.yml中维护多个模型配置代码里通过参数选择模型ai: models: fast: model: gpt-4o-mini temperature: 0.7 accurate: model: gpt-4o temperature: 0.2 local: model: qwen2.5:7b base-url: http://localhost:11434在ChatService中读取配置根据 key 选择模型。这样当某个模型服务商出现“平台腐化”时你只需要修改配置即可完成迁移。5. 常见问题与排查思路实际开发中AI 应用遇到的坑比想象中要多。我整理了下面几个高频问题基本都是团队在开发 AI 应用时踩过的真实坑点。5.1 常见问题表格问题现象常见原因解决思路启动时报ApiKey缺失环境变量未设置或配置项写错检查OPENAI_API_KEY环境变量确认 application.yml 中的占位符语法调用 API 返回 401API Key 无效或权限不足检查 Key 是否过期查看平台后台的模型访问权限调用 API 返回 429触发限流请求频率过高或额度不足降低并发增加重试机制检查账户额度返回内容与预期严重不符模型版本或 temperature 参数波动固定模型版本降低 temperature记录调用日志对比响应速度很慢模型较大、网络问题或上下文过长缩短 prompt使用流式输出切换到轻量模型处理敏感数据时担心合规数据发送到外部 API改用私有化部署模型建立数据分级机制5.2 排查思路建议遇到 AI 应用异常时不要急着改代码按下面的顺序排查先看日志确认请求有没有发出去、模型有没有返回、返回了什么错误码。复现最小请求用 curl 等方式发一个最简单的请求排除业务代码干扰。对比不同模型如果同一个 prompt 在两个模型上表现差异巨大说明是模型层面的问题。确认配置是否生效Spring Boot 中配置优先级多检查环境变量、命令行参数、配置文件之间的覆盖关系。检查数据链路如果是 RAG 应用先确认检索结果是否准确很多时候不是模型的问题而是检索到的上下文就是错的。5.3 缓存命中与成本控制成本控制的另一个重要手段是缓存。对于重复性高的用户 query可以使用本地缓存或 Redis 缓存。下面给一个缓存示例的伪代码思路Service public class CachedChatService { private final ChatService chatService; private final ConcurrentHashMapString, String cache new ConcurrentHashMap(); public CachedChatService(ChatService chatService) { this.chatService chatService; } public String chatWithCache(String message, String model) { String cacheKey model : message; // 简单本地缓存生产环境建议使用 Redis 并设置过期时间 return cache.computeIfAbsent(cacheKey, key - chatService.chat(message, model)); } }注意本地缓存只适合单机部署和小规模场景。真正生产环境建议使用 Redis并对缓存设置 TTL避免缓存数据过期导致信息陈旧。6. 最佳实践与工程建议前面这些实战内容已经能帮你跑通一个基本项目。下面从工程角度给出一套完整的“反腐化”最佳实践清单。6.1 模型层保持可替换性模型服务商是“腐化”风险最集中的地方因此架构上要保证模型可替换。不要直接在你的业务代码里调用某个模型的 SDK而是通过统一接口封装一层。在配置文件维护多个模型源包括云端和本地。定期做模型评测掌握不同模型在你自己业务数据上的表现而不是只看榜单分数。6.2 数据层守住数据主权数据是 AI 应用最核心的资产数据主权问题必须纳入设计对数据进行分级分类明确哪些数据可以调用外部 API哪些必须留在私有环境。使用本地向量数据库存储业务知识库比如 Chroma、Milvus、Weaviate 等做好权限控制。如果必须使用外部 API尽量在传输层加密并在协议层面明确数据使用条款。6.3 成本层建立可观测体系成本失控是 AI 应用最常见的翻车点。建议建立三层成本观测体系日志层记录每次请求的 token 数量、耗时、模型、触发场景。监控层用 Prometheus Grafana 监控 token 消耗趋势设置预算告警。治理层针对高消耗场景设置限额、缓存、模型降级策略。6.4 评估层防止模型悄悄变差模型版本漂移很难完全避免所以要建立自己的评估基线沉淀一份覆盖业务核心场景的评测集比如 50 到 100 条典型提问。每次更换模型或供应商时用评测集跑一遍回归测试。在线上对模型输出做抽样监控把 responses 存到日志系统中便于事后对比。6.5 安全层最小权限原则生产环境涉及 AI 模型调用时安全问题往往会被忽略API Key 使用环境变量或密钥管理服务管理严禁硬编码到代码仓库。对调用模型的接口做鉴权防止被刷接口造成额度损失。对用户输入做基础的内容安全过滤防止通过 prompt 注入获取系统提示词或越权行为。7. 总结与动手实践建议Cory Doctorow 提出的 Enshittification 框架提醒我们一个平台为用户提供价值的意愿通常会在锁定用户之后下降。这条规律在 AI 平台上体现得尤其明显因为模型 API 的切换成本非常高开发者一旦把应用建立在某个平台之上就很容易丧失议价能力。但这并不代表开发者只能被动接受。通过模型抽象层、私有化部署、数据主权控制、成本可观测、评估基线建设这五个手段我们完全可以把主动权收回来一些。如果要把这篇文章沉淀成可执行的动作我会建议你按下面的顺序动手实践用 Spring AI 搭一个最小的 chat 项目先跑通默认模型。修改base-url接入一个 OpenAI 兼容的本地模型或开源模型网关体验一种模型切换。在项目里加入缓存机制观察 token 成本变化。写一个简单的评测脚本给 10 个常用问题固定模型输出保存为基线。定期复跑评测基线观察模型是否出现“偷偷变笨”或输出漂移的情况。你真正开始独立控制模型选型、切换和数据流转之后才能理解“平台腐化”这件事对工程细节的影响有多深。AI 技术更新很快但架构上的主动权思维会比任何一个具体模型都更持久。如果这篇文章对你有帮助可以收藏备用后续有新的实践心得我会继续补充。