LLM应用工程实践:从模型选型到FastAPI问答服务
发布时间:2026/8/29 1:50:13 作者:尧图编辑部 阅读量:1,286

在实际的 LLM 应用开发里真正决定项目成败的往往不是模型跑分差几分而是“这个模型在具体业务里能不能稳定、便宜、合规地跑起来”。围绕 LLM 的讨论里常出现“Google doesn’t need the LLM crown”这类判断它提醒我们一件事一个公司或一个团队的价值不是靠一个最强模型证明的而是靠“模型 推理基建 框架 产品场景”的整体组合。这篇文章不从新闻评论的角度出发而是把它当成一个工程选题当我们不对着排行榜选型时LLM 应用应该怎么设计、怎么部署、怎么验证、怎么上线。读者在读完以后应该能完成一个带上下文的 LLM 问答服务并掌握本地与云端的取舍逻辑以及 ComfyUI 这类图像生成工具与 LLM 之间如何正确规划硬件资源。1. 从“LLM 王冠”说起应用工程真正要争的不是排行榜1.1 排行榜衡量的是模型能力不是系统能力LLM 是 Large Language Model 的缩写中文通常叫大语言模型。从形态上看它是一组经过大规模预训练的神经网络参数核心任务是根据已经出现的 token 预测下一个 token 的概率。生成一段文本时模型反复执行“预测下一个 token - 拿到结果 - 重新预测”最终拼出完整回复。这里的“概率预测”决定了 LLM 的输出天生具有不确定性同样的输入两次生成可能完全不一样。公开排行榜通常衡量的是模型在某个测试集上的能力比如代码生成、数学推理、安全问答。这类评测对研究有价值但它衡量的是模型的静态能力不是系统能力。一个可以交付的 LLM 系统除了模型本身还包括请求过滤、提示词构造、上下文管理、输出校验、权限控制、缓存、日志和监控。这些部分都不出现在排行榜的分数里。换句话说即使模型只排在中等位置只要系统工程做得完整用户体验和业务落地效果可能远超一个只堆了最强模型的粗糙原型。1.2 Google 的例子解释了一个工程判断生态比单点更强以 Google 在 LLM 领域的布局为例它能拿出手的不只是某个模型而是一套完整链路面向开发者的 API 接口、云端推理基础设施例如 TPU 和 AI 加速器这类专用硬件、多年积累的分布式系统经验以及搜索、办公套件、云平台等大量产品入口。这些组件组合起来即便某一款模型没有在公开榜单位列第一开发者和企业照样能在该生态里获得可用能力。对个人开发者和中小团队来说这个工程判断是一样的你不需要成为“最强模型”的使用者你只需要成为“最适合自己场景的模型”的使用者。判断依据是延迟、成本、数据边界、许可证和可维护性而不是单纯追求排行榜上的名次。1.3 三个比跑分更重要的指标在 LLM 选型阶段建议先把下面三个指标整理出来再去看具体模型指标含义对业务的影响首 token 延迟从发出请求到收到第一个 token 的时间影响用户等待体验单次请求成本按输入输出 token 计费或按自建资源摊销决定功能能否长期运行数据边界输入内容是否离开自己服务器是否被第三方记录决定能否用于敏感业务例如一个内部知识库问答系统对数据边界要求很高可能更适合本地部署小参数量模型一个面向公开用户的营销文案生成器更看重生成质量和并发能力则可以优先考虑云端 API。这些选择都应该基于上述三个指标来做而不是“因为这个模型跑分更高”。2. 核心概念先理清模型、框架和推理服务之间存在三层边界2.1 模型层你面对的可以是权重文件也可以是 API模型层是能力本体。它可能以权重文件形式存在也可能隐藏在云 API 后面。对应用工程师来说模型层真正需要关心的是几个固定参数上下文窗口大小、单次请求最大 token 数、支持的语言和代码类型、许可证要求以及部署方式。举个例子一个 7B 参数的量化模型在消费级显卡上就能运行适合本地实验一个百亿甚至千亿参数模型则需要多卡并行或云端高性能实例。两者在调用代码上的差异不大差异主要体现在资源规划、推理吞吐和运维复杂度。2.2 框架层把提示词、工具调用和记忆组织起来框架层负责把原始模型能力变成可复用的应用逻辑。常见的 LLM 框架有 LangChain、LlamaIndex、Haystack它们主要解决四类问题构造提示模板统一管理 system、user、assistant 三类消息。管理工具调用例如搜索、数据库查询、HTTP 请求。实现 RAG 流程把文档切块、向量化、检索后拼进 prompt。维护多轮对话历史控制上下文不要无限增长。但框架不是越多越好。如果核心流程只是“把用户输入转发给模型并把结果返回”不引入任何框架反而更简单因为框架版本升级、模型接口变更和依赖冲突都会带来额外成本。下面是一段不依赖框架的提示模板示例它展示了“框架层要解决的问题”的最小形态def build_messages(system_prompt: str, history: list[dict], user_input: str) - list[dict]: messages [{role: system, content: system_prompt}] messages.extend(history) messages.append({role: user, content: user_input}) return messages system_prompt 你是一个只回答开发问题的技术助手。如果问题与开发无关请礼貌拒绝回答。 history [ {role: user, content: 什么是 RAG}, {role: assistant, content: RAG 是检索增强生成先从外部知识库中找到相关内容再把这些内容交给模型生成回答。}, ] messages build_messages(system_prompt, history, 它有什么缺点) print(messages)这段代码没有引入任何框架但它已经把消息结构、角色语义和上下文拼接的基本逻辑写清楚了。实际项目里如果需求变复杂再引入框架也不迟。2.3 推理服务层模型如何被加载、调度和对外提供 HTTP 接口推理服务层解决的是“模型以什么形式运行”的问题。本地自建时常见方案包括 Ollama、vLLM、Text Generation InferenceTGI。云端使用时直接调用供应商提供的 HTTP 接口即可。对应用层开发者来说推理服务层通常被统一的接口抽象掉。例如很多本地推理工具提供 OpenAI 兼容接口意味着你只需要修改base_url就能把客户端代码从云端切到本地。自建时需要额外关注并发、显存占用、请求排队和故障恢复。推理服务适用场景特点Ollama本地快速实验、单机部署安装简单支持 GPU 和 CPU 混合运行vLLM高并发线上推理吞吐高支持 PagedAttention占用显存更高效TGI企业级服务化面向生产环境支持连续批处理云端 API无需自建算力有免费额度按 token 计费运维成本低把模型、框架、推理服务三层分开思考选型时就不会把所有问题都归到“哪个模型更强”上面。3. 架构选型本地部署和云端 API 的取舍以及 ComfyUI 与 LLM 是否必须同机3.1 本地部署适合什么场景本地部署指把模型权重下载到自己的服务器或电脑上通过本地推理服务对外提供接口。它的核心优势是数据不出内网、按需使用算力、长期成本可控。但在实际项目中本地部署并不轻松。第一要准备足够的显存参数越大显存要求越高第二模型的生成质量通常不如同规模商业 API需要做评估和微调第三推理服务的并发能力、GPU 利用率、模型版本升级都成为日常运维工作。推荐优先考虑本地部署的场景数据敏感不允许把文本发送到第三方服务器。需要离线运行网络环境受限。调用量很大长期按 token 付费不划算。对模型行为有定制需求需要微调或换不同权重。3.2 云端 API 适合什么场景云端 API 是把模型托管在供应商侧应用通过 HTTP 接口调用。它的优势是接入快、生成质量高、按调用量付费不需要自己维护 GPU 服务器。它的主要风险有两个一是数据离开自己的网络边界合规敏感场景不宜使用二是单位调用量一旦增大成本会线性上升可能超过自建资源。推荐优先考虑云端 API 的场景快速验证产品可行性不想一开始就投入大量硬件。需要当前最优质的模型能力。并发波动大云端自动扩容更省心。团队缺少 GPU 运维经验。3.3 ComfyUI 与 LLM 是否必须放在同一台电脑上这是 LLM 应用搭建过程中经常被问到的问题。先看清楚 ComfyUI 是什么它是一个基于节点的工作流工具主要用于扩散模型图像生成例如 Stable Diffusion 系列模型。它的运行负载主要是图像生成过程中的大规模矩阵计算对显存要求很高而 LLM 运行负载虽然也是矩阵计算但更偏向文本 token 的序列推理两者属于不同类型的工作负载。结论是ComfyUI 与 LLM 不需要必须在同一台电脑上。它们之间没有同一主机的强依赖关系。常见的组合方式有三种组合方式说明适用场景ComfyUI 和 LLM 放同一台机器共用 GPU 资源节省硬件成本个人电脑、实验环境负载不高时分开两台机器各自独占 GPU避免显存争抢图像和文本并发需求都很高的生产环境图像本机、LLM 用云端 API本机跑 ComfyUI文本推理走 API避免为 LLM 额外购置硬件如果你确实想在同一台机器上同时跑 ComfyUI 和本地 LLM推荐先检查显卡显存。以一张 24GB 显存的显卡为例同时加载一个 8GB 的扩散模型和一个 7B 量化 LLM 很紧张容易触发显存溢出。更稳妥的做法是让 ComfyUI 独享 GPULLM 调用轻量级 API或者分别部署到不同实例。3.4 混合架构的常见形态混合架构指的是根据请求类型和模型能力自动选择本地模型或远端 API。例如简单的文本分类用本地小模型处理复杂代码生成任务跑到云端大模型。这种架构能兼顾成本和效果但实现时要注意请求路由和结果回退。def route_request(user_input: str) - str: # 简单关键词命中直接走本地轻量模型 if len(user_input) 10 and 版本 in user_input: return call_local_model(user_input) # 其他请求走云端大模型 return call_cloud_model(user_input)在系统设计层面混合架构需要维护两套接口、两套密钥体系和不一致的错误处理逻辑。建议在需求明确之前先用一条主链路跑通再考虑智能路由。4. 最小可运行案例用 FastAPI 搭一个带上下文的问答服务4.1 案例目标与约束这一节的目的是跑通一个最小闭环客户端传入用户问题和历史对话服务端负责拼装 system 消息、维护上下文、调用 OpenAI 兼容接口并返回模型回答。由于各家云服务大多提供 OpenAI 兼容端点下面示例不绑定具体供应商你只需要设置模型名称和BASE_URL就能对接不同服务。示例代码不包括数据库、权限、限流也不会处理生产环境的高并发问题。它适合作为学习环境的最小骨架生产化建议放在后文。4.2 项目结构和依赖目录结构建议如下llm-qa-demo/ ├── .env ├── requirements.txt ├── main.py └── test_client.py依赖文件requirements.txt内容如下fastapi0.110.0 uvicorn[standard]0.29.0 openai1.30.0 python-dotenv1.0.0安装依赖pip install -r requirements.txt这里使用 OpenAI Python SDK 作为客户端因为很多本地推理服务和云端 API 都提供 OpenAI 兼容接口写一遍代码就能适配多个后端。4.3 配置环境变量在项目根目录创建.env文件LLM_API_KEYyour_api_key_here LLM_BASE_URLhttps://your-llm-service.example.com/v1 LLM_MODELdefault-model-name SYSTEM_PROMPT你是一个严谨的技术助手回答要简洁、准确不确定时明确说明。需要注意BASE_URL在不同服务上差异较大。以本地 Ollama 为例通常可以设置为http://localhost:11434/v1如果是云端兼容接口则按服务商文档填写。原始材料没有给出统一地址实际开发时务必先看对应文档。4.4 编写 FastAPI 服务main.py示例代码如下import os from typing import Optional from dotenv import load_dotenv from fastapi import FastAPI, HTTPException from openai import OpenAI from pydantic import BaseModel, Field load_dotenv() app FastAPI(titleLLM QA Demo) client OpenAI( api_keyos.getenv(LLM_API_KEY, not-required), base_urlos.getenv(LLM_BASE_URL, http://localhost:11434/v1), ) MODEL_NAME os.getenv(LLM_MODEL, default-model-name) SYSTEM_PROMPT os.getenv(SYSTEM_PROMPT, 你是一个严谨的技术助手。) class ChatMessage(BaseModel): role: str Field(..., description消息角色system、user 或 assistant) content: str Field(..., description消息内容) class ChatRequest(BaseModel): prompt: str Field(..., min_length1, description用户输入) history: list[ChatMessage] Field(default_factorylist, description历史消息) temperature: Optional[float] Field(default0.7, ge0.0, le2.0) class ChatResponse(BaseModel): answer: str model: str app.post(/chat, response_modelChatResponse) def chat(request: ChatRequest): messages [{role: system, content: SYSTEM_PROMPT}] messages.extend( [{role: item.role, content: item.content} for item in request.history] ) messages.append({role: user, content: request.prompt}) try: completion client.chat.completions.create( modelMODEL_NAME, messagesmessages, temperaturerequest.temperature, ) answer completion.choices[0].message.content return ChatResponse(answeranswer, modelMODEL_NAME) except Exception as exc: raise HTTPException(status_code502, detailf模型调用失败: {exc})这段代码做了几件关键的事通过load_dotenv()读取.env避免在代码里硬编码密钥。使用 OpenAI SDK 创建客户端base_url可以从本地环境切换到云端环境。请求体用 Pydantic 模型校验保证prompt不为空temperature在合法范围内。history列表允许客户端传入多轮历史服务端只负责拼装不负责存储。4.5 编写客户端测试脚本test_client.py用来验证服务是否正常import requests payload { prompt: 请用一句话解释什么是 LLM 框架, history: [ {role: user, content: 你好}, {role: assistant, content: 你好有什么可以帮你}, ], temperature: 0.5, } response requests.post(http://127.0.0.1:8000/chat, jsonpayload) print(response.status_code) print(response.json())启动服务uvicorn main:app --host 0.0.0.0 --port 8000正常返回示例{ answer: LLM 框架是用于组织提示词、历史对话和外部工具调用的开发库。, model: default-model-name }如果看到这个结构说明最小链路已经跑通。5. 参数、上下文和工程化配置的细节5.1 核心生成参数速查模型接口里最常见的几个参数直接决定结果质量。下表给出了参数含义和调整方向参数作用常见值调整影响temperature控制随机性0 到 1代码类任务用 0.2创意类用 0.8越大越随机越小越确定top_p核采样保留累计概率范围内的 token0.9与 temperature 二选一交互调整max_tokens限制单次回复最大 token 数按业务自定义过小会截断回答过大会增加费用presence_penalty让模型更愿意使用新词0 到 1越大越不重复但也可能更跳跃frequency_penalty惩罚高频重复词0 到 1越大越避免重复但可能降低流畅度一个常见误区是同时调整 temperature 和 top_p。推荐的做法是先固定一个再通过实验调整另一个。代码生成场景更推荐temperature0.2, top_p0.9开放对话场景可以尝试temperature0.8, top_p0.9。5.2 上下文窗口和消息历史管理模型上下文窗口是固定的。比如 8K、32K、128K 指的都是模型能同时处理的输入输出 token 总量。超过窗口上限请求会直接失败常见报错是类似于context length exceeded。管理历史消息有如下几种策略策略做法适用场景截断只保留最近 N 条消息代码简单适合实验滑动窗口按 token 数量丢弃最早的对话中等复杂服务摘要压缩把旧对话总结成一段摘要长会话场景RAG 检索只把与当前问题相关的片段拼入 prompt知识库问答main.py示例里直接把 history 原样传给模型这种方式在历史很短时没问题但生产环境必须控制 token 数量。一种简单写法def trim_history(history, max_messages6): return history[-max_messages:]5.3 成本控制与 token 统计调用云端 API 时费用按输入 token 和输出 token 分别计费。同一个模型长文档分析任务费用高因为输入内容多代码补全任务费用相对低因为输出通常较短。推荐在服务端记录每次请求的prompt_tokens和completion_tokens。OpenAI 兼容接口返回的 completion 对象里一般包含usage字段usage completion.usage print(f输入 token: {usage.prompt_tokens}, 输出 token: {usage.completion_tokens})把 token 使用量写入日志或监控指标可以帮助后续优化提示词长度和缓存策略。6. 运行验证与评估功能跑通只是第一步还要检查响应质量和资源占用6.1 接口功能验证启动服务后先用最小请求验证基本功能。curl -X POST http://127.0.0.1:8000/chat \ -H Content-Type: application/json \ -d { prompt: Python 里如何读取环境变量, history: [], temperature: 0.3 }预期结果应返回一个包含answer和model的 JSON。接着验证带历史的多轮对话确认系统 prompt 和上一轮消息能被正确拼装。6.2 响应质量评估功能跑通后需要评估输出质量。推荐的评估维度包括准确性答案是否与问题相关是否包含明显错误。一致性同一问题多次请求结果是否在可接受范围内。格式规范性回答中代码块、列表、标点是否正确。上下文理解多轮对话中是否记住了前文信息。可以准备一组固定测试用例写入 JSON 文件后批量执行{ questions: [ 什么是 RAG, 使用 Python 有哪些常见坑, 如何降低 LLM 推理成本 ], history: [] }评估时不要只看一两条回复。LLM 输出具有随机性至少每个用例执行 3 次观察稳定性。如果结果差异太大就要降低 temperature 或调整提示词。6.3 资源占用检查如果用的是本地推理服务必须关注 GPU 和内存占用。常用命令nvidia-smi free -h重点检查显存使用率、GPU 利用率和内存 swap 情况。如果显存接近上限说明模型规模或并发请求超出机器容量。如果 GPU 利用率长期低于 5%说明请求量小本地部署过于浪费资源可以考虑切到云端 API。7. 常见问题排查从报错日志倒推原因7.1 错误现象与处理方案速查现象常见原因检查方式处理建议返回 401 或 403API 密钥错误或权限不足检查.env中密钥是否有效重新生成密钥确认环境变量已加载提示 context length exceeded输入加输出超过模型上下文窗口查看请求 token 数启用历史截断或换更大上下文模型model not found模型名称与后端不匹配列出后端可用模型修改LLM_MODEL为正确名称接口超时模型推理慢或请求排队查看推理服务日志降低并发或换更强 GPUGPU out of memory显存不足运行nvidia-smi查看显存使用量化模型或减少并发返回中文乱码或空字符编码或模型输出内容被截断检查响应内容设置max_tokens更大值确认请求编码为 UTF-87.2 典型排查路径接口报错从状态码开始当/chat接口返回错误时按以下顺序排查查看错误状态码。4xx 通常是参数或密钥问题5xx 通常是服务端或模型问题。确认LLM_BASE_URL是否正确。很多本地服务和云端接口路径都以/v1结尾写错会导致 404。调用同一模型的原生命令行或官方示例排除应用代码问题。查看推理服务日志。Ollama 和 vLLM 都提供日志能够显示模型加载和请求处理过程。减小请求复杂度。去掉 history只传一条 prompt观察是否能成功。curl http://localhost:11434/v1/models上面命令可以列出本地推理服务支持的模型名称用于确认配置里的模型名是否真实存在。7.3 环境差异导致的隐藏问题学习环境很容易跑通切到生产环境却报错常见原因有三个.env文件未上传到服务器导致密钥和BASE_URL缺失。生产环境的 Python 版本与本地不一致造成依赖兼容问题。防火墙限制导致生产服务器无法访问云端 API 域名。推荐在项目根目录准备一个config.example.env模板部署时复制为.env再填写真实值。同时使用 requirements 或 lock 文件锁定依赖版本。8. 生产化建议与下一步扩展方向8.1 从实验代码到生产服务的最小检查清单上面的 FastAPI 示例只是骨架生产环境至少还需要补全以下项配置外置化密钥、模型名称、超时时间都从环境变量读取不写入代码库。日志与追踪记录每次请求的输入摘要、输出 token 数、耗时和状态码。错误处理对模型超时、限流、无效参数做分类返回可读的错误信息。限流与配额防止单个用户刷爆 token 预算。内容安全增加输入输出过滤至少过滤明显违规内容和敏感关键词。版本管理固定模型版本或记录模型快照避免模型升级后结果行为变化。回滚方案当新模型表现异常时能快速切回旧模型或旧版本代码。8.2 面向业务场景的三条扩展路线第一条路线是 RAG 知识库问答。在现有服务基础上加入文档切分、向量化和检索模块让模型基于本地文档回答问题。这时引入 LangChain 或 LlamaIndex 会比较有价值因为检索链路本身需要编排。第二条路线是工具调用与智能体。让模型能够根据用户意图调用外部接口例如查询订单、查天气、执行搜索。实现时需要注意工具参数校验和调用结果反馈避免模型编造参数。第三条路线是评估与微调。收集一批真实业务样本建立自动化评估集对比不同模型和提示词的效果。当开源模型的评估结果不足以支撑业务时再考虑微调或换用更大模型。8.3 对新手最重要的一个练习建议不要一开始就同时部署多个模型也不要从复杂框架入手。建议先用一个最容易跑通的小模型或云 API完成一个不超过三个接口的最小应用记录延迟、成本和输出质量。当你对模型的调用方式、参数行为和错误链路都熟悉以后再去研究框架、RAG 和部署优化。这样积累起来的技术判断比单纯追求“最强模型”可靠得多。回到开头那句“Google doesn’t need the LLM crown”。这句话放在工程实践里真正有价值的部分是它提醒我们模型能力不是终点围绕模型构建的工程系统才是。把基础链路跑通、把参数吃透、把成本和数据边界算清楚项目才能真正立得住。