9月与10月总结
一个多月了, 已经很久没有打开过这个博客文件夹了. 这两天执行git时都快忘记基本的操作代码了..
说说这一个多月(从开学进入学校开始)我都干了些啥, 有什么样的收获吧.
1 实验室的体验
有幸通过了初筛, 获得了在暑期学校期间同步在实验室学长下接收指导的机会. 历时4周, 主要是观看老师的讲课视频并且总结, 加上阅读几篇行业内的论文并且用自己的语言在其他同学面前讲解.
第一周观看是王老师的课程视频, 主要讲了人工智能行业这几十年来的发展历程和这个学科的一些专业名词. 第二周是准备一篇有关MPNN(学长研究的领域)的论文的讲解ppt. 这篇论文主要是介绍了一种在图环境下的预测模型和算法, 具体来讲, 就是利用图中的信息, 对于每一个图中的节点的具体信息进行计算和预测, 最后生成整一个计算后的图. 这个论文用的是一个分子数据集, 由一个权威的机构提供.
其实这个体验过程还是蛮有意思的, 因为以前也没有去阅读过这么专业的论文. 直到真正去探查里面的内容, 才知道其实每一种研究课题都需要从始至终的认真态度. 比如说, 这篇论文中引用了前人的近十篇相关的研究内容, 然后选取其中一两篇的结果进行继续的研究. 里面的研究方法也体现了不同论文的成果的结合, 当然, 数据集本身的庞大也是这篇论文有意义的关键前提.做研究可能就是需要非常严谨和敬业的态度, 加上很好的资源, 才能有突出的成果吧.
2 独立开发-个人项目 [ChatGPT ChatTree🌳]
第二个很值得记录的经历就是暑期学校期间我进行的一个独立开发的项目-ChatGPTTree. 这个脚本是JavaScript语言编写的, 使用场景是 https://chat.openai.com/c/*
链接下的任意网页, 能够一键将整个对话流程(包括所有的分支对话)记录下来并且可视化这些数据. 由于整个对话过程就像一颗树一样生长(每一个对话都可以生出很多枝条), 所以取名”ChatTree”. 其实这个项目最初只是源于我自己的需求: 长期管理一个与ChatGPT的对话.
特别是当我和别人(某个平台拼车)合租GPT-4后, 我总是发现昨天的对话历史被别人莫名其妙删除了.导致我很重要的内容有需要GPT-4重新提供, 不仅浪费时间, 还浪费3-hours-50-times次数.
于是, 这个项目就显得尤为重要. 只要某一个对话曾今存在, 虽然它可能被删除了而无法查看或者继续进行, 但是我能够永久保存这个对话内容, 这不是听起来很酷的事情嘛.
于是我就从零开始学习网页脚本(JavaScript)的开发, 首先询问了GPT-4我需要提前学习什么知识, 于是我去网上找了HTML, CSS, DOM等的前端课程, 掌握了一些基本的开发方法和技巧, 然后就直接上手了.
开发历程
确定数据结构&&所有信息的存储方式
我们假设这是一个面向对象的操作, 每一个对话的类型名字叫做DialogueNode, 每一个新生成的DialogueNode都配有一个全局唯一的uuid.
第一个就是对于这个对话树如何选择合适的存储结构. 我的第一个想法就是, 每个对象有一个children(数组)属性,里面存储每一个其后代(直属的)的uuid. 这样就可以通过children这个属性获得所有的后代. 我之所以这么考虑, 是因为我担心如果children存的是对象类型, 会导致严重的内存占用.
但是这个思维是错误的. 实际上, JavaScript中, 几乎所有的变量都以引用存在. 所以, 即使是存储一个对象在children里, 也相当于存储一个变量的地址而已. 知道这一点后, 我就选择将所有的对话节点都存在rootNode内了.于是, 整个rootNode的children数组本身就是一个树状的结构.
1 | class DialogueNode { |
接下来就是如何将这些个对话树长期存储在使用者的电脑中? 询问GPT后得知, 有两种方案: localStorage 和IndexedDB. 前者能够存储大约5MB的数据, 后者则可以存储大到以G为单位的数据. 鉴于如果对话是长期维护且有多个对话树, 我选择了IndexedDB作为数据库.
虽然这样可以长期存储, 不过我后来发现如果C盘空间剩余较少的情况下(例如只有5G), IndexedDB的内容会被完全删掉, 所以需要预留足够多的空间来确保数据的存储, 并且, 在不同浏览器上运行的脚本的数据不会共享, 所以这里就引申出来了如何在更高维度来维护数据的问题
对话树的交互的设计&&实现
我用到的是d3.js这个库. 库其实最主要的就是帮我完成了树的绘制. 绘制一颗树其实是一门学问来的, 因为如何有效地排布这些节点之间的位置是很复杂的一项工作/算法. 我专门看了一些文章和项目, 自己本来动手实现了一个算法, 但是有两个缺点: 1, 节点没有紧密排布; 2, 页面更新/刷新速度太慢. 后来在gpt指导下, 去看了d3, 还挺不错, 于是就一步一步跟着学.
如果我们的tree已经可视化成功了, 那么我们比较希望可以做一些交互的操作, 例如, 当鼠标移入节点, 可能想要预览这个节点的对话内容. 当鼠标移出, 预览内容消失. 鼠标拖动, 这个节点和其下方的节点一起动, 鼠标点击, 我们就要详细查看这个节点, 甚至要让页面跳转到这个点击的节点的位置. 其实这些操作都跟addEventListener这个API有关. 本质上就是处理一些情况下的条件, 重点要使用uuid这个特别的属性.
这其实是整个项目的第二大难关, 不过毕竟暑期学校也没怎么去上过课, 所以白天(瞎说啥, 基本是12点才起床呢😂)和晚上有大把的时间来攻克一个一个问题, 那段时间, 基本上每一天都在学习新的函数/用法/算法/思维, 提升很大, 也很痛苦. 好在天天灌乐虎, 熬过来了.
附加功能的实现: 搜索, 注释, 复制内容
其实还不够, 因为仅仅展示一棵树并不能有什么作用, 我们要解决实际问题. 刚刚不是说了嘛, 可以跳转到某一个在页面上的实际位置. 那么这个节点本身也要被找到才行. 如何找到? 自然是搜索. 于是, 又自学了搜索的一些知识: anime.js(搜索框的动态展示和消失), bfs/dfs(这就是暑假学数据结构的好处了, 虽然这部分工作我也是交给gpt做, 但是本身熟悉这个树的知识点让我在搜索算法的选取上没有花费/浪费太多时间, 且这个算法部分的熟悉度让我的整个自信稍微起来一点), 提示方式, 转到上/下一个, 表单设计, 选项框等等的知识点.
注释也是很重要的, 因为这也是长期管理一个对话的必要的部分. 所以每一个节点都有一个comment的属性来存储注释, 并且要探测一些边界条件, 例如注释是否为空, 是否全为空格等等.
多语言(适配不同语言的用户)
其实这个项目在上线前还做了很多工作, 例如, 左下角有一个缩略图, 用来提示当前的位置在整个树的那一部分(虽然看起来有点鸡肋, 不过算了吧哈哈, 做都做了), 还有一些例如给不同的节点换上不同颜色, 每一个节点的信息都加上gpt或者用户的头像, 右上角详情节点的拖动/缩放及其边界条件的探测等等等等…. 不过这些展开来本来也很枯燥, 再一个也是我和gpt一起写的, 我也讲不出啥深度来, 就放一边去吧.
多语言也是一个很有意思的活. 我去查看了别的脚本(当然是下载量比较多的一类)是如何处理多语言这个问题的.有一个方法很有意思:
用这个方式:
1 | let keys = |
然后, 实际使用就:
1 | someDOMElement.innerHTML = `${translate(${文本1})}`; |
反正大概是这样吧, 看着有点唬人, 实际上这个方法就是建一个key和实际语言的文本之间的映射而已, 而且故弄玄虚在这里还先用中文建一个键的转换器, 其实完全没必要.
我的方法比上面简单, 但是不得不说是从上面找的灵感:
1 |
|
这样, 实际上我们只需要调用(或者说是写)一个翻译函数即可, 简洁明了. 唯一的缺点是, 我们在实际调用的时候需要用operationPhrase是英文(问就是喜欢), 对英文的理解要求能力有一定. 鉴于英语还不错且是个人开发, 问题不大.
这样, 任何页面上的元素理论上都可以实施切换成用户的语言对应的文本, 脚本的受众面广了不少.
发布&&推广
其实这里碰壁还是很多的, 一个是我需要(或者说是我想)发布两个调查问卷, 语言分别是中和英, 第二个是我的推广渠道? 我只在QQ群和微信朋友圈进行过推广, 不过效果还勉强过的去. 其实可以大胆尝试一些其它的方式, 例如拍个视频在b站上放一下啥的. 我在推上看到一句话其实很触动自己的:”程序员都有一个误区, 那就是好的产品就一定有更多的用户”. 确实, 一个只会写程序的人未必能给自己的产品卖出一个好的价钱, 特别是个人开发, 全栈都要会, 全程要参与/负责/介入, 说是累, 实际上成长了不少. Come On!
后记
其实这篇文章主要就是为了稍微总结一下由于某个过于夸张/逆天的想法而带动的脚本的开发的全程经历. 记录一下, 过几年回过来再看, 也许有不一样的感触也说不定? 从脚本开发起已经80天, 上线已一个月. 每天都还有很多新的点子浮现在脑海, 很多需要改的bug催促着自己, 很多用户的反馈(哈哈别骗自己了, 根本没多少反馈!)需要回复. 希望这一部车能跑得更远, 更快.