最近在做一个类飞书文档的企业内部知识库项目,前端部分从文档解析、向量检索到 AI 问答全链路都踩了一遍。这套方案并不算新颖,但真正从零搭起来,尤其是想把AI Agent和RAG结合进一个可维护的前端工程里,细节远比想象中多。
这篇文章直接给结论:如果你是前端工程师,想在企业内部搭建一套“文档知识库 + 向量检索 + 智能问答”的系统,无论选 Dify 还是自研 RAG 流程,核心链路都是通的。本文会把全链路拆开讲清楚:文档怎么接入、切片怎么做、向量和 BM25 混合检索怎么实现、Agent 怎么调用知识库、前端怎么接入接口,以及部署和性能排查时最容易踩的坑。
全文没有厂商绑定,思路可以落地到任意技术栈,代码示例以通用模板为主,实际路径需要按你的项目环境替换。
1. 核心能力速览
先给一张规格表,快速判断这套方案适不适合你的场景。
| 能力项 | 说明 |
|---|---|
| 项目类型 | 企业级知识库 + RAG 检索增强生成 + AI Agent 问答 |
| 核心功能 | 文档解析、文本切片、向量化、混合检索、Rerank 重排、智能问答、引用溯源 |
| 典型技术栈 | 前端:React/Vue + TypeScript;后端:Node.js/Python;向量库:Milvus/pgvector/ES;模型:Embedding + LLM |
| 检索方式 | 向量检索 + BM25 关键词检索,多路召回后 Rerank 精排 |
| 支持批量任务 | 支持文档批量导入、切片批量写入、异步索引任务 |
| 接口能力 | 提供检索接口、问答接口、文档管理接口,可接入内部系统 |
| 启动方式 | Docker Compose 编排,或分服务独立启动 |
| 显存/GPU 要求 | 取决于 Embedding 和 LLM 模型;纯 API 调用不要求本机 GPU |
| 适合场景 | 企业规章制度查询、产品文档问答、研发知识库、客服辅助 |
比较关键的一点:这套方案的前端工作量并不小,但有固定的套路可循。你不需要成为算法专家,但必须理解检索链路,否则页面做得再好看,问答结果也是“幻觉”。
2. 适用场景与使用边界
2.1 适合谁
- 前端工程师:想从“调 API”升级到“理解 RAG 链路”,自己搭一套知识库 Demo 或生产系统。
- 全栈开发者:需要在企业内部文档系统和 AI 能力之间做胶水层。
- 产品/项目负责人:评估自研知识库与采购商业产品的成本边界。
2.2 能解决什么问题
这类系统的本质是:把企业分散的文档资料变成一个可检索、可引用、可对话的知识资产。
具体来说:
- 传统全文搜索搜不到同义表达,比如搜“报销流程”匹配不到“差旅费用报销单”。
- 大模型直接问答会“幻觉”,需要把检索到的原文片段作为上下文约束。
- 文档数量多、更新频繁,靠人工维护问答对不现实。
2.3 不适合什么场景
- 单文档问答:只有几份 PDF,不需要做全链路知识库,直接丢给大模型即可。
- 实时性要求极高:文档刚更新就要秒级检索到,需要额外做增量索引和缓存设计。
- 强结构化数据:大量表格、数据库记录,应该走 Text2SQL 或 BI 工具,而不是 RAG。
2.4 版权、隐私与安全边界
这块必须强调,企业内部知识库涉及大量敏感资料,落地时要注意:
- 只索引有授权来源的文档,不要抓取或上传来源不明的数据。
- 涉及客户个人信息、员工隐私、财务数据时,先做脱敏和权限隔离。
- 如果使用外部 LLM API,内部数据会离开企业网络,要评估合规要求;建议优先考虑私有化部署或内部模型服务。
- 问答结果必须带引用来源,避免模型“编造”内容被当作事实扩散。
3. RAG 全链路架构设计与技术选型
一个完整的 RAG 知识库系统,链路可以拆成四个阶段:数据接入 -> 索引构建 -> 检索召回 -> 生成回答。前端工程师最容易忽略的是前两个阶段,但问答质量的好坏恰恰取决于这里。
3.1 全链路架构
文档来源(飞书/语雀/内部 Wiki/本地文件) | v 文档解析与清洗(提取正文、表格、图片说明) | v 文本切片(按标题层级/段落/Token 切分) | v 索引构建(向量化 + 倒排索引) | v 在线检索(向量检索 + BM25 关键词检索) | v Rerank 精排(融合排序,过滤无关片段) | v LLM 生成回答(携带引用片段与出处) | v 前端问答界面 / API 输出3.2 技术选型建议
组件选型没有唯一答案,取决于团队技术栈和预算。
| 组件 | 可选方案 | 建议 |
|---|---|---|
| 向量数据库 | Milvus、pgvector、Elasticsearch、Qdrant | 已有 ES 运维经验优先选 ES;数据量小选 pgvector 最省事 |
| Embedding 模型 | BGE、M3E、OpenAI Embedding、Cohere | 中文场景优先 BGE/M3E;支持私有化部署 |
| LLM | GPT 系列、Claude、通义千问、DeepSeek、GLM | 企业内部敏感场景选私有化模型 |
| 文档解析 | unstructured、PyMuPDF、Tika、飞书开放 API | 非结构化文档用多个解析器组合 |
| Rerank 模型 | BGE-Reranker、Cohere Rerank | 多路召回后必须加精排,能明显提升准确率 |
| 编排框架 | LangChain、LlamaIndex、Dify、自研 Pipeline | 前端团队建议先自研,理解链路后再引框架 |
需要说明的是,不存在“最好的组合”,只有最匹配团队维护能力的组合。前端团队如果对 Python 不熟,可以选 Node.js 实现解析与检索,再配合独立向量库服务。
3.3 为什么前端工程师要理解全链路
很多前端项目把 RAG 当“黑盒 API”调用,结果就是:用户问了一个问题,回答质量差,但前端查不了问题,只能干瞪眼。
如果你理解链路,排查时就有一条清晰的路径:
- 文档有没有成功解析?
- 切片有没有切碎或切错?
- 检索有没有召回相关片段?
- Rerank 之后是不是把正确片段排后面了?
- LLM 是否被无关上下文干扰?
每个环节都可能出问题,前端接入只是“最后一公里”。
4. 环境准备与前置条件
4.1 环境检查清单
无论用 Docker 还是裸机部署,先检查这五项:
| 检查项 | 说明 |
|---|---|
| 操作系统 | Linux/macOS/Windows 均可,生产环境建议 Linux |
| Docker / Docker Compose | 跑向量库和基础服务最方便 |
| Node.js | 推荐 18+,前端工程和 Node API 服务需要 |
| Python | 如果自研解析和向量化,推荐 3.9+ |
| 磁盘空间 | 取决于文档数量和 Embedding 模型体积,预留 10G 以上比较稳妥 |
| 内存 | 32G 内存跑向量库 + 服务端比较舒适;8G 也可以跑 Demo,但要控制并发 |
4.2 端口规划
常见服务端口容易冲突,建议提前固定:
| 服务 | 默认端口 |
|---|---|
| 前端开发服务器 | 5173 / 3000 |
| API 服务 | 8000 / 3001 |
| Elasticsearch | 9200 |
| Milvus | 19530 |
| 知识库管理后台 | 8080 |
如果端口被占用,优先改 API 服务和前端端口,不要改 ES 的节点通信端口,容易踩坑。
4.3 Docker Compose 启动示例
下面是一个通用编排示例,实际服务镜像和版本需要按项目替换:
version: "3.8" services: api: build: ./server ports: - "8000:8000" environment: - VECTOR_DB_HOST=elasticsearch - LLM_API_KEY=${LLM_API_KEY} depends_on: - elasticsearch elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.10.0 environment: - discovery.type=single-node - xpack.security.enabled=false ports: - "9200:9200" volumes: - es_data:/usr/share/elasticsearch/data frontend: build: ./web ports: - "5173:5173" depends_on: - api volumes: es_data:这只是模板,跑之前需要确认镜像版本和 Docker 环境是否匹配。
5. 文档接入与解析
这一阶段的目标是把飞书、语雀、Wiki、PDF、Word 里的内容变成“干净的纯文本”。
5.1 文档来源接入
类飞书场景通常走开放平台 API,拿文档内容。通用步骤:
- 创建企业自建应用,申请文档读写权限。
- 通过 API 获取文档 Token 列表。
- 按 Token 拉取文档内容,转成 Markdown 或纯文本。
- 将文档元信息(标题、作者、更新时间、URL)一并存储。
飞书文档的富文本结构比较复杂:标题层级、表格、代码块、图片说明都要提取。如果解析不干净,后续切片质量会直线下降。
5.2 解析注意事项
| 文件类型 | 解析方案 | 常见坑 |
|---|---|---|
| 飞书/语雀文档 | 开放平台 API 导出 Markdown | 表格结构丢失 |
| PyMuPDF / pdfplumber | 扫描版 PDF 需要 OCR | |
| Word | python-docx / mammoth | 图片和嵌入表格丢失 |
| Markdown 文件 | 直接读取 | 代码块被错误截断 |
解析完成后,建议统一转成 Markdown 中间格式,后续切片和展示都方便。
5.3 解析结果质量控制
解析不是“跑通就行”,要留一个质量抽检的环节。可以在管理后台添加“文档预览”页面,展示解析后的 Markdown 原文,方便确认图片、表格和标题层级是否正确。
6. 文本切片策略
切片是整个 RAG 链路中最容易被低估的环节。切片切得不好,检索结果就会“答非所问”。
6.1 常见的切片方式
| 方式 | 做法 | 适用场景 |
|---|---|---|
| 固定长度切片 | 按 200/400/800 Token 切,带重叠 | 通用、简单 |
| 标题层级切片 | 按 Markdown 标题切分 | 文档结构清晰时效果好 |
| 段落语义切片 | 按段落边界切分 | 叙事类文档 |
| 父子切片 | 小切片用于检索,大切片用于生成 | 兼顾召回准确率和上下文完整度 |
从实践来看,先按 Markdown 标题层级切,再对超长段落做二次切分是稳健做法。可以设计一个通用的切片配置:
{ "chunk_size": 500, "chunk_overlap": 100, "split_by": "heading", "min_chunk_length": 50 }其中chunk_overlap是为了避免把一句话从中间截断,主题跳跃。
6.2 切片质量自检
切片完成后,可以用三个问题自检:
- 每个切片是否有完整语义?
- 每个切片的长度是否均衡?
- 引用出处能否定位到原文?
一个比较实用的技巧是:把切片结果导出成 Markdown 文件,人工翻阅一遍。如果切片把“背景介绍”和“实施方案”切进同一段,后面问答一定出问题。
6.3 是否需要“ES 库与知识库同步”
第一次做知识库的人常问:ES 索引和源文档库要不要同步?答案是:要做增量同步,但不建议在源文档编辑时同步写索引。
更合理的做法是:源文档库是“事实源”,索引库是“派生数据”。文档更新后,标记为“待索引”,由后台任务异步消费,重新解析、切片、向量化、写入索引。这样可以避免源文档服务被检索任务拖垮。
7. 向量化与混合检索实现
7.1 向量化
切片完成后,需要调用 Embedding 模型,把文本变成向量。这一步有两个关键点:
- 中文场景下,Embedding 模型的选择对效果影响非常大。
- 向量维度不用太纠结,768/1024 是常见选择,重点是模型对中文长文的支持。
通用 Python 示例(需要替换成实际模型和调用方式):
from sentence_transformers import SentenceTransformer model = SentenceTransformer("BAAI/bge-large-zh-v1.5", device="cpu") chunks = ["企业报销制度", "差旅费用报销流程", "采购合同审批流程"] embeddings = model.encode(chunks, normalize_embeddings=True) print(embeddings.shape) # (3, 1024)7.2 向量检索 + BM25 混合检索
只用向量检索是不够的。问题在于:向量检索擅长语义匹配,但对“精确关键词”“产品型号”“编号类问题”不敏感。比如用户搜“BUG-2024-001”,如果有精确的倒排索引,能直接命中;但向量检索可能找出一堆语义相近的无关内容。
所以生产系统通常采用多路召回:
- 向量检索:召回 Top 50 语义相近片段。
- BM25 关键词检索:召回 Top 50 含关键词的片段。
- 合并去重后进入 Rerank 精排。
用 Elasticsearch 实现时,核心思路是使用bool查询,同时执行knn和match,再用 RRF(Reciprocal Rank Fusion)或者脚本评分。下面是通用结构:
{ "query": { "bool": { "should": [ { "match": { "content": "报销流程" } }, { "knn": { "embedding": { "vector": [0.1, 0.2, 0.3], "k": 50 } } } ] } } }需要说明的是,knn查询语法在不同 ES 版本中不一致,实际要以你使用的 ES 版本为准。
7.3 Rerank 精排
多路召回之后,向量相关性和 BM25 相关性很难直接对比。直接拼接排序,效果会很飘。所以需要一个 Rerank 模型,对候选片段与用户问题做交叉编码打分,重新排序。
推荐链路是:
用户问题 -> 向量召回 Top 50 + BM25 召回 Top 50 -> 合并去重 -> Rerank 打分 -> 取 Top 5 -> 装配 Prompt -> LLM 生成回答Rerank 这一步不是可选项。没有 Rerank 时,Top 5 里经常会混进不相关片段,LLM 被误导后就会“幻觉”。加了 Rerank 后,效果提升非常明显。
8. AI Agent 与智能问答链路
8.1 Agent 在这里起什么作用
RAG 回答的基础链路是“检索 -> 生成”。引入 AI Agent 之后,系统可以做更多事情:
- 判断问题是否需要检索:闲聊直接回复,不查知识库。
- 多轮对话改写:把“它怎么申请”改写为“差旅费怎么申请”。
- 多路检索规划:一次提问同时查文档库、表格库和外部资料。
- 工具调用:查询后调用内部 API 获取结构化数据。
但是引入 Agent 也要付出代价:链路变长、延迟增加、可控性下降。建议一开始先跑通“简单 RAG”,再逐步增加 Agent 能力。
8.2 普通 RAG 与 Agentic RAG 的取舍
| 维度 | 普通 RAG | Agentic RAG |
|---|---|---|
| 延迟 | 低 | 高,多轮调用 |
| 可控性 | 高 | 中,需要约束工具调用 |
| 问题理解 | 一般 | 强,可多轮改写 |
| 实现复杂度 | 低 | 高 |
| 适合场景 | 固定知识库问答 | 复杂查询、多数据源、任务编排 |
从项目稳定性的角度考虑,第一版建议做普通 RAG,把工具调用和规划能力放到第二阶段。
8.3 Prompt 组装示例
生成回答前,要把检索到的片段组装成 Prompt。通用模板如下:
你是一个企业知识库问答助手。请根据以下参考资料回答问题。 参考资料: <context> [{source: "产品手册.pdf", content: "导出报表支持 CSV 和 Excel 格式。"}] </context> 问题:导出报表支持哪些格式? 要求: 1. 如果参考资料中没有答案,请明确说明“未在知识库中找到相关内容”。 2. 回答末尾附上引用来源。 3. 不要编造知识库中不存在的信息。这个模板虽然简单,但“未找到就承认”和“引用来源”这两条非常关键,能明显减少幻觉的扩散。
8.4 引用溯源设计
回答中需要把片段映射回原始文档和位置,前端才能展示“来源链接”。数据模型至少包含:
{ "answer": "导出报表支持 CSV 和 Excel 格式。", "references": [ { "source": "产品手册.pdf", "url": "https://wiki.example.com/xxx", "chunk_text": "导出报表支持 CSV 和 Excel 格式。", "score": 0.87 } ] }前端拿到references数组后,可以渲染成“参考来源”折叠面板。
9. 前端集成与交互设计
9.1 类飞书知识库的前端页面结构
一个相对完整的知识库前端至少包含这几个模块:
- 文档列表页:浏览知识库中的所有文档,支持搜索和分类筛选。
- 文档详情页:展示解析后的 Markdown 内容,提供“对此文档提问”入口。
- 全局问答页:跨文档问答,支持多轮对话。
- 管理后台页:文档上传、解析状态、索引状态、批量操作。
9.2 前端如何调用检索与问答接口
问答接口通常是一个异步流式接口,前端需要处理流式响应,实现“打字机”效果。
通用 API 设计如下:
POST /api/chat { "question": "报销流程是什么", "session_id": "abc-123", "document_ids": ["doc_001", "doc_002"] }返回时通过 SSE(Server-Sent Events)或 WebSocket 流式输出:
data: {"type": "start"} data: {"type": "token", "content": "根据"} data: {"type": "token", "content": "知识库"} data: {"type": "reference", "references": [...]} data: {"type": "end"}前端用原生EventSource或 PostMessage 方式监听即可。
9.3 代码块与 Markdown 渲染
知识库内容本身就是富文本,渲染时建议统一用react-markdown或同类库。渲染时要注意:
- 表格样式要单独处理,默认 Markdown 表格在移动端容易溢出。
- 代码块要高亮,方便阅读技术文档。
- 图片懒加载,大图等比缩放。
9.4 状态管理
问答页是典型的“流式 + 多轮 + 引用”场景,建议状态设计如下:
interface ChatMessage { id: string; role: "user" | "assistant"; content: string; references: Reference[]; status: "pending" | "streaming" | "done" | "error"; }前端要处理流式过程中的中间状态,避免出现“消息闪一下消失”的体验问题。
10. 接口 API 与批量任务
10.1 API 模块划分
一个可维护的后端接口,通常划分为:
| 模块 | 接口 | 说明 |
|---|---|---|
| 文档管理 | POST /api/documents | 上传/导入文档 |
| 索引管理 | POST /api/documents/{id}/index | 触发单文档索引 |
| 检索 | POST /api/search | 纯检索,不生成回答 |
| 问答 | POST /api/chat | 检索 + LLM 生成 |
| 批量任务 | POST /api/batch/index | 批量索引任务 |
10.2 curl 调用问答接口示例
curl -X POST "http://127.0.0.1:8000/api/chat" \ -H "Content-Type: application/json" \ -d '{ "question": "报销流程是什么", "session_id": "abc-123" }'10.3 Python 调用示例
import requests url = "http://127.0.0.1:8000/api/chat" payload = { "question": "报销流程是什么", "session_id": "test-session" } response = requests.post(url, json=payload, timeout=60) print(response.json())10.4 批量任务设计
文档导入是典型的异步场景,不能同步处理大数据量的切片和向量化。建议的批量任务实现模式:
- 用户上传文档后,立即返回“已接收”。
- 后台任务解析文档。
- 状态流转:
pending -> parsing -> chunking -> embedding -> indexing -> done。 - 失败自动重试 2-3 次,超过次数标记为
failed。 - 提供一个任务列表接口,前端轮询展示进度。
任务状态字段示例如下:
{ "task_id": "task_001", "document_id": "doc_001", "status": "embedding", "progress": 0.6, "error_message": null }批量任务的要点是“可观测”,前端能实时看到每个文档处于什么阶段,而不是一个永远转圈的加载提示。
11. 资源占用与性能观察
从工程实际角度看,这套系统的性能瓶颈通常不在 LLM,而在文档解析、向量化、索引写入和检索延迟这四个环节。
11.1 资源占用观察方法
- 本地部署时,用 Docker 监控内存和 CPU:
docker stats - 观察 API 服务的响应时间:在关键节点打日志或使用 APM 工具
- 向量库索引构建时,持续观察 CPU 和磁盘 IO
11.2 影响检索性能的因素
| 因素 | 影响 |
|---|---|
| 文档数量与切片数量 | 切片越多,向量检索耗时可接受,但不建议单索引无限膨胀 |
| 召回数量 | 召回 50 和召回 500 的耗时差异明显 |
| Rerank 候选数 | 候选越多,延迟越高,建议控制在 20 以内 |
| LLM 生成长度 | 生成长度越长,首字延迟越明显 |
11.3 降低资源占用的策略
- 文档解析用独立工作进程,避免阻塞 API 服务。
- Embedding 支持批量推理,一次处理 32/64 条,而不是逐条调用。
- 向量索引可以设置合适的 HNSW 参数,在召回率和内存之间平衡。
- LLM 开启流式输出,减少用户体感等待时间。
- 如果不需要实时更新,索引构建可以放到深夜批量执行。
11.4 前端性能优化
前端层主要是渲染和流式处理:
- Markdown 渲染开启缓存,相同内容不重复解析。
- 引用列表折叠展示,避免长回答阻塞页面渲染。
- 长会话历史做虚拟滚动,避免 DOM 节点过多。
- 文档列表分页或按目录懒加载。
12. 常见问题与排查方法
以下是这套链路里高频出现的问题,按现象、原因、排查方式和解决方案整理。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 文档解析后内容为空 | 无权限 / API 未授权 | 检查接口返回和日志 | 确认应用权限,重新授权 |
| 搜索能搜到,但问答答非所问 | 切片质量差或 Rerank 缺失 | 导出切片人工检查 | 调整切片策略,加入 Rerank 精排 |
| 向量检索召回结果差 | Embedding 模型不适配 | 替换模型对比测试 | 用中文场景模型替换,如 BGE/M3E |
| 问答结果没有引用来源 | Prompt 未要求 / 引用字段丢失 | 检查 API 返回结构 | 在 Prompt 中强制要求引用来源 |
| 批量导入卡住 | 任务队列未消费失败 | 查看任务状态 API | 增加失败重试和超时机制 |
| 索引更新不生效 | 增量同步未触发 | 检查文档更新标记 | 文档保存后标记待索引,异步消费 |
| 问答延迟过高 | LLM 生成过长 + 检索链路慢 | 查看分段耗时 | 限制生成长度、减少召回和 Rerank 候选数 |
| 端口冲突 | 多个服务占用同一端口 | lsof -i:端口查看进程 | 修改服务启动配置,更换端口 |
| 多租户数据串场 | 未做租户隔离过滤 | 检索日志中检查返回文档 ID | 在检索查询中加入租户 ID 过滤条件 |
13. 最佳实践与使用建议
这套链路跑起来不难,跑得稳需要遵循一些工程化经验。
13.1 先从最小链路跑通
不要一开始就追求完整的 Agent 规划能力。第一次尝试建议:
- 准备 10 到 20 份格式统一的文档。
- 用标题切片,手动检查切片质量。
- 调用两个 Embedding 模型对比检索效果。
- 直接评估 Rerank 前的 Top 5 和 Rerank 后的 Top 5。
- 确认效果稳定后,再补批量导入、权限、多租户等能力。
13.2 数据与代码分目录管理
建议目录结构:
project/ ├── docs/ # 原始文档备份 ├── chunks/ # 切片结果导出,用于人工质检 ├── embeddings/ # 向量缓存或索引备份 ├── outputs/ # 问答日志和测试结果 ├── server/ # API 服务 ├── web/ # 前端工程 └── docker/ # 编排配置13.3 检索结果必须可回放
问答接口的请求和响应要落日志,尤其是references字段。这样可以回放“为什么模型给出这个回答”,是排查幻觉问题的关键。
13.4 发布前做效果复核
千万不要把 RAG 问答直接对全员开放。建议先找一批种子用户试用,收集三类信息:
- 回答正确率。
- 引用来源是否真实匹配。
- 用户提问中高频出现的“知识库覆盖不到”的问题。
根据这些问题反推缺哪些文档,或者哪个环节需要优化。
13.5 考虑 Skill 还是 Tool
在 Agent 场景里,经常要决策“把某个能力做成 Skill 还是 Tool”。以“查报销制度”为例:
- Tool 更适合单次执行、参数明确的调用,比如“调用报销查询 API”。
- Skill 更适合多步骤、需要状态记忆和拆解流程的复杂任务,比如“帮我写一份出差申请并在最后提交审批”。
第一版建议全部做成 Tool,简单直接;等出现了需要多步推理的明确场景,再升级为 Skill。不要为了“Agent 化”而过度设计。
14. 总结与下一步
这套全链路方案值得前端团队认真做一次,核心价值在于:你不需要依赖黑盒产品,也能为自己企业搭建一套可控、可扩展、可追溯的知识库问答系统。
最先要验证的功能不是问答,而是“检索质量”——先用检索接口看 Top 10 是不是相关,再接入 LLM 生成。最容易踩的坑也是检索质量,而不是代码本身。
下一步可以按这个方向扩展:
- 引入更细粒度的权限体系,按文档目录和用户角色过滤检索结果。
- 做文档级和切片级的引用评分体系,低分片段不入 Prompt。
- 在问答链路中加入任务编排,对接工单系统、CRM 等内部工具。
- 建立评估集(例如 50 条标准问答),每次模型或切片策略变更后回归测试。
如果你的目标是自己动手搭一套企业级知识库,这篇文章可以作为路线图:先理清文档解析和切片,再跑通向量加 BM25 的混合检索,最后接入 Agent 问答和前端的流式交互。建议收藏备用,等真正动手时再按章节对照实现。