xiaolin

5 阅读17分钟

FastAPI 入门:结合 xiaolin 项目来理解

这篇文档是给“还没真正用过 FastAPI”的你准备的。我们不从抽象概念开始,而是直接拿 xiaolin 这个项目来理解:FastAPI 到底是怎么把“浏览器发请求”变成“后端返回结果”的。

1. 先记住:FastAPI 是干什么的

你可以先把 FastAPI 理解成一套“写后端接口的框架”。

它主要帮你做几件事:

  • 定义接口路径,比如 /api/chat
  • 接收前端传来的参数
  • 校验参数格式
  • 调用业务代码
  • 返回 JSON 数据
  • 自动生成接口文档 /docs

在这个项目里,FastAPI 是整个后端的“入口层”。

2. 项目里 FastAPI 从哪里启动

入口文件是:

  • xiaolin/app/main.py

这里最重要的代码可以概括成三步:

  1. 创建 FastAPI(...) 应用对象
  2. 注册中间件和路由
  3. 挂载静态页面和首页

你可以重点看这几个部分:

app = FastAPI(
    title=config.app_name,
    version=config.app_version,
    description="基于 LangChain 的智能oncall运维系统",
    lifespan=lifespan
)

这段的意思是:创建一个 Web 应用,并且把“启动时要做什么、关闭时要做什么”交给 lifespan 管理。

3. lifespan 是什么

xiaolin/app/main.py 里,项目定义了:

@asynccontextmanager
async def lifespan(app: FastAPI):
    milvus_manager.connect()
    yield
    milvus_manager.close()

你可以把它理解成“应用级别的初始化和清理逻辑”。

这个项目里它做了两件很关键的事:

  • 服务启动时连接 Milvus 向量库
  • 服务关闭时断开 Milvus 连接

这类工作如果不放在启动阶段,每次请求都重新连数据库会很慢,也更乱。

4. 路由是什么

FastAPI 最核心的概念之一就是“路由”。

路由就是:

  • 什么请求路径
  • 用什么 HTTP 方法
  • 对应哪个 Python 函数处理

比如在 xiaolin/app/main.py

app.include_router(chat.router, prefix="/api", tags=["对话"])
app.include_router(file.router, prefix="/api", tags=["文件管理"])
app.include_router(aiops.router, prefix="/api", tags=["AIOps智能运维"])

这表示:

  • 对话相关接口在 app/api/chat.py
  • 文件上传相关接口在 app/api/file.py
  • AIOps 相关接口在 app/api/aiops.py

也就是说,main.py 不自己写所有接口,而是把接口拆到不同模块里。

这是很典型、也很推荐的 FastAPI 项目结构。

5. 看一个最简单的接口:普通聊天

文件:

  • xiaolin/app/api/chat.py

核心代码:

@router.post("/chat")
async def chat(request: ChatRequest):
    answer = await rag_agent_service.query(
        request.question,
        session_id=request.id
    )
    return {
        "code": 200,
        "message": "success",
        "data": {
            "success": True,
            "answer": answer,
            "errorMessage": None
        }
    }

这段逻辑非常值得你记住,因为它就是很多后端接口的标准套路:

  1. @router.post("/chat") 表示这是一个 POST /api/chat 接口
  2. request: ChatRequest 表示请求体会自动按 Pydantic 模型校验
  3. 调用 service 层
  4. 返回统一 JSON 结果

6. Pydantic 在这里干了什么

文件:

  • xiaolin/app/models/request.py
class ChatRequest(BaseModel):
    id: str = Field(..., description="会话 ID", alias="Id")
    question: str = Field(..., description="用户问题", alias="Question")

意思是:

  • 前端要传 Id
  • 前端要传 Question
  • FastAPI 会自动把它解析成 Python 对象

比如当前端发送:

{
  "Id": "session-123",
  "Question": "什么是向量数据库?"
}

在接口函数里你就可以直接用:

  • request.id
  • request.question

这就是 FastAPI 很舒服的地方:参数校验和解析都很自然。

7. FastAPI 这里为什么“路由层很薄”

这个项目结构上做得比较清楚:

  • api/ 负责收请求、回响应
  • services/ 负责真正的业务逻辑
  • models/ 负责数据模型
  • core/ 负责底层组件

比如聊天接口并不直接写模型调用,而是交给:

  • xiaolin/app/services/rag_agent_service.py

文件上传接口也不直接写向量化细节,而是交给:

  • xiaolin/app/services/vector_index_service.py

这是你以后写后端时非常值得模仿的分层方式。

8. 文件上传接口能帮你理解完整业务流

文件:

  • xiaolin/app/api/file.py

上传接口做了这些事:

  1. 校验文件名和扩展名
  2. 保存到 uploads/
  3. 调用 vector_index_service.index_single_file(...)

也就是说,FastAPI 这里只负责“HTTP 接口入口”,真正的 RAG 建库逻辑继续往 service 层走。

这个调用链是:

前端上传文件
-> /api/upload
-> file.py
-> vector_index_service.py
-> document_splitter_service.py
-> vector_embedding_service.py
-> vector_store_manager.py
-> Milvus

9. 流式输出是怎么做的

这个项目不只是普通 JSON 返回,还支持流式对话。

文件:

  • xiaolin/app/api/chat.py

核心用法是:

from sse_starlette.sse import EventSourceResponse

return EventSourceResponse(event_generator())

这里用的是 SSE。

你可以先简单理解为:

  • 普通接口:一次性返回完整结果
  • SSE 接口:后端边生成边推给前端

所以 /api/chat_stream 适合模型流式回答。

10. FastAPI 为什么很适合做 AI 应用后端

从这个项目你能看到几个原因:

  • 写 REST API 很快
  • 天然支持异步 async/await
  • 很适合接 SSE 流式输出
  • 和 Pydantic 配合非常顺手
  • 自动生成文档,调试方便

对 AI 项目来说很常见的场景是:

  • 聊天接口
  • 文件上传
  • 流式回答
  • 后台任务
  • 配置化服务启动

FastAPI 对这些都比较友好。

11. 你可以怎么理解这个项目里的 FastAPI 分层

建议你先用下面这个脑图去记:

main.py
-> 创建 FastAPI 应用
-> 注册路由
-> 配置 CORS
-> 挂载静态资源

api/
-> 定义接口路径
-> 接收请求
-> 调 service
-> 返回响应

services/
-> 真正业务逻辑

models/
-> 请求和响应的数据结构

core/
-> 模型、Milvus、配置等底层能力

12. 你现在最值得先读哪几个文件

如果你是第一次接触 FastAPI,我建议按这个顺序看:

  1. xiaolin/app/main.py
  2. xiaolin/app/api/chat.py
  3. xiaolin/app/models/request.py
  4. xiaolin/app/api/file.py
  5. xiaolin/app/api/aiops.py

这样你会先掌握:

  • 应用怎么启动
  • 接口怎么定义
  • 参数怎么接收
  • 普通返回和流式返回有什么区别

13. 一句话总结

xiaolin 里,FastAPI 不是“负责智能”的那一层,它更像整个系统的交通枢纽:

  • 接住前端请求
  • 交给 LangChain/RAG/AIOps 服务处理
  • 再把结果用 JSON 或 SSE 返回出去

如果你刚入门,先把 FastAPI 理解成“写接口 + 管理请求/响应 + 连接前后端”的框架就够了。

下一步最适合继续看的,就是这个项目里的 LangChain 和 RAG 调用链。

LangChain 与 RAG 入门:结合 xiaolin 项目来理解

这篇文档专门帮你建立一个清晰认识:

  • LangChain 在这个项目里干什么
  • RAG 是怎么跑起来的
  • LangGraph、Tool、Milvus 分别是什么角色

如果你之前没做过这类 AI 应用,可以先记一句最重要的话:

这个项目不是“直接把问题发给大模型”那么简单,而是把“检索知识库、调用工具、保留会话、流式输出”都组织起来了。

1. 先分清 4 个概念

1.1 大模型

这里用的是通义千问。

xiaolin/app/services/rag_agent_service.py 里:

self.model = ChatQwen(
    model=self.model_name,
    api_key=config.dashscope_api_key,
    temperature=0.7,
    streaming=streaming,
)

也就是:

  • 底层模型提供回答能力
  • 项目通过 LangChain 生态去组织它

1.2 LangChain

LangChain 可以理解为“帮你把模型、工具、知识库、消息历史串起来的应用框架”。

它不等于模型本身。

它做的是“编排”。

1.3 RAG

RAG = Retrieval-Augmented Generation,检索增强生成。

最直白的理解是:

  1. 先从知识库里找相关资料
  2. 再把资料交给模型回答

这样比“模型纯靠自己记忆答题”更适合企业知识库场景。

1.4 LangGraph

LangGraph 可以理解为“更适合有状态工作流的 LangChain 组件”。

这个项目里:

  • 聊天 Agent 用了 create_agent + checkpointer
  • AIOps 用了显式的 StateGraph

所以你会看到 LangChain 和 LangGraph 是一起出现的。

2. 这个项目里的 RAG 主流程是什么

建议你先记住这条主线:

上传文档
-> 文档切分
-> 生成 embedding
-> 存入 Milvus

用户提问
-> Agent 判断是否要查知识库
-> 调用 retrieve_knowledge 工具
-> 从 Milvus 检索相关文档
-> 把检索结果作为上下文交给模型
-> 模型生成最终回答

这就是这个项目的核心。

3. 上传文档后,知识库是怎么建立的

入口在:

  • xiaolin/app/api/file.py

上传成功后会调用:

vector_index_service.index_single_file(str(file_path))

然后继续往下走。

4. 文档怎么切分

文件:

  • xiaolin/app/services/document_splitter_service.py

这个服务做了两件很关键的事:

  1. 如果是 Markdown,先按标题切
  2. 再按长度做二次切分

项目里用了:

  • MarkdownHeaderTextSplitter
  • RecursiveCharacterTextSplitter

这很重要,因为 RAG 不是把整篇文章直接丢给向量库,而是切成很多“较小、可检索”的分片。

这里的好处是:

  • 检索更精准
  • 不会把整篇超长文档都塞给模型
  • 能保留标题层级信息

项目还给每个分片加了元数据:

  • _source
  • _extension
  • _file_name
  • h1h2 等标题信息

这些元数据后面在检索结果展示时很有用。

5. embedding 是什么

文件:

  • xiaolin/app/services/vector_embedding_service.py

embedding 你可以先理解成:

  • 把一段文字转换成一串数字向量
  • 让“语义相近的文本”在向量空间里更靠近

这样用户提问时,系统就可以按“语义相似度”找相关知识,而不是只靠关键词匹配。

这个项目封装了一个 DashScopeEmbeddings,实现了 LangChain 的标准接口:

  • embed_documents
  • embed_query

这意味着上层 LangChain 组件可以无缝调用它。

6. 向量库存在哪里

文件:

  • xiaolin/app/services/vector_store_manager.py

这里项目用的是:

  • Milvus
  • langchain_milvus.Milvus

注意这里不是直接自己手写很多 Milvus 查询逻辑,而是用了 LangChain 的 VectorStore 封装。

它负责:

  • add_documents(documents)
  • similarity_search(query, k=3)
  • as_retriever(...)

所以从编程体验上,你可以把它理解成:

  • 一个“可以存向量、查相似文本”的知识库对象

7. 真正的检索工具在哪里

文件:

  • xiaolin/app/tools/knowledge_tool.py

这里定义了:

@tool(response_format="content_and_artifact")
def retrieve_knowledge(query: str):

这就是一个 LangChain Tool。

它的作用是:

  1. 收到查询词
  2. 从向量库里检索文档
  3. 把文档格式化成适合模型阅读的上下文文本

这里格式化后的内容长这样:

【参考资料 1】
标题: ...
来源: ...
内容:
...

也就是说,模型不是直接看原始 Milvus 结果,而是看被整理过的“参考资料”。

8. Agent 是怎么组织起来的

文件:

  • xiaolin/app/services/rag_agent_service.py

最核心的代码是:

self.agent = create_agent(
    self.model,
    tools=all_tools,
    checkpointer=self.checkpointer,
)

这里你可以把 create_agent(...) 理解成:

  • 给模型一组工具
  • 给它消息历史能力
  • 让它在对话时自行决定要不要用工具

这里的工具包含两类:

  • 本地工具:retrieve_knowledgeget_current_time
  • MCP 工具:从外部 MCP 服务动态加载

所以这个 Agent 并不是“只会聊天”,而是“会按需用工具聊天”。

9. 会话历史是怎么保留的

还是在 rag_agent_service.py

self.checkpointer = MemorySaver()

然后每次请求会传:

config_dict = {
    "configurable": {
        "thread_id": session_id
    }
}

这意味着:

  • 同一个 session_id
  • 会共享同一条对话上下文

所以用户连续问几轮,Agent 是“记得前文”的。

这个能力是很多聊天产品的基础。

10. 流式输出是怎么和 LangChain 结合的

query_stream(...) 里用了:

async for token, metadata in self.agent.astream(
    input=agent_input,
    config=config_dict,
    stream_mode="messages",
):

这表示 Agent 在生成过程中不断吐出消息片段。

后端再把这些片段包装成 SSE,前端就能边收边显示。

所以整体链路是:

LangChain / LangGraph astream
-> FastAPI SSE
-> 浏览器逐段渲染

11. 为什么说这个项目是“RAG + Tool + Agent”

因为它不只是“检索后拼 prompt”那么简单。

它至少做了三层事:

11.1 RAG

通过 retrieve_knowledge 访问向量库。

11.2 Tool Calling

通过 @tool 和 MCP 工具,让模型可以主动调用外部能力。

11.3 Agent

通过 create_agent(...) 让模型在对话中自己决定:

  • 需不需要查知识库
  • 需不需要查时间
  • 需不需要调 MCP 工具

这比“固定流程的问答机器人”更灵活。

12. AIOps 为什么又用了 LangGraph

文件:

  • xiaolin/app/services/aiops_service.py

这里不是普通对话 Agent,而是显式构建了一个工作流图:

  • planner
  • executor
  • replanner

代码里用了:

workflow = StateGraph(PlanExecuteState)

这说明 LangGraph 特别适合这种:

  • 有状态
  • 有多个步骤
  • 会根据中间结果决定下一步怎么走

的任务。

也就是说:

  • 聊天场景:偏 Agent
  • AIOps 场景:偏 Workflow

这也是 LangGraph 很常见的实际用法。

13. 你应该怎样理解 MCP

文件:

  • xiaolin/app/agent/mcp_client.py

MCP 可以先理解成“给模型接外部工具的一种统一协议”。

在这个项目里,它接入了:

  • 日志查询服务
  • 监控服务

这样模型就不只是“会说”,而是“能查真实系统数据”。

这对 AIOps 很重要,因为真正的故障诊断不能靠编。

14. 你可以把整个 LangChain 架构记成这样

FastAPI 路由
-> rag_agent_service
-> ChatQwen 大模型
-> Tools
   -> retrieve_knowledge
   -> get_current_time
   -> MCP tools
-> checkpointer 记忆
-> 流式输出 / 一次性输出

retrieve_knowledge 背后又是:

retrieve_knowledge
-> VectorStoreManager
-> Milvus
-> Embeddings
-> DocumentSplitter

15. 对你这种刚入门的人,最容易混淆的点

这里我帮你拆一下:

  • LangChain 不是模型,它是编排框架
  • RAG 不是模型训练,它是“检索 + 生成”
  • Milvus 不是大模型,它是向量数据库
  • Tool 不是普通函数,它是“可以被模型调用的能力”
  • LangGraph 不是前端图,而是后端工作流状态图

16. 你接下来最建议看的文件顺序

建议按这条线读:

  1. xiaolin/app/services/rag_agent_service.py
  2. xiaolin/app/tools/knowledge_tool.py
  3. xiaolin/app/services/vector_index_service.py
  4. xiaolin/app/services/document_splitter_service.py
  5. xiaolin/app/services/vector_store_manager.py
  6. xiaolin/app/services/vector_embedding_service.py
  7. xiaolin/app/services/aiops_service.py

17. 一句话总结

这个项目里的 LangChain 不是单纯“调用一下模型 API”,而是把下面这些能力组装成了一个完整 AI 应用:

  • 多轮对话
  • 工具调用
  • RAG 检索
  • 会话记忆
  • 流式输出
  • AIOps 工作流

如果你把这条主线看懂了,后面再学更复杂的 Agent 系统就会轻松很多。

Vue 入门:先理解 Vue,再看 xiaolin 项目前端和它的关系

先说最重要的结论:

xiaolin 这个项目的前端页面并不是标准 Vue 项目。

它当前是这种结构:

  • xiaolin/static/index.html
  • xiaolin/static/app.js
  • xiaolin/static/styles.css

也就是说,它是“静态 HTML + 原生 JavaScript”页面,不是你常见的那种:

  • src/App.vue
  • src/components/*.vue
  • vite.config.ts
  • package.json

所以这一篇我会做两件事:

  1. 先帮你快速入门 Vue
  2. 再告诉你这个项目前端和 Vue 的区别到底在哪

1. Vue 是干什么的

Vue 是前端框架。

你可以先把它理解成:

  • 专门用来做页面交互
  • 帮你更容易管理页面状态
  • 帮你把 UI 拆成组件

如果不用 Vue,页面复杂起来后,你经常会自己手写很多:

  • document.querySelector
  • addEventListener
  • DOM 增删改查
  • 手动同步数据和页面

Vue 的核心价值,就是让你更多关注“数据是什么”,而不是“DOM 怎么改”。

2. Vue 最核心的 3 个概念

2.1 数据驱动视图

意思是:

  • 数据变了
  • 页面自动跟着变

比如计数器:

<template>
  <button @click="count++">点击了 {{ count }} 次</button>
</template>

<script setup>
import { ref } from 'vue'
const count = ref(0)
</script>

这里你没有手动去找按钮再改文本,Vue 会帮你更新页面。

2.2 组件化

Vue 鼓励你把页面拆成一个个小模块。

比如聊天系统可以拆成:

  • 输入框组件
  • 消息列表组件
  • 历史对话组件
  • 顶部操作栏组件

这样代码更好维护。

2.3 指令和事件绑定

Vue 常见写法有:

  • v-model
  • v-if
  • v-for
  • @click
  • :class

它们分别对应:

  • 双向绑定
  • 条件渲染
  • 列表渲染
  • 事件监听
  • 动态绑定属性

3. 标准 Vue 项目一般长什么样

如果你以后新建 Vue 项目,通常会看到这种结构:

src/
  App.vue
  main.js
  components/
package.json
vite.config.js

App.vue 里通常会写:

<template>
  <div>{{ message }}</div>
</template>

<script setup>
const message = 'Hello Vue'
</script>

<style scoped>
div { color: red; }
</style>

这就是 Vue 单文件组件。

4. 这个项目前端现在是什么模式

文件:

  • xiaolin/static/index.html
  • xiaolin/static/app.js

它的做法更像传统页面开发:

  • HTML 里先把页面骨架写好
  • JS 里用类和 DOM API 管理交互
  • 通过 fetch 或 SSE 和后端通信

比如在 app.js 里你会看到大量:

  • document.querySelector(...)
  • document.getElementById(...)
  • addEventListener(...)

这说明它不是 Vue 的响应式写法,而是手动操作页面元素。

5. 你可以把这个项目前端理解成“Vue 之前的写法”

举个例子。

在这个项目里,初始化元素的方式是:

this.sidebar = document.querySelector('.sidebar');
this.newChatBtn = document.getElementById('newChatBtn');
this.messageInput = document.getElementById('messageInput');

这种写法的问题不是“不能用”,而是页面越来越复杂时,会出现:

  • 状态分散
  • DOM 引用很多
  • 修改一处容易影响多处
  • 功能再大一点就难维护

Vue 出现后,很多这类逻辑会换成“状态驱动组件”。

6. 如果用 Vue,这个聊天页大概会怎么拆

这正好帮助你理解 Vue。

当前这个项目页面至少可以拆成这些组件:

  • Sidebar.vue
  • ChatMessages.vue
  • ChatInput.vue
  • ModeSelector.vue
  • HistoryList.vue
  • LoadingOverlay.vue

然后在页面层统一管理状态,比如:

  • 当前会话 ID
  • 当前模式 quick/stream
  • 历史对话列表
  • 当前消息列表
  • 是否正在流式输出

这就是 Vue 擅长的地方。

7. 用 Vue 会怎么管理输入框

当前项目是手动取值和绑定事件。

如果换成 Vue,通常会写成:

<template>
  <input v-model="message" @keydown.enter="sendMessage" />
</template>

<script setup>
import { ref } from 'vue'

const message = ref('')

function sendMessage() {
  console.log(message.value)
}
</script>

你可以看到区别:

  • 原生 JS:手动找 DOM、手动读值
  • Vue:数据本身就是核心,页面只是显示结果

8. 用 Vue 会怎么渲染消息列表

当前项目如果要追加消息,通常要自己创建 DOM 或拼 HTML。

Vue 常见写法会是:

<template>
  <div v-for="item in messages" :key="item.id">
    {{ item.content }}
  </div>
</template>

只要 messages 变化,页面就自动刷新。

这对聊天应用特别舒服。

9. 这个项目前端和 Vue 的相同点

虽然它没真正用 Vue,但你还是能看出一些“前端应用思路”上的共性:

  • 有明确的状态,比如 currentModeisStreaming
  • 有初始化逻辑
  • 有事件绑定
  • 有按模块划分的 UI 区域
  • 有和后端 API 的通信

也就是说,它已经有“单页应用”的味道了,只是没有使用 Vue 来管理。

10. 这个项目前端和 Vue 的最大不同点

差别主要有 4 个:

10.1 没有响应式系统

Vue 会自动追踪数据变化并更新页面。

这个项目需要手动更新 UI。

10.2 没有组件系统

现在基本都在 app.js 一个大类里。

Vue 通常会拆成多个组件文件。

10.3 没有构建工具链

标准 Vue 一般会有:

  • Vite
  • npm
  • package.json

这个项目没有这套工程化结构。

10.4 没有模板语法

Vue 会在模板里写:

  • v-for
  • v-if
  • @click

这里还是原生 HTML + JS 逻辑分离的方式。

11. 为什么这个项目不用 Vue 也能跑

因为前端本质上就是:

  • 浏览器加载 HTML
  • 执行 JavaScript
  • 发 HTTP 请求给后端
  • 把响应显示出来

Vue 只是让这个过程更易维护,不是唯一方案。

所以这个项目当前这种写法,做一个中小型 demo 是完全可行的。

12. 如果你打算以后认真学前端,建议怎么理解 Vue

你可以把 Vue 看成“帮你管理复杂页面的工具”。

当页面开始出现下面这些东西时,Vue 的优势就会越来越明显:

  • 多个组件之间传数据
  • 很多交互状态
  • 列表渲染
  • 条件展示
  • 路由切换
  • 状态共享

聊天系统、管理后台、知识库页面,都是很常见的 Vue 使用场景。

13. 对照这个项目,Vue 最适合优化哪些地方

如果以后把 xiaolin 前端改成 Vue,我会优先做这些事情:

  1. app.js 按组件拆开
  2. 把对话状态改成响应式数据
  3. 把聊天记录渲染改成列表模板
  4. 把模式选择、文件上传、消息流式显示拆成独立组件
  5. 把 API 请求封装成单独模块

这样代码会比现在更清晰。

14. 如果你是第一次学 Vue,应该先掌握什么

建议你先只学这几个点,不要一上来就学太杂:

  1. refreactive
  2. v-model
  3. v-for
  4. v-if
  5. @click
  6. 组件 props
  7. onMounted
  8. fetch/axios 调接口

掌握这些后,再回头看这个聊天页面,你就会很容易想到“如果用 Vue,该怎么重构”。

15. 给你一个最实用的结论

对这个项目来说:

  • 后端框架是 FastAPI
  • AI 编排核心是 LangChain / LangGraph
  • 前端不是 Vue,而是原生静态页面

所以你现在别把它误认为“FastAPI + Vue 全栈项目”。

更准确地说,它是:

FastAPI + LangChain/LangGraph + 静态前端页面

16. 一句话总结

Vue 你可以先理解成“让前端页面从手动改 DOM,升级成用数据驱动组件”的框架。

xiaolin 这个项目虽然没真正用 Vue,但正因为它现在还是原生 JS,你反而更容易看懂 Vue 为什么会流行,以及 Vue 能帮这个聊天页面解决什么问题。