我如何将 ChatGPT 放入编辑器中

随着所有炒作的进行,人工智能(或者更确切地说是机器学习(ML)和大型语言模型(LLM))无处不在。就个人而言,我可能不会经常使用 ChatGPT(和类似的替代品),但我确实依赖GitHub Copilot之类的东西(用于 VS Code 中的智能自动完成)或Grammarly(用于编辑我的博客文章)每天。

我认为我们距离 AGI 仍有相当多的突破,而目前的技术不足以让我们到达那里(谢天谢地或不谢天谢地)。也就是说,我们已经深入到“AI 增强型”应用程序的时代,顶级应用程序可能没有最好的 AI 系统,但它们以最好的方式集成了它们。

这就是为什么这是一个有趣的过程,探索 OpenAI 的 API 并尝试将其集成到 Vrite 的富文本编辑器 (RTE) — 我的开源无头 CMS。

扩展所见即所得编辑器

对于那些不熟悉的人来说,简而言之,Vrite 是一个用于技术内容的无头 CMS,例如编程博客或软件文档。它可以看作是两个应用程序合二为一——用于内容管理的看板仪表板和用于编写的所见即所得编辑器,以及嵌入式代码片段编辑器和格式化程序等附加的开发友好功能。

Vrite 的最新重要补充是早期扩展系统,可以轻松构建集成并扩展 Vrite 的功能。对我来说,这似乎是将 ChatGPT 作为扩展引入编辑器的完美方式

阻止操作

为了能够使用扩展系统将 ChatGPT 集成到编辑器中,必须引入一个新的 API。我称它为Block Action API,因为它专门用于向编辑器添加快速操作,对顶级内容块(如段落、标题或图像)进行操作,如下所示:

使用 Block Actions API,扩展可以读取活动块的 JSON 内容并使用 HTML 格式的内容更新它,就像在 Vrite API 中所做的一样(一方面,解析 JSON 输出更容易,另一方面,HTML 更容易)适合将内容转换成)。

从 UI 端来看,块操作显示为主动选择的块一侧的按钮。他们可以直接在点击时调用一个动作,或者——就像 ChatGPT 一样——打开一个下拉菜单来提示用户提供更多详细信息。

按钮必须绝对定位,这需要自定义TipTap扩展和更深入地挖掘底层ProseMirror(这两个库都支持 Vrite 编辑器)。

该过程基本上归结为确定块节点的位置和大小,给定整个顶级节点或仅其子节点的选择(源代码):

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">BlockActionMenuPlugin</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">Extension</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">create</span><span style="color:var(--syntax-text-color)">({</span><span style="color:var(--syntax-comment-color)">// ...</span><span style="color:var(--syntax-name-color)">onSelectionUpdate</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">selection</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">state</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">isTextSelection</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span> <span style="color:var(--syntax-declaration-color)">instanceof</span> <span style="color:var(--syntax-name-color)">TextSelection</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">selectedNode</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeAfter</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">selectedNode</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">display</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">none</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">view</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">this</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">node</span> <span style="color:var(--syntax-error-color)">=</span><span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeDOM</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span><span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeDOM</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">parentOffset</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">||</span><span style="color:var(--syntax-name-color)">view</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">domAtPos</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">)?.</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">blockParent</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">getBlockParent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">parentPos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">document</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">getElementById</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">pm-container</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">)</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">getBoundingClientRect</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">childPos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">blockParent</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">getBoundingClientRect</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">parentPos</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">relativePos</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">top</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">right</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">right</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">right</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">bottom</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">bottom</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">bottom</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">left</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">childPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-text-color)">};</span><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-name-color)">rangeFrom</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-name-color)">rangeTo</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$to</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">pos</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">relativePos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">top</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">px`</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">relativePos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">left</span> <span style="color:var(--syntax-error-color)">+</span> <span style="color:var(--syntax-name-color)">parentPos</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">width</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">px`</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">display</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">block</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">isTextSelection</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">try</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">p</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">findParentAtDepth</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">selection</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">$from</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">rangeFrom</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">p</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">start</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">rangeTo</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">p</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">start</span> <span style="color:var(--syntax-error-color)">+</span> <span style="color:var(--syntax-name-color)">p</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">node</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeSize</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-literal-color)">1</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">e</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">box</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">style</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">display</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">none</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-comment-color)">// ...</span><span style="color:var(--syntax-text-color)">},</span>
<span style="color:var(--syntax-text-color)">});</span></code></span></span>

替换编辑器内容

第二部分涉及处理用新提供的内容替换块内容的实际过程。这里最棘手的事情是获取块节点的正确范围(ProseMirror 中的开始和结束位置)。这是使用 TipTap 的命令正确替换范围所必需的。

如果您仔细查看最后的代码片段 — 其代码已经存在。在每次选择更新时,块的范围以及块操作 UI 定位都会更新。

用新内容实际替换范围要容易得多。它所要做的就是将 HTML 转换为遵循模式的 JSON 并涉及适当的命令(源代码):

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-comment-color)">// ...</span>
<span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">replaceContent</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">unlock</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">clear</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-name-color)">setLocked</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">range</span><span style="color:var(--syntax-text-color)">())</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-name-color)">size</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">nodeOrFragment</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">createNodeFromContent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">props</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">state</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">schema</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">nodeOrFragment</span> <span style="color:var(--syntax-declaration-color)">instanceof</span> <span style="color:var(--syntax-name-color)">PMNode</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">size</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">nodeOrFragment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">nodeSize</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">else</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">size</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">nodeOrFragment</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">size</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-name-color)">props</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">state</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">chain</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">focus</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">insertContentAt</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">range</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">generateJSON</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">props</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">state</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">editor</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">extensionManager</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">extensions</span><span style="color:var(--syntax-text-color)">)</span><span style="color:var(--syntax-text-color)">)</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">scrollIntoView</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">focus</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">run</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-name-color)">setRange</span><span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">from</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">range</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">from</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">to</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">range</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-error-color)">!</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-error-color)">+</span> <span style="color:var(--syntax-name-color)">size</span> <span style="color:var(--syntax-error-color)">-</span> <span style="color:var(--syntax-literal-color)">1</span> <span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-name-color)">computeDropdownPosition</span><span style="color:var(--syntax-text-color)">()();</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-name-color)">unlock</span><span style="color:var(--syntax-text-color)">();</span>
<span style="color:var(--syntax-text-color)">};</span>
<span style="color:var(--syntax-comment-color)">// ...</span></code></span></span>

replaceContent()然后可以通过向主框架发送适当的消息,从扩展的沙箱远程调用该函数。

为了启用像 ChatGPT 集成这样的用例,其中内容将在过程完成之前连续多次更新(即替换),该函数还会在调用函数的短时间内锁定编辑器并更新范围,和每次调用的 UI 定位。但为什么需要这样做呢?

与 OpenAI 的 API 集成

集成 OpenAI 的 API 的过程在其官方文档中有很好的记录。鉴于官方提供了SDK,整个过程只需要几行代码就可以搞定:

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">input</span> <span style="color:var(--syntax-text-color)">})</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">configuration</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Configuration</span><span style="color:var(--syntax-text-color)">({</span><span style="color:var(--syntax-name-color)">apiKey</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">fastify</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">config</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OPENAI_API_KEY</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">organization</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">fastify</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">config</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OPENAI_ORGANIZATION</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">openai</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">OpenAIApi</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">configuration</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">response</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">openai</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">createChatCompletion</span><span style="color:var(--syntax-text-color)">({</span><span style="color:var(--syntax-name-color)">model</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">gpt-3.5-turbo</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">messages</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[{</span> <span style="color:var(--syntax-name-color)">role</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">user</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">input</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">prompt</span> <span style="color:var(--syntax-text-color)">}],</span><span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">};</span></code></span></span>

现在,所有这些都是真的,但前提是您愿意等待通常为+20 秒的单个响应!这对于单个请求来说已经很多了。max_tokens从更改服务器位置到通过限制或自定义其他参数来优化请求,都没有任何效果。这一切都归结为当前的 LLM(至少是 GPT-3 级别的)仍然相当缓慢。

话虽如此,ChatGPT 应用程序仍然设法被认为相当快速和响应迅速。这要归功于流媒体和服务器发送事件(SSE)的使用。

流式聊天 GPT 响应

OpenAI 的 API 的聊天完成和其他端点支持通过服务器发送的事件进行流式传输,本质上是保持一个开放的连接,新令牌一旦可用就会通过该连接发送。

不幸的是,官方 Node.js SDK不支持流式传输,需要您使用变通方法才能使其正常工作,从而导致需要更多代码,仅用于连接 API(源代码):

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">input</span> <span style="color:var(--syntax-text-color)">})</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">configuration</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">Configuration</span><span style="color:var(--syntax-text-color)">({</span><span style="color:var(--syntax-name-color)">apiKey</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">fastify</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">config</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OPENAI_API_KEY</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">organization</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">fastify</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">config</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">OPENAI_ORGANIZATION</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">openai</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">OpenAIApi</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">configuration</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">response</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">openai</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">createChatCompletion</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">model</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">gpt-3.5-turbo</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">stream</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">messages</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">[{</span> <span style="color:var(--syntax-name-color)">role</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">user</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">input</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">prompt</span> <span style="color:var(--syntax-text-color)">}],</span><span style="color:var(--syntax-text-color)">},</span><span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">responseType</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">stream</span><span style="color:var(--syntax-string-color)">'</span> <span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">res</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">raw</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">writeHead</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-literal-color)">200</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-text-color)">...</span><span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">res</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">getHeaders</span><span style="color:var(--syntax-text-color)">(),</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">content-type</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">text/event-stream</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">cache-control</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">no-cache</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">connection</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">keep-alive</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-declaration-color)">return</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-text-color)">Promise</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-declaration-color)">void</span><span style="color:var(--syntax-error-color)">></span><span style="color:var(--syntax-text-color)">((</span><span style="color:var(--syntax-name-color)">resolve</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">responseData</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">response</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">data</span> <span style="color:var(--syntax-declaration-color)">as</span> <span style="color:var(--syntax-name-color)">unknown</span> <span style="color:var(--syntax-declaration-color)">as</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">on</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">event</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-declaration-color)">void</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-declaration-color)">void</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">};</span><span style="color:var(--syntax-name-color)">responseData</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">on</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">data</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">lines</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">toString</span><span style="color:var(--syntax-text-color)">()</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">split</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-literal-color)">\n</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">)</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">filter</span><span style="color:var(--syntax-text-color)">((</span><span style="color:var(--syntax-name-color)">line</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-name-color)">line</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">trim</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-error-color)">!==</span> <span style="color:var(--syntax-string-color)">''</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">for</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">line</span> <span style="color:var(--syntax-declaration-color)">of</span> <span style="color:var(--syntax-name-color)">lines</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">message</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">line</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">replace</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">/^data: /</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-string-color)">''</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">message</span> <span style="color:var(--syntax-error-color)">===</span> <span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">[DONE]</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">res</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">raw</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">end</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-name-color)">resolve</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-declaration-color)">continue</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-declaration-color)">try</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">parsed</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">JSON</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">parse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">message</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">content</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">parsed</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">choices</span><span style="color:var(--syntax-text-color)">[</span><span style="color:var(--syntax-literal-color)">0</span><span style="color:var(--syntax-text-color)">].</span><span style="color:var(--syntax-name-color)">delta</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">content</span> <span style="color:var(--syntax-error-color)">||</span> <span style="color:var(--syntax-string-color)">''</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">if</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">res</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">raw</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">write</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">`data: </span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-text-color)">encodeURIComponent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">)}</span><span style="color:var(--syntax-string-color)">`</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">ctx</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">res</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">raw</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">write</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-literal-color)">\n\n</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">catch</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">error</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">console</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">error</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-string-color)">Could not JSON parse stream message</span><span style="color:var(--syntax-string-color)">'</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">message</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">error</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">};</span></code></span></span>

最重要的是,你还必须在你的 API 服务器和 Web 客户端之间支持流式传输,对于 Vrite,这意味着将 SSE 与Fastify和tRPC集成。不是最干净的解决方案,但仍然非常稳定。

从前端(准确地说是扩展沙箱),必须建立与新流端点的连接并正确处理传入数据(源代码):

<span style="color:var(--syntax-text-color)"><span style="color:var(--syntax-text-color)"><code><span style="color:var(--syntax-declaration-color)">import</span> <span style="color:var(--syntax-text-color)">{</span> <span style="color:var(--syntax-name-color)">fetchEventSource</span> <span style="color:var(--syntax-text-color)">}</span> <span style="color:var(--syntax-declaration-color)">from</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">@microsoft/fetch-event-source</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">;</span>
<span style="color:var(--syntax-comment-color)">// ...</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">generate</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">ExtensionBlockActionViewContext</span><span style="color:var(--syntax-text-color)">):</span> <span style="color:var(--syntax-text-color)">Promise</span><span style="color:var(--syntax-error-color)"><</span><span style="color:var(--syntax-declaration-color)">void</span><span style="color:var(--syntax-error-color)">></span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">includeContext</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">temp</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">includeContext</span> <span style="color:var(--syntax-declaration-color)">as</span> <span style="color:var(--syntax-name-color)">boolean</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">prompt</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">temp</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">prompt</span> <span style="color:var(--syntax-declaration-color)">as</span> <span style="color:var(--syntax-declaration-color)">string</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-declaration-color)">let</span> <span style="color:var(--syntax-name-color)">content</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-string-color)">""</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">setTemp</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">$loading</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">true</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-text-color)">window</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">currentRequestController</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-declaration-color)">new</span> <span style="color:var(--syntax-name-color)">AbortController</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-text-color)">window</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">currentRequestController</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">signal</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">addEventListener</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">abort</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-error-color)">=></span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">setTemp</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">$loading</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">refreshContent</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-declaration-color)">await</span> <span style="color:var(--syntax-name-color)">fetchEventSource</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">https://extensions.vrite.io/gpt</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">method</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">POST</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-name-color)">headers</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">Content-Type</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">application/json</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">Accept</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">text/event-stream</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">},</span><span style="color:var(--syntax-name-color)">body</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">JSON</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">stringify</span><span style="color:var(--syntax-text-color)">({</span><span style="color:var(--syntax-name-color)">prompt</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">includeContext</span> <span style="color:var(--syntax-text-color)">?</span> <span style="color:var(--syntax-string-color)">`"</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">gfmTransformer</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">)}</span><span style="color:var(--syntax-string-color)">"\n\n</span><span style="color:var(--syntax-text-color)">${</span><span style="color:var(--syntax-name-color)">prompt</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-string-color)">`</span> <span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-name-color)">prompt</span><span style="color:var(--syntax-text-color)">}),</span><span style="color:var(--syntax-name-color)">signal</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-text-color)">window</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">currentRequestController</span><span style="color:var(--syntax-text-color)">?.</span><span style="color:var(--syntax-name-color)">signal</span><span style="color:var(--syntax-text-color)">,</span><span style="color:var(--syntax-declaration-color)">async</span> <span style="color:var(--syntax-name-color)">onopen</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">return</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">},</span><span style="color:var(--syntax-name-color)">onerror</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">error</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">setTemp</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">$loading</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">refreshContent</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">notify</span><span style="color:var(--syntax-text-color)">({</span> <span style="color:var(--syntax-name-color)">text</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">Error while generating content</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-name-color)">type</span><span style="color:var(--syntax-text-color)">:</span> <span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">error</span><span style="color:var(--syntax-string-color)">"</span> <span style="color:var(--syntax-text-color)">});</span><span style="color:var(--syntax-declaration-color)">throw</span> <span style="color:var(--syntax-name-color)">error</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-text-color)">},</span><span style="color:var(--syntax-name-color)">onmessage</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">event</span><span style="color:var(--syntax-text-color)">)</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-declaration-color)">const</span> <span style="color:var(--syntax-name-color)">partOfContent</span> <span style="color:var(--syntax-error-color)">=</span> <span style="color:var(--syntax-text-color)">decodeURIComponent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">event</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">data</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">content</span> <span style="color:var(--syntax-error-color)">+=</span> <span style="color:var(--syntax-name-color)">partOfContent</span><span style="color:var(--syntax-text-color)">;</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">replaceContent</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">marked</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">parse</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-name-color)">content</span><span style="color:var(--syntax-text-color)">));</span><span style="color:var(--syntax-text-color)">},</span><span style="color:var(--syntax-name-color)">onclose</span><span style="color:var(--syntax-text-color)">()</span> <span style="color:var(--syntax-text-color)">{</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">setTemp</span><span style="color:var(--syntax-text-color)">(</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-string-color)">$loading</span><span style="color:var(--syntax-string-color)">"</span><span style="color:var(--syntax-text-color)">,</span> <span style="color:var(--syntax-declaration-color)">false</span><span style="color:var(--syntax-text-color)">);</span><span style="color:var(--syntax-name-color)">context</span><span style="color:var(--syntax-text-color)">.</span><span style="color:var(--syntax-name-color)">refreshContent</span><span style="color:var(--syntax-text-color)">();</span><span style="color:var(--syntax-text-color)">}</span><span style="color:var(--syntax-text-color)">});</span>
<span style="color:var(--syntax-text-color)">};</span>
</code></span></span>

不幸的是,用于处理 SSE 的EventSource Web API(内置于大多数现代浏览器中)仅支持GET请求,这在需要POST具有较大 JSON 数据的请求时非常有限。body作为替代方案,您可以使用 Fetch API 或现成的库,例如Microsoft 的 Fetch Event Source。

同样,启用流式传输后,您现在将在可用时立即收到新令牌。鉴于 OpenAI 的 API 在其响应格式中使用 Markdown,因此需要将传入的令牌放在一起并解析为函数接受的 HTML 的完整消息replaceContent。为此,我使用了Marked.js 解析器。

现在,随着每个新令牌的出现,更大的响应正在建立。每次出现新的标记时,都会解析完整的 Markdown 并更新内容,从而形成一种很好的“类似打字的效果”。

虽然这个过程确实有一些开销,但在使用中并不明显,而 Markdown 只需用每个新标记进行解析,因为它可能包含例如代码块的结束或格式化段的结尾。因此,虽然这个过程可能会被优化,但在大多数情况下它不会导致任何可识别的性能改进。

最后,值得注意的是 的使用AbortController,它可用于在用户选择的任何时间停止流。这对于较长的响应尤其有用。

动图

底线

总的来说,我对结果很满意。得益于 Markdown 解析,数据流、良好的打字效果以及与编辑器现有内容块的良好集成——所有这些共同创造了引人注目的用户体验。

现在,肯定还有改进的余地。Block Actions API 以及 Vrite Extensions 作为一个整体,在它们可以被其他用户创建之前,还有很多开发工作要做。其他需要考虑的 UI/UX 改进,比如一次在多个块上操作(例如,用于 ChatGPT 的额外上下文)和内联显示 UI(很像 Notion AI)而不是模糊视图,这些只是我正在考虑的几个例子。也就是说,要很好地实施这些想法还需要一些时间。

Vrite 不仅仅是一个 GPT 增强型编辑器。它是一个完整的开源 CMS,专注于编程博客等技术内容,包括代码编辑器、API、看板管理仪表板和简单的发布集成。因此,如果您有兴趣尝试并可能使用它来为您的博客提供动力,请一定要检查一下!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/33931.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

手把手教你实现折线图之------安卓最好用的图表库hellocharts之最详细的使用介绍

因为项目需要搞一个折线图&#xff0c;按照日期显示相应的成绩&#xff0c;所以有了本文。 以前用过一次XCL-chart&#xff0c;但是感觉只适合固定图表&#xff0c;不去滑动的那种&#xff0c;因为你一滑动太卡了你懂得&#xff08;毕竟作者好久没更新优化了&#xff09;&#…

一文学会炫酷图表利器pyecharts,领导能不重用你?

前言 前段时间&#xff0c;领导突然把我叫到办公室… 公司高层要看看上半年度项目组业绩数据分析&#xff0c;准备下吧&#xff01;可以嘛&#xff1f; 没办法&#xff0c;硬着头皮也要上&#xff01;一口答应&#xff0c;保证完成任务&#xff01; 说到数据分析&#xff0…

手把手教你用plotly绘制excel中常见的8种图表

最近不是在学习plotly嘛&#xff0c;为了方便理解&#xff0c;我们这里取excel绘图中常见的16种图表为例&#xff0c;分两期演示这些基础图表怎么用plotly进行绘制&#xff01; 第一部分&#xff1a;柱状图、条形图、折线图、面积图、饼图与圆环图、散点图、气泡图和极坐标(雷达…

这几个用 Pyecharts 做出来的交互图表,领导说叼爆了!

点击上方蓝色小字&#xff0c;关注“涛哥聊Python” 重磅干货&#xff0c;第一时间送达 作者 | 旧时晚风拂晓城 编辑 | JackTian来源 | 杰哥的IT之旅&#xff08;ID&#xff1a;Jake_Internet&#xff09;转载请联系授权&#xff08;微信ID&#xff1a;Hc220066&#xff09; 一…

程序员最关心的问题,我都帮你们问AI了

前言 微信关注【小白技术圈】发送【Y99】获取国内免登录免梯子玩法 最近几天互联网刮起了一阵ChatGPT风&#xff0c;起因是OpenAI发布了一个全新的聊天机器人模型—— ChatGPT&#xff0c;可以像人类交谈般回答大部分问题甚至还能直接帮你写代码。 我们先来试试让它帮我们写个代…

SCITIC论坛 | 数字化创意探索——从流浪地球说起

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 随着《流浪地球2》的火热出圈&#xff0c;数字虚拟人、元宇宙、AIGC等AI元素备受关注。去年11月&#xff0c;美国人工智能研究公司Open AI发布聊一款全新聊天机器人模型“ChatGPT”, 推出后迅速火爆全球&#x…

今天15:00 | 从流浪地球说起,一起聊一聊数字创意

点击蓝字 关注我们 AI TIME欢迎每一位AI爱好者的加入&#xff01; 随着《流浪地球2》的火热出圈&#xff0c;数字虚拟人、元宇宙、AIGC等AI元素备受关注。去年11月&#xff0c;美国人工智能研究公司Open AI发布聊一款全新聊天机器人模型“ChatGPT”, 推出后迅速火爆全球&#x…

【诏安扑克牌玩法 —— 宝】庄家(宝官)一定会赢吗?

统计庄家&#xff08;宝官&#xff09;赢得概率 宝的玩法介绍关于洗牌实验步骤实验结果原始数据源代码&#xff08;Python&#xff09;要想不输&#xff0c;惟有不赌&#xff01; 宝的玩法介绍 这是一种简单、不需要技术仅靠运气的扑克牌玩法。 开始前先在牌组中取出大小王&a…

Datawhale:愿竞赛圈少一些人身攻击和热点炒作

作者&#xff1a;Datawhale 关于Kaggle瓜事件&#xff0c;最近得到很多竞赛圈朋友的关注&#xff0c;也导致很多参加竞赛的朋友深受其扰。事实上整个事件是因为kaggle比赛上两个队伍因为提交的sub一样&#xff0c;被取消了成绩&#xff0c;不管原因是什么&#xff0c;有错认错&…

pygame实现王思聪吃热狗小游戏(双人版)

游戏介绍: 一款单人版的思聪吃热狗游戏&#xff0c;你可以自己调节思聪的位置&#xff0c;移动时会消耗能量10&#xff0c;游戏中吃到热狗分数加 1&#xff0c;能量加 20&#xff0c;最后的目标就是称霸世界咯&#xff0c;吃掉所有的热狗即游戏胜利。王思聪能量消耗完毕即游戏…

基于C++实现惊险刺激的Flappy Bird设计

资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85941853 资源下载地址&#xff1a;https://download.csdn.net/download/sheziqiong/85941853 目录 1、 题目描述和题目要求 2 2、 需求分析 2 3、总体设计 5 3.1 功能模块设计 5 3. 状态信息栏&#x…

[COGS 2897] [THUPC 2017] 天天爱射击

COGS传送门 题目描述 小C爱上了一款名字叫做《天天爱射击》的游戏&#xff0c;在这款游戏中可以用子弹将木板打碎。如图所示&#xff0c;这个游戏有一些平行于x轴的木板。现在有一些子弹&#xff0c;按顺序沿着y轴方向向这些木板射去。第 i i i块木板被 S i S_i Si​个子弹击…

【选恐症安利】小熊猫C++原创配色

【选恐症安利】小熊猫C原创配色! 本人是超级强迫症和选择恐惧症&#xff01;经过三天的调色&#xff0c;成就了下面的情景&#xff1a; 哇&#xff0c;绝了&#xff01; 你可以在这里导入配色&#xff1a; 好了&#xff0c;放链接&#xff01; wwt.lanzoum.com/iv4VW0cbkep…

[day2]python网络爬虫实战:爬取美女写真图片(增强版)

l> 我的新书《Android App开发入门与实战》已于2020年8月由人民邮电出版社出版&#xff0c;欢迎购买。点击进入详情 文章目录 1.开发环境2.第三方库3.实现1.分析url格式2.分析图片格式3.保存图片到本地4.输入页数 4.优化1.防止被封2.多线程下载3.便捷获取图片地址 5.效果6.G…

Python写网络爬虫(一)

文章目录 网络爬虫简介爬虫在使用场景中的分类爬虫的矛与盾需要知道的协议常用请求头信息常用响应头信息requests模块如何使用requests&#xff1a;&#xff08;requests模块的编码流程&#xff09;新手实战演练正式入门爬虫get 方法的常用参数&#xff1a;简易网页采集器 首先…

那个顶撞雷军还爱在办公室打乒乓的副总裁——周光平

文章原创来自洞见网&#xff1a;http://www.localonline.com.cn/start/people/712.html&#xff0c;转载请注明出处。 近日&#xff0c;东南大学(原南京工学院)1977级校友周光平、严星夫妇捐资2000万元&#xff0c;在母校设立“平星基金”&#xff0c;用于支持信息科学与工程学…

雷军—我十年的程序员生涯

内容转自&#xff1a;http://blog.sina.com.cn/s/blog_4b0e23c90100b2qf.html 最近&#xff0c;和UCWEB同事讨论&#xff0c;怎么才能把我们的UCWEB做到极致。我说&#xff0c;“手机上的平台非常多&#xff0c;如果想做好&#xff0c;需要足够多、足够优秀的程序员。优秀的程序…

雷军谈人生三段低谷:站店卖货、泡吧泡论坛、错失互联网第一波浪潮!

自2020年小米十周年雷军进行了人生首次公开演讲以来&#xff0c;他似乎想要养成一种习惯&#xff0c;每年都举办一次年度演讲。 继2020年“相信自己&#xff0c;一往无前”和2021年“我的梦想&#xff0c;我的选择”两场年度演讲后&#xff0c;这不&#xff0c;在小米迎来12周…

中国第一程序员求伯君,WPS之父,雷军也佩服的人

中国第一程序员求伯君&#xff0c;WPS之父&#xff0c;最强码农的传奇经历 转载知乎冷冷读书 https://www.zhihu.com/people/leng-leng-80-6 2018年底&#xff0c;金山举办创业三十年庆典&#xff0c;三位创始人&#xff0c;求伯君、雷军和张旋龙相继到场。庆生中&#xff0c;雷…

用互联网思想武装自己---雷军

两年前的4月6日&#xff0c;我们几个人&#xff0c;在北四环的银谷大厦静悄悄的创办了小米公司&#xff0c;一起喝了碗小米粥&#xff0c;就开始艰难的创业之旅。仅仅两年时间&#xff0c;小米在百度手机品牌排行榜排在前五名&#xff0c;也在淘宝销售排行榜名列前茅&#xff0…