skip to content
Joey 起居注

typink AI 体验增强了

/ 11 min read

Table of Contents

上周对 typink 的 AI 能力做了一些优化,这里我将介绍下这些优化对产品的影响。

diff 结构优化

之前在代码框如何返回diff,方便用户一键应用到编辑器中,这个数据结构是我跟AI一起设计的,大致如下:

<DIFF>
--- 旧内容
+++ 新内容
</DIFF>

这样只要自己写一个 parser 去解析 DIFF 标签,然后再匹配 --- 解析出旧内容,匹配 +++ 解析出新内容就行。这种方法看起来很直观,但是我在接入 gemini 接口的时候发现很难让 gemini 按照这种格式返回数据,因此我就又看了一下 cline 的代码,因为cline集成了多个 llm provider,它的提示词肯定是验证过了很多llm都能理解的。在 cline 的代码库中我发现它是这么给AI描述变更的:

<replace_in_file>
<diff>
<<<<<<< SEARCH
原始内容(包含足够的上下文以准确定位)
=======
新内容
>>>>>>> REPLACE
</diff>
</replace_in_file>

在这个 replace_in_file 的 tool 里面包含了 diff 标签,然后里面使用了 git 风格的 diff 格式。我也照着改了一下,修改后的提示词 gemini 理解起来就简单多了,基本能按要求返回正确的diff结构。 对我来说改动也不大,原来parser的正则是去匹配 <DIFF>\n--- ,现在只要改为 <DIFF>\n<<<<<<< SEARCH 就行。解析后的数据结构也没变,让 AI 重新生成一些测试用例,就可以自信的部署这个更新了。 使用这种通用的diff结构一大好处就是方便 AI 理解,不然还得自己写一堆例子告诉AI哪种结构是对的。

新增模型切换功能

在上周 typink 的 AI 助手只支持一个模型,就是 deepseek,但是 deepseek 有个问题,它的模型上下文长度有点小,只有 128k,大概就几万个字吧,如果编辑大文件,又是多轮对话,很容易就超过这个窗口上限,我知道 cline 做了一个历史长度的预判断,即会估算剩余可用的上下文长度,来决定使用多长的历史记录,但是我不想做那么复杂的东西,目前我是固定取前面2轮对话内容作为历史的,所以要比较小心不要达到了128k这个上限。

其次 deepseek 还有一个问题就是生成的 typst 代码不太准确,虽然我已经在提示词里包含一些常见的错误了,还是会给我犯同样的错,所以我想试试其他模型在生成 typst 代码这里是否会更优秀。

我先看了一下 claude,但是人家官网对于中国人不太友好,检测到我的 ip 就不允许我使用,所以我暂且放弃,以后有必要再想想办法如何使用它。 然后我又调研了一下 gemini,这个模型号称支持100w token的上下文,就是把一本书丢进去也能完全理解,这点很吸引我,等于是比 deepseek 的上下文长度多了10倍,对于我这个场景来说几乎不可能达到上限。

于是我就去google cloud console开通了 gemini 的 api 结算,并且集成到代码中了。生成token的速度还是很快的,就是对于中国也不太友好,我虽然是通过 cloudflare ai gateway 来访问的,但是如果不开翻墙就会检测到我在北京,然后给我返回400错误不让我访问。因为我的需求比较简单,就是根据上下文补齐文本,所以在正确率上deepseek 和 gemini 我没发现太大区别,对于 typst 代码的语法都不太懂,该犯错的地方都会犯错。希望未来 typst 语言火了之后,这些AI 在训练的时候能多学习下 typst 的语法。

在国内还是优先使用deepseek 好了,如果真的没办法再切换到 gemini 试试,如果以后有老外用户可以考虑优先使用 gemini,因为生成速度是真的快。

新增文本引用功能

上周我的AI 对话上下文还只是当前编辑器的内容,但是不可能用户永远只基于一个文件来提问。如果未来有重度用户来使用我这个产品,比如说一个项目下有十几个 typst 文件,那么在问 AI 的时候就要将这十几个文件也包含进来,这个技术叫做工作区索引,我对这点的感触是很深的。今年2月份之前,github copilot 还没升级,那时候github copilot 使用体验还很差,没法感知到其它文件来提供额外的上下文,但是也就在2个月前,更新了新版 github copilot 后,就升级为一个真正的 coding agent了,在我提问的时候有时候会触发工作区的重新索引。我觉得应该就是将项目下所有代码文件的大致内容都编到一个索引文件去了,这样在提问的时候,会先判断用户意图,去索引文件中找出相关的文件,然后读取文件内容,作为上下文提供给 AI。

我这个编辑器也可以类似的生成一个索引文件,但是这样工程上太复杂了,维护这个索引要花费非常多token成本,github copilot 一个月10美元,cursor一个月20美元,能负担这个成本,我现在还没有付费功能,如果不考虑成本的话,可能初期都撑不过去。所以我决定采用简单的,通过 @filename 来显式提供上下文,用户在输入框按下 @ 符号时,就会去查询项目下的文本文件,当选中一个文件,并且输入提示词发送时,系统会解析 @filename 这种模式,用真实的文件内容替换,这样就实现了手动引用文本的功能。

网上很多教人做ppt的教程都是这样的:

  1. 去deepseek 描述需求,让它帮你生成ppt大纲
  2. 粘贴到kimi 或者 ppt岛上面选择模板然后生成

使用我这个文件引用,上面的流程就可以变为:

  1. 去 deepseek 描述需求,生成大纲
  2. 粘贴到 typink 里面,命名为 deepseek-outline.md
  3. 在 typink AI 对话栏输入 @deepseek-outline.md 请根据这份大纲帮我生成一个ppt

然后AI就能读取到 deepseek-outline.md 里面的大纲内容,思考后会按照 typst ppt 的格式返回代码,一键接受的话就会更新到 main.typ 文件里。

目前的引文我只支持文本文件,因为内容直接就可读,我知道很多AI都能理解ppt, word, pdf里面的内容,但是我暂时不打算做,一是会提高项目复杂度,二是会增加很多成本。试想一下,如果用户上传一个20MB的pdf文件到项目中,然后引用这个文件,也许直接就超出我的上下文窗口了,用户体验也不一定好。

不过我知道上个月微软开源了 markitdown 这个库,能将多种格式的文件转为 markdown,未来也许会集成这个库,让用户将上传的 pdf, word 文件转为markdown格式,再被引用。现在用户如果一定要提供pdf里的内容作为上下文,得先去第三方平台提取出pdf里面的内容,然后到 typink 里创建一个文本文件,将内容粘贴进去才行,虽然不方便但是功能可用。

总结

以上就是这几天我对于 typink AI 的优化了,目前最大的问题还是 AI 在生成 typst 代码的正确率上实在是不高,几乎每次生成完都要人工干预一下,虽然都是小问题,改几个地方就能改完,但是你为啥就不能生成一次就能编译通过的代码呢?遥想23年的时候,让 AI 生成一个快速排序的代码都不准确,但是到24年生成前后端代码基本都是对的,也不知道啥时候 AI 生成 typst 代码能准确点。