# ran — full documentation corpus # https://ran.chaxus.com • auto-generated at build time ================================================================ # https://ran.chaxus.com/cn/src/article/ai/ ================================================================ --- description: 'AI Agent 的工作原理:Prompt、角色、工具与上下文——LLM 智能体背后的基础构件。' --- # Prompt ## System Prompt 用来描述 AI 的角色,性格,背景知识,语气等,总之只要不是用户直接说出来的内容,都可以放到 System Prompt 里面。 ## User Prompt 用户直接发送给 AI 的内容。 每次用户发送 User Prompt 的时候,系统会自动把 System Prompt 也一起发起 AI 模型,这样就会显得非常自然。 但到了这个程度,AI 也只是一个聊天机器人,只能问问题,然后 AI 模型去回复问题。 那么如何让 AI 模型去自动的执行一些任务呢?这时候,Agent 就出现了。 # Agent 比如期望用 AI 来管理一些文件,那么得先写好一些文件管理函数: - list_file 列出目录下的所有文件 - read_file 读取文件的内容 然后把这些函数及其他们的功能描述,使用方法,注册到 Agent 中。 Agent 会根据这些信息,生成一个 System Prompt,告诉 AI 模型,用户给了哪些工具,能够做什么。 以及 AI 使用他们应该返回什么样的格式。 当用户在发送 User Prompt 的时候,连同 System Prompt,一起发送给 AI 模型。 如果 AI 模型足够聪明,就会返回一个格式:需要调用某个函数,比如 list_file 给 Agent。 Agent 解析之后,就会调用对应的函数,然后把结果再给 AI,AI 再根据 Agent 返回的结果,再决定下一步去做什么操作。 这个过程就这样反复,直到任务完成为止。 最后,把这种在 AI 模型,提供的工具 (list_file,read_file),和最终用户之间传话的工具,就叫 AI Agent。 这些提供给 AI Agent 调用的函数,或者服务,就叫 Agent Tool。 但这样可能存在一个问题,虽然我们在 System Prompt 里面描述和规定了 AI 应该用什么格式进行返回,但 AI 模型说到底是一个概率模型。还是有可能返回的格式不对。 一般的 AI Agent 会判断,如果 AI 模型返回的格式不对,会自动进行重试。面对这种场景,FunctionCalling 就出现了。 # FunctionCalling 核心作用就是统一格式,规范描述,比如上面的 System Prompt,使用自然语言描述的,AI 看的懂就行。 FunctionCalling 则对这些描述进行了标准化,每一个 Tool 都用一个 json 来定义,比如: ```json { "name": "list_file", "desc": "列出目录下的所有文件", "params": { "path": "str" } } ``` 然后这些字段也从 System Prompt 中剥离了出来,这样,所有的工具定义,描述,和返回都放在了相同的地方。这样 AI 使用工具时,也会遵循相同 json 格式进行回复。于是人们就可以更加有针对性的训练 AI 模型。甚至在这种情况下,如果 AI 依然返回了错误的回复,因为这种返回的格式是固定的,AI 服务端自己就能检测到,然后进行重试。这样用户根本感觉不到,降低了用户端的开发难度,同时也节约了重试的 token 成本。 但 FunctionCalling 也有自己的问题,那就是没有统一的标准。目前每家大厂的 API 格式都不太一样。甚至还有一些模型,不支持 FunctionCalling,所以,要写一个通用的 AI Agent,还是挺麻烦的。 因此 FunctionCalling 和 System Prompt 这两种方式,在市面上都是并存的。 以上都是 AI Agent 如何和 AI 模型之间的通信,那么 Agent Tool 和 Agent 是怎么通信的呢。 # MCP 一般都是把 Agent 和 Agent Tool 写到一个程序里,这样就能直接调用,搞定。 但后来人们发现,有些 Agent Tool 还是挺通用的,比如浏览网页的功能,每个 Agent 都需要,那么总不能在每个 Agent 里面都拷贝一份吧。于是,就想到来一个办法: 把 Tool 做成一个服务,进行统一的托管。让所有的 Agent 都来调用,这个过程,就是 MCP。 MCP 是一个通信协议,专门用来规范 Agent 和 Tool 服务之间是怎么交互的。 运行 Tool 的服务叫 MCP Server,调用它的 Agent 叫 MCP Client。 MCP 规定了 MCP Server 如何和 MCP Client 进行通信,以及 MCP Server 要提供哪些接口,比如查询 MCP Server 有哪些接口,接口的功能,描述,如何使用。除了普通的 Tool 这种函数的调用形式。 MCP 也可以直接提供数据,提供文件读取的服务 Resources,或者为 Agent 提供提示词的模版叫 Prompt。 MCP Server 既可以和 Agent 跑在同一台机器上,通过标准输入输出进行通信,也可以部署在网络上,通过 http 进行通信。 虽然 MCP 是为了 AI 而定制出来的标准,但实际上,MCP 本身和 AI 模型没有关系,它并不关心 Agent 用的是哪个模型,MCP 只负责帮 Agent 管理工具,资源和提示词。 最后,总结一下全部的流程: 用户 --> 发送消息给 Agent,Agent(MCP Client)调用 MCP Server 的函数,并把结果一起给 AI 模型。 AI 模型通过 FunctionCalling 或者普通回复的方式,产生调用 Tool 的请求,Agent 收到这个请求后,通过 MCP 协议去调用 MCP Server 的工具。将结果返回给 Agent,Agent 再把结果给 AI 模型,AI 模型再把最终结果,返回个 Agent,Agent 再发送给用户。 # 写一个 Agent Agent 是一个在用户,AI 模型,工具函数之间进行传话的程序。 ```mermaid sequenceDiagram Tools->>Agent: 将 Tools 里的函数注册到 Agent,让 Agent 知道有哪些工具函数可以使用 User->>Agent: 将问题发送给 Agent Agent->>AI: 用户的问题是 User Prompt,还有将工具函数信息通过 System Prompt 或者 FunctionCalling 的方式告诉 AI 模型 AI->>Agent: 思考用户的问题,并返回响应的指令,告诉 Agent 应该执行 Tools 中的哪些方法。 Agent->>Tools: 使用 Tools 中的方法,并获得结果 Agent->>AI: 将结果告诉给 AI 模型,AI 模型继续分析问题,看是否需要继续调用 Agent,直到结束。 AI->>Agent: 告诉 Agent 思考过程结束,并将结果发送给 Agent。 Agent->>User: 将最终结果给用户输出。 ``` 简单来说,就是下面这种方式 ```mermaid graph TD customer[用户] --> Agent Agent --> AI Tools[工具函数] --> Agent Agent --> customer[用户] Agent --> Tools[工具函数] Agent --> AI ``` ================================================================ # https://ran.chaxus.com/cn/src/article/doc_preview ================================================================ --- title: 'docx / pptx / xlsx / pdf 文件预览方案' description: '最全的浏览器端文件预览方案总结:docx、pptx、xlsx(Excel)、pdf,含取舍与踩坑。' ---

最全的 docx,pptx,xlsx(excel),pdf 文件预览方案总结

最近遇到了文件预览的需求,但一搜索发现,这还不是一个简单的功能。于是又去查询了很多资料,调研了一些方案,也踩了好多坑。最后总结方案如下 1. 花钱解决 (使用市面上现有的文件预览服务) 1. 微软 2. google 3. 阿里云 IMM 4. XDOC 5. Office Web 365 6. wps 开放平台 2. 前端方案 1. pptx 的预览方案 2. pdf 的预览方案 3. docx 的预览方案 4. xlsx(excel) 的预览方案 5. 前端预览方案总结 3. 服务端方案 1. openOffice 2. kkFileView 3. onlyOffice 如果有其他人也遇到了同样的问题,有了这篇文章,希望能更方便的解决。 基本涵盖了所有解决方案。因此,标题写上 **最全** 的文件预览方案调研总结,应该不为过吧。 ## 一:市面上现有的文件预览服务 ### 1.微软 `docx`,`pptx`,`xlsx`可以说是`office`三件套,那自然得看一下 **微软官方** 提供的文件预览服务。使用方法特别简单,只需要将文件链接,拼接到参数后面即可。 记得`encodeURL` ```js https://view.officeapps.live.com/op/view.aspx?src=${encodeURIComponent(url)} ``` #### (1).PPTX 预览效果: ![image.png](../../../assets/article/docPreview/ms_ppt.webp) - 优点:还原度很高,功能很丰富,可以选择翻页,甚至支持点击播放动画。 - 缺点:不知道是不是墙的原因,加载稍慢。 #### (2).Excel 预览效果: ![image.png](../../../assets/article/docPreview/ms_excel.webp) #### (3).Doxc 预览效果 ![image.png](../../../assets/article/docPreview/ms_word.webp) #### (4).PDF 预览效果 这个我测试没有成功,返回了一个错误,其他人可以试试。 ![image.png](../../../assets/article/docPreview/ms_file_not.webp) #### (5).总的来说 对于`docx`,`pptx`,`xlsx`都有较好的支持,`pdf`不行。 还有一个坑点是:这个服务是否稳定,有什么限制,是否收费,都查不到一个定论。在`office`官方网站上甚至找不到介绍这个东西的地方。 目前只能找到一个`Q&A`:https://answers.microsoft.com/en-us/msoffice/forum/all/what-is-the-status-of-viewofficeappslivecom/830fd75c-9b47-43f9-89c9-4303703fd7f6 微软官方人员回答表示: ![image.png](../../../assets/article/docPreview/ms_answer.webp) 翻译翻译,就是:几乎永久使用,没有收费计划,不会存储预览的文件数据,限制文件`10MB`,建议用于 **查看互联网上公开的文件**。 但经过某些用户测试发现: ![image.png](../../../assets/article/docPreview/ms_answer_2.webp) 使用了微软的文件预览服务,然后删除了文件地址,仍然可访问,但过一段时间会失效。 ### 2.Google Drive 查看器 接入简单,同 `Office Web Viewer`,只需要把 `src` 改为`https://drive.google.com/viewer?url=${encodeURIComponent(url)}`即可。 限制`25MB`,支持以下格式: ![image.png](../../../assets/article/docPreview/google_doc_view.webp) 测试效果,支持`docx,pptx,xlsx,pdf`预览,但`pptx`预览的效果不如微软,没有动画效果,样式有小部分会错乱。 **由于某些众所周知的原因,不可用** ### 3.阿里云 IMM 官方文档如下:https://help.aliyun.com/document_detail/63273.html ![image.png](../../../assets/article/docPreview/ali_doc.webp) 付费使用 ### 4.XDOC 文档预览 说了一些大厂的,在介绍一些其他的,**需要自行分辨** 官网地址:https://view.xdocin.com/view-xdocin-com_6x5f4x.htm ![image.png](../../../assets/article/docPreview/xdoc.webp) ### 5.Office Web 365 需要注意的是,虽然名字很像`office`,但我们看网页的`Copyright`可以发现,其实是一个西安的公司,**不是微软**。 但毕竟也提供了文件预览的服务 官网地址:https://www.officeweb365.com/ ![image.png](../../../assets/article/docPreview/ow365.webp) ### 6.WPS 开放平台 官方地址:https://solution.wps.cn/ ![image.png](../../../assets/article/docPreview/wps_office.webp) 付费使用,价格如下: ![image.png](../../../assets/article/docPreview/wps_office_price.webp) ## 二:前端处理方案 ### 1.pptx 的预览方案 先查一下有没有现成的轮子,目前`pptx`的开源预览方案能找到的只有这个:https://github.com/g21589/PPTX2HTML。但已经六七年没有更新,也没有维护,笔者使用的时候发现有很多兼容性问题。 简单来说就是,没有。对于这种情况,我们可以自行解析,主要步骤如下: 1. 查询`pptx`的国际标准 2. 解析`pptx`文件 3. 渲染成`html`或者`canvas`进行展示 我们先去找一下`pptx`的国际标准,官方地址:[officeopenxml](http://officeopenxml.com/) 先解释下什么是`officeopenxml`: > Office OpenXML,也称为 OpenXML 或 OOXML,是一种基于 XML 的办公文档格式,包括文字处理文档、电子表格、演示文稿以及图表、图表、形状和其他图形材料。该规范由微软开发,并于 2006 年被 ECMA 国际采用为 ECMA-376。第二个版本于 2008 年 12 月发布,第三个版本于 2011 年 6 月发布。该规范已被 ISO 和 IEC 采用为 ISO/IEC 29500。 > 虽然 Microsoft 继续支持较旧的二进制格式 (.doc、.xls 和.ppt),但 OOXML 现在是所有 Microsoft Office 文档 (.docx、.xlsx 和.pptx) 的默认格式。 由此可见,`Office OpenXML`由微软开发,目前已经是国际标准。接下来我们看一下`pptx`里面有哪些内容,具体可以看`pptx`的官方标准:[officeopenxml-pptx](http://officeopenxml.com/anatomyofOOXML-pptx.php) > PresentationML 或.pptx 文件是一个**zip 文件**,其中包含许多“部分”(通常是 UTF-8 或 UTF-16 编码)或 XML 文件。该包还可能包含其他媒体文件,例如图像。该结构根据 OOXML 标准 ECMA-376 第 2 部分中概述的开放打包约定进行组织。 ![image.png](../../../assets/article/docPreview/xml.webp) 根据国际标准,我们知道,`pptx`文件本质就是一个`zip`文件,其中包含许多部分: > 部件的数量和类型将根据演示文稿中的内容而有所不同,但始终会有一个 [Content_Types].xml、一个或多个关系(.rels)部件和一个演示文稿部件(演示文稿.xml),它位于 ppt 文件夹中,用于 Microsoft Powerpoint 文件。通常,还将至少有一个幻灯片部件,以及一张母版幻灯片和一张版式幻灯片,从中形成幻灯片。 那么`js`如何读取`zip`呢? 找到一个工具:https://www.npmjs.com/package/jszip 于是我们可以开始尝试解析`pptx`了。 ```ts import JSZip from 'jszip'; // 加载 pptx 数据 const zip = await JSZip.loadAsync(pptxData); ``` - 解析`[Content_Types].xml` 每个`pptx`必然会有一个 `[Content_Types].xml`。此文件包含包中部件的所有内容类型的列表。每个部件及其类型都必须列在 `[Content_Types].xml` 中。通过它里面的内容,可以解析其他的文件数据 ```ts const filesInfo = await getContentTypes(zip); async function getContentTypes(zip: JSZip) { const ContentTypesJson = await readXmlFile(zip, '[Content_Types].xml'); const subObj = ContentTypesJson['Types']['Override']; const slidesLocArray = []; const slideLayoutsLocArray = []; for (let i = 0; i < subObj.length; i++) { switch (subObj[i]['attrs']['ContentType']) { case 'application/vnd.openxmlformats-officedocument.presentationml.slide+xml': slidesLocArray.push(subObj[i]['attrs']['PartName'].substr(1)); break; case 'application/vnd.openxmlformats-officedocument.presentationml.slideLayout+xml': slideLayoutsLocArray.push(subObj[i]['attrs']['PartName'].substr(1)); break; default: } } return { slides: slidesLocArray, slideLayouts: slideLayoutsLocArray, }; } ``` - 解析演示文稿 先获取`ppt`目录下的`presentation.xml`演示文稿的大小 由于演示文稿是`xml`格式,要真正的读取内容需要执行 `readXmlFile` ```ts const slideSize = await getSlideSize(zip); async function getSlideSize(zip: JSZip) { const content = await readXmlFile(zip, 'ppt/presentation.xml'); const sldSzAttrs = content['p:presentation']['p:sldSz']['attrs']; return { width: (parseInt(sldSzAttrs['cx']) * 96) / 914400, height: (parseInt(sldSzAttrs['cy']) * 96) / 914400, }; } ``` - 加载主题 根据 `officeopenxml`的标准解释 > 每个包都包含一个关系部件,用于定义其他部件之间的关系以及与包外部资源的关系。这样可以将关系与内容分开,并且可以轻松地更改关系,而无需更改引用目标的源。 > 除了包的关系部分之外,作为一个或多个关系源的每个部件都有自己的关系部分。每个这样的关系部件都可以在部件的\_rels 子文件夹中找到,并通过在部件名称后附加“.rels”来命名。 其中主题的相关信息就在`ppt/_rels/presentation.xml.rels`中 ```ts async function loadTheme(zip: JSZip) { const preResContent = await readXmlFile(zip, 'ppt/_rels/presentation.xml.rels'); const relationshipArray = preResContent['Relationships']['Relationship']; let themeURI; if (relationshipArray.constructor === Array) { for (let i = 0; i < relationshipArray.length; i++) { if ( relationshipArray[i]['attrs']['Type'] === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' ) { themeURI = relationshipArray[i]['attrs']['Target']; break; } } } else if ( relationshipArray['attrs']['Type'] === 'http://schemas.openxmlformats.org/officeDocument/2006/relationships/theme' ) { themeURI = relationshipArray['attrs']['Target']; } if (themeURI === undefined) { throw Error("Can't open theme file."); } return readXmlFile(zip, 'ppt/' + themeURI); } ``` 后续`ppt`里面的其他内容,都可以这么去解析。根据`officeopenxml`标准,可能包含: | Part | Description | | -------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | Comments Authors | Contains information about each author who has added a comment to the presentation. | | Comments | Contains comments for a single slide. | | Handout Master | Contains the look, position, and size of the slides, notes, header and footer text, date, or page number on the presentation's handout. There can be only one such part. | | Notes Master | Contains information about the content and formatting of all notes pages. There can be only one such part. | | Notes Slide | Contains the notes for a single slide. | | Presentation | Contains the definition of a slide presentation. There must be one and only one such part. See [Presentation](http://officeopenxml.com/PrPresentation.php). | | Presentation Properties | Contains all of the presentation's properties. There must be one and only one such part. | | Slide | Contains the content of a single slide. | | Slide Layout | Contains the definition for a slide template. It defines the default appearance and positioning of drawing objects on the slide. There must be one or more such parts. | | Slide Master | Contains the master definition of formatting, text, and objects that appear on each slide in the presentation that is derived from the slide master. There must be one or more such parts. | | Slide Synchronization Data | Contains properties specifying the current state of a slide that is being synchronized with a version of the slide stored on a central server. | | User-Defined Tags | Contains a set of user-defined properties for an object in a presentation. There can be zero or more such parts. | | View Properties | Contains display properties for the presentation. | 等等内容,我们根据标准一点点解析并渲染就好了。 完整源码:[ranui](https://github.com/chaxus/ran/tree/main/packages/ranui) 使用文档:[preview 组件](https://ran.chaxus.com/src/ranui/preview/) ### 2.pdf 的预览方案 #### (1).iframe 和 embed `pdf`比较特别,一般的浏览器默认支持预览`pdf`。因此,我们可以使用浏览器的能力: ```html