尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

AI工程实践:从技术选型到容器化部署的完整指南

AI工程实践:从技术选型到容器化部署的完整指南 在 AI 项目从 Demo 走向生产的过程中我遇到最多的不是模型效果不理想而是技术选型反复横跳模型用开源还是闭源、推理用原生 transformers 还是 vLLM、服务层用 Python FastAPI 还是 Spring AI 接入几乎每个环节都在“选边”。如果选型时没有把团队技术栈、硬件资源和业务约束想清楚后面重写的成本非常高。这篇文章就把一套完整的 AI 工程实践路径整理出来包含环境准备、选型对比、可运行的服务代码、容器化部署以及高频踩坑点适合正在从零搭建 AI 应用的开发者也适合正在做技术调研的团队。1. 背景与核心概念1.1 为什么 AI 工程实践越来越受关注过去两年AI 领域最大的变化不是某个模型效果突然提升而是把模型接入业务的成本明显下降了。大量开源模型可以本地部署云厂商提供标准 API开发框架也开始把 Prompt、模型调用、工具调用封装成普通编程接口。换句话说“训练模型”已经变成少数团队的专项工作绝大多数开发者真正要面对的是“如何把模型稳定、可控、低成本地集成进业务系统”。AI 工程实践的核心就是把模型当作一个组件而非黑盒围绕它解决性能、可靠性、可观测性和成本问题。一个可行的 AI 服务至少要考虑模型加载、推理加速、接口封装、并发控制、异常降级、日志监控等多个环节。只在本机跑通 notebook 和在生产环境稳定运行差距非常大。1.2 一个完整的 AI 应用系统由哪些部分组成我们先看一个典型的生成式 AI 应用它通常包含以下模块模块职责典型实现模型层完成文本生成、理解、推理Qwen、Llama、GPT 系列等推理引擎加载模型并高效执行推理transformers、vLLM、llama.cpp服务层对外提供 HTTP 接口FastAPI、Spring Boot业务编排处理多轮对话、任务调度、工具调用LangChain、Spring AI、自研逻辑数据层保存对话记录、向量索引、业务数据Redis、PostgreSQL、Faiss基础设施提供 GPU 资源、容器环境、监控告警Docker、Kubernetes、Prometheus对这个结构有整体印象之后才知道每个技术选型决定落在哪一层也更容易判断一个框架是否适合当前项目。1.3 本文适合谁、能学到什么如果你已经跑通过一个简单的大模型 Demo但是对“怎么把 Demo 变成能对外提供服务的小系统”没有完整思路这篇文章比较适合你。读完你会掌握模型与部署方式的核心对比维度一套可复制的本地 AI 推理服务代码Spring AI 接入常见开源模型的配置方式Docker 部署 GPU 服务的正确姿势遇到显存不足、加载慢、响应超时等问题的排查顺序。需要说明的是AI 领域版本迭代太快本文示例不锁定某个具体版本重点讲思路和可迁移的工程方法。2. 环境准备与版本说明2.1 硬件与运行环境本地推理最关键的前提是 GPU。模型体积和显存的关系大致可以这样估算加载一个 7B 模型16 位精度大约需要 14GB 显存如果使用 4 位量化显存需求可以降到 6GB 左右。所以在准备环境前先确认两件事GPU 显存总量CUDA 驱动版本。如果是纯 CPU 环境小模型可以运行但推理速度会明显变慢建议先关注量化方案。实际操作时可以用nvidia-smi查看显卡信息这本是 NVIDIA 驱动自带的命令不需要额外安装。nvidia-smi输出中重点关注“Driver Version”和显存大小。如果这里看不到 GPU说明驱动没有正确安装后续 PyTorch 将无法使用 CUDA。2.2 Python 与 CUDA 环境建议使用 Python 3.10 或 3.11虚拟环境工具可以选择 conda 或 venv。PyTorch 需要根据 CUDA 版本安装例如conda create -n ai-eng python3.10 -y conda activate ai-eng pip install torch --index-url https://download.pytorch.org/whl/cu121其中cu121表示 CUDA 12.1需要和本机驱动匹配。如果不确定可以先安装 CPU 版本跑通流程再根据实际情况切换。版本在这里要强调一点不要照抄某个博客的安装命令一定要看 PyTorch 官方页面对应 CUDA 版本的说明。2.3 项目结构规划后续实战案例的目录结构如下ai-engineering-demo/ ├── app/ │ ├── main.py # FastAPI 服务入口 │ └── model_server.py # 模型加载与推理逻辑 ├── spring-client/ # Spring AI 调用示例 ├── requirements.txt ├── Dockerfile └── README.md先规划目录的好处是环境、代码、镜像三层能分开管理排查问题的时候很容易定位是哪一层出的故障。3. 技术路线选型如何“Pick Sides”很多人拿到一个 AI 任务第一反应是“我该用哪个模型”但真正决定项目走向的是技术路线选型。这里不是选最好的模型而是选最适合当前团队约束的方案。3.1 模型选择开源权重还是闭源 API开源权重模型例如 Qwen、Llama 系列优点是可以私有化部署数据不出内网长期成本可控缺点是 GPU 资源占用大工程维护成本高。闭源 API 的优点是开箱即用、效果稳定适合快速验证缺点是数据合规要求高单次调用的边际成本可能不小。我的建议是数据敏感或需要深度定制的场景选开源模型快速原型和通用问答场景选 API。实际项目中也可以两条腿走路先用 API 验证业务价值再在需要降低成本和保护数据时切到私有化部署。3.2 部署方式本地推理还是云端 API这个选择本质上是在算力和便利性之间做取舍。本地推理适合对延迟敏感、请求量稳定、有 GPU 资源的团队云端 API 适合流量波动大、不想占用运维精力的团队。另一种折中方案是使用云上的 GPU 实例自建推理服务既有本地推理的可控性又不用自己维护物理机器。无论选哪种都要把“推理服务”单独抽象出来对外提供统一接口。这样后续切换模型提供方时业务代码不需要大规模改动。3.3 推理引擎transformers、vLLM 与 llama.cpp加载同一个模型可以用不同的推理引擎它们的效果几乎一致但性能和工程特性差异很大。transformers生态最完善调试方便适合开发阶段和自定义逻辑场景。vLLM吞吐量高支持 PagedAttention、Continuous Batching适合高并发在线服务。llama.cppCPU/边缘设备友好支持多种量化方案适合资源受限环境。如果只是写 Demo直接用 transformers 即可。如果目标是高并发接口直接上 vLLM 会省不少事。vLLM 还提供 OpenAI 兼容接口业务代码可以用标准 OpenAI SDK 直接对接。3.4 Agent/开发框架LangChain、Spring AI 还是自研Agent 是当前 AI 应用的热门方向但框架选择非常考验团队基础。LangChain 生态丰富组件多适合 Python 团队快速组合能力Spring AI 对 Java 技术栈友好能复用 Spring Boot 的配置体系和依赖管理自研编排则适用于业务流程固定、不想被框架绑定太深的团队。我倾向于这样判断如果你所在的团队 Java 项目多就优先考虑 Spring AI如果团队以 Python 算法工程师为主LangChain 或直接基于调用代码封装更合适。框架不是越重越好关键是能落在团队现有工程体系里。4. 完整实战案例构建一个本地 AI 推理服务为了让整套流程可落地我们实现一个最小但完整的推理服务加载一个开源对话模型用 FastAPI 暴露 HTTP 接口再用 Spring AI 客户端调用该服务最后容器化部署。4.1 准备依赖创建requirements.txt内容如下transformers accelerate fastapi uvicorn[standard] pydantic sentencepiece这里故意不锁版本因为不同镜像和 Python 环境下能安装到的最新版本不同。建议安装完成后用pip freeze requirements-lock.txt锁定实际版本方便复现。执行安装pip install -r requirements.txt部分模型还需要einops、tiktoken等额外依赖遇到导入错误时按提示补齐即可。4.2 编写模型加载与推理脚本在app/model_server.py中编写模型加载逻辑# 文件路径app/model_server.py import torch from transformers import AutoTokenizer, AutoModelForCausalLM _model None _tokenizer None def load_model(model_id: str): global _model, _tokenizer if _model is not None: return _model, _tokenizer tokenizer AutoTokenizer.from_pretrained( model_id, trust_remote_codeTrue ) model AutoModelForCausalLM.from_pretrained( model_id, torch_dtypetorch.float16, device_mapauto, trust_remote_codeTrue, ) model.eval() _model model _tokenizer tokenizer return model, tokenizer def generate(prompt: str, max_new_tokens: int 256, temperature: float 0.7) - str: model, tokenizer load_model(Qwen/Qwen2.5-7B-Instruct) messages [ {role: system, content: 你是一个专业的技术助手。}, {role: user, content: prompt}, ] text tokenizer.apply_chat_template( messages, tokenizeFalse, add_generation_promptTrue ) inputs tokenizer([text], return_tensorspt).to(model.device) with torch.no_grad(): outputs model.generate( **inputs, max_new_tokensmax_new_tokens, do_sampleTrue, temperaturetemperature, top_p0.9, ) response tokenizer.decode( outputs[0][inputs.input_ids.shape[1]:], skip_special_tokensTrue ) return response这里有几个关键点需要解释。第一trust_remote_codeTrue表示允许执行模型仓库里的自定义代码只有在信任模型来源时才开启。第二torch_dtypetorch.float16能显著减少显存占用但 CPU 环境需要改成torch.float32。第三apply_chat_template是 transformers 4.31 之后引入的接口不同模型模板格式不同推荐优先使用它而不是手写模板。4.3 封装 FastAPI 服务在app/main.py中创建 HTTP 服务# 文件路径app/main.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from model_server import generate app FastAPI(titleAI Local Inference Service) class ChatRequest(BaseModel): prompt: str max_new_tokens: int 256 temperature: float 0.7 app.get(/health) def health(): return {status: ok} app.post(/v1/chat) def chat(req: ChatRequest): if not req.prompt.strip(): raise HTTPException(status_code400, detailprompt cannot be empty) try: response generate( promptreq.prompt, max_new_tokensreq.max_new_tokens, temperaturereq.temperature, ) return {response: response} except Exception as exc: raise HTTPException(status_code500, detailstr(exc))启动服务uvicorn app.main:app --host 0.0.0.0 --port 8000服务启动后先用健康检查确认进程正常curl http://localhost:8000/health预期返回{status:ok}然后调用对话接口curl -X POST http://localhost:8000/v1/chat \ -H Content-Type: application/json \ -d {prompt: 用一句话介绍 AI Agent}这里要提醒一个常见误区FastAPI 服务进程启动时并不会加载模型模型是在第一次请求进来时才加载的。因此第一次调用的等待时间会明显更长后续请求才会进入正常响应范围。生产环境建议提供预热接口让容器启动后自动触发一次空请求。4.4 Spring AI 客户端接入对于 Java 项目Spring AI 可以把上面的服务接入 Spring Boot。Spring AI 支持 OpenAI 兼容协议所以即使本地部署的不是 OpenAI 模型只要推理服务暴露 OpenAI 兼容接口也可以复用这套配置。添加依赖dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-openai-spring-boot-starter/artifactId /dependency在application.yml中配置服务地址spring: ai: openai: base-url: ${AI_BASE_URL:http://localhost:8000/v1} api-key: ${AI_API_KEY:demo-key}编写调用代码import org.springframework.ai.chat.client.ChatClient; import org.springframework.stereotype.Service; Service public class ChatService { private final ChatClient chatClient; public ChatService(ChatClient.Builder builder) { this.chatClient builder.build(); } public String chat(String prompt) { return chatClient.prompt() .user(prompt) .call() .content(); } }Spring AI 的 API 在不同版本之间变化比较快例如ChatClient的获取方式在新版本中改为ChatClient.builder(chatModel).build()或通过自动配置注入。如果你使用的版本找不到某个类优先查看当前版本的官方文档而不是强行复用旧代码。4.5 容器化部署GPU 环境的 Docker 镜像推荐基于 PyTorch 官方镜像构建因为镜像中已经预装了 CUDA 和 PyTorch避免自己安装时出现版本不匹配。# 文件路径Dockerfile FROM pytorch/pytorch:2.2.0-cuda12.1-cudnn8-runtime WORKDIR /app COPY requirements.txt . # 镜像已自带 torch因此只安装补充依赖 RUN pip install --no-cache-dir -r requirements.txt COPY app ./app EXPOSE 8000 CMD [uvicorn, app.main:app, --host, 0.0.0.0, --port, 8000]这里的pytorch/pytorch:2.2.0-cuda12.1-cudnn8-runtime是镜像标签实际使用时建议到 Docker Hub 确认当前环境的 CUDA 版本是否匹配。构建并启动容器docker build -t ai-eng-demo . docker run --gpus all -p 8000:8000 ai-eng-demo启动后可以用同样的方式访问接口。如果容器内看不到 GPU先检查 Docker 版本是否支持--gpus以及 NVIDIA Container Toolkit 是否安装完整。4.6 结果说明整个流程跑通后你的架构是三层业务端Spring AI调用服务端FastAPI服务端再调用推理引擎transformers最终落到本地 GPU 上的模型。这种拆分方式有一个明显好处业务端和模型服务可以独立扩展模型服务内部换引擎或者换模型业务端基本无感。如果后续请求量变大可以把model.generate换成 vLLM 的LLM服务只需要保证 HTTP 接口不变整条链路不会断。5. 常见问题与排查思路本地推理服务的坑主要集中在环境依赖、显存和推理速度三个方向。下面整理一份高频问题清单。问题现象常见原因解决思路启动时报CUDA out of memory模型太大或并发请求太多换 4 位量化模型、降低 batch size、升级显存assert torch.cuda.is_available()失败PyTorch 与 CUDA 驱动不匹配重新安装对应 CUDA 版本的 PyTorch模型加载特别慢首次从 Hugging Face 下载权重提前下载到本地缓存或配置镜像源第一次请求超时服务启动后才加载模型增加预热接口部署时自动调用CPU 环境推理太慢未使用任何加速方案使用量化版本或部署到 GPU 环境输出乱码或异常符号缺少模型依赖的分词器安装 sentencepiece、einops 等依赖Spring AI 启动失败配置属性版本不兼容以官方文档为主检查依赖版本下面展开两个最容易踩的坑。第一个是显存不足。一个 7B 模型以 16 位加载大约要 14GB 显存实际推理过程还要预留 KV Cache 空间。如果显存只有 8GB可以改用 4 位量化模型例如Qwen/Qwen2.5-7B-Instruct-GPTQ-Int4加载时指定device_mapauto让模型自动分配到可用设备。如果显存仍然不够就只能换更小的模型。第二个是模型下载慢或超时。Hugging Face 默认从境外服务器下载部分网络环境下会很慢。这时可以设置环境变量使用镜像站export HF_ENDPOINThttps://hf-mirror.com这里需要说明的是镜像站只是加速下载的一个选择实际可用性取决于你的网络环境确认好之后配置在 CI/CD 和 Dockerfile 中即可。生产环境更推荐把下载好的模型放在私有对象存储或内网文件服务器中避免每次部署都重新下载。6. 最佳实践与工程建议6.1 模型与依赖版本管理模型权重和代码依赖一样必须做版本管理。代码里写死Qwen/Qwen2.5-7B-Instruct之后如果模型仓库更新了权重你的服务可能在不知情的情况下变化。建议在配置文件中声明模型名称和 revision例如model: id: Qwen/Qwen2.5-7B-Instruct revision: main如果模型有明确的 commit hash优先锁到具体 commit保证可复现性。6.2 接口设计统一无论底层是开源模型还是闭源 API对外接口尽量保持统一。推荐直接使用 OpenAI 兼容的/v1/chat/completions格式因为大量开源服务和 SDK 都支持这个协议。这样业务端不用关心模型供应商是谁切换成本会低很多。6.3 异常处理与降级模型服务是外部依赖不是本地函数调用。必须处理超时、限流、模型不可用等情况。常见做法包括在服务入口设置请求超时时间失败时先重试一次再返回降级文案对并发请求做排队或限流防止 GPU 被打满记录每次请求的模型、耗时、token 数量和错误信息。6.4 数据安全与合规如果业务数据需要保密尽量避免把数据发给外部 API。本地部署模型时也要注意模型仓库的许可证和合规问题。涉及用户个人信息时还需要在日志中做脱敏不要在 Prompt 和响应日志里记录敏感信息。6.5 成本与性能监控模型服务上线后要监控几个核心指标GPU 利用率显存占用请求平均延迟和 P99 延迟每分钟处理 token 数单次请求成本估算。这些指标能帮你判断是加机器还是换模型是调并发还是做量化。没有监控的模型服务出问题之后很难定位。6.6 安全边界如果推理服务部署在公网必须做好鉴权不能裸奔。简单方式是在服务前面加一层 API Key 校验更严格的方式是接入内部网关。涉及代码执行的场景比如 Agent 工具调用需要严格限制工具白名单避免让模型直接执行任意系统命令或eval用户输入。安全问题的核心原则是模型永远只能调用你允许它调用的能力。7. 总结与学习路线本文从 AI 工程实践的全局视角出发围绕“技术选型”与“模型部署”两条主线完整演示了如何用 transformers 加载开源模型、用 FastAPI 封装推理接口、用 Spring AI 接入业务系统再到用 Docker 容器化部署。这套流程覆盖了一个小型 AI 应用从开发到上线的主要环节。你读完至少应该记住三点第一选型的核心是匹配团队算力和业务约束而不是追新第二接口层统一抽象能降低模型替换成本第三显存、版本、鉴权和监控是生产环境最容易出问题的地方。下一步可以按兴趣深入如果追求高并发学习 vLLM 和 OpenAI 兼容服务如果做 Java 应用继续研究 Spring AI 的工具调用与 Agent 模式如果想提升效果了解 RAG 和向量数据库如果关心资源消耗研究 GPTQ、AWQ 等量化方案。动手是唯一能把这些知识变成经验的方式。先在本机把最小服务跑起来再逐步叠加鉴权、预热、监控和容器化你会在这个过程里遇到更多具体问题而解决每个问题都会比读十篇文章更有收获。
返回列表