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

资讯详情

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

AI工程落地实战:用RAG构建会议资料问答助手

AI工程落地实战:用RAG构建会议资料问答助手 1. “AI4”不是一场大会而是一次工程复盘从一场 AI 活动现场回来之后很多开发者的第一感受不是兴奋而是巨大的落差。台上的嘉宾把大模型、智能体、多模态说得仿佛可以一夜之间重构所有业务回到工位我们面对的却是 prompt 不稳定、模型幻觉、接口限流、数据权限审批、成本账单超标这些细碎问题。标题里的 “AI4: The Conference Inside the Mirage” 并不是某个真实存在的官方会议。我把它理解成一种隐喻会议上的 AI 像海市蜃楼远看完整而华丽走近之后才发现它和真实生产环境之间隔着一条巨大的工程鸿沟。大部分 AI 项目不是输在模型能力上而是输在“把模型能力变成稳定服务”这件事上。这篇文章不打算复述某一场大会的 PPT而是希望把“AI 工程落地”拆成四个可以执行的主题AI for Engineering、AI for Application、AI for Infrastructure、AI for People。围绕这四个主题我会讲清楚概念边界、环境准备、代码实现、常见问题与工程建议并用一个可以运行的“会议资料问答助手”把全流程串起来。无论你是刚接触大模型应用开发的新手还是已经在团队里做技术选型的中级开发都可以按这篇文章的思路搭建一个最小可用系统并把它逐步改造成适合业务场景的生产服务。1.1 会议上的 AI 和生产里的 AI差在哪里先聊一个最常见也最容易误导人的现象在会场上你看到的最多的是“效果展示”。模型能写周报、能总结合同、能根据几张图表生成经营分析。但生产环节考验的不是模型单次输出的上限而是系统在连续调用过程中的稳定性。会议演示通常只有一条精心调整过的输入模型答得漂亮并不奇怪生产系统却要面对不同风格的用户表达、超长文档、缺失上下文、恶意输入、并发流量。同样一段 prompt在 Demo 里效果惊艳上线后却可能频繁出现答非所问。问题往往不是模型变笨了而是输入分布变了而我们没有为这种变化设计兜底策略。另一个容易被忽略的差距是“上下文控制”。会场演示多半是单轮问答到了业务系统我们必须限制模型输出范围要把企业内部资料做脱敏和权限隔离要避免模型把没有依据的内容当作事实输出。这些能力不会凭空出现在模型本身必须由工程层来解决。所谓“海市蜃楼”本质就是把模型单点能力误当成系统交付能力。1.2 AI4 四个关键词Engineering、Application、Infrastructure、People“AI4”并不是一个官方的技术缩写在本文中我用它代表四个判断 AI 项目能否落地的关键视角。AI for EngineeringAI 如何改变软件研发流程本身包括代码生成、测试生成、文档生成以及我们把 AI 能力封装成接口的开发过程。AI for ApplicationAI 能力如何变成业务功能例如知识库问答、客服助手、内容总结。对应的技术形态通常包括 RAG、Agent、提示词工程。AI for Infrastructure模型部署、API 网关、缓存、向量数据库、日志监控、评测系统等支撑 AI 应用的底层设施。AI for People使用者和决策者如何理解模型能力边界如何建立内容安全、数据隐私、成本治理、责任归属等机制。这四个关键词不是并列的课程大纲而是同一个 AI 项目从想法到上线必须依次迈过的台阶。很多项目停留在“Application”的效果展示上却没有在“Infrastructure”和“People”上投入资源最终只能停留在演示环境。后文会按照这四个关键词逐步展开。先从会场上听到最多的几个概念说起把边界理清楚再进入代码实战。2. 概念边界大模型、RAG、Agent分别解决什么问题这一节主要给刚接触 AI 应用开发的同学划边界。很多“海市蜃楼”式的讨论本质是把大模型、RAG、Agent 混为一谈。它们不是互相替代的关系而是不同层次的工程选择。2.1 大模型基础能力不是银弹大模型Large Language ModelLLM是对话、总结、生成代码等能力的底座。它的优点是通用性强不需要针对每个业务场景单独训练缺点是它不了解企业私有数据也不清楚文档中的最新口径还可能出现“幻觉”也就是输出看起来合理但实际没有依据的内容。在工程上我建议把大模型看成“一个远程文本函数”输入一段文本输出一段文本。既然是调函数就要管理它的入参、出参、超时、限流、成本和异常。很多初期团队失败是因为只把大模型当成一个更聪明的搜索框问什么答什么没有设计调用层、校验层和兜底层。等到模型偶尔抽风时才发现整个应用没有任何保护。2.2 RAG把外部知识注入生成过程RAG 的全称是 Retrieval-Augmented Generation也就是检索增强生成。它的核心思路并不复杂先从知识库中检索出与用户问题相关的资料片段再把资料片段和用户问题一起交给大模型让模型基于这些资料来回答。RAG 之所以是目前落地最稳的方案之一是因为它解决了三个现实问题第一回答有依据模型不再是凭空编造第二知识库可以随时更新不需要每次更新都重新训练模型第三资料来源可追溯业务方可以接受“答案来自哪份文档”这种审计方式。那什么时候该用 RAG当知识库内容会持续更新、当回答需要引用依据、当公司数据不足以支撑微调时RAG 都是比微调更稳妥的起点。RAG 的难点不在调用模型而在检索质量文档切得不好、索引结构不合理检索返回的资料与问题不相关再强的模型也答不好。这也是后文实战部分把重点放在检索层的原因。2.3 Agent 与工作流自动化能力的边界Agent智能体是比 RAG 更复杂的一层。它不只是“检索加生成”而是结合工具调用、循环决策和环境反馈来完任务。例如一个数据分析 Agent 可能需要先查询数据库再编写 Python 代码然后读取执行结果最后生成报告。这已经超出了单次问答的范畴。Agent 的工程风险也更明显循环可能停不下来工具权限可能过大中间结果可能不可控。如果模型在单轮问答中已经存在幻觉问题那么在多步循环里错误会被不断放大最终生成一份看起来完整但完全不可信的报告。所以我的建议是先做好 RAG再考虑 Agent。如果单轮问答的检索、生成、评估都还没有稳定的评价体系直接上多步 Agent 只会让问题更难排查。Agent 的引入应当是一个渐进过程而不是一次推倒重来。3. 环境准备与接口选型开始编码前先把开发环境说明白。下面的示例以 Python 为主因为大模型应用开发生态最成熟、示例最多。版本号需要根据你所在项目的实际情况调整这里给的是通用思路。3.1 本地开发环境建议使用 Python 3.10 或更高版本并创建独立的虚拟环境避免依赖冲突污染系统 Python。mkdir mirage-ai-demo cd mirage-ai-demo python -m venv venv source venv/bin/activate # Windows 下使用 venv\Scripts\activate pip install --upgrade pip安装依赖时不需要一次性装很多库。最小演示只需要几类包模型接口调用、Web 服务、中文分词工具。把依赖写入requirements.txt方便其他同事复现环境。# 文件路径requirements.txt openai1.30.0 fastapi0.110.0 uvicorn[standard]0.27.0 jieba0.42.1这里没有引入 LangChain 这类重量级框架是为了先展示底层流程。等理解了切块、检索、生成的每个环节后再使用框架提升效率也不迟。框架能隐藏复杂度但也可能隐藏问题。3.2 模型接口的统一接入不同大模型厂商的接口参数不完全相同但很多服务都提供与 OpenAI 协议兼容的接口我们可以用统一的方式接入。为了演示通用性我把接口地址、密钥和模型名称全部放进了环境变量。在项目根目录创建.env文件同时把真实密钥写入.gitignore避免提交到代码仓库。# 文件路径.env OPENAI_API_KEYsk-xxxxxxxx OPENAI_BASE_URLhttps://api-internal.example.com/v1 LLM_MODELgpt-4o-mini实际项目中请通过企业内部审批过的模型网关来调用模型不要将内部数据直接发往未经授权的外部接口。这个安全习惯要从第一天就养成否则后续做数据安全评审会非常被动。3.3 项目结构项目结构尽量保持简单让读者能一眼看清核心流程。我们准备一个data目录存放示例会议资料一个app目录存放代码。mirage-ai-demo/ ├── .env ├── .gitignore ├── requirements.txt ├── app/ │ ├── __init__.py │ ├── config.py # 读取环境变量 │ ├── llm.py # 大模型调用封装 │ ├── retriever.py # 文档切块与检索 │ └── main.py # FastAPI 入口 └── data/ └── meeting_notes.md # 示例会议资料这样的结构虽然简单却已经包含了配置管理、数据接入、模型调用、接口暴露四个核心部分。后续扩展向量数据库、添加缓存、引入评测服务时都能在现有结构上继续叠加。4. 完整实战从零构建一个会议资料问答助手下面进入核心章节。我们要实现一个“会议资料问答助手”给一段会议纪要用户提问后系统先从纪要中检索相关片段再让大模型基于检索结果回答。这个项目的使用场景很具体新同学入职后想了解某项决策背景不用再把几十页会议记录翻一遍运营同学想确认某个指标口径直接提问就能得到带出处的答案。更关键的是这套流程可以迁移到员工手册问答、项目文档问答、产品帮助文档等场景。4.1 加载与切分会议文档先准备一个示例会议文档内容精简但覆盖性能、AI 上线、数据安全三类高频话题。# 文件路径data/meeting_notes.md ## 议题一QPS 性能优化 本次会议讨论网关接口的 QPS 优化。 目标将核心接口 QPS 从 3000 提升到 10000。 措施 1. 增加本地缓存减少 Redis 访问次数。 2. 引入限流组件防止突发流量打垮下游。 3. 对慢 SQL 进行索引优化减少数据库连接占用。 ## 议题二AI 客服上线计划 计划在 6 月底上线 AI 客服能力。 知识库导入流程先经过数据脱敏再进行文本切块最后做向量化。 上线指标首响时间小于 3 秒人工转接率下降 20%。 ## 议题三数据安全评审 数据安全评审结论 - 内部文档不能直接发送到外部模型。 - 演示环境与生产环境使用不同的模型网关。 - 用户日志需要脱敏后保留 30 天。接下来写加载和切分逻辑。切块的目的是把长文本拆成多个可检索的“阅读单位”避免把整份文档作为一个整体丢失细节。这里采用按字符长度切分并保留一定重叠防止一句话刚好被切成两半。# 文件路径app/retriever.py # 文档加载与切分 from pathlib import Path def load_text(file_path: str) - str: 读取文本文件内容统一使用 UTF-8 编码。 return Path(file_path).read_text(encodingutf-8) def split_text(text: str, chunk_size: int 300, overlap: int 50) - list[str]: 按字符长度切分文本。 参数说明 - chunk_size单个文档块的目标字符数 - overlap相邻块之间的重叠字符数 切块是 RAG 中最基础也最容易出错的一步。 块太小会导致语义不完整块太大会混入大量无关信息。 chunks [] start 0 while start len(text): end start chunk_size chunk text[start:end] if chunk.strip(): chunks.append(chunk.strip()) if end len(text): break start end - overlap return chunks在正式环境中建议先按一级标题、二级标题分段再根据段落长度动态决定是否继续切分。这样能够保留文档原有结构避免把不同议题的内容拼到同一个块里。示例代码使用固定长度切分只是为了降低理解成本。4.2 实现检索层检索层负责回答一个问题给定用户查询哪些文档块最相关为了让示例不依赖重量级向量数据库我使用 jieba 分词后的关键词重叠作为相关性打分基础。这个思路虽然简单但可以平滑迁移到 BM25 或向量检索。# 文件路径app/retriever.py续 import jieba def tokenize(text: str) - list[str]: 中文分词返回去除空白字符后的词列表。 return [word for word in jieba.cut(text) if word.strip()] def search(query: str, chunks: list[str], top_k: int 3) - list[str]: 最简单的相关性检索统计查询词在文档块中出现的次数。 注意这是教学演示版本生产环境建议使用向量检索。 query_tokens set(tokenize(query)) scored [] for index, chunk in enumerate(chunks): chunk_tokens tokenize(chunk) score len(query_tokens set(chunk_tokens)) scored.append((score, index, chunk)) scored.sort(keylambda x: x[0], reverseTrue) return [chunk for _, _, chunk in scored[:top_k] if _ 0]这个实现的优点是依赖少、代码可读性高能直观展示“检索”在做什么。缺点是它只能做字面匹配无法识别同义词。比如用户问“首响时长”而文档里写的是“响应时间”关键词匹配就会失效。生产环境中升级的方向通常有两个一是引入 embedding 模型把文本向量化后存入向量数据库用余弦相似度召回二是使用 BM25 这类经典排序算法再与语义检索做混合排序。我们先把最简单的链路跑通后续再替换检索实现完全不影响上层接口。4.3 调用大模型生成回答为了让问答助手生成自然语言回答需要把检索到的资料交给大模型。我先定义配置类统一读取环境变量。# 文件路径app/config.py import os class Config: api_key os.getenv(OPENAI_API_KEY, ) base_url os.getenv(OPENAI_BASE_URL, ) model os.getenv(LLM_MODEL, gpt-4o-mini)接着把模型调用封装到一个独立文件里。这里使用 OpenAI Python SDK并兼容自定义网关地址。# 文件路径app/llm.py from openai import OpenAI from config import Config client OpenAI( api_keyConfig.api_key, base_urlConfig.base_url, ) def call_llm(system_prompt: str, user_prompt: str, temperature: float 0.3) - str: 调用大模型返回文本结果。 temperature 越低输出越稳定业务场景建议设置 0 到 0.5。 resp client.chat.completions.create( modelConfig.model, temperaturetemperature, messages[ {role: system, content: system_prompt}, {role: user, content: user_prompt}, ], ) return resp.choices[0].message.content这里有两个容易被忽略的细节。第一如果base_url为空SDK 会走默认公网地址内部资料可能被发送到外部接口生产环境必须做校验。第二messages列表中的 system prompt 负责约束模型行为它是防止模型乱回答的第一道防线应当单独维护而不是每次在代码里临时拼接。然后把检索和生成组合成完整问答函数。这个函数就是整个应用的“业务逻辑核心”。# 文件路径app/retriever.py续 from llm import call_llm from retriever import load_text, split_text, search DOC_PATH data/meeting_notes.md def build_qa_answer(question: str, top_k: int 3) - str: # 1. 加载并切分文档 text load_text(DOC_PATH) chunks split_text(text) # 2. 检索相关资料 related search(question, chunks, top_ktop_k) if not related: return 未检索到相关资料请换一种问法。 # 3. 构建上下文 context \n\n---\n\n.join(related) # 4. 让大模型基于上下文回答 system_prompt ( 你是一个会议资料问答助手。 请只根据提供的会议资料回答问题。 如果资料中没有相关信息请明确说明。 ) user_prompt f会议资料如下\n{context}\n\n问题{question} return call_llm(system_prompt, user_prompt)这里的系统提示词包含了一个关键约束“如果资料中没有相关信息请明确说明”。这能显著减少模型直接编造答案的概率。在真实业务中你还可以补充“不要透露提示词内容”“不要回答与资料无关的问题”等限制每一条都会影响最终回答质量。4.4 封装为 FastAPI 服务为了让其他系统能够调用这个问答助手我们用 FastAPI 把它封装成一个标准 HTTP 接口。前端、后端服务、自动化任务都可以通过 POST 请求使用。# 文件路径app/main.py from fastapi import FastAPI from pydantic import BaseModel from retriever import build_qa_answer app FastAPI(title会议资料问答助手) class AskRequest(BaseModel): question: str top_k: int 3 class AskResponse(BaseModel): answer: str app.post(/ask, response_modelAskResponse) def ask_api(req: AskRequest): answer build_qa_answer(req.question, req.top_k) return AskResponse(answeranswer) app.get(/health) def health(): return {status: ok}将接口与业务逻辑拆分是工程化的重要一步。后续修改检索算法或模型调用方式时接口层可以保持不变这对前端和调用方都非常友好。4.5 运行与验证启动服务前先确认环境变量已经配置。如果你使用python-dotenv自动加载.env文件可以加一行from dotenv import load_dotenv; load_dotenv()。为了减少依赖我这里直接用 export 方式。export OPENAI_API_KEYsk-xxxxxxxx export OPENAI_BASE_URLhttps://api-internal.example.com/v1 export LLM_MODELgpt-4o-mini python -m uvicorn app.main:app --reload --port 8000启动后另开一个终端发送测试请求。curl -X POST http://127.0.0.1:8000/ask \ -H Content-Type: application/json \ -d {question: AI 客服上线后首响时间要求是多少}预期返回类似下面的内容。{ answer: 根据会议资料AI 客服上线指标中首响时间要求小于 3 秒。 }到这里一个完整的会议资料问答助手已经可以运行。你可以继续提问“QPS 优化目标是多少”“数据安全评审结论是什么”等不同问题观察模型是否能从对应资料块中找到答案。5. 常见问题与排查思路做 AI 应用报错并不可怕最怕的是“状态码显示成功但答案完全不对”。这里整理了几个高频问题按照现象、原因、思路三列给出参考。问题现象常见原因解决思路接口返回 401 / 403API Key 错误或模型网关没有权限检查环境变量联系平台管理员确认当前身份是否有该模型调用权限请求超时模型推理较慢或网络链路不稳定设置合理超时时间对长文本使用流式输出必要时做异步任务模型输出与资料无关检索召回结果不相关优化切块策略使用向量检索或提高 top_k 并加权排序回答出现编造内容模型幻觉或 prompt 没做强约束在 system prompt 中明确“只能基于资料回答”必要时加入后校验上下文超长检索内容过多超过模型窗口限制检索片段数量和长度采用摘要或重新切块中文检索效果差关键词匹配无法识别同义词引入词向量检索或使用 BM25 加语义检索的混合方案输出包含 Markdown 标记模型默认输出格式与展示端不匹配后端做格式清洗或在前端使用 Markdown 渲染组件除了表格里的问题还有两个非常常见的工程问题值得单独说明。第一个是“模型返回了 Markdown 格式但前端只需要纯文本”。很多大模型会输出**加粗**、## 标题、- 列表等标记。如果在展示层没有对应的渲染组件用户会看到一堆奇怪的符号。解决办法是在后端统一加一个格式清洗函数或者在前端使用 Markdown 渲染组件。第二个是“看起来没报错但每次都答非所问”。我建议不要先怀疑模型而是先打印检索结果。把search()返回的 top_k 片段输出到日志看看模型到底拿到了哪些资料。如果检索到的内容本身就不相关那后续生成阶段再怎么调整 prompt 也救不回来。排查建议按固定顺序执行先看接口有没有报错再确认模型调用是否正常接着检查检索结果是否合理最后调整 prompt。只要按照这个链路逐步定位绝大多数问题都能找到根因。6. 工程化落地的六条建议会议上的精彩案例很多但把 AI 项目真正落到生产环境需要格外关注六个方面。这一部分对应“AI4”中的 Infrastructure 和 People也是很多团队最容易踩坑的地方。6.1 数据安全与最小权限不要将敏感数据直接发送到未经授权的外部模型接口。企业内部应当建设统一的模型网关由网关做权限控制、审计和脱敏。用户文件、权限标识、内部系统信息在进入 prompt 之前就要完成清洗。在接口设计上建议把“数据来源”和“权限级别”作为字段带入链路避免出现低权限用户通过 AI 接口读取高权限资料的情况。比如一个普通员工不应该能通过问答助手获取只有管理层可见的会议纪要。这一条是整个 AI 应用能否通过安全评审的前提。6.2 成本控制与缓存大模型调用按 token 计费成本会随调用量线性上升。实际项目中至少要做两层控制。第一层是缓存相同或近似问题在短时间内命中缓存可以直接返回结果不必重复调用模型。第二层是限制 token 量检索片段不是越多越好超出模型窗口反而浪费成本。把 top_k 控制在 3 到 5单块长度控制在 300 字以内是很多业务场景的合理起点。此外日报、周报类需求适合使用异步任务离线生成而不是在用户请求时实时调用模型。把耗时长、时效性要求低的场景迁移到离线链路能够显著降低成本峰值。6.3 评测要前置不要等系统上线后再靠用户反馈来发现问题。应当提前准备一套“黄金问答集”覆盖常见问题、边界问题、无答案问题、敏感问题四类。每次修改 prompt、切块策略、检索算法后都要跑一遍评估集对比答案质量。没有评测体系的 AI 应用就像没有自动化测试的代码。你也许能靠“手动点几个问题”通过验收但下一次别人改动 prompt 时你可能完全没有感知到质量下降。6.4 提示词版本化提示词是产品功能的一部分应当纳入版本管理。不要把 prompt 直接写在业务代码里建议放到配置文件或独立的提示词管理平台。每次修改都要记录变更原因和评估结果方便回滚。团队协作时这个问题尤其突出。没有版本控制的 prompt最终会变成所有人共享的“黑盒”谁改了哪里、为什么改完全无法追溯。线上回答风格突变时甚至连定位问题的入口都没有。6.5 日志留痕所有模型请求都应记录用户输入、检索结果、模型输出、耗时、token 消耗、提示词版本号。日志不仅能用于排查问题还能为后续评测和成本分析提供原始数据。但要注意日志中不能包含敏感明文字段。需要对用户标识、内容做脱敏后再存储同时设置合理的日志保留周期。这既是安全要求也是合规要求。6.6 从业务场景出发不追概念最后一条也是最容易被忽略的一条不要为了用 Agent 而用 Agent不要为了上大模型而上大模型。如果一个场景用规则匹配就能解决就不要引入模型如果一个需求单轮 RAG 能解决就不要设计复杂的多步流程。判断一项技术是否值得引入核心指标是它对业务指标的贡献而不是它是否足够热门。真实项目里稳定、可控、可解释往往比功能炫酷更重要。7. 写在最后把海市蜃楼变成可运行的代码回到开头的比喻。AI 会议像是海市蜃楼远远望去宏大、精致、充满想象力真正走近之后留下来的并不是某一句金句而是切分文本的规则、检索调参的经验、提示词版本记录、评测集里逐条积累的问题。这篇以“AI4”为线索整理的实战笔记核心想表达一个朴素的观点AI 落地的差距通常不在模型能力而在工程化能力。模型选择、接口封装、检索质量、评测体系、安全边界、成本治理每一项都需要投入扎实的工程时间。如果你正在规划自己的第一个 AI 项目建议按这个顺序推进先准备环境跑通最简单的模型调用再准备一份结构化文档实现检索增强问答接着把服务接口化最后建立评测和日志。每一步都先求“能跑”再求“好用”。遇到问题不要急着否定模型先检查检索、提示词、配置这三个最容易出错的环节。把这一套流程走完你离“会议里的 AI”和“生产里的 AI”之间的距离就已经很近很近了。
返回列表