LangChain+FastAPI从零构建RAG问答Agent
最近我从空目录开始,搭了一个能真正跑起来的 RAG 问答 Agent。 Vue 3 做聊天页面,FastAPI 接住请求,LangChain 和 LangGraph 处理模型调用与流程,Chroma、PostgreSQL、Redis 和腾讯云 COS 各管一摊。
FIG. 01 手写框架,AI 提效,人工 Review做到后面,登录鉴权、联网判断、知识库检索、连续对话和 SSE 流式输出都加了进来。我的习惯是先手写骨架,把边界和主链路跑通; 重复代码再交给 AI 提速。生成结果不会直接合并,我会逐项 Review。
01框架自己搭关键链路亲自跑通
02AI 帮忙提速处理重复编码工作
03人工 Review代码合并前必须看过
一个问题,背后是一整套系统
这是一个移动端金融知识问答 H5。用户登录后可以接着上一轮继续问。 普通问题先查私有 PDF;碰到股价、政策、新闻这类有时效性的内容, 再走联网搜索。答案通过 SSE 一段段回到聊天窗口。
FIG. 02 一个问题如何穿过整个系统FastAPI:Agent 后端必须有一层可靠的服务
项目还小,两层就够了。Controller 负责路由、鉴权、数据校验和请求处理; Service 写业务逻辑,通过 ORM 访问数据库。Repository 以后真有需要再加, 现在硬塞一层,只会让找代码变慢。
FIG. 03 简单项目,两层已经足够清晰lifespan 与存储生命周期
应用启动时连接 Redis、PostgreSQL 和 COS,退出时按顺序释放。 每个请求拿到自己的 PostgreSQL Session:成功就提交,报错就回滚, 用完关闭。业务代码只管业务,不必到处复制连接和清理逻辑。
FIG. 04 启动时连接,关闭时完整释放统一异常、拆分路由、SSE 输出
参数错了、业务失败了,或者遇到没预料到的异常,接口都返回同一种结构。 用户、状态、会话各自放在独立路由里。流式接口只转发 LangGraph 的 answer 消息,最后补一个 [DONE]。
有了 AI 编程,为什么还要学技术细节?
骨架跑通以后,登录接口、类型定义、测试样例这些重复工作,交给 AI 确实省时间。但生成的代码不会直接进入主分支,业务逻辑、异常路径和数据边界, 还得自己过一遍。
AI 很快就能给出一份“看起来能跑”的实现,却不知道这个项目真正的边界: 会话应该怎样隔离,接口需要兼容什么,哪条权限规则绝不能放松。 它看到的是当前提示词和有限上下文,程序员面对的是整个系统和真实用户。
FIG. 13 没有人工 Review,AI 编程很容易越跑越偏人工 Review 也不只是检查缩进和命名。它要确认代码会不会串用户、丢数据、 越过权限边界,服务重启后能不能恢复。问题真的发生时,还得有人顺着日志、 请求、状态和数据一路定位根因,而不是让 AI 一轮轮碰运气。
这个项目里,至少有四个坑需要人来判断
01 会话隔离thread_id 如果只按会话生成,没有绑定当前用户, 两个人可能读到同一份对话状态。代码可以正常运行,结果却是严重的数据越权。
02 检索质量 模型回答不对,未必是 Prompt 写差了。PDF 分块过碎、Embedding 不合适或 Top K 设置失衡,都可能让模型拿到一堆无关证据。
03 流式边界 一次网络读取不等于一个完整 SSE 事件。前端如果直接解析当前分片, 中文、JSON 或 [DONE] 都可能被截成两半。
04 运行状态MemorySaver 本地调试很好用,放到多实例环境就会丢失共享状态。 容器显示“已启动”,也不代表 PostgreSQL 和 Redis 已经可以接请求。
FIG. 11 AI 显示通过,不代表代码真的可以上线AI 像一台干活很快的施工机器,复制、改写、补样板代码都很拿手。 但房间怎么分、承重墙在哪,它替你做不了主。HTTP、异步、数据库、 鉴权、向量检索和状态管理这些细节,最后都落在三件事上:判断、验收和排错。
不懂细节时,调试往往只剩一句:“还是不对,再改一下。” 懂一点底层,你会先去看流式响应有没有拆坏、检索召回的数据是否相关, 再决定该改代码、检索参数还是 Prompt。同一个 AI,用法一下就拉开了差距。
这也是现阶段程序员仍然难被替代的部分。敲代码只占工作的一部分, 剩下的是理解模糊需求、权衡成本与风险、为上线结果负责,并在故障发生后把系统救回来。 AI 能放大编码速度,但谁来判断“什么代码应该上线”,答案目前仍然是人。
FIG. 08 自动生成以后,真正的工作是验证有了 Coze、Dify,为什么还要学习 LangChain?
Coze、Dify 很适合把想法迅速做出来。界面可视化,知识库和插件也准备好了, 非程序员同样能参与配置。很多标准场景,用它们更省事。
如果需求只是上传资料、设置提示词、发布机器人,低代码平台通常就是更好的选择。 一旦检索规则、用户状态或业务系统需要深度定制,就会碰到平台的边界。 这时 LangChain 的价值才出来:每个环节都能用代码接管。
FIG. 10 标准场景快速上线,定制场景自由组合- 验证想法速度快
- 常用能力开箱即用
- 可视化配置门槛低
- 适合标准化场景
- 检索策略可自定义
- 状态与用户体系打通
- 模型和向量库可替换
- 适合深度业务集成
Coze、Dify 像点外卖:打开菜单,选好套餐,下单没多久就能吃。 LangChain 更像自己下厨,模型、检索、记忆和工具都是手边的食材, 先放什么、放多少、怎么出锅都能改。只是想快速吃顿饭,就别急着装修厨房; 客户开始要求“少盐、免辣、不要香菜,还得分桌上菜”时, 能自己掌勺就很重要了。
RAG 到底解决了什么问题?
模型没看过我们的私有资料,也可能把不知道的内容说得很像真的。 把整本 PDF 塞进 Prompt 又贵又笨,上下文窗口也未必装得下。
FIG. 06 不把整本资料塞给模型,只取与问题有关的证据当前项目使用 PyPDFLoader 读取 PDF,清理多余空白并跳过过短页面, 再用 RecursiveCharacterTextSplitter 切块。每块默认 700 个字符,重叠 80 个字符,尽量在段落和中文标点处分割。
用户提问时,系统从 Chroma 召回最相关的 3 个文本块,并过滤低相关内容。 模型收到问题时,还会一起拿到三份候选证据。回答里保留资料编号, 用户至少能顺着出处回去核对,不必只凭一句“听起来挺对”。
LangGraph:把 Agent 的脑回路拆开
FIG. 05 状态沿节点依次流转LangGraph:把脑回路拆成节点
当前 Agent 的主流程是 decide_search → retrieve → answer。 decide_search 判断要不要查实时信息, retrieve 找回本地资料,answer 拿着联网结果或知识库上下文组织回答。
以后要加问题改写、多路召回、结果重排、敏感内容检查或人工确认, 就继续加节点。否则这些分支迟早会挤进同一个函数,最后只剩一大团 if...else。
FastAPI:把能力包装成服务
FastAPI 负责 JWT 鉴权、应用生命周期、普通聊天接口和 SSE 流式接口。服务启动时初始化 Redis、PostgreSQL 和 COS,结束时统一释放连接。 流式接口只转发 LangGraph 中 answer 节点产生的消息片段。
Vue 3:接住后端吐出的每一个字
前端通过 ReadableStream 持续读取 SSE 数据,将网络片段写入 buffer,再按事件边界解析。第一段文字到了就创建助手消息,后面的内容继续往上追加, 读到 [DONE] 才算收完。
FIG. 09 网络片段不等于完整 SSE 事件Docker 部署:按正确顺序启动
阿里云 ECS:用 Docker Compose 部署整套服务
这套服务通过 docker-compose.yaml 部署在阿里云 ECS。 服务器访问 Docker Hub 偶尔会超时,所以远程镜像统一切到了国内镜像源: Python、Node、Nginx、PostgreSQL 和 Redis 都通过 DaoCloud 镜像地址拉取;APT、APK、PyPI 和 npm 也分别配置了国内软件源。 这一步不改变镜像内容,主要是避免部署卡在 pull 或依赖下载阶段。
FIG. 07 一条 Compose 命令背后,还有镜像、检查与启动顺序最后,做个总结
这个项目并不复杂,但一条完整的问答链路已经齐了:FastAPI 管接口和资源生命周期,LangGraph 管状态与流程,LangChain 把模型、检索和工具接起来,Docker 负责让整套服务按顺序启动。
FIG. 12 AI 负责提速,人负责把整条链路接对先把框架写明白。 Controller 和 Service 各做什么,数据怎样流动,状态放在哪里,这些边界需要先定下来。
再让 AI 处理重复工作。 类型定义、样板接口和测试代码可以提速,但业务逻辑、异常路径和数据安全仍要人工 Review。
工具按场景选。 标准需求用 Coze、Dify 更快;需要定制检索、状态和业务流程时,再用 LangChain 接管细节。
真正值得带走的,是对整条链路的判断力。知道问题出在接口、状态、检索还是模型, 下一次换框架、换模型,仍然知道该从哪里下手。
