尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

AI应用工程化落地:模型网关、RAG与Agent完整实践

AI应用工程化落地:模型网关、RAG与Agent完整实践 做 AI 应用第一年我踩过最重的坑不是模型效果差而是“一个能用的大模型 API 接进来之后项目依然跑不起来”。代码写好了数据准备好了模型也调通了真正让项目卡住的往往是工程问题配置文件散落各处、Prompt 没有版本管理、向量库和模型服务耦合太深、日志里看不到任何上下文、一个模型切换就要改十几处代码。如果你也在做 AI Agent、AI 编程助手、智能问答这类应用这篇文章要聊的就是这些问题的解药。我们以“Beetles AI”为代号从零实现一个面向生产环境的 AI 应用骨架覆盖模型接入、RAG 检索、Agent 编排、服务发布和故障排查。读完你可以直接照着搭一套属于自己的工程化底座。先说判断AI 应用开发的分水岭不在“会不会调大模型 API”而在“有没有一套能把模型、数据、业务逻辑、可观测性组织起来的工程框架”。模型能力是平台的工程能力才是你自己的。这篇文章不追任何新概念只讲能落地的东西。1. Beetles AI 是什么一个 AI 应用工程的完整示例1.1 一句话定位Beetles AI 不是一个需要去官网下载的闭源产品而是本文约定的一个开源式 AI 应用示例项目名称。我们的目标是用它承载一套通用的 AI 工程实践把大模型、向量检索、Agent 任务编排、外部工具调用和 Web 服务整合在一起最终暴露成可用的 API 接口。这样做的好处是不管未来你接手的是智能客服、知识库问答、代码助手还是内容生成平台底层面对的问题都一样模型怎么接、上下文怎么组织、工具怎么调、服务怎么部署。把这一套骨架搭好具体业务只是往里面填数据、写 Prompt、注册工具函数而已。1.2 为什么需要这样的框架很多初学者会问直接用 OpenAI SDK 或各家大模型 SDK 写一个对话接口不就行了确实可以但那只适合体验 Demo。真实业务里你至少会遇到下面这些变化。模型会换。今天用开源模型明天换商业模型后天可能同时接多个模型做路由。如果所有代码都直接依赖某一家 SDK 的 ChatCompletion切换成本会高到让你想重构。上下文会变长。业务问答不能只靠用户一句话还要带上历史会话、业务知识、检索到的文档片段。这些内容从哪里来、怎么拼接、怎么控制长度需要一层专门的编排逻辑。工具调用会变多。AI Agent 一旦要执行任务就得调用搜索、数据库查询、HTTP API、内部服务。这些工具注册、参数校验、返回结果解析需要统一管理。服务要稳定。生产环境要求接口可监控、日志可排查、超时和重试有策略。这都要求从第一天就按工程化方式组织代码而不是把逻辑全写在路由函数里。1.3 核心能力拆解围绕上述问题Beetles AI 被拆成四个核心模块。模块职责对应技术选型ModelGateway统一模型接入屏蔽各家 API 差异Python OpenAI SDK 风格调用MemoryService管理会话记忆与长短期上下文Dict 存储或 RedisRAGEngine文档加载、切片、向量化、相似度检索ChromaDB Embedding 模型AgentRuntime工具注册、任务规划、工具调用循环FastAPI Python 函数注册后面我们会逐个模块落地。2. 核心概念大模型、Agent 与 RAG2.1 大模型 API 与本地模型的选择先说清楚两个最容易混淆的概念。大模型 API 是指通过 HTTP 调用云端模型服务你不需要关心模型部署只需要传 Prompt 和参数拿到返回文本本地模型则是在自己的服务器上部署开源模型权重推理计算发生在你自己的机器上。选择的关键是看业务诉求。数据敏感度业务数据不能出内网优先本地模型。实时性和稳定性API 服务通常有 SLA本地部署则需要自己保障 GPU 资源和运维能力。成本模型API 按 Token 计费本地模型按 GPU 采购和电力成本计费调用量小用 API 划算调用量大且稳定则本地部署可能更优。效果要求商业模型整体效果通常更好但最近一些开源模型已经接近可用。Beetles AI 的设计思路是让上层业务代码统一走 ModelGateway不关心到底接的是云端 API 还是本地模型。这样切换模型只是改配置不动业务代码。2.2 Agent从“聊天”到“任务执行”普通对话接口做的事情是接收用户消息拼好 Prompt调用模型返回回答。Agent 则更进一步——它不仅会聊天还会判断“要完成这个任务需要调用哪些工具”然后循环执行“思考 - 调用工具 - 观察结果 - 再思考”的过程。用人话解释聊天机器人是“你说我听我答”Agent 是“你这个目标我来拆解需要的资源我去取遇到问题我调整方案继续执行”。实现 Agent 有两条路线。一条是使用开源 Agent 框架由框架处理规划循环另一条是自己写一个最小化的 ReAct 循环。本文采用后者因为它能把原理讲透而且不依赖特定框架版本。2.3 RAG让模型回答业务问题RAGRetrieval-Augmented Generation检索增强生成是目前落地最多的 AI 应用模式。它的核心思想是模型不知道你的业务数据那就先从你的知识库中检索出相关内容塞进 Prompt再让模型基于这些内容回答。没有 RAG 时你只能把整份文档塞进 Prompt。很快会遇到两个问题Token 超长费用变高模型会被无关信息干扰回答质量下降。有了 RAG 之后流程变成离线阶段把业务文档切分成小块向量化存入向量数据库。在线阶段用户提问先把问题向量化从向量库中检索最相似的 TopK 片段。生成阶段把检索到的片段和用户问题拼成 Prompt交给模型生成答案。RAG 的关键在于把“知识”从模型参数中剥离出来变成可更新、可追溯、可控制的数据资产。这也让 AI 应用的回答有了依据而不是模型凭空“编造”。3. 环境准备与前置条件3.1 运行环境Beetles AI 示例代码基于 Python 3.10 编写操作系统不限Windows、macOS、Linux 都可以。建议使用虚拟环境隔离依赖。本文重点演示通用工程思路不针对特定云厂商或特定模型品牌。你可以把 MODEL_API_KEY 换成你实际使用的模型服务密钥或使用本地模型服务地址。3.2 技术栈与依赖示例项目用到以下组件FastAPIWeb 服务框架提供异步支持和自动 API 文档。OpenAI SDK这里的 OpenAI SDK 已成为事实上的标准接口风格很多兼容服务也使用相同协议因此用它做 ModelGateway 是通用选择。ChromaDB轻量级向量数据库支持本地文件持久化适合项目初期和中小规模数据。Docker用于部署和镜像化。先创建项目目录并安装依赖。mkdir beetles-ai cd beetles-ai python -m venv venv source venv/bin/activate # Windows 下执行 venv\Scripts\activatepip install fastapi uvicorn openai chromadb python-dotenv如果你需要启动一个本地模型服务作为兼容后端推荐使用 vLLM 或 Ollama本文不展开部署细节。3.3 项目目录结构按照职责划分项目结构如下。beetles-ai/ ├── app/ │ ├── __init__.py │ ├── main.py # FastAPI 入口 │ ├── config.py # 全局配置 │ ├── gateway.py # 模型网关 │ ├── rag.py # RAG 检索实现 │ ├── agent.py # Agent 运行时 │ ├── tools.py # 工具函数注册 │ └── schemas.py # 请求响应数据结构 ├── data/ │ ├── docs/ # 业务文档 │ └── vector_store/ # 向量数据库持久化目录 ├── .env # 环境变量 └── requirements.txt从开始就按模块拆分文件而不是把所有代码塞进一个 main.py这是工程化第一步。3.4 配置文件根目录下创建.env文件保存环境变量。MODEL_API_KEYyour-api-key-here MODEL_BASE_URLhttps://your-model-service.example.com/v1 MODEL_NAMEgpt-3.5-turbo EMBEDDING_MODELtext-embedding-3-small VECTOR_STORE_PATH./data/vector_store注意这个文件不要提交到 Git 仓库应在.gitignore中排除。密钥泄露是 AI 项目最常见的安全事故之一。4. 从零搭建一个最小可用服务4.1 配置管理所有配置集中读取避免在代码中硬编码密钥。创建app/config.py。# 文件路径app/config.py import os from dotenv import load_dotenv load_dotenv() class Settings: MODEL_API_KEY: str os.getenv(MODEL_API_KEY, ) MODEL_BASE_URL: str os.getenv(MODEL_BASE_URL, ) MODEL_NAME: str os.getenv(MODEL_NAME, gpt-3.5-turbo) EMBEDDING_MODEL: str os.getenv(EMBEDDING_MODEL, text-embedding-3-small) VECTOR_STORE_PATH: str os.getenv(VECTOR_STORE_PATH, ./data/vector_store) settings Settings()这里的关键点是所有可能变化的内容都从环境变量读取。以后更换模型、迁移服务器只改环境变量即可不需要动业务代码。4.2 模型网关模型网关层是所有模型调用的统一出口。创建app/gateway.py。# 文件路径app/gateway.py from openai import OpenAI from app.config import settings client OpenAI( api_keysettings.MODEL_API_KEY, base_urlsettings.MODEL_BASE_URL or None, ) def chat(messages, temperature0.7): response client.chat.completions.create( modelsettings.MODEL_NAME, messagesmessages, temperaturetemperature, ) return response.choices[0].message.content def embed_texts(texts): response client.embeddings.create( modelsettings.EMBEDDING_MODEL, inputtexts, ) return [item.embedding for item in response.data]以后如果要切换模型只需要修改配置中的 MODEL_NAME 和 MODEL_BASE_URL。如果某个模型不支持 Embedding 接口可以在网关层做一次本地模型与 API 模型的适配上层无感知。4.3 FastAPI 入口创建一个最小服务用于验证整个链路是否打通。# 文件路径app/main.py from fastapi import FastAPI from app.schemas import ChatRequest, ChatResponse from app.gateway import chat app FastAPI(titleBeetles AI) app.post(/chat, response_modelChatResponse) async def chat_endpoint(req: ChatRequest): messages [{role: user, content: req.message}] reply chat(messages) return ChatResponse(replyreply)创建请求和响应的数据结构。# 文件路径app/schemas.py from pydantic import BaseModel class ChatRequest(BaseModel): message: str class ChatResponse(BaseModel): reply: str这一步是为了验证“模型接入 - Web 服务 - 客户端调用”这条主链路。跑通之后再往里面加 RAG 和 Agent。5. 加入 RAG让模型回答业务知识5.1 文档切片与向量化RAG 的第一步是把文档切块。切块不能太大否则检索结果不精准也不能太小否则语义不完整。常用策略是固定块大小并设置重叠或者按标题、段落结构切分。我们用一个简单函数实现按字符切块# 文件路径app/rag.py from app.gateway import embed_texts import chromadb from app.config import settings def split_text(text, chunk_size500, overlap50): chunks [] start 0 while start len(text): end start chunk_size chunks.append(text[start:end]) start end - overlap return chunks def build_vector_store(docs, collection_namebusiness_docs): client chromadb.PersistentClient(pathsettings.VECTOR_STORE_PATH) collection client.get_or_create_collection(namecollection_name) all_chunks [] for doc in docs: all_chunks.extend(split_text(doc)) embeddings embed_texts(all_chunks) for idx, (chunk, emb) in enumerate(zip(all_chunks, embeddings)): collection.add( ids[str(idx)], embeddings[emb], documents[chunk], ) return len(all_chunks)这段代码展示了 RAG 最核心的流程文档 - 切块 - 向量化 - 入向量库。实际项目中文档往往来自 PDF、Word、Markdown 或数据库记录需要增加解析层。但核心思路不变。5.2 检索与生成在线问答时需要把用户问题向量化然后到向量库中检索最相似的片段把片段和问题一起交给大模型。# 文件路径app/rag.py追加 def retrieve(query, top_k3, collection_namebusiness_docs): client chromadb.PersistentClient(pathsettings.VECTOR_STORE_PATH) collection client.get_or_create_collection(namecollection_name) query_emb embed_texts([query])[0] results collection.query( query_embeddings[query_emb], n_resultstop_k, ) return results[documents][0] def rag_chat(query, collection_namebusiness_docs): retrieved_docs retrieve(query, collection_namecollection_name) system_prompt 你是 Beetles AI 助手请仅基于以下材料回答问题。如果材料中没有答案请明确说明。 context \n\n.join(retrieved_docs) user_content f材料\n{context}\n\n问题{query} messages [ {role: system, content: system_prompt}, {role: user, content: user_content}, ] return chat(messages)这一步体现 RAG 的核心价值把检索到的业务知识作为上下文注入 Prompt让模型基于事实回答而不是凭空编造。需要注意System Prompt 中要明确限定“只基于材料回答”否则模型仍然会用自己的知识自由发挥。5.3 完整接口把 RAG 接口暴露到 FastAPI 中。# 文件路径app/main.py追加 from pydantic import BaseModel from app.rag import rag_chat class RagRequest(BaseModel): question: str class RagResponse(BaseModel): answer: str sources: list app.post(/rag, response_modelRagResponse) async def rag_endpoint(req: RagRequest): from app.rag import retrieve, rag_chat sources retrieve(req.question) answer rag_chat(req.question) return RagResponse(answeranswer, sourcessources)这里把检索到的来源也返回给前端方便用户在页面上看到答案的依据。这一点在知识库问答场景里非常重要有来源的答案才可信、可审计。6. 实现一个最小 Agent工具调用编排6.1 Agent 的核心循环Agent 的核心是一个循环模型根据用户目标和可用工具决定调用哪个工具、传入什么参数程序执行工具把结果返回给模型模型继续判断是继续调用工具还是给出最终答案。这里用一个最小实现展示原理不引入重型 Agent 框架。工具函数放在app/tools.py。# 文件路径app/tools.py import datetime TOOL_DESCRIPTIONS [ { type: function, function: { name: get_current_time, description: 获取当前日期和时间, parameters: { type: object, properties: {}, }, }, }, { type: function, function: { name: calculate, description: 计算简单数学表达式, parameters: { type: object, properties: { expression: { type: string, description: 数学表达式例如 12*40, } }, required: [expression], }, }, }, ] def get_current_time(): return datetime.datetime.now().strftime(%Y-%m-%d %H:%M:%S) def calculate(expression: str): try: result eval(expression) return str(result) except Exception as e: return f计算失败: {str(e)}注意示例中calculate使用了eval实际生产环境非常不推荐直接 eval 用户输入存在代码注入风险。这里仅用于演示工具调用流程。生产环境应该使用表达式解析库或限制输入字符集合。6.2 Agent 运行时创建app/agent.py实现工具调用的循环逻辑。# 文件路径app/agent.py import json from app.gateway import chat from app.config import settings from app.tools import TOOL_DESCRIPTIONS, get_current_time, calculate TOOL_MAP { get_current_time: get_current_time, calculate: calculate, } def run_agent(user_message, max_steps5): messages [ {role: system, content: 你是 Beetles AI Agent可以调用工具完成任务。请按需调用不要闲聊。}, {role: user, content: user_message}, ] for step in range(max_steps): response client.chat.completions.create( modelsettings.MODEL_NAME, messagesmessages, toolsTOOL_DESCRIPTIONS, tool_choiceauto, ) message response.choices[0].message messages.append(message) if not message.tool_calls: return message.content for tool_call in message.tool_calls: function_name tool_call.function.name arguments json.loads(tool_call.function.arguments) if function_name in TOOL_MAP: result TOOL_MAP[function_name](**arguments) else: result f未找到工具: {function_name} messages.append({ role: tool, tool_call_id: tool_call.id, content: str(result), }) return 达到最大执行步数任务中止。这里需要补上client引用或者从 gateway 中导出。为保持代码清晰可以在 agent.py 中重新初始化一个 client。这个循环就是 Agent 的工作原理。你可以看到它并不神秘本质上是“模型决策 程序执行”的多次往返。工具越多、工具描述写得越好Agent 完成复杂任务的能力就越强。7. 运行与效果验证7.1 启动服务在项目根目录执行uvicorn app.main:app --host 0.0.0.0 --port 8000启动成功后终端会显示 Uvicorn running on http://0.0.0.0:8000。此时访问 http://localhost:8000/docs 可以看到 FastAPI 自动生成的接口文档。7.2 接口测试先用 curl 测试基础对话接口。curl -X POST http://localhost:8000/chat \ -H Content-Type: application/json \ -d {message: 你好请介绍一下你自己}预期返回一段模型的自我介绍文本。测试 RAG 接口前需要先写入一批业务文档到向量库。可以准备一个data/init_vector.py脚本# 文件路径data/init_vector.py from app.rag import build_vector_store docs [ Beetles AI 是一个 AI 应用工程示例项目包含模型网关、RAG 检索、Agent 编排功能。, Beetles AI 的模型网关支持通过统一接口切换不同模型服务。, Beetles AI 使用 ChromaDB 作为向量数据库适合中小规模知识库场景。, ] count build_vector_store(docs) print(f已写入 {count} 个文本块)执行python data/init_vector.py然后测试 RAG 接口curl -X POST http://localhost:8000/rag \ -H Content-Type: application/json \ -d {question: Beetles AI 使用什么向量数据库}如果返回的答案中包含“ChromaDB”且 sources 非空说明 RAG 链路正常。7.3 验证 Agent测试 Agent 接口需要先把 Agent 接口加到 main.py 中可以问curl -X POST http://localhost:8000/agent \ -H Content-Type: application/json \ -d {message: 现在几点了}如果 Agent 正常它会返回当前时间并且服务日志中可以看到“get_current_time”这个工具被调用了一次。7.4 失败时先看哪里如果接口报错优先检查三个地方配置是否有效MODEL_API_KEY 是否配置、模型服务地址是否能通。日志中是否有 HTTP 4xx/5xx模型服务返回错误时通常会在日志中带出具体状态码和原因。向量库是否为空如果 RAG 接口提示没有来源检查是否执行过初始化脚本。8. 常见问题与排查思路问题现象可能原因排查方式解决方案启动时报 OpenAI 连接超时模型服务地址不通或网络受限curl 测试模型服务地址检查 base_url确认网络可达使用内网模型服务返回 401 鉴权失败API Key 错误或未配置检查 .env 文件是否加载重新配置密钥确认从环境变量读取RAG 接口回答与知识库无关向量库写入失败或 Embedding 模型不一致检查 sources 字段确认写入和检索使用同一个 Embedding 模型重新初始化向量库回答问题没有引用业务材料System Prompt 没有限定范围检查 RAG Prompt 模板在 System Prompt 中明确要求“只基于材料回答”Agent 不调用工具工具描述不清晰或模型不支持工具调用打开日志查看模型返回的消息确认模型是否支持 tools 参数优化工具描述更换支持工具调用的模型向量库文件损坏ChromaDB 持久化过程中进程被强制终止查看服务运行日志检查向量库目录恢复备份重建向量库模型返回内容被截断max_tokens 设置过小查看返回结果的 finish_reason调大 max_tokens 或限制输出长度这些问题是 AI 应用上线前最常见的几类。记住一个原则先查配置再查网络最后查代码。9. 最佳实践从 Demo 到生产环境的工程建议9.1 模型网关是刚需不要省略哪怕你现在只接一个模型也强烈建议保留模型网关层。原因是业务一旦上线模型切换的诉求几乎必然出现。价格调整、效果升级、合规要求都可能迫使你换模型。有了网关层切换只动配置没有网关层你要全项目搜索openai.ChatCompletion然后逐行替换。9.2 Prompt 需要版本管理Prompt 是 AI 应用的核心资产但很多人把 Prompt 写在代码里改一次就覆盖一次。建议把 Prompt 模板收敛到独立目录放到配置中心或 Git 仓库中管理。上线后如果 Prompt 调整导致回答质量下降可以快速回滚。9.3 安全边界不能放松AI 应用的安全主要体现在三个层面密钥安全API Key 只存在环境变量或密钥管理服务中禁止硬编码。注入防护用户输入可能包含恶意指令System Prompt 要设置边界工具函数绝不能直接 eval 用户输入。数据合规发送给外部模型服务的数据要脱敏敏感业务优先选择私有化部署或本地模型。9.4 可观测性建设给每次模型调用记录日志时至少包含请求 ID、会话 ID、模型名称、Token 消耗、延迟、Prompt 摘要和响应状态。这样出了问题才能回溯。生产环境建议引入链路追踪工具把模型调用、工具调用、向量检索都纳入同一追踪链路。9.5 成本控制Token 是 AI 应用的主要成本。控制成本的手段包括缓存重复请求、控制 Prompt 长度、只对必要内容做 Embedding、对高调用量场景使用批量处理。上线前一定要估算不同调用量下的成本上限。9.6 从单体到微服务的演进节奏不要一开始就上微服务。先保持单体应用把模块边界画清楚。当某个模块比如 RAG 引擎独立扩展成为瓶颈时再把它拆成独立服务。过早拆分只会增加部署和调试成本。10. 总结与后续学习方向Beetles AI 这个示例项目的意义不在于它本身有多强而在于它把 AI 应用开发中真正考验人的部分展示了出来模型接入、RAG 检索、Agent 编排、服务发布、问题排查每一层都有工程决策。如果你正在开发自己的 AI 应用建议从本文的最小骨架开始先跑通一次完整链路。然后在实际业务中逐步替换或增强把内存向量库换成生产级向量数据库把函数工具换成真正的 HTTP API 调用把单体服务按模块拆分。下一步值得深入了解的方向包括Agent 框架的底层原理、RAG 的混合检索与重排序、模型微调与评测体系、多模型路由与成本优化、AI 应用的可观测性与持续集成。这些内容之间是层层递进的关系先能跑通再能上线最后能优化。如果你手头正好有一个带着业务数据的 AI 项目建议现在就把本篇文章的骨架复制一份替换成你自己的业务逻辑和数据。先跑通再迭代。很多问题只有真正部署到服务里才会暴露而本文的工程化思路就是让你在暴露问题时有地方可以查、有计划可以改。
返回列表