ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

本地大模型工程化实战:从RAG到AI Agent的落地指南

本地大模型工程化实战:从RAG到AI Agent的落地指南 前阵子帮一位同事排查本地大模型应用时发现一个很有意思的现象同一套代码在 A 机器上回答又快又稳在 B 机器上却频繁超时、答非所问甚至把同一个句子重复输出好几遍。事后对比环境才发现问题并不在模型本身而是“AI 落地的工程环境差异”被忽略了。这种由硬件、依赖版本、调用方式、提示词策略共同叠加出来的最终效果差异就是本文要讨论的 AI Effect。这个概念在技术圈有两种理解宏观层面它指 AI 技术对研发方式、产品形态乃至岗位分工带来的连锁影响工程层面它更指向“同样一个模型在不同环境、不同调用姿势下最终效果差距极大”这一现实。本文不打算做宏观趋势分析而是围绕一个更具体的问题展开当你拿到一个开源模型如何把它真正跑起来并让它在你的业务里产生正向效果同时我们又该如何应对 AI 幻觉、上下文超限、GPU 不生效这些副作用文章会包含本地大模型搭建、RAG 检索增强、AI Agent 工具调用、Spring AI 集成等几个实战模块代码尽量完整可运行方便你在本地环境直接复现。1. 什么是“AI Effect”先看清两面性1.1 技术定义中的“AI 效应”在计算机科学领域AI Effect 最早被用来描述一种现象当某项 AI 技术足够成熟它就会被视为“理所当然的软件功能”人们不再把它当作人工智能。例如早期的拼写检查、垃圾邮件过滤如今很少有人认为它们属于 AI。但在工程实践里AI Effect 还有另一层含义AI 系统的输出质量严重依赖“非模型因素”。同一个大模型可能因为提示词少了一句约束就给出完全不同的答案可能因为温度参数设置太高就开始胡编乱造也可能因为本机没有 GPU 加速接口延迟从 500ms 飙到 8 秒。这意味着AI 项目失败很多时候不是模型不行而是工程链路不行。1.2 正向效应效率的指数级提升对一个研发团队来说大模型带来的正向效应非常明显重复性代码生成效率大幅提升比如模板代码、单元测试、SQL 语句。非结构化数据处理能力增强可以从文档、日志、客服会话中提取结构化信息。自然语言交互降低了使用门槛业务人员可以直接用中文查询数据。这些正向效果并不是“接入一个 API”就能自动获得的而是需要工程化调优。1.3 负向效应幻觉、漂移与不可控负向效应同样不可忽视。最典型的是 AI 幻觉模型会一本正经地给出不存在的事实、编造代码库中不存在的 API甚至伪造引用来源。此外还有上下文漂移长对话中模型可能忘记最初的约束以及输出不可控同样的输入在不同时间可能得到不同结果。本文的核心思路就是围绕这两面性展开先搭建一套可控的本地 AI 运行环境再通过 RAG、Agent、参数调优等手段放大正向效果、抑制负向效果。2. 环境准备搭建本地大模型运行环境2.1 硬件选型思路不同硬件条件下本地大模型的运行策略完全不同。如果是普通办公笔记本内存 16GB 左右没有独立显卡适合运行 3B 到 7B 参数量的量化模型。例如 Qwen2.5 系列、Llama 3.2 系列。如果是游戏本或工作站带 NVIDIA 显卡可以优先考虑通过 CUDA 加速运行 13B 甚至更大参数的模型。如果用的是带集成 NPU 的处理器例如 AMD Ryzen AI 9 HX 370 这类平台情况会更复杂NPU 的计算能力和驱动支持仍在快速演进Ollama 这类工具默认未必能直接调度 NPU很多时候模型还是会跑在 CPU 或 iGPU 上。我的建议是一开始不要追求在 NPU 上跑模型所有方案统一走 Ollama 的 GPU 能力抽象层第一步先让它跑起来第二步再考虑性能优化。2.2 安装 Ollama 并下载模型跨平台方案我个人最推荐 Ollama。它把模型下载、量化、API 服务都封装好了对新手非常友好。安装完成后先启动服务# 在终端中启动 Ollama 服务 ollama serve然后下载一个适合本地运行的模型。以阿里的 Qwen2.5 为例# 下载 7B 指令版本默认是量化后的 Q4 格式 ollama pull qwen2.5:7b # 如果是内存较小的机器可以换成 3B ollama pull qwen2.5:3b下载完成后可以先做一次最简单的对话测试确认整个链路是通的。2.3 检查模型是否真正使用 GPU很多新手在这里会踩坑模型确实能跑但速度很慢CPU 占用 100%。原因通常是模型根本没有调用 GPU。Ollama 提供了查看当前模型运行状态的命令ollama ps输出结果类似这样NAME ID SIZE PROCESSOR UNTIL qwen2.5:7b xxxxxxxx 4.7 GB 100% CPU 4 minutes如果 PROCESSOR 列显示100% CPU说明模型没走 GPU 加速。带 NVIDIA 显卡的环境需要确认驱动和 CUDA 环境正常带 AMD 显卡或集成显卡的环境Ollama 通常通过 Vulkan 后端调用 GPU。不同版本的 Ollama 和显卡驱动行为差异较大遇到 GPU 不生效时排查顺序建议是运行ollama ps确认当前状态。查看 Ollama 服务日志确认是否检测到可用 GPU。检查显卡驱动是否为最新版本。到 Ollama 官方文档或 GitHub Issue 中搜索对应显卡型号的支持状态。版本需要根据你的项目实际情况调整本文重点演示配置思路不以某个特定版本为绝对标准。2.4 验证本地 API 服务Ollama 启动后默认会在http://localhost:11434上提供 HTTP API。可以直接通过下面的命令查看本机已下载的模型列表curl http://localhost:11434/api/tags返回结果是 JSON 格式其中models数组里就是我们可用的模型列表。这一步验证通过说明本地环境已经具备开发条件。3. 核心概念拆解模型调用、Embedding 与结构化输出在写实战代码之前先梳理几个最重要的概念。这些概念会直接出现在后面的代码里。3.1 Chat Completion最基础的对话接口Ollama 的/api/chat接口是大模型应用最常用的入口。它接收model、messages、stream三个核心参数。import requests import json response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: [ {role: user, content: 用一句话解释什么是向量数据库} ], stream: False } ) data response.json() print(data[message][content])这里的messages数组是 OpenAI 风格的对话结构system系统级指令通常用来定义角色和约束。user用户输入。assistant模型历史回复在多轮对话中需要带上。stream参数设为False时API 会等模型生成完整内容后一次性返回便于调试。生产环境为了降低首字延迟通常开启流式输出。3.2 Embedding让文本变成向量Embedding 的作用是把一段文本映射为一个固定维度的向量。语义越接近的文本向量在空间中的距离越近。这是 RAG 检索增强的核心基础。Ollama 也提供 embedding 接口通过它可以把句子转换成向量response requests.post( http://localhost:11434/api/embed, json{ model: qwen2.5:7b, input: 什么是事务 } ) embedding response.json()[embeddings][0] print(f向量维度: {len(embedding)})注意不同版本 Ollama 的 embedding 接口命名不一样。较新版本是/api/embed旧版本可能是/api/embeddings而且返回字段也有差异。调用前可以先在浏览器或 Postman 里试一下。3.3 结构化输出让模型返回 JSON业务系统直接接收自由文本通常很麻烦所以我们经常要求模型返回 JSON 格式。Ollama 支持 JSON Mode在请求体中加入format: json即可response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: [ { role: user, content: 从这句话中提取城市和时间返回 JSON明天北京天气怎么样 } ], format: json, stream: False } ) content json.loads(response.json()[message][content]) print(content)不过 JSON Mode 只是约束输出格式并不保证字段完全正确。更稳妥的做法是在提示词里给一个具体的 JSON 示例也就是 few-shot。4. 完整实战手写一个本地 RAG 问答助手现在进入本文的第一个完整实战。目标是写一个本地运行的问答助手用户提问后程序先从知识库中检索最相关的片段再把片段和问题一起交给大模型最终生成带来源依据的回答。这个方案在工程上叫 RAGRetrieval-Augmented Generation检索增强生成。4.1 功能设计整个流程分三步对知识库文档做向量化这里的知识库我们先简化为一段文档列表。对用户问题做向量化并与文档向量做相似度计算。把最相关的文档片段拼接进提示词调用大模型生成最终回答。为了不引入过重的框架向量检索部分手写一个简单的词频向量实现。这样只要安装了 Python 和 requests就能直接跑起来。4.2 项目结构与依赖项目结构如下rag-demo/ ├── main.py └── requirements.txtrequirements.txt内容requests numpy安装依赖pip install -r requirements.txt4.3 编写核心代码下面是完整的main.py包含分词、向量化、相似度计算、检索、LLM 生成五部分import re import json import math import requests from collections import Counter # 1. 简单的分词函数 # 支持中英文混排场景这里不做复杂分词直接切分成词条 def tokenize(text): return re.findall(r[\w\u4e00-\u9fa5], text.lower()) # 2. 构建词表 def build_vocab(docs): vocab set() for doc in docs: vocab.update(tokenize(doc)) return sorted(vocab) # 3. 文本转词频向量 def tf_vector(text, vocab): counter Counter(tokenize(text)) return [counter.get(word, 0) for word in vocab] # 4. 余弦相似度 def cosine_sim(vec_a, vec_b): dot sum(x * y for x, y in zip(vec_a, vec_b)) norm_a math.sqrt(sum(x * x for x in vec_a)) norm_b math.sqrt(sum(y * y for y in vec_b)) if norm_a 0 or norm_b 0: return 0.0 return dot / (norm_a * norm_b) # 5. 检索最相关的文档片段 def retrieve(query, docs, vocab, top_k2): query_vec tf_vector(query, vocab) scored [] for doc in docs: doc_vec tf_vector(doc, vocab) score cosine_sim(query_vec, doc_vec) scored.append((score, doc)) scored.sort(keylambda x: x[0], reverseTrue) return [doc for _, doc in scored[:top_k]] # 6. 调用 Ollama 生成回答 def call_llm(system_prompt, user_content): response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: [ {role: system, content: system_prompt}, {role: user, content: user_content} ], stream: False } ) data response.json() return data[message][content] if __name__ __main__: # 模拟知识库 docs [ MySQL 默认使用 InnoDB 存储引擎支持事务和行级锁。, Redis 是一个基于内存的键值数据库常用来做缓存和消息队列。, RAG 全称是 Retrieval-Augmented Generation也就是检索增强生成。, Docker 是一个容器化平台可以实现应用的打包、分发和隔离运行。 ] vocab build_vocab(docs) query 什么是RAG context retrieve(query, docs, vocab) print( 检索到的资料 ) for i, doc in enumerate(context, 1): print(f{i}. {doc}) system_prompt ( 你是一个严谨的问答助手。 请只根据提供的资料回答问题。 如果资料中找不到答案请直接回复资料库中暂无相关信息。 ) user_content f资料\n{chr(10).join(context)}\n\n问题{query} print( 模型回答 ) answer call_llm(system_prompt, user_content) print(answer)4.4 运行与结果说明在命令行执行python main.py正常情况下会先输出检索到的两段资料再输出模型回答。因为知识库中只有 RAG 这一条命中模型最终会基于这段资料生成回答。这个例子虽然简单但已经包含 RAG 的完整链路。真实项目中你只需要替换两部分把docs列表换成 Elasticsearch、Milvus、Chroma 等向量数据库。把tf_vector换成真正的文本 Embedding 模型。4.5 为什么推荐 RAG 而不是微调很多刚接触大模型开发的读者会问既然想让它答得更准为什么不直接微调RAG 和微调解决的问题不同。RAG 解决的是“模型不知道最新信息”和“答案需要可溯源”的问题。它不改变模型权重只是动态注入相关资料适合知识库频繁更新的场景。微调则是把特定知识固化到模型参数中适合改变风格、学习固定流程但成本高、更新周期长。实际项目中RAG 的性价比通常更高。5. AI Agent 开发从“回答问题”到“执行任务”RAG 解决了知识来源问题但 AI 应用还有一个更高级的需求让模型主动完成某件事比如查天气、查时间、操作内部系统。这就要引入 Agent 概念。5.1 Agent 的基本构成一个标准的 Agent 循环包含四个部分模型负责理解用户意图和生成决策。工具模型可以调用的外部函数如天气接口、数据库查询、文件操作。循环模型决定调用哪个工具拿到工具返回结果后继续判断。终止条件模型认为任务已完成输出最终回答。开发 Agent 最成熟的方式是 Function Calling让模型输出的内容直接指向某个工具。但不同模型对 Function Calling 的支持程度不同这里给一个更通用的简化实现思路用提示词让模型返回 JSON程序解析 JSON 后决定调用哪个工具。import json import requests def call_llm(messages): response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: messages, format: json, stream: False } ) return json.loads(response.json()[message][content]) def get_weather(city): # 这里只做演示实际项目中替换为真实天气接口 return f{city} 今天多云气温 18℃~26℃ def get_current_time(city): # 简化实现实际应该查时区或调用时间服务 return f{city} 当前时间为 14:30 def run_agent(user_input): # 第一步让模型判断该调用哪个工具 system_prompt ( 你可以调用下面的工具\n 1. get_weather(city)获取某个城市的天气\n 2. get_current_time(city)获取某个城市的当前时间\n 请分析用户意图只返回一个 JSON格式为 {tool: 工具名, args: {city: 城市名}} ) decision call_llm([ {role: system, content: system_prompt}, {role: user, content: user_input} ]) tool_name decision.get(tool) args decision.get(args, {}) # 第二步执行工具 if tool_name get_weather: result get_weather(args.get(city, 北京)) elif tool_name get_current_time: result get_current_time(args.get(city, 北京)) else: result 没有找到合适的工具 # 第三步把工具结果交给模型生成最终回复 final_response call_llm([ {role: user, content: user_input}, {role: assistant, content: f工具调用结果{result}}, {role: user, content: 请把工具结果用自然语言回复给用户。} ]) return final_response if __name__ __main__: print(run_agent(北京明天天气怎么样))这个实现有三个明显特点工具调用不依赖模型原生的 Function Calling 能力兼容性好。提示词中明确列出了可用工具模型不会随意编造工具名。工具执行结果会再一次交给模型由模型组织成自然语言回复。5.2 如何让 Agent 更稳定简化实现的稳定性取决于 JSON 输出质量。如果模型输出了一个 JSON 语法错误的字符串程序就会崩溃。生产环境需要增加以下处理JSON 解析失败时重试最多重试 2 次。模型返回的工具名不在白名单中时直接拒绝执行。工具执行必须设置超时时间避免阻塞整个请求。所有工具调用记录日志方便事后审计。尤其是工具白名单这是 Agent 安全边界的第一道防线。一定不要直接执行模型返回的任意函数名否则模型一旦被恶意提示词诱导后果非常严重。6. Java 生态集成Spring AI 接入本地模型如果你的项目是 Java 技术栈可以通过 Spring AI 快速接入 Ollama。Spring AI 是 Spring 官方推出的 AI 应用框架提供了类似 Spring Data 的开发体验。6.1 添加依赖以 Maven 项目为例Spring AI 的依赖可以这样引入dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-ollama-spring-boot-starter/artifactId /dependency需要注意的是Spring AI 版本更新很快API 变化也比较大。上面的依赖坐标在不同版本中可能有调整实际使用时以 Spring AI 官方文档中的版本为准。6.2 基础配置在application.properties中配置 Ollama 服务和模型信息spring.ai.ollama.base-urlhttp://localhost:11434 spring.ai.ollama.chat.options.modelqwen2.5:7b spring.ai.ollama.chat.options.temperature0.2base-url指向 Ollama 服务地址model指定模型名temperature控制输出的随机性数值越低输出越稳定。6.3 编写调用代码import org.springframework.ai.chat.ChatClient; import org.springframework.stereotype.Service; Service public class AiService { private final ChatClient chatClient; public AiService(ChatClient chatClient) { this.chatClient chatClient; } public String ask(String question) { return chatClient.call(question); } }这里的ChatClient是 Spring AI 的统一接口底层实际请求的是 Ollama 的 Chat API。如果你的业务需要更复杂的提示词模板可以再结合 Spring AI 的PromptTemplate来实现。6.4 服务化建议Java 服务中接入 AI最需要注意的是线程池隔离。大模型接口的响应时间远高于普通数据库接口如果和业务线程池混用很容易拖垮整个应用。建议单独为 AI 调用配置一个独立线程池并设置合理的超时时间。Bean public ExecutorService aiExecutor() { return Executors.newFixedThreadPool(8, r - { Thread t new Thread(r); t.setName(ai-call-thread); t.setDaemon(true); return t; }); }更稳妥的方式是使用消息队列削峰把 AI 调用请求先写入 MQ再由独立消费者批量处理。7. AI 幻觉AI Effect 中最需要警惕的副作用前面几章都在讨论怎么把 AI 用起来这一章要专门讲 AI 的负面效应。在实际项目中幻觉问题如果不处理再好的检索链路也会被模型的“自信编造”毁掉。7.1 什么是 AI 幻觉AI 幻觉是指模型生成的内容虽然语法通顺、逻辑看似自洽但事实是错误的。比如模型会编造一个不存在的开源库并给出一个路径或者把 A 公司的产品功能安到 B 公司头上。幻觉产生的原因非常复杂最核心的一点是大模型本质上是概率模型它生成的是“最像的文本”而不是“正确的事实”。训练数据中不存在的知识它会用拼接和推测来填补。7.2 一个温度参数的演示温度参数是控制幻觉的最直接手段。温度越高模型越倾向于选择概率较低的词输出越有“创造力”幻觉风险也越高。下面的代码演示同一个问题在不同温度下的表现import requests def ask_with_temperature(question, temperature): response requests.post( http://localhost:11434/api/chat, json{ model: qwen2.5:7b, messages: [{role: user, content: question}], options: {temperature: temperature}, stream: False } ) return response.json()[message][content] question 请详细介绍一下 Python 的 pandas 库包括它不存在的版本号。 print(温度 0.2 的输出) print(ask_with_temperature(question, 0.2)) print(\n温度 1.2 的输出) print(ask_with_temperature(question, 1.2))更低的温度倾向于更保守的生成方式但并不能完全消除幻觉。正确做法是在工程层面做约束。7.3 抑制幻觉的四个思路检索增强让模型只基于给定的资料回答而不是凭空发挥。也就是第四章实现的 RAG 链路。提示词硬约束“请回答‘不知道’不要编造。”这套字面约束对模型有一定效果。引用溯源要求模型回答时标注来源编号方便人工核验。后置校验对模型输出的关键实体、URL、版本号做规则校验不合法就拦截。这四个方法中RAG 的效果最明显因为它改变了模型回答的“上下文基础”。8. 常见问题与排查思路本地部署大模型应用时下面这些问题出现概率最高。我整理成了一张表方便你按图索骥排查。问题现象常见原因排查思路模型加载慢启动时卡顿模型文件较大首次加载需要写入内存先运行ollama ps检查是否已在内存准备足够内存或换更小模型GPU 显示未生效CPU 飙升驱动问题或 Ollama 未识别 GPU查看 Ollama 日志检查显卡驱动搜索官方支持列表接口响应特别慢模型太大或本机无 GPU 加速考虑换量化更低的模型或者部署到服务器中文回答质量差模型本身对中文支持有限优先使用 Qwen 系列等中文模型并在提示词中给示例模型回答出现乱码字符编码问题检查终端编码设置PYTHONIOENCODINGutf-8JSON 解析频繁失败模型输出不是合法 JSON开启format: json同时做重试和兜底解析同一问题答案漂移严重温度参数过高把 temperature 降到 0.2 以下模型把不存在的 API 描述为真实AI 幻觉引入 RAG并添加“未找到资料时请说不知道”的提示词上下文越长回答越差模型超出有效上下文窗口对历史对话做截断或摘要保留关键信息即可真实的排错过程并没有固定的魔法命令。建议的排查顺序是先确认请求参数是否正确再确认资源占用情况最后检查提示词是否合理。大多数本地部署问题都出在这三个环节。9. 最佳实践与工程建议到这里我们已经把本地 AI 应用的整个链路走了一遍。最后总结几点工程落地建议这些经验来自实际项目的反复踩坑。9.1 提示词也要纳入版本管理提示词不是写一次就能用很久的。业务规则变了模型升级了提示词都可能要调。建议把提示词模板放到配置中心或独立文件中按业务模块分类管理并加上版本号。最简单的做法是只用一个 Python 文件保存提示词prompts { rag_system: 你是一个严谨的问答助手只依据资料回答……, intent_route: 你可以调用以下工具…… }不要散落在业务代码里否则后面改一处要搜半天。9.2 温度参数按场景区分不同的业务场景应该配置不同的温度参数信息抽取、SQL 生成、代码生成温度 0.0 到 0.2追求确定性。客服问答温度 0.2 到 0.5保持一定自然度。创意写作、头脑风暴温度 0.7 到 1.2允许发散。不要用一套参数应对所有场景。9.3 所有 AI 调用都要有日志和降级方案大模型接口是不稳定的无论是本地模型还是云 API都可能出现超时、限流、服务不可用。生产环境必须做好降级方案AI 调用失败时可以降级到关键词匹配方案保证核心功能可用。对每次调用的请求参数、响应结果、耗时、Token 消耗做全量日志。设置超时时间默认 30 秒以上避免线程长时间阻塞。9.4 安全边界要前置设计涉及权限、内容安全、工具调用时一定要遵循最小权限原则。模型的输出不能直接作为系统操作的最终指令必须经过白名单校验和人工确认。尤其是 Agent 场景工具执行的权限范围要尽量收紧。9.5 永远准备一个测试集不要靠肉眼看几个例子判断 AI 应用是否好用。准备一个固定的小测试集包含 20 到 50 条典型输入每次调整提示词或模型后都跑一遍对比输出质量。这本质上是把 AI 应用当成普通软件来对待有测试、有回归、有版本记录系统才会稳定。10. 总结与下一步学习方向本文从 AI Effect 这个概念出发把本地大模型应用的完整工程链路拆成了五个可落地的模块基于 Ollama 搭建本地模型运行环境并验证 GPU 加速情况。理解 Chat、Embedding、JSON Output 三个核心接口。实现了一个手写 RAG 问答助手掌握检索增强的核心流程。通过提示词加工具调用的方式实现了一个简化版 AI Agent。在 Java 生态中通过 Spring AI 接入 Ollama并讨论了线程池隔离方案。同时我们也分析了 AI 幻觉这个最大的负面效应并给出 RAG、提示词约束、引用溯源、后置校验四条治理思路。如果你对 AI 工程实践感兴趣下一步可以顺着两条线继续深入第一条线是数据工程学习如何把 PDF、网页、数据库表统一转成向量索引并接入 Milvus 或 Elasticsearch 做大规模检索第二条线是 Agent 工程学习 Function Calling、LangGraph、多 Agent 协作等更复杂的编排方式。最后分享一条实战体会AI 应用开发真正的难点永远不在“调通 API”而在“控制输出的质量与稳定性”。把模型当成一个能力不稳定的实习生来管理给它明确的资料、清晰的任务边界、可靠的校验机制才是让 AI Effect 产生正向价值的关键。
返回列表