尧图网络科技YAOTU DIGITAL 获取报价
获取报价
首页 / 资讯中心 / 文章详情

AI应用开发实战:从零构建可交付的RAG与Agent系统

发布时间:2026/9/13 5:07:58

资讯中心
01
ARTICLE

AI应用开发实战:从零构建可交付的RAG与Agent系统

AI应用开发实战:从零构建可交付的RAG与Agent系统
1. 这不是“学AI”而是“用AI造东西”的实战路线图我带过三十多个从零起步的AI应用开发学员最常听到的一句话是“老师我学了半年LangChain还是不会做一个能帮销售自动写客户跟进邮件的小工具。”这句话背后藏着一个被严重低估的事实AI应用开发不是算法研究也不是模型调参它是把大模型当作一个新型API、一种新式数据库、一套可编程的智能中间件来构建解决真实业务问题的软件系统。你不需要懂Transformer的梯度下降怎么算但必须清楚什么时候该用RAG加知识库什么时候该切Agent做多步推理什么时候干脆扔掉LLM、用规则引擎更快更稳。这个学习计划就是为那些想在三个月内独立交付一个能跑在公司内网、被真实用户每天点开使用的AI小应用的人准备的——比如一个自动整理会议纪要并生成待办事项的Web工具一个根据产品手册回答客服问题的内部知识助手或者一个能解析Excel销售数据并生成周报文字摘要的桌面程序。它不教你怎么训练千卡集群但会手把手带你把OpenAI API、本地Ollama模型、向量数据库和前端React组件串成一条能稳定跑通的数据流水线。关键词里的“AI应用开发”四个字核心在“应用”——应用意味着有界面、有输入输出、有错误处理、有用户反馈、有上线部署它本质上是一门融合了Prompt工程、后端服务编排、轻量级前端交互和基础运维能力的新型全栈开发。很多人一上来就扎进《深度学习》教材或Hugging Face文档结果学了三个月还在调试CUDA版本兼容性离做出一个能用的东西越来越远。这就像想学盖房子却先花半年研究水泥分子结构。真正的起点是你打开浏览器注册一个免费API Key用二十行Python代码调通第一个/chat/completions接口看到屏幕上跳出“你好我是AI助手”——那一刻你才真正站在了AI应用开发的起跑线上。这个计划的设计逻辑非常朴素以终为始用交付倒逼学习。每一周的目标都对应一个可演示、可截图、可给同事试用的最小功能模块。第一周结束时你得有一个能通过网页表单提交问题、返回AI答案的静态页面第三周结束时它得能记住你上次问过什么上下文不丢失第六周结束时它应该已经接入了你公司的产品文档PDF能准确回答“XX型号设备的保修期是多久”这种具体问题。没有抽象的概念堆砌只有具体的文件创建、命令执行、错误日志分析和用户反馈迭代。如果你的目标是写一份拿得出手的AI应用开发简历那么这份计划里每一个完成的项目都是你作品集里实实在在的一页如果你的目标是让老板看到AI能立刻提升部门效率那么第六周那个能自动处理报销单据的原型就是你争取资源的最好筹码。它不承诺让你成为算法科学家但它保证三个月后你能清晰说出“我的AI应用为什么选Qwen2而不是Llama3”、“为什么这里用Chroma而不是Pinecone”、“这个超时错误是因为前端没加loading状态还是后端流式响应没处理好”——这些才是AI应用开发者每天真正在意的问题。2. 学习路径设计三层漏斗式能力构建法2.1 为什么是“三层漏斗”而不是线性章节我见过太多失败的学习计划它们像一本教科书目录第一章Python基础第二章HTTP协议第三章大模型原理……学完六章人已经倦怠连第一个API请求都没发出去。问题出在认知负荷上——人类大脑无法同时消化语法、协议、模型架构和工程实践四层抽象。这个计划采用“三层漏斗”结构本质是把复杂系统拆解为三个可独立验证、又能逐层叠加的认知单元最上层是“用户可见的价值交付”中间层是“数据与逻辑的可靠流动”底层是“环境与资源的稳定支撑”。每一层都只解决一类问题且下一层的存在是为了让上一层更简单、更鲁棒。比如你不需要一开始就理解Embedding向量的余弦相似度计算但你需要知道当用户问“如何重置密码”系统必须从知识库中精准召回“密码重置流程.pdf”第3页的内容而不是泛泛而谈。这个“精准召回”需求自然会把你引向向量数据库的学习而它的选型Chroma vs. Qdrant则取决于你当前项目的规模和部署环境——一个单机运行的内部工具Chroma的轻量级足够一个需要高并发查询的SaaS产品Qdrant的分布式能力就成了刚需。这种由问题驱动的学习记忆深刻迁移性强。2.2 第一层价值交付层Week 1–3这一层的核心任务是建立“我能造出东西”的绝对信心。它完全绕过模型细节聚焦于输入-处理-输出的闭环。Week 1的目标极其明确用HTMLJavaScript写一个静态页面用户在文本框输入问题点击按钮页面下方显示AI返回的答案。技术栈极简前端纯静态后端用Flask写一个三行代码的API代理接收前端POST请求转发给OpenAI API再把结果原样返回。这里的关键不是代码多优雅而是亲手完成一次完整的HTTP请求-响应链路。你会第一次看到curl命令返回的JSON里choices[0].message.content字段会第一次在浏览器开发者工具Network标签页里亲眼看到自己发出的请求和收到的响应。Week 2引入状态管理让AI记住对话历史。这不再是简单的单次请求而是需要在后端维护一个session ID对应的对话列表并在每次请求时把整个历史作为messages数组传给模型。你会遇到第一个真实坑Token长度限制。当对话变长messages数组超过4096个tokenAPI直接报错。解决方案不是去学BPE分词而是实操——用tiktoken库实时计算token数当接近上限时自动裁剪掉最早几轮对话。Week 3加入基础交互支持上传PDF文件。用户拖拽一个产品说明书系统自动提取文字存入内存中的简易知识库。这里你学到的是文件处理的通用范式request.files[file]获取二进制流PyPDF2或pymupdf解析PDFtextwrap.fill()处理长段落换行。所有这些都不涉及模型训练但构成了一个真实AI应用的骨架——有输入文本/文件有处理调用API/解析文档有输出文字回答/状态提示。2.3 第二层数据与逻辑层Week 4–8当骨架立住肌肉就开始生长。这一层解决的是“如何让AI的回答更准、更稳、更可控”。Week 4的核心是RAG检索增强生成的落地。你不再把整本PDF塞进prompt而是把文档切分成段落用Sentence-BERT模型生成每个段落的向量存入Chroma数据库。当用户提问系统先用同样的模型将问题转为向量在Chroma里搜索最相似的3个段落再把这些段落内容和问题一起组装成新的prompt发给大模型。这里的关键洞察是RAG不是魔法它是一个精确的“信息筛选器”。你必须亲自测试不同切分策略按字符数按标题按语义对召回效果的影响。实测下来按\n\n双换行切分技术文档比固定500字符效果好得多——因为技术文档的自然段落本身就承载了完整语义。Week 5深入Agent模式。当单一API调用无法解决问题比如“帮我对比A和B两款产品的优缺点并生成采购建议”就需要Agent协调多个步骤先查A产品参数再查B产品参数再调用模型做对比分析。你用LangChain的ReAct框架搭建一个最简Agent它只有两个Tool一个是查产品数据库的SQL查询函数另一个是调用大模型的llm.invoke()。你会立刻发现Agent的脆弱性当SQL查询返回空结果Agent会卡死。解决方案不是改模型而是加一层防御性编程——在Tool函数里如果查询无结果强制返回“未找到相关产品信息请确认型号是否正确”避免Agent陷入无限循环。Week 6聚焦可靠性流式响应与错误处理。用户不想等3秒后突然看到全部答案而是希望文字像打字一样逐字出现。你改造Flask后端用yield关键字生成Server-Sent Events (SSE)前端用EventSource监听。同时你必须处理所有可能的异常API密钥无效、网络超时、模型返回格式错误。每一个try...except块都对应一个真实的用户投诉场景——比如超时错误不能只返回“请求失败”而要给出“正在重试中…”的友好提示并自动发起第二次请求。2.4 第三层环境与支撑层Week 9–12最后一层是让应用脱离你的笔记本真正活在生产环境里。Week 9的主题是本地化部署。你下载Ollamaollama pull qwen2:7b然后把之前调用OpenAI的代码无缝切换到调用本地http://localhost:11434/api/chat。你会发现延迟从几百毫秒降到几十毫秒但代价是显存占用飙升。这时你必须做取舍是牺牲一点响应速度保显存还是加一块二手3090Week 10解决知识库持久化。Chroma默认存在内存里重启就丢数据。你配置它使用SQLite后端chroma_client chromadb.PersistentClient(path./chroma_db)并写一个初始化脚本确保每次启动应用前知识库已加载完毕。Week 11是容器化打包。用Dockerfile把Python后端、前端静态文件、Ollama模型打包成一个镜像。关键一步是docker build -t my-ai-app .之后docker run -p 5000:5000 -v ./chroma_db:/app/chroma_db my-ai-app——这个-v参数就是生产环境数据不丢失的生命线。Week 12完成CI/CD闭环用GitHub Actions当master分支有新commit自动触发构建、测试、推送镜像到Docker Hub并SSH到云服务器执行docker pull和docker restart。整个过程你写的不是“AI算法”而是Dockerfile里的COPY requirements.txt .、.yml文件里的run: docker login -u ${{ secrets.DOCKER_USERNAME }} -p ${{ secrets.DOCKER_PASSWORD }}。这些看似枯燥的运维指令恰恰是区分“玩具Demo”和“可用应用”的分水岭。3. 核心技术点拆解与实操细节3.1 Prompt工程不是写诗是写接口契约很多初学者把Prompt当成玄学反复修改“请用专业、简洁、友好的语气回答”结果效果平平。真相是Prompt的本质是定义大模型这个“黑盒API”的输入输出契约。它必须像RESTful API文档一样精确。举个真实案例一个销售线索评分应用需要AI根据客户公司简介输出一个0-100分的评分和三条理由。错误的Prompt是“请给这家公司打分并说明理由。” 正确的Prompt必须包含你是一个资深B2B销售专家任务是为客户公司进行商机评分。请严格按以下JSON格式输出不要有任何额外文字 { score: 0-100的整数, reasons: [理由1, 理由2, 理由3] } 输入公司简介{{company_bio}}这里的关键设计点有三第一角色定义“资深B2B销售专家”框定了知识边界避免模型胡扯第二强制JSON格式消除了后端解析的歧义json.loads(response)就能直接拿到结构化数据第三{{company_bio}}是占位符实际调用时用prompt.replace({{company_bio}}, user_input)注入这是安全的字符串模板杜绝了Prompt注入攻击。我在带学员时会让他们用Postman手动测试这个Prompt粘贴进去点Send看返回是不是严格的JSON。如果不是立刻调整直到10次测试10次都成功。这比看一百篇“Prompt写作技巧”文章都管用。另一个高频坑是“温度值”temperature的滥用。新手常设temperature0.8追求“创意”结果销售评分理由每次都不一样无法复现。实测经验对于需要确定性输出的任务如评分、分类、提取固定字段temperature0是黄金法则只有在生成营销文案、 brainstorming点子时才考虑提高到0.3-0.5。3.2 RAG知识库向量不是万能钥匙切分才是灵魂RAG失效的头号原因从来不是模型不够强而是知识库切分chunking太粗糙。我见过一个医疗问答系统把整本《临床诊疗指南》按每1000字符切分结果用户问“糖尿病足溃疡的清创原则”系统召回的chunk里只有“糖尿病”三个字后面全是无关的血糖监测内容。正确的切分策略必须匹配文档的天然结构。技术文档按二级标题##切分合同文本按条款编号“第一条”、“第二条”切分会议记录按发言人“张经理”、“李总监”切分。工具上langchain.text_splitter提供了MarkdownHeaderTextSplitter和RecursiveCharacterTextSplitter但后者需要精细调参。实测参数组合chunk_size300, chunk_overlap50对大多数技术文档效果最佳。chunk_overlap不是为了“多留点信息”而是为了保留上下文锚点——比如一个chunk结尾是“该设备支持”下一个chunk开头是“USB 3.0和HDMI 2.1接口”重叠的50字符确保了“支持”这个词不会被孤立。向量化环节all-MiniLM-L6-v2模型在精度和速度上取得了极佳平衡单次embedding耗时约150msCPU远快于text-embedding-ada-002的API调用延迟。部署时把embedding模型和Chroma一起打包进Docker镜像彻底摆脱对外部API的依赖这才是企业级应用的底气。3.3 Agent工作流用状态机思维替代“智能幻想”把Agent想象成一个严谨的流程工程师而非一个有意识的“小助手”。它的核心是状态state和转换transition。一个采购建议Agent的状态机可能是IDLE-QUERY_PRODUCT_A-QUERY_PRODUCT_B-GENERATE_COMPARISON-IDLE。每个状态对应一个明确的Tool调用和预期的返回格式。LangChain的StateGraph正是为此而生。你定义一个State类class AgentState(TypedDict): messages: Annotated[Sequence[BaseMessage], operator.add] product_a_data: str product_b_data: str comparison_result: str然后为每个节点写纯函数def query_product_a(state: AgentState) - AgentState: # 调用SQL Tool查询产品A data db_query(fSELECT * FROM products WHERE name{state[messages][-1].content}) return {product_a_data: data} def generate_comparison(state: AgentState) - AgentState: # 组装Prompt调用LLM prompt f对比产品A:{state[product_a_data]} 和产品B:{state[product_b_data]} result llm.invoke(prompt) return {comparison_result: result.content}这种写法的好处是每个函数职责单一可独立单元测试状态流转清晰debug时一眼看出卡在哪一步更重要的是它强迫你思考“如果Tool失败了状态该如何回滚”——比如query_product_a返回空状态机不应崩溃而应转入HANDLE_NOT_FOUND状态返回友好提示。这比任何“让Agent更聪明”的尝试都更接近工程现实。3.4 前端交互用渐进式增强对抗AI的不确定性AI的不可预测性是前端最大的敌人。用户点击“生成报告”按钮3秒后屏幕一片空白这是最差体验。解决方案是“渐进式增强”Progressive Enhancement先提供确定性反馈再叠加AI能力。第一步按钮点击后立即禁用并显示“正在分析您的数据…”第二步后端返回流式token时前端用span idoutput/span逐字追加同时加一个CSS动画模拟打字光标第三步当AI返回最终答案再用highlight.js对代码块自动语法高亮。关键技巧在于错误兜底如果流式响应中断前端EventSource的onerror事件会触发此时不是显示“网络错误”而是自动降级——用一个预设的、基于规则的模板生成答案“根据您提供的数据我们建议优先考虑方案A因其成本较低。详细分析请稍后查看。” 这个模板可以是Jinja2渲染的完全不依赖AI确保用户体验不中断。我在一个金融风控项目里甚至为每个AI调用设置了“影子模式”AI生成答案的同时后台并行跑一个传统规则引擎两者结果对比差异超过阈值时自动告警并人工复核。这并非不信任AI而是对业务负责的工程态度。4. 实操全流程从零到上线的十二周手记4.1 Week 1Hello World但必须是可部署的周一上午我要求学员做的第一件事不是写代码而是注册一个Cloudflare Tunnel免费账号。为什么因为本地开发时http://localhost:5000只能自己访问而“可演示”意味着要让同事也能打开链接。Cloudflare Tunnel能在5分钟内把你的本地Flask服务暴露成一个https://my-ai-app.trycloudflare.com的公网地址且自带HTTPS证书。技术栈锁定Python 3.11, Flask 2.3, OpenAI Python SDK 1.35。app.py核心代码仅12行from flask import Flask, request, jsonify import openai app Flask(__name__) openai.api_key sk-... # 从环境变量读取此处为演示 app.route(/api/chat, methods[POST]) def chat(): data request.json response openai.chat.completions.create( modelgpt-3.5-turbo, messages[{role: user, content: data[question]}] ) return jsonify({answer: response.choices[0].message.content}) if __name__ __main__: app.run(host0.0.0.0, port5000)前端index.html用纯HTML/CSS/JSfetch调用/api/chat。关键细节fetch必须设置mode: cors否则浏览器会因跨域拦截response.json()后用document.getElementById(output).innerText data.answer更新DOM。周五下午所有人必须把自己的https://xxx.trycloudflare.com链接发到群里我挨个点击测试。一个都不能少。这个看似简单的“Hello World”完成了三个隐性目标熟悉API调用范式、掌握跨域调试、建立公网可访问的最小闭环。没有这一步后续所有学习都是空中楼阁。4.2 Week 4RAG上线但必须验证召回率这一周的里程碑是让系统能准确回答“我们的CRM系统支持哪些支付方式”这类问题。知识库来源是公司内部Confluence导出的HTML文档。实操步骤1) 用BeautifulSoup解析HTML提取h2和p标签内容过滤掉导航栏和页脚2) 按h2标签切分每个标题及其后续段落作为一个chunk3) 用all-MiniLM-L6-v2模型批量生成embedding存入Chroma4) 编写检索函数输入问题返回top-3 chunk。但到这里只是开始。真正的验收标准是“召回率测试”准备20个真实业务问题如“如何导出客户列表”、“审批流程需要几个节点”手动检查每个问题对应的top-1 chunk是否包含了正确答案。如果低于80%就必须回溯切分逻辑。我让学员用Excel表格记录问题 | 期望答案位置 | 实际召回chunk | 是否命中 | 原因分析。常见原因有二一是HTML解析时丢失了关键div classcontent包裹二是切分时把“支付方式”和“退款政策”混在同一个chunk里。解决方案是增加CSS选择器精度或在切分后对chunk内容做关键词TF-IDF加权确保“支付”这个词权重最高。这个测试过程比写一百行代码更能教会你什么是“高质量知识库”。4.3 Week 8Agent上线但必须有熔断机制采购比价Agent上线当天我们遭遇了第一次生产事故供应商数据库临时宕机Agent在QUERY_PRODUCT_A状态卡死前端Loading图标一直转。根本原因是缺少熔断Circuit Breaker。我们在Agent的Tool函数里加入了tenacity库的重试与熔断from tenacity import retry, stop_after_attempt, wait_exponential, retry_if_exception_type retry( stopstop_after_attempt(3), waitwait_exponential(multiplier1, min4, max10), retryretry_if_exception_type((ConnectionError, TimeoutError)) ) def db_query(sql): # 数据库查询逻辑 pass更关键的是熔断器当连续3次失败熔断器开启后续请求直接返回预设的fallback数据不再尝试连接数据库。tenacity的CircuitBreaker类完美支持此模式。同时我们在前端加了“手动重试”按钮用户点击后前端发送一个特殊信号后端重置熔断器状态。这个设计把一次可能持续数小时的服务不可用压缩到了30秒内自动恢复。它再次印证AI应用的健壮性不在于模型多强大而在于工程防护网是否严密。4.4 Week 12CI/CD上线但必须有灰度发布最后一步不是docker push而是灰度发布Canary Release。我们用Nginx做反向代理配置两个上游upstream backend_stable { server 127.0.0.1:5001; # 旧版本 } upstream backend_canary { server 127.0.0.1:5002; # 新版本 } location /api/ { # 5%流量导向新版本 if ($request_uri ~ ^/api/chat) { set $canary 1; } if ($canary 1) { proxy_pass http://backend_canary; } proxy_pass http://backend_stable; }GitHub Actions的workflow里deploy.yml不仅构建镜像还SSH到服务器执行docker-compose up -d --no-deps --force-recreate ai-app-canary。所有新版本的API调用都会被记录日志与旧版本日志并行分析。我们监控两个核心指标1) 平均响应时间新版本不能比旧版本慢20%以上2) 错误率新版本5xx错误率不能超过0.5%。只有当这两个指标连续1小时达标才执行docker-compose scale ai-app-canary0 ai-app-stable10全量切换。这个流程把一次可能影响全体用户的上线变成了可控的、数据驱动的渐进式升级。它标志着你交付的不再是一个Demo而是一个真正经得起业务考验的AI应用。5. 常见问题与独家避坑指南5.1 “模型不听话”不是模型问题是契约没写好现象反复强调“只回答技术问题”AI还是开始聊天气。根源在于Prompt里缺少“拒答协议”。正确做法是在Prompt末尾加上一句硬性约束重要如果问题与技术无关如天气、政治、个人生活请严格回复“我专注于技术问题解答暂不讨论此话题。” 不要解释不要道歉只返回这句话。更进一步后端加一层正则过滤if re.search(r(天气|今天|心情|政治|宗教), user_input): return 我专注于技术问题解答...。双重保险确保万无一失。这并非限制AI而是明确服务边界保护应用的专业形象。5.2 “知识库搜不到”90%是PDF解析失败不是向量问题现象上传PDF后搜索关键词毫无结果。第一反应不该是换向量模型而是检查PDF解析质量。用pymupdf时务必启用textpage模式doc fitz.open(manual.pdf) for page in doc: text page.get_text(text) # 错误可能漏字 # 正确 textpage page.get_textpage() text textpage.extractText()对于扫描版PDFpymupdf完全失效必须用OCR。Tesseract是开源首选但中文识别需额外安装chi_sim语言包。实测命令tesseract manual.pdf stdout -l chi_sim --psm 6。把OCR后的纯文本再喂给向量库召回率立刻提升。记住向量库存储的是“文字”不是“图片”源头文字质量决定一切。5.3 “前端卡死”流式响应的隐藏杀手是内存泄漏现象连续提问10次后浏览器内存占用飙升最终卡死。罪魁祸首是前端未清理EventSource实例。正确写法let eventSource null; function startStream() { if (eventSource) { eventSource.close(); // 关键每次新请求前关闭旧实例 } eventSource new EventSource(/api/stream?question encodeURIComponent(q)); eventSource.onmessage function(e) { /* 处理 */ }; eventSource.onerror function() { /* 错误处理 */ }; }同时后端流式响应必须设置Content-Type: text/event-stream和Cache-Control: no-cache否则浏览器可能缓存旧的SSE连接。这个坑几乎每个初学者都会踩但修复只需两行代码。5.4 “部署失败”Docker里缺的不是依赖是GPU驱动现象本地ollama run qwen2:7b正常Docker里报错CUDA error: no kernel image is available for execution on the device。这不是Dockerfile写错了而是宿主机的NVIDIA驱动版本与容器内CUDA Toolkit版本不匹配。解决方案不是升级驱动可能影响其他服务而是指定兼容的模型版本ollama run qwen2:0.5b-cuda小模型对驱动要求低或在docker run时添加--gpus all,capabilitiescompute,utility显式声明GPU能力。更稳妥的做法是用nvidia-smi查看宿主机驱动版本然后在Ollama官网查对应支持的模型tag。这个细节决定了你的AI应用是能跑在一台旧工作站上还是必须采购新显卡。提示所有技术选型都应遵循“够用就好”原则。Qwen2-0.5B在单卡3090上推理速度达15 tokens/s足以支撑10人以内团队的内部工具Chroma在SQLite模式下百万级向量查询延迟200msFlask虽非异步框架但配合Gunicorn多worker轻松应对50 QPS。过度追求“最新最强”往往换来的是部署复杂度指数级上升和稳定性下降。注意简历上写“精通LangChain”不如写“用LangChain实现RAG支持100份PDF知识库平均召回准确率92%”。数字是工程师最好的语言。每一次调试、每一次测试、每一次用户反馈都要转化为可量化的成果这才是AI应用开发者最硬核的竞争力。
02
RELATED NEWS

相关资讯

更多网站建设与数字化升级内容

03
WHY YAOTU

想打造同款高转化官网?

懂行业、懂生意,从建站到增长一站式陪跑

场景化定制

不做模板站,围绕你的业务场景量身设计,小众不撞款。

营销型架构

以转化目标组织内容与路径,让官网真正带来询盘。

全周期服务

设计、开发、运营、运维一体,上线只是开始。

免费获取你的建站方案

留下需求,专属顾问 24 小时内为你输出方案建议。