拓十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

【AI大模型教程】手搓一个与多PPT文档对话的RAG引擎,并封装MCP Server,建议收藏!!

【AI大模型教程】手搓一个与多PPT文档对话的RAG引擎,并封装MCP Server,建议收藏!! 1. 多PPT文档对话的真实痛点与场景拆解多PPT文档对话这件事听起来像是把文件丢给大模型就能问但真动手做会发现坑比想象中多。PPT不像纯文本那样信息直给也不像表格有固定结构它的信息散落在标题、正文、图表、流程图、备注里甚至同一页里文字和图形表达的是互补关系。你直接抽文字图表里的趋势、对比、占比全丢了你只截图喂模型又没法做语义检索问第三季度哪条业务线增速最快这种问题模型只能靠猜。我试过的场景是这样的手头有十几份行业分析PPT、产品路线图PPT、季度复盘PPT想做一个能跨文档问答的知识库。需求很具体——问2024年AIGC在营销领域的落地案例有哪些系统要能定位到具体是哪份PPT的哪一页最好还能把那一页的截图一起返回方便核对。这就要求RAG引擎不仅能检索文本还要保留页面图像作为可溯源证据。所以这篇要交付的是一条完整链路PPT解析成页面图像视觉模型把图像转成结构化Markdown嵌入向量库做语义索引再封装成MCP Server暴露给客户端或Agent调用。适合谁适合已经了解RAG基本概念、想动手做一个能跑通的多文档对话系统的开发者也适合想把内部PPT知识库接进AI工作流的团队。下面从环境准备开始一步步把这条链路搭起来。2. TaoToken前置统一Key与API通道接入在动手写RAG引擎之前先把模型调用通道理顺。多PPT文档对话会频繁调用两类模型视觉模型解析PPT页面图像和嵌入模型生成向量。如果每个模型都单独配Key、单独处理计费调试阶段会非常碎。TaoToken的做法是提供一个统一的API通道你用同一个Key就能调用不同模型省去多平台切换的麻烦。接入步骤不复杂。先到官网注册并进入控制台在API Keys页面创建一个Key。这个Key就是你后续所有模型调用的凭证。创建时建议给它起个能识别的名字比如ppt-rag-dev方便区分开发和生产。拿到Key之后配置两个地址官网入口是https://taotoken.net/?utm_sourcetaotoken_aicg_blog_endutm_mediumcsdnutm_campaignrewriteutm_contentAPI基地址是https://taotoken.net/api。注意API地址后面不加UTM参数保持干净。模型对话、Coding Plan、控制台、API Keys、接入文档这些入口都可以从官网导航进入按需跳转。注意Key不要硬编码在源码里提交到仓库。建议用环境变量或本地配置文件管理后面配置文件骨架里会给出示例。如果你后续要做长期编码或Agent类应用可以关注Coding Plan如果只是验证模型连通性直接用模型对话页面测一下就行。接入文档里有各语言SDK的调用示例遇到参数问题优先查文档。3. 可复制配置settings.json与config.toml骨架这一节给出可直接复制的配置文件骨架。整个项目分两块配置一块是MCP Server的客户端配置settings.json一块是RAG引擎自身的配置config.toml。先看MCP Server的配置它决定了客户端怎么启动和连接你的Server。{ mcpServers: { ppt-rag-server: { command: python, args: [-m, ppt_rag.server], env: { TAOTOKEN_API_KEY: 你的Key, TAOTOKEN_BASE_URL: https://taotoken.net/api, PPT_RAG_CONFIG: ./config.toml } } } }这段配置的意思是客户端通过python -m ppt_rag.server启动你的MCP Server启动时注入环境变量其中TAOTOKEN_API_KEY和TAOTOKEN_BASE_URL就是上一步拿到的凭证和地址。PPT_RAG_CONFIG指向RAG引擎的配置文件路径。再看RAG引擎的config.toml[llm] provider taotoken base_url https://taotoken.net/api vision_model doubao-vision embedding_model openai-embedding api_key_env TAOTOKEN_API_KEY timeout 120 max_retries 3 [vector_store] type chroma persist_dir ./data/chroma collection_name ppt_slides [index] top_k 5 cache_dir ./data/cache enable_cache true [ppt] libreoffice_path /usr/bin/libreoffice image_dpi 150 parse_prompt 用中文提取图片中的详细信息并使用Markdown格式化输出。 对于其中的文字使用OCR识别并尽量保持原格式输出。 对于表格与统计图表选择表格结合文字的方式进行描述。 对于图形、流程图等视觉元素用文字详细描述其内容和布局。 合理排版使得输出内容清晰易懂。 这里有几个参数值得说明。vision_model负责把PPT页面图像解析成Markdownembedding_model负责把文本块转成向量。top_k控制检索召回数量太小可能漏掉关键页太大则引入噪声5是个比较稳的起点。image_dpi影响页面截图清晰度150在清晰度和文件体积之间比较平衡。parse_prompt就是视觉模型的解析提示词直接决定解析质量后面排障部分会讲怎么调。配置文件放好后目录结构建议这样组织ppt-rag/ ├── config.toml ├── settings.json ├── ppt_rag/ │ ├── __init__.py │ ├── server.py │ ├── engine.py │ └── ppt_utils.py └── data/ ├── cache/ └── chroma/server.py是MCP Server入口engine.py是RAG引擎核心ppt_utils.py封装PPT转图像的逻辑。这样分层的好处是RAG引擎可以单独测试不用每次都启动MCP Server。4. 验证请求MCP Server启动与多文档问答配置就绪后先单独验证RAG引擎再启动MCP Server。RAG引擎的核心流程分索引和生成两段。索引阶段先把PPT转成PDF再转成页面图像然后逐页调用视觉模型解析成Markdown最后构造文本节点并嵌入向量库。PPT转图像这一步依赖LibreOffice和Pdfium。LibreOffice负责把.pptx转成.pdfPdfium负责把PDF每页渲染成PNG。命令层面大致是这样libreoffice --headless --convert-to pdf --outdir ./data/tmp ./docs/report.pptx转换完成后用Pdfium逐页导出图像每页对应一张PNG文件名带上页码。这一步封装在ppt_utils.py里核心逻辑是遍历PDF页数、按DPI渲染、保存到缓存目录。接下来是解析。对每张页面图像调用视觉模型传入parse_prompt拿到Markdown格式的解析结果。为了提高速度用异步并行发起请求import asyncio async def parse_all_pages(image_paths, llm): tasks [llm.parse_image(path, promptPARSE_PROMPT) for path in image_paths] return await asyncio.gather(*tasks)解析结果拿到后构造文本节点。每个节点除了文本内容还要带元数据源文件路径、文件ID用文件内容MD5生成、页码、对应图像路径。文件ID很关键后续删除索引、按文档过滤检索都靠它。from llama_index.core.schema import TextNode import hashlib def build_nodes(ppt_path, image_paths, parsed_contents): doc_id hashlib.md5(open(ppt_path, rb).read()).hexdigest() nodes [] for i, (img, content) in enumerate(zip(image_paths, parsed_contents)): node TextNode( textcontent, metadata{ source: ppt_path, source_file_id: doc_id, doc_name: ppt_path.split(/)[-1], page_num: i 1, image_path: img, doc_type: ppt_slide, }, ) nodes.append(node) return nodes节点构造好后插入向量索引self._index.insert_nodes(nodes) self._persist_index()生成阶段就是检索加回答。用户提问后检索器召回top_k个节点取出节点文本和对应图像路径组装成提示词连同图像一起发给视觉模型。提示词里要求模型说明答案来自Markdown还是图像并给出参考页码和图像路径这样回答可溯源。from llama_index.core.vector_stores import MetadataFilters, MetadataFilter, FilterOperator filters MetadataFilters( filters[MetadataFilter(keysource_file_id, valuedoc_id, operatorFilterOperator.EQ)] ) retriever VectorIndexRetriever(indexself._index, similarity_top_kself.top_k, filtersfilters)带过滤的检索器在多文档场景下很有用问某个特定PPT时先按文件ID过滤再语义检索精度和速度都更好。RAG引擎单独测试通过后启动MCP Server。Server暴露四个工具index_status查索引状态add_ppt添加文档chat_with_ppt对话查询delete_ppt删除文档。启动命令python -m ppt_rag.server --transport sse --port 8080启动后用MCP测试客户端连接先调add_ppt添加两份PPT再调index_status确认索引信息最后调chat_with_ppt提问。比如问这份报告里AIGC在营销领域的案例有哪些返回结果里应该包含答案文本、参考页码和图像路径。如果返回的页码和图像路径对得上说明整条链路跑通了。5. 本篇常见错排查LibreOffice转换失败或输出为空。最常见的原因是路径含中文或空格或者LibreOffice没装全。先确认libreoffice --version能正常输出转换时用绝对路径输出目录提前创建好。如果PPT里有特殊字体转换后可能排版错位这属于正常现象不影响视觉模型理解大意。视觉模型解析结果质量差。解析质量高度依赖parse_prompt。如果发现表格被解析成流水账可以在提示词里强调表格必须用Markdown表格输出保留行列对应关系。如果流程图描述太简略加上流程图需说明节点顺序和分支条件。另外image_dpi太低会导致文字模糊调到150以上再试。嵌入向量维度不匹配。换嵌入模型时容易遇到这个问题。向量库里的collection一旦建立维度就固定了。换模型后要么新建collection要么清空重建。建议在config.toml里把嵌入模型名和collection名关联起来换模型时同步换collection名。检索召回不准。先看top_k是不是太小调到8或10试试。如果还是不准检查节点文本是不是太长或太短。一页PPT解析出的Markdown如果超过2000字可以考虑按段落切分如果只有几十字可能是解析失败回去查解析日志。MCP Server启动后客户端连不上。先确认端口没被占用再检查settings.json里的command和args路径是否正确。如果用的是SSE模式客户端配置里的URL要和Server启动的端口一致。环境变量没注入也会导致启动失败可以在Server入口打印一下TAOTOKEN_API_KEY是否存在注意别把Key本身打出来。索引缓存导致更新不生效。修改了PPT内容但检索结果还是旧的是因为缓存没失效。add_ppt时传force_reprocessTrue强制重新解析或者手动清空cache_dir。生产环境建议用文件修改时间做缓存键内容变了自动重新处理。6. 语义一致CTA按场景选择入口排障和接入相关的问题优先看API Keys和接入文档。API Keys页面管理你的调用凭证接入文档里有各语言SDK的详细参数说明和错误码解释遇到401、429这类报错先查文档。验证模型连通性或者临时测一下视觉模型效果直接用模型对话入口不用写代码就能试提示词。如果你打算把这个PPT RAG引擎长期用起来或者要接进Agent做更复杂的任务关注Coding Plan它在长期编码和Agent场景下有更合适的额度与通道配置。整条链路跑通后你可以继续优化的方向不少给每页PPT生成摘要或假设性问题一起参与向量化提高召回覆盖对首次召回结果做相关性评估不够就做查询转换加Rerank针对概括性问题增加SummaryIndex在客户端引入Agentic RAG让Agent在一次查询里多次调用RAG工具。这些都是在当前可跑通版本上做增量不影响主链路。先把基础版跑稳再按需叠加。
返回列表