Gemma-3-12b-it部署指南Ollama FastAPI Gradio构建生产级多模态API1. 引言为什么需要构建自己的多模态API想象一下你手头有一个强大的多模态AI模型它能看懂图片、理解文字还能进行复杂的推理。但每次使用你都得打开一个网页手动上传图片、输入问题然后等待结果。对于个人探索来说这没问题。但如果你想把它集成到自己的应用里比如做一个智能客服机器人、一个自动生成商品描述的电商工具或者一个辅助学习的教育平台这种手动操作的方式就完全行不通了。这就是我们今天要解决的问题。Gemma-3-12b-it是一个功能强大的多模态模型能同时处理文本和图像。通过Ollama我们可以很方便地在本地或服务器上运行它。但Ollama默认的Web界面只是一个交互工具不是为程序调用设计的。我们需要一个标准的、稳定的、可扩展的API接口让其他程序也能方便地调用这个模型。本文将带你一步步从零开始用Ollama部署Gemma-3-12b-it模型然后用FastAPI给它穿上“API的外衣”最后用Gradio搭建一个美观的演示界面。最终你将拥有一个完整的、可用于生产环境的AI服务后端。2. 环境准备与核心工具介绍在开始动手之前我们先快速了解一下需要用到的几个核心工具以及它们各自扮演的角色。2.1 核心组件分工整个系统的架构可以看作一个三层结构每个工具负责一层Ollama模型运行层这是我们的“发动机”。它负责在后台加载和运行Gemma-3-12b-it模型处理最核心的AI计算任务。它本身提供了一个简单的本地API通常在http://localhost:11434但功能比较基础。FastAPIAPI服务层这是我们的“调度中心”和“标准化接口”。我们将基于Ollama的本地API用FastAPI构建一个更强大、更规范、更安全的RESTful API。它可以处理复杂的请求逻辑、输入验证、错误处理并方便我们日后添加身份验证、速率限制、日志记录等生产级功能。Gradio用户界面层这是我们的“展示窗口”和“测试平台”。Gradio能快速为我们的FastAPI后端生成一个交互式的Web界面。这样我们不仅可以通过代码调用API还能通过一个直观的网页来测试和演示模型的所有功能。简单来说Ollama负责“思考”FastAPI负责“沟通”Gradio负责“展示”。2.2 系统与依赖要求为了顺利完成本教程你需要准备以下环境操作系统Linux如Ubuntu 20.04、macOS或Windows建议使用WSL2以获得最佳体验。Python版本 3.8 或更高。这是运行FastAPI和Gradio所必需的。内存运行Gemma-3-12b-it模型建议至少有16GB以上的可用内存。Ollama已安装并正确运行。确保你能在命令行中通过ollama run gemma3:12b与模型进行简单的文本对话。如果你还没有安装Ollama可以访问其官方网站根据你的操作系统选择对应的安装方式通常只是一条命令的事情。3. 第一步通过Ollama部署Gemma-3-12b-it我们的旅程从启动模型开始。确保你的Ollama服务正在运行。3.1 拉取并运行模型打开你的终端命令行执行以下命令。这会从Ollama的模型库中下载Gemma-3-12b-it模型如果尚未下载并以交互模式运行它。ollama run gemma3:12b首次运行会下载约12B参数大小的模型文件需要一些时间和磁盘空间。下载完成后你会进入一个交互式聊天界面可以输入纯文本进行测试例如输入“Hello, tell me a joke.”。这证明模型的基础文本功能是正常的。关键一步为了后续通过API调用我们需要让Ollama在后台以服务模式运行并开放其API接口。通常安装Ollama后它会自动作为一个系统服务启动并监听http://localhost:11434。你可以通过以下命令检查服务状态# Linux/macOS curl http://localhost:11434/api/tags # Windows (在PowerShell中) Invoke-RestMethod -Uri http://localhost:11434/api/tags如果返回一个JSON格式的模型列表其中包含gemma3:12b说明Ollama服务运行正常API可访问。3.2 理解Ollama的原始APIOllama的API非常简单。我们主要关注两个端点POST /api/generate: 用于生成文本补全。POST /api/chat: 用于多轮对话更适合与Gemma这样的对话模型交互。对于多模态图像输入我们需要使用/api/chat端点并在请求体中按照特定格式传递图像数据。这是我们需要用FastAPI来封装和简化的关键点。4. 第二步使用FastAPI构建标准化API现在我们将创建一个Python项目用FastAPI来封装Ollama的API使其更易用、更健壮。4.1 创建项目并安装依赖首先创建一个新的项目目录并进入该目录。mkdir gemma-multimodal-api cd gemma-multimodal-api然后创建一个requirements.txt文件列出我们需要的Python库。# requirements.txt fastapi[all] uvicorn[standard] gradio httpx python-multipart pydantic接着使用pip安装这些依赖。pip install -r requirements.txt4.2 编写FastAPI核心应用创建一个名为main.py的文件我们将在这里编写主要的API逻辑。# main.py from fastapi import FastAPI, UploadFile, File, Form, HTTPException from fastapi.responses import JSONResponse from pydantic import BaseModel from typing import Optional, List import httpx import base64 import logging import json # 配置日志 logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) # 初始化FastAPI应用 app FastAPI( titleGemma-3-12b-it 多模态API服务, description基于Ollama和FastAPI构建的生产级多模态模型API, version1.0.0 ) # 配置Ollama API地址 OLLAMA_BASE_URL http://localhost:11434 # 定义请求/响应模型 class ChatMessage(BaseModel): role: str # user 或 assistant content: str images: Optional[List[str]] None # 存储Base64编码的图片 class ChatRequest(BaseModel): model: str gemma3:12b # 默认模型 messages: List[ChatMessage] stream: bool False # 是否使用流式响应 class ChatResponse(BaseModel): model: str message: ChatMessage total_duration: Optional[int] None app.get(/) async def root(): API根路径返回服务信息 return { service: Gemma-3-12b-it Multimodal API, status: running, endpoints: { chat: /v1/chat/completions (POST), models: /v1/models (GET) } } app.get(/v1/models) async def list_models(): 获取可用的模型列表 try: async with httpx.AsyncClient() as client: response await client.get(f{OLLAMA_BASE_URL}/api/tags) models_data response.json() # 格式化返回模仿OpenAI API格式 return { object: list, data: [ { id: model[name], object: model, created: 0, # Ollama不提供此信息 owned_by: ollama } for model in models_data.get(models, []) ] } except Exception as e: logger.error(f获取模型列表失败: {e}) raise HTTPException(status_code500, detail无法连接至Ollama服务) app.post(/v1/chat/completions) async def chat_completion( request: ChatRequest, image_files: Optional[List[UploadFile]] File(None) ): 核心聊天补全端点支持多模态输入。 图像可以通过Base64编码在messages中传递也可以通过文件上传。 try: # 准备发送给Ollama的消息 ollama_messages [] for msg in request.messages: message_content msg.content # 处理图像数据 images_base64 [] if msg.images: images_base64.extend(msg.images) # 构建Ollama期望的格式 ollama_msg { role: msg.role, content: message_content, } # 如果有图像添加到消息中 if images_base64: ollama_msg[images] images_base64 ollama_messages.append(ollama_msg) # 构建Ollama请求体 ollama_request { model: request.model, messages: ollama_messages, stream: request.stream } logger.info(f向Ollama发送请求模型: {request.model}) # 发送请求到Ollama async with httpx.AsyncClient(timeout60.0) as client: response await client.post( f{OLLAMA_BASE_URL}/api/chat, jsonollama_request, headers{Content-Type: application/json} ) if response.status_code ! 200: error_detail response.text logger.error(fOllama API错误: {error_detail}) raise HTTPException( status_coderesponse.status_code, detailfOllama服务错误: {error_detail} ) ollama_response response.json() # 格式化响应以匹配OpenAI风格 formatted_response { id: fchatcmpl-{hash(str(ollama_response))}, object: chat.completion, created: 0, # 时间戳可在此处添加 model: request.model, choices: [{ index: 0, message: { role: assistant, content: ollama_response.get(message, {}).get(content, ) }, finish_reason: stop }], usage: { prompt_tokens: 0, # Ollama未提供可留空或估算 completion_tokens: 0, total_tokens: 0 } } return formatted_response except httpx.RequestError as e: logger.error(f连接Ollama失败: {e}) raise HTTPException(status_code503, detail无法连接至AI模型后端) except Exception as e: logger.error(f处理请求时发生未知错误: {e}) raise HTTPException(status_code500, detail内部服务器错误) # 一个便捷的端点用于处理文件上传并自动Base64编码 app.post(/v1/upload_and_chat) async def upload_and_chat( prompt: str Form(...), model: str Form(gemma3:12b), files: List[UploadFile] File(...) ): 简化接口上传图片文件并与文本提示一起发送 try: images_base64 [] for file in files: if file.content_type.startswith(image/): contents await file.read() base64_encoded base64.b64encode(contents).decode(utf-8) images_base64.append(base64_encoded) else: raise HTTPException(status_code400, detailf文件 {file.filename} 不是图片格式) # 构建消息 messages [ChatMessage(roleuser, contentprompt, imagesimages_base64)] chat_request ChatRequest(modelmodel, messagesmessages) # 复用上面的聊天端点逻辑 # 这里为了简化我们直接调用内部函数 # 在实际项目中你可能需要重构代码以避免重复 return await chat_completion(chat_request) except Exception as e: logger.error(f上传处理失败: {e}) raise HTTPException(status_code500, detail处理上传文件时出错) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)这个main.py文件做了以下几件关键事情定义了标准的请求/响应格式使用了Pydantic模型这让API接口清晰且能自动验证数据。创建了兼容OpenAI格式的端点/v1/chat/completions和/v1/models这使得你的服务可以轻松替换一些原本使用OpenAI API的代码。处理了多模态输入核心逻辑是将用户上传的图片或Base64编码的图片数据正确格式化成Ollama API能理解的格式包含在messages字段的images数组中。添加了错误处理与日志使服务更加健壮便于排查问题。提供了一个便捷的上传接口/v1/upload_and_chat让通过表单上传图片文件变得非常简单。4.3 启动并测试FastAPI服务保存main.py后在终端中运行uvicorn main:app --reload --host 0.0.0.0 --port 8000--reload代码修改后自动重启适合开发。--host 0.0.0.0允许其他设备访问如果需要在局域网内测试。--port 8000指定服务端口。启动后打开浏览器访问http://localhost:8000/docs。你会看到FastAPI自动生成的交互式API文档Swagger UI。在这里你可以直接测试/v1/chat/completions接口。如何测试在/v1/chat/completions的“Try it out”区域。输入一个JSON请求体例如{ model: gemma3:12b, messages: [ { role: user, content: 请描述这张图片里有什么。, images: [这里需要替换成真实的Base64编码图片字符串很长] } ] }点击“Execute”发送请求。如果一切正常你会收到模型返回的文本描述。通过Swagger UI测试成功意味着你的API后端已经构建完成现在我们可以为它添加一个前端界面了。5. 第三步用Gradio打造友好交互界面虽然有了API但一个可视化的网页界面对于演示和快速测试来说非常方便。Gradio能让我们用很少的代码实现这个功能。创建一个新的文件gradio_app.py。# gradio_app.py import gradio as gr import requests import base64 import os import tempfile # 配置后端API地址 API_BASE_URL http://localhost:8000 # 假设FastAPI运行在8000端口 def chat_with_gemma(prompt, image_files, model_choice, history): 处理Gradio界面的聊天请求。 history: Gradio管理的聊天历史记录 if not prompt and not image_files: return history, 请输入问题或上传图片。 # 准备消息历史简化处理实际可根据需要调整 messages [] # 如果有历史记录将其转换为API格式这里做简单拼接对于多轮对话需更精细处理 if history: # 注意这里简化了历史处理。生产环境需要将历史对话正确格式化为多轮消息。 # 为了演示我们只发送最新一轮的用户输入。 pass # 处理当前轮次的用户输入 user_content prompt if prompt else 请分析这张图片。 images_base64 [] # 处理上传的图片文件 if image_files: for img_path in image_files: try: with open(img_path, rb) as f: img_data f.read() encoded base64.b64encode(img_data).decode(utf-8) images_base64.append(encoded) except Exception as e: print(f处理图片 {img_path} 时出错: {e}) # 构建最终发送给API的消息 final_messages [ { role: user, content: user_content, images: images_base64 if images_base64 else None } ] # 移除为空的images字段 for msg in final_messages: if msg.get(images) is None: msg.pop(images, None) # 准备请求数据 request_data { model: model_choice, messages: final_messages, stream: False } # 调用我们自己的FastAPI后端 try: response requests.post( f{API_BASE_URL}/v1/chat/completions, jsonrequest_data, timeout60 ) if response.status_code 200: result response.json() assistant_reply result[choices][0][message][content] # 更新Gradio聊天历史 # 添加用户消息如果有图片用特殊标记表示 user_display prompt if prompt else [图片] if image_files: user_display (已上传图片) history.append((user_display, assistant_reply)) return history, # 清空输入框 else: error_msg fAPI请求失败: {response.status_code} - {response.text} print(error_msg) return history, error_msg except requests.exceptions.RequestException as e: error_msg f连接API服务失败: {e} print(error_msg) return history, error_msg except Exception as e: error_msg f处理过程中发生错误: {e} print(error_msg) return history, error_msg def clear_chat(): 清空聊天历史 return [], # 创建Gradio界面 with gr.Blocks(titleGemma-3-12b-it 多模态演示, themegr.themes.Soft()) as demo: gr.Markdown(# Gemma-3-12b-it 多模态交互演示) gr.Markdown(上传图片并提问体验模型的多模态理解能力。) # 聊天历史记录显示 chatbot gr.Chatbot(label对话历史, height400) # 状态存储 chat_state gr.State([]) with gr.Row(): with gr.Column(scale3): # 文本输入 prompt_input gr.Textbox( label输入你的问题, placeholder例如描述这张图片的内容..., lines3 ) # 图片上传支持多文件 image_input gr.Files( label上传图片可选, file_types[image], file_countmultiple ) # 模型选择 model_dropdown gr.Dropdown( choices[gemma3:12b], # 可以从 /v1/models 端点动态获取 valuegemma3:12b, label选择模型 ) with gr.Row(): submit_btn gr.Button(发送, variantprimary) clear_btn gr.Button(清空对话) # 状态信息显示 status_output gr.Textbox(label状态, interactiveFalse) # 右侧可以添加一些说明或示例 with gr.Column(scale1): gr.Markdown(### 使用示例) gr.Markdown( **1. 图片描述** 上传一张风景照输入“这张图片是在哪里拍的” **2. 图表分析** 上传一个图表截图输入“总结这个图表的主要趋势。” **3. 多图推理** 上传多张相关图片输入“比较这两款产品的设计特点。” ) # 绑定事件 submit_btn.click( fnchat_with_gemma, inputs[prompt_input, image_input, model_dropdown, chatbot], outputs[chatbot, status_output] ) # 回车键也触发发送 prompt_input.submit( fnchat_with_gemma, inputs[prompt_input, image_input, model_dropdown, chatbot], outputs[chatbot, status_output] ) clear_btn.click(fnclear_chat, inputs[], outputs[chatbot, status_output]) # 启动Gradio应用 if __name__ __main__: # 先检查后端API是否可用 try: test_resp requests.get(f{API_BASE_URL}/, timeout5) if test_resp.status_code 200: print(✅ 检测到FastAPI后端服务运行正常。) else: print(f⚠️ 后端服务响应异常: {test_resp.status_code}) except: print(❌ 无法连接到FastAPI后端请确保已运行 uvicorn main:app --reload。) demo.launch( server_name0.0.0.0, server_port7860, shareFalse # 设置为True可生成临时公网链接 )这个Gradio应用提供了一个聊天机器人界面直观显示对话历史。文本输入框用于输入问题。图片上传组件支持一次上传多张图片。模型选择目前固定为Gemma-3-12b但你可以扩展为从API动态获取。示例提示给用户一些使用灵感。5.1 启动完整应用栈现在你需要打开两个终端窗口终端1运行FastAPI后端cd /path/to/your/gemma-multimodal-api uvicorn main:app --reload --host 0.0.0.0 --port 8000终端2运行Gradio前端cd /path/to/your/gemma-multimodal-api python gradio_app.pyGradio应用默认会在http://localhost:7860启动。打开浏览器访问这个地址你就可以看到一个完整的、可交互的多模态AI应用了上传一张图片输入一个问题点击“发送”就能看到Gemma-3-12b-it模型的分析结果。6. 总结从个人工具到生产服务通过以上三步我们完成了一个完整的生产级多模态API服务的搭建模型层Ollama我们利用Ollama的易用性轻松部署并运行了强大的Gemma-3-12b-it多模态模型。服务层FastAPI我们构建了一个标准化、健壮的RESTful API。这个API不仅封装了模型调用还提供了错误处理、输入验证和OpenAI兼容格式极大地方便了与其他系统的集成。交互层Gradio我们创建了一个美观且功能完备的Web界面使得非技术用户也能轻松体验和测试模型能力。这个架构的优势在于解耦与灵活每一层都可以独立升级或替换。例如你可以更换后端模型如使用其他Ollama模型而无需修改API接口或前端界面。易于扩展你可以在FastAPI层轻松添加中间件实现身份验证API Key、速率限制、请求日志、监控指标等功能。部署简便整个应用可以容器化使用Docker轻松部署到任何云服务器或本地服务器。你现在拥有的不再只是一个命令行工具而是一个真正的、可对外提供服务的AI能力端点。你可以将它集成到你的网站、移动应用或自动化工作流中让Gemma-3-12b-it的视觉理解能力为你创造实际价值。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。
gemma-3-12b-it部署指南:Ollama + FastAPI + Gradio构建生产级多模态API
Gemma-3-12b-it部署指南Ollama FastAPI Gradio构建生产级多模态API1. 引言为什么需要构建自己的多模态API想象一下你手头有一个强大的多模态AI模型它能看懂图片、理解文字还能进行复杂的推理。但每次使用你都得打开一个网页手动上传图片、输入问题然后等待结果。对于个人探索来说这没问题。但如果你想把它集成到自己的应用里比如做一个智能客服机器人、一个自动生成商品描述的电商工具或者一个辅助学习的教育平台这种手动操作的方式就完全行不通了。这就是我们今天要解决的问题。Gemma-3-12b-it是一个功能强大的多模态模型能同时处理文本和图像。通过Ollama我们可以很方便地在本地或服务器上运行它。但Ollama默认的Web界面只是一个交互工具不是为程序调用设计的。我们需要一个标准的、稳定的、可扩展的API接口让其他程序也能方便地调用这个模型。本文将带你一步步从零开始用Ollama部署Gemma-3-12b-it模型然后用FastAPI给它穿上“API的外衣”最后用Gradio搭建一个美观的演示界面。最终你将拥有一个完整的、可用于生产环境的AI服务后端。2. 环境准备与核心工具介绍在开始动手之前我们先快速了解一下需要用到的几个核心工具以及它们各自扮演的角色。2.1 核心组件分工整个系统的架构可以看作一个三层结构每个工具负责一层Ollama模型运行层这是我们的“发动机”。它负责在后台加载和运行Gemma-3-12b-it模型处理最核心的AI计算任务。它本身提供了一个简单的本地API通常在http://localhost:11434但功能比较基础。FastAPIAPI服务层这是我们的“调度中心”和“标准化接口”。我们将基于Ollama的本地API用FastAPI构建一个更强大、更规范、更安全的RESTful API。它可以处理复杂的请求逻辑、输入验证、错误处理并方便我们日后添加身份验证、速率限制、日志记录等生产级功能。Gradio用户界面层这是我们的“展示窗口”和“测试平台”。Gradio能快速为我们的FastAPI后端生成一个交互式的Web界面。这样我们不仅可以通过代码调用API还能通过一个直观的网页来测试和演示模型的所有功能。简单来说Ollama负责“思考”FastAPI负责“沟通”Gradio负责“展示”。2.2 系统与依赖要求为了顺利完成本教程你需要准备以下环境操作系统Linux如Ubuntu 20.04、macOS或Windows建议使用WSL2以获得最佳体验。Python版本 3.8 或更高。这是运行FastAPI和Gradio所必需的。内存运行Gemma-3-12b-it模型建议至少有16GB以上的可用内存。Ollama已安装并正确运行。确保你能在命令行中通过ollama run gemma3:12b与模型进行简单的文本对话。如果你还没有安装Ollama可以访问其官方网站根据你的操作系统选择对应的安装方式通常只是一条命令的事情。3. 第一步通过Ollama部署Gemma-3-12b-it我们的旅程从启动模型开始。确保你的Ollama服务正在运行。3.1 拉取并运行模型打开你的终端命令行执行以下命令。这会从Ollama的模型库中下载Gemma-3-12b-it模型如果尚未下载并以交互模式运行它。ollama run gemma3:12b首次运行会下载约12B参数大小的模型文件需要一些时间和磁盘空间。下载完成后你会进入一个交互式聊天界面可以输入纯文本进行测试例如输入“Hello, tell me a joke.”。这证明模型的基础文本功能是正常的。关键一步为了后续通过API调用我们需要让Ollama在后台以服务模式运行并开放其API接口。通常安装Ollama后它会自动作为一个系统服务启动并监听http://localhost:11434。你可以通过以下命令检查服务状态# Linux/macOS curl http://localhost:11434/api/tags # Windows (在PowerShell中) Invoke-RestMethod -Uri http://localhost:11434/api/tags如果返回一个JSON格式的模型列表其中包含gemma3:12b说明Ollama服务运行正常API可访问。3.2 理解Ollama的原始APIOllama的API非常简单。我们主要关注两个端点POST /api/generate: 用于生成文本补全。POST /api/chat: 用于多轮对话更适合与Gemma这样的对话模型交互。对于多模态图像输入我们需要使用/api/chat端点并在请求体中按照特定格式传递图像数据。这是我们需要用FastAPI来封装和简化的关键点。4. 第二步使用FastAPI构建标准化API现在我们将创建一个Python项目用FastAPI来封装Ollama的API使其更易用、更健壮。4.1 创建项目并安装依赖首先创建一个新的项目目录并进入该目录。mkdir gemma-multimodal-api cd gemma-multimodal-api然后创建一个requirements.txt文件列出我们需要的Python库。# requirements.txt fastapi[all] uvicorn[standard] gradio httpx python-multipart pydantic接着使用pip安装这些依赖。pip install -r requirements.txt4.2 编写FastAPI核心应用创建一个名为main.py的文件我们将在这里编写主要的API逻辑。# main.py from fastapi import FastAPI, UploadFile, File, Form, HTTPException from fastapi.responses import JSONResponse from pydantic import BaseModel from typing import Optional, List import httpx import base64 import logging import json # 配置日志 logging.basicConfig(levellogging.INFO) logger logging.getLogger(__name__) # 初始化FastAPI应用 app FastAPI( titleGemma-3-12b-it 多模态API服务, description基于Ollama和FastAPI构建的生产级多模态模型API, version1.0.0 ) # 配置Ollama API地址 OLLAMA_BASE_URL http://localhost:11434 # 定义请求/响应模型 class ChatMessage(BaseModel): role: str # user 或 assistant content: str images: Optional[List[str]] None # 存储Base64编码的图片 class ChatRequest(BaseModel): model: str gemma3:12b # 默认模型 messages: List[ChatMessage] stream: bool False # 是否使用流式响应 class ChatResponse(BaseModel): model: str message: ChatMessage total_duration: Optional[int] None app.get(/) async def root(): API根路径返回服务信息 return { service: Gemma-3-12b-it Multimodal API, status: running, endpoints: { chat: /v1/chat/completions (POST), models: /v1/models (GET) } } app.get(/v1/models) async def list_models(): 获取可用的模型列表 try: async with httpx.AsyncClient() as client: response await client.get(f{OLLAMA_BASE_URL}/api/tags) models_data response.json() # 格式化返回模仿OpenAI API格式 return { object: list, data: [ { id: model[name], object: model, created: 0, # Ollama不提供此信息 owned_by: ollama } for model in models_data.get(models, []) ] } except Exception as e: logger.error(f获取模型列表失败: {e}) raise HTTPException(status_code500, detail无法连接至Ollama服务) app.post(/v1/chat/completions) async def chat_completion( request: ChatRequest, image_files: Optional[List[UploadFile]] File(None) ): 核心聊天补全端点支持多模态输入。 图像可以通过Base64编码在messages中传递也可以通过文件上传。 try: # 准备发送给Ollama的消息 ollama_messages [] for msg in request.messages: message_content msg.content # 处理图像数据 images_base64 [] if msg.images: images_base64.extend(msg.images) # 构建Ollama期望的格式 ollama_msg { role: msg.role, content: message_content, } # 如果有图像添加到消息中 if images_base64: ollama_msg[images] images_base64 ollama_messages.append(ollama_msg) # 构建Ollama请求体 ollama_request { model: request.model, messages: ollama_messages, stream: request.stream } logger.info(f向Ollama发送请求模型: {request.model}) # 发送请求到Ollama async with httpx.AsyncClient(timeout60.0) as client: response await client.post( f{OLLAMA_BASE_URL}/api/chat, jsonollama_request, headers{Content-Type: application/json} ) if response.status_code ! 200: error_detail response.text logger.error(fOllama API错误: {error_detail}) raise HTTPException( status_coderesponse.status_code, detailfOllama服务错误: {error_detail} ) ollama_response response.json() # 格式化响应以匹配OpenAI风格 formatted_response { id: fchatcmpl-{hash(str(ollama_response))}, object: chat.completion, created: 0, # 时间戳可在此处添加 model: request.model, choices: [{ index: 0, message: { role: assistant, content: ollama_response.get(message, {}).get(content, ) }, finish_reason: stop }], usage: { prompt_tokens: 0, # Ollama未提供可留空或估算 completion_tokens: 0, total_tokens: 0 } } return formatted_response except httpx.RequestError as e: logger.error(f连接Ollama失败: {e}) raise HTTPException(status_code503, detail无法连接至AI模型后端) except Exception as e: logger.error(f处理请求时发生未知错误: {e}) raise HTTPException(status_code500, detail内部服务器错误) # 一个便捷的端点用于处理文件上传并自动Base64编码 app.post(/v1/upload_and_chat) async def upload_and_chat( prompt: str Form(...), model: str Form(gemma3:12b), files: List[UploadFile] File(...) ): 简化接口上传图片文件并与文本提示一起发送 try: images_base64 [] for file in files: if file.content_type.startswith(image/): contents await file.read() base64_encoded base64.b64encode(contents).decode(utf-8) images_base64.append(base64_encoded) else: raise HTTPException(status_code400, detailf文件 {file.filename} 不是图片格式) # 构建消息 messages [ChatMessage(roleuser, contentprompt, imagesimages_base64)] chat_request ChatRequest(modelmodel, messagesmessages) # 复用上面的聊天端点逻辑 # 这里为了简化我们直接调用内部函数 # 在实际项目中你可能需要重构代码以避免重复 return await chat_completion(chat_request) except Exception as e: logger.error(f上传处理失败: {e}) raise HTTPException(status_code500, detail处理上传文件时出错) if __name__ __main__: import uvicorn uvicorn.run(app, host0.0.0.0, port8000)这个main.py文件做了以下几件关键事情定义了标准的请求/响应格式使用了Pydantic模型这让API接口清晰且能自动验证数据。创建了兼容OpenAI格式的端点/v1/chat/completions和/v1/models这使得你的服务可以轻松替换一些原本使用OpenAI API的代码。处理了多模态输入核心逻辑是将用户上传的图片或Base64编码的图片数据正确格式化成Ollama API能理解的格式包含在messages字段的images数组中。添加了错误处理与日志使服务更加健壮便于排查问题。提供了一个便捷的上传接口/v1/upload_and_chat让通过表单上传图片文件变得非常简单。4.3 启动并测试FastAPI服务保存main.py后在终端中运行uvicorn main:app --reload --host 0.0.0.0 --port 8000--reload代码修改后自动重启适合开发。--host 0.0.0.0允许其他设备访问如果需要在局域网内测试。--port 8000指定服务端口。启动后打开浏览器访问http://localhost:8000/docs。你会看到FastAPI自动生成的交互式API文档Swagger UI。在这里你可以直接测试/v1/chat/completions接口。如何测试在/v1/chat/completions的“Try it out”区域。输入一个JSON请求体例如{ model: gemma3:12b, messages: [ { role: user, content: 请描述这张图片里有什么。, images: [这里需要替换成真实的Base64编码图片字符串很长] } ] }点击“Execute”发送请求。如果一切正常你会收到模型返回的文本描述。通过Swagger UI测试成功意味着你的API后端已经构建完成现在我们可以为它添加一个前端界面了。5. 第三步用Gradio打造友好交互界面虽然有了API但一个可视化的网页界面对于演示和快速测试来说非常方便。Gradio能让我们用很少的代码实现这个功能。创建一个新的文件gradio_app.py。# gradio_app.py import gradio as gr import requests import base64 import os import tempfile # 配置后端API地址 API_BASE_URL http://localhost:8000 # 假设FastAPI运行在8000端口 def chat_with_gemma(prompt, image_files, model_choice, history): 处理Gradio界面的聊天请求。 history: Gradio管理的聊天历史记录 if not prompt and not image_files: return history, 请输入问题或上传图片。 # 准备消息历史简化处理实际可根据需要调整 messages [] # 如果有历史记录将其转换为API格式这里做简单拼接对于多轮对话需更精细处理 if history: # 注意这里简化了历史处理。生产环境需要将历史对话正确格式化为多轮消息。 # 为了演示我们只发送最新一轮的用户输入。 pass # 处理当前轮次的用户输入 user_content prompt if prompt else 请分析这张图片。 images_base64 [] # 处理上传的图片文件 if image_files: for img_path in image_files: try: with open(img_path, rb) as f: img_data f.read() encoded base64.b64encode(img_data).decode(utf-8) images_base64.append(encoded) except Exception as e: print(f处理图片 {img_path} 时出错: {e}) # 构建最终发送给API的消息 final_messages [ { role: user, content: user_content, images: images_base64 if images_base64 else None } ] # 移除为空的images字段 for msg in final_messages: if msg.get(images) is None: msg.pop(images, None) # 准备请求数据 request_data { model: model_choice, messages: final_messages, stream: False } # 调用我们自己的FastAPI后端 try: response requests.post( f{API_BASE_URL}/v1/chat/completions, jsonrequest_data, timeout60 ) if response.status_code 200: result response.json() assistant_reply result[choices][0][message][content] # 更新Gradio聊天历史 # 添加用户消息如果有图片用特殊标记表示 user_display prompt if prompt else [图片] if image_files: user_display (已上传图片) history.append((user_display, assistant_reply)) return history, # 清空输入框 else: error_msg fAPI请求失败: {response.status_code} - {response.text} print(error_msg) return history, error_msg except requests.exceptions.RequestException as e: error_msg f连接API服务失败: {e} print(error_msg) return history, error_msg except Exception as e: error_msg f处理过程中发生错误: {e} print(error_msg) return history, error_msg def clear_chat(): 清空聊天历史 return [], # 创建Gradio界面 with gr.Blocks(titleGemma-3-12b-it 多模态演示, themegr.themes.Soft()) as demo: gr.Markdown(# Gemma-3-12b-it 多模态交互演示) gr.Markdown(上传图片并提问体验模型的多模态理解能力。) # 聊天历史记录显示 chatbot gr.Chatbot(label对话历史, height400) # 状态存储 chat_state gr.State([]) with gr.Row(): with gr.Column(scale3): # 文本输入 prompt_input gr.Textbox( label输入你的问题, placeholder例如描述这张图片的内容..., lines3 ) # 图片上传支持多文件 image_input gr.Files( label上传图片可选, file_types[image], file_countmultiple ) # 模型选择 model_dropdown gr.Dropdown( choices[gemma3:12b], # 可以从 /v1/models 端点动态获取 valuegemma3:12b, label选择模型 ) with gr.Row(): submit_btn gr.Button(发送, variantprimary) clear_btn gr.Button(清空对话) # 状态信息显示 status_output gr.Textbox(label状态, interactiveFalse) # 右侧可以添加一些说明或示例 with gr.Column(scale1): gr.Markdown(### 使用示例) gr.Markdown( **1. 图片描述** 上传一张风景照输入“这张图片是在哪里拍的” **2. 图表分析** 上传一个图表截图输入“总结这个图表的主要趋势。” **3. 多图推理** 上传多张相关图片输入“比较这两款产品的设计特点。” ) # 绑定事件 submit_btn.click( fnchat_with_gemma, inputs[prompt_input, image_input, model_dropdown, chatbot], outputs[chatbot, status_output] ) # 回车键也触发发送 prompt_input.submit( fnchat_with_gemma, inputs[prompt_input, image_input, model_dropdown, chatbot], outputs[chatbot, status_output] ) clear_btn.click(fnclear_chat, inputs[], outputs[chatbot, status_output]) # 启动Gradio应用 if __name__ __main__: # 先检查后端API是否可用 try: test_resp requests.get(f{API_BASE_URL}/, timeout5) if test_resp.status_code 200: print(✅ 检测到FastAPI后端服务运行正常。) else: print(f⚠️ 后端服务响应异常: {test_resp.status_code}) except: print(❌ 无法连接到FastAPI后端请确保已运行 uvicorn main:app --reload。) demo.launch( server_name0.0.0.0, server_port7860, shareFalse # 设置为True可生成临时公网链接 )这个Gradio应用提供了一个聊天机器人界面直观显示对话历史。文本输入框用于输入问题。图片上传组件支持一次上传多张图片。模型选择目前固定为Gemma-3-12b但你可以扩展为从API动态获取。示例提示给用户一些使用灵感。5.1 启动完整应用栈现在你需要打开两个终端窗口终端1运行FastAPI后端cd /path/to/your/gemma-multimodal-api uvicorn main:app --reload --host 0.0.0.0 --port 8000终端2运行Gradio前端cd /path/to/your/gemma-multimodal-api python gradio_app.pyGradio应用默认会在http://localhost:7860启动。打开浏览器访问这个地址你就可以看到一个完整的、可交互的多模态AI应用了上传一张图片输入一个问题点击“发送”就能看到Gemma-3-12b-it模型的分析结果。6. 总结从个人工具到生产服务通过以上三步我们完成了一个完整的生产级多模态API服务的搭建模型层Ollama我们利用Ollama的易用性轻松部署并运行了强大的Gemma-3-12b-it多模态模型。服务层FastAPI我们构建了一个标准化、健壮的RESTful API。这个API不仅封装了模型调用还提供了错误处理、输入验证和OpenAI兼容格式极大地方便了与其他系统的集成。交互层Gradio我们创建了一个美观且功能完备的Web界面使得非技术用户也能轻松体验和测试模型能力。这个架构的优势在于解耦与灵活每一层都可以独立升级或替换。例如你可以更换后端模型如使用其他Ollama模型而无需修改API接口或前端界面。易于扩展你可以在FastAPI层轻松添加中间件实现身份验证API Key、速率限制、请求日志、监控指标等功能。部署简便整个应用可以容器化使用Docker轻松部署到任何云服务器或本地服务器。你现在拥有的不再只是一个命令行工具而是一个真正的、可对外提供服务的AI能力端点。你可以将它集成到你的网站、移动应用或自动化工作流中让Gemma-3-12b-it的视觉理解能力为你创造实际价值。获取更多AI镜像想探索更多AI镜像和应用场景访问 CSDN星图镜像广场提供丰富的预置镜像覆盖大模型推理、图像生成、视频生成、模型微调等多个领域支持一键部署。