普通视图

发现新文章,点击刷新页面。
昨天以前Reorx’s Forge

Rabbit R1 - The Upgraded Replacement for Smart Phones

2024年1月10日 11:22

It all started with The Light Phone.

Living in a world full of technology and digital devices today, I constantly feel distracted, unfocused, and lost. The smartphone has become a part of our bodies, and we have already evolved into a new species - cyborgs. We no longer solely rely on our biological brains for looking and thinking; much of our important information is stored in our phones, our new “organ.” However, it is dumb in comparison because the only efficient way to interact with it is to use our fingers, which is much slower than other natural organs connected and controlled by neural networks. I want to get rid of it, but deep down I know I cannot live without it.

After discovering The Light Phone, I realized that we may not rely on apps and information as much as we think. However, despite initially being intrigued, I eventually found myself unable to resist returning to my iPhone 7. This begs the question: why did I make this decision?

The Light Phone

It’s a downgrade of a person’s capabilities. I can no longer take photos wherever I find something interesting, listen to music whenever I feel like it, or call a taxi wherever I want to go.

It shouldn’t be this way when it comes to enhancing our lifestyle. From a young age, I have never held a high opinion of those individuals in history who chose to live in the wilderness as a means to find mental tranquility. I greatly admire those who can actively engage in society while maintaining a clear mind, living in a manner that brings them personal satisfaction.

Years passed, and it suddenly came to the era of LLMs. One day, I found AI Pin, a wearable device made by Humane with the power of OpenAI. My eyes lit up; I sensed a similar aesthetic and philosophy to the Light Phone. However, that was not enough. The AI Pin has no screen, meaning that I cannot interact with it through vision and touch, a significant loss of HCI technology of the past 60 years.

AI Pin

That being said, AI Pin is heading in the right direction, because what wastes our time and distracts us when using smartphones are the tedious and complex operations with apps. LLM excels at converting natural language into a sequence of machine-understandable commands, which is clearly the way to reduce the friction of using a digital device. I feel that the future is near, only a few steps away, but I didn’t expect it to arrive so quickly.

Today, Rabbit R1 has been released, and I view it as a milestone in the evolution of our digital organ.

Rabbit R1

R1 is definitely an upgraded replacement for smartphones. It’s versatile and fulfills all everyday requirements, with an interaction style akin to talking to a human. Other devices, such as the Light Phone and AI Pin, though minimalist and distraction-free, fall short of enabling us to accomplish our daily tasks. The disadvantages outweigh the benefits in the ultimate goal of improving our lifestyle.

So, why is R1 great? In my opinion, it’s primarily due to the two new technologies it employs: Agent and LAM.

The concept of an Agent is simple: you instruct the AI what to do, and it will analyze your words to formulate a clear goal. Subsequently, it breaks this goal down into various tasks, orchestrates the completion of these tasks, and finally assembles the results to fulfill the goal. The Agent resolves the issue of LLM’s inefficiency at task completion. AutoGPT is the first proof-of-concept that has popularized the Agent. From a certain perspective, R1 can be perceived as a physical embodiment of an Agent.

The following is LAM, which I believe is the most impressive and exciting feature in R1’s presentation. LAM, or Large-Action-Model, possesses the capability to comprehend any user interfaces and act accordingly. Utilizing LAM, Rabbit has developed a framework. This endows R1 with its most potent tool - the ability to learn.

With the power of teaching mode, R1 can acquire a new skill simply through screen recording and voice instructions. In the presentation, Jesse showed how to instruct R1 to use Midjourney for creating images from user voice commands. This proves that R1 is not limited to what the developers built in; users can tweak it to tailor it to their own needs. There’s no compromise of losing capacities while maintaining a minimalist shape and intuitive use.

Someone once stated, the primary distinction between humans and robots is self-learning. While R1 cannot learn independently, it certainly serves as a commendable human companion.

Generally, I believe R1 has the potential to change the world. This is a thought that seldom comes to my mind, as I have seen numerous new technologies and inventions. However, R1 is different; it’s not just another device to please a certain niche. It’s meticulously designed to serve one significant goal for all people: to improve lifestyle in the digital world.

Debounce and Throttle

2023年12月21日 20:23

概念

Debounce 和 Throttle 是两种相似的频率限制手段。Debounce 顾名思义,去掉弹跳/抖动,能看出防止误操作的意味;Throttle 的意思是节流阀,更加直接了当。作为两种常见的设计模式,理解他们的工作原理和细微区别能够帮助我们写出更健壮的应用程序。

虽然是两个通用的概念,但它们的确主要在 JavaScript 中被提及和使用,究其原因,JavaScript 中常常出现连续发生的大量事件,如果不对调用频率做出限制,会产生严重的性能问题。且这些事件是可以舍弃的,一段时间内只需要产生一次有效调用即可。而在后端则很少出现这种情况,所有的事件都必须要处理,性能问题通常通过异步和分布式调用来解决。

下面是我对这两种设计模式的理解。

  • Debounce: 将间隔不超过设定时间的多次连续调用变成一次。

    从工作原理上来讲,Debounce 会使目标函数变为延迟生效,当对其进行连续多次调用时,若前后两次调用的时间间隔不超过设定值,则前一次调用会被取消。直到某次调用后,在设定的时间内没有出现下一次调用,那么这次调用将不会被取消,从而最终被执行。

  • Throttle: 确保一个函数被连续多次调用时,在设定时间内最多只实际执行一次。

放在一起对比的话:

  • 相似之处:Debounce 和 Throttle 都限制了函数执行的最大频率不超过每设定时间一次
  • 不同之处:在快速(间隔小于设定时间)连续调用时,Throttle 确保了函数会规律执行,但 Debounce 只有当连续调用放缓(间隔大于设定时间)时才会执行。

应用场景

设想如下几个场景:

  1. 当用户改变网页窗口的大小后,调用一个函数以调整 UI 布局
  2. 当用户在滚动浏览网页内容时,根据内容所处的位置,持续更新大纲目录中的链接高亮
  3. 在一个输入框的下方,让搜索提示结果随着用户的输入持续不断地更新

我们一起来看看每个场景分别应用 Debounce 和 Throttle 会有什么样的效果,并评判哪个是更加合适的频率控制方式。

场景 1

用户按住鼠标不松一直改变窗口大小,使用 Debounce 的情况下,UI 在用户停顿或者松开鼠标时才会改变;使用 Throttle 的情况下,用户会观察到 UI 在拖拽窗口大小的过程中每隔一会改变一次,容易给人一种反应迟钝或卡住的错觉,因此 Debounce 是更好的选择。

场景 2

用户持续向下滚动鼠标滑轮,使用 Debounce 的情况下,大纲的高亮只有当用户停止滚动时才会更新。所以当用户一次性滚动很长时,只能看到一次高亮的改变,中间仿佛跳过了一般;而使用 Throttle 的情况下,随着用户滚动,高亮会稳定地以设定的时间间隔更新,因此 Throttle 是更好的选择。

场景 3

用户以较快地速度连续输入字符,使用 Debounce 的情况下,只有当用户停止输入时搜索提示才会更新;使用 Throttle 的情况下,搜索提示会稳定地以设定的时间间隔更新,但如果用户输入最后一个字符的时间,正好处于上一次调用后的间隔期,无法触发新的调用,那么用户所看到的提示就不是根据完整的输入内容做出的。Debounce 由于能够保证函数总是在用户停止输入时执行,是比 Throttle 更好的选择。

库的使用

首选 lodash,因为它是一个非常流行且久经考验的库。但如果不想让整个 lodash 混入项目的构建结果,可以安装 lodash.throttlelodash.debounce 两个独立的库。如果你使用的 bundler 支持 tree-shaking,也可以通过 lodash-es 来 import 这两个函数,最终构建结果中只会包含与之相关的代码。

npm 狂魔 sindresorhus 也维护了两个包,debouncethrottleit, 如果你想要更简洁的实现,可以考虑使用。

下面以 lodash.throttle 为例,展示其如何在一个 TypeScript 项目里安装和使用 :

npm i lodash.throttle
# 还需要额外安装 `@types/` 的类型定义
npm i -D @types/lodash.throttle

引入和调用:

import throttle from 'lodash/throttle';

const onScroll = () = {/* 实现细节 */}

// onScroll 执行的最高频率为每 100 毫秒一次
document.addEventListener('scroll', throttle(onScroll, 100));

后续思考

这篇笔记来源于重构 GitHub TOC Sidebar 扩展时对场景 2 的思考,之前用的是自己手写的 Debounce,在滚动过程中经常看不到 ToC 的高亮变化,这次换成了 lodash.throttle,终于达到了预期的效果。其实这三个场景我都在过往的开发经历中遇到过,并且是在不了解这两个概念的情况下独立思考出了(简陋或丑陋的)解决方案,直到最近才重新审视,阅读了相关的文章,学习了更好的实现方式。这也是为什么我在关于状态机的短文中感叹基础知识的重要性,如果能更早地知道这两个概念,就能避免曾经在黑暗中摸索的痛苦。当然,因为自己琢磨过,当看到更系统更高级的实现时,就会有更深刻的理解,这大概是这位推友希望自己是通过 Vanilla JS 学习前端的原因。

Twitter

如果重学前端,我肯定老老实实地用原生 JS 去完整地做几个项目,去 appendChild,去切身体会一下手动管理一切状态,命令式更新 UI,还要时刻让它和状态保持一致的痛苦。然后再去学框架,把这些项目重写一遍。 很遗憾我没有写过 jQuery,太早接触框架的最大问题就是对它们在解决什么问题理解得不够深刻

— Sixian (@noworkforsixian) December 19, 2023

一言以蔽之,开发遇到困难免不了自己琢磨,但在琢磨时多想想能否将问题定义出来,符合一个已有的概念,然后去参考现实世界中系统和标准的解决方案;如果没有也无所谓,未来某一刻这种思考过程会化作某种领悟,不会白费。

参考资料

Window Opener for Chrome

2023年4月4日 11:46

最近又做了一个新的扩展——Window Opener,这篇文章介绍它的动机、开发过程和用法说明。

Window Opener - Chrome Web Store

动机

我平时主屏的窗口布局一般是 Chrome 占 3/5 靠左,VSCode 占 1/2 靠右,交叠的部分一般不会影响两边的浏览。 最近关于 AI 的新闻几乎都从 Twitter 上获取,我很希望它以一个单独的窗口出现在主窗口的右侧,这样我在打开来自 Twitter 的链接时仍然可以继续向下滚动,得到更好的浏览体验。 我在 Moom 1 上添加了让窗口以 1/5 的屏幕大小靠 Chrome 右侧的布局,但仍然觉得很麻烦,因为我在专注工作时会关闭 Twitter,而每次打开时,都要走一遍 [打开新窗口] → [输入 twit 回车] → [快捷键唤出 Moom] → [快捷键应用布局] 的流程。于是我便想,要是能够一键把 Twitter 在当前窗口的侧边以特定大小打开就好了,既然没有这样的工具,何不自己做一个呢?

开发过程

如果你对此不感兴趣,可以直接跳到下一个章节查看插件的功能和用法介绍

说干就干,我从自己的 webpack-chrome-boilerplate 脚手架中复制了 vanilla-ext 到新的项目,为它取了一个简单直接的名字 window-opener. 我的脚手架的 tech stack 为 TypeScript + Webpack,其中内置了一些常用的库比如用于 DOM 操作的 cash-dom 和用于记录日志的 loglevel,不过最重要的一个包是 @reorx/webpack-ext-reloader, 这是我维护的用于自动重载扩展的工具,能够减少开发时每次保存就要手动点击 reload extension 的心智负担。

为了快速实现一个 demo,我首先想到的是让扩展的图标点击就可以打开 Twitter。我在 manifest.json 里添加了下面的配置

  "action": {
    "default_title": "Open a window"
  },

这使得当扩展的图标被点击时可以触发一个事件,从而执行打开新窗口的操作。以下是 background.ts 的代码:

chrome.action.onClicked.addListener(async () => {
  const window = await chrome.windows.getCurrent()
  const context = {
    windowWidth: window.width ?? 0,
    windowHeight: window.height ?? 0,
    screenWidth: 2560,
    screenHeight: 1440,
    xOffset: 58,
  }

  const windowArgs = {
    left: context.windowWidth + context.xOffset,
    top: 0,
    width: context.screenWidth - context.windowWidth - context.xOffset,
    height: context.screenHeight,
  }

  chrome.windows.create({
    url: 'https://twitter.com',
    focused: true,
    ...windowArgs,
  })
})

一个简单的 Proof-of-Concept 便完成了,点击扩展,便会在当前窗口右侧打开高度和屏幕一致、宽度占满剩余空间的 Twitter 窗口。这里用到的最核心的 API 是 chrome.windows 2,实现了当前窗口大小的获取,和新窗口的大小、位置的控制。为了计算出相对于屏幕的空间,我将自己所用屏幕的大小赋值给了 screenWidthscreenHeight, 但这样做不具备通用性,我希望动态获取当前窗口所在屏幕的大小。讽刺的是,Chrome 扩展的 API 竟然无法实现3,经过各种尝试,最终我通过在扩展的设置页获取 window.screen 对象的方式得到了这些数值。(注意这里的 window 并非 chrome.windows.Window, 而是 DOM 的 window。)

核心功能完成后,我又为扩展增加了易于使用的界面。如果是非常简单的扩展 (比如 refgen,未来会写篇单独的文章介绍), 我会直接使用原生的 DOM 接口来实现页面交互,但这次我感觉到编辑界面有一定的复杂度,于是将脚手架换为 webpack-chrome-boilerplate 中的 react-ext,用 React 来增加代码的模块化和可维护性。

一直以来我一直都习惯用 Vanilla JS 来调用 Chrome 扩展接口,但引入 React 后不得不考虑状态管理,于是我找到了 use-chrome-storage,它能够以 hooks 的方式获取和保存扩展数据,使我免于用 useEffect 重新实现。下面是代码示例:

/* define settings store hook */

export interface Settings {
  iconAction: IconAction
  windows: WindowData[]
}

export const INITIAL_SETTINGS: Settings = {
  iconAction: IconAction.defaultWindow,
  windows: [],
}

export const useSettingsStore = createChromeStorageStateHookSync(STORAGE_KEY, INITIAL_SETTINGS);


/* use settings store hook */

const Popup = () => {
  const [settings, setSettings, isPersistent, error, isInitialStateResolved] = useSettingsStore();

  if (!isInitialStateResolved) {
    return (
      <div>loading</div>
    )
  }

  return (
    ...
  )
}

在 Options 页面中,我实现了一个窗口管理器组件 WindowsManager,它会循环渲染所有窗口的编辑界面,而每个窗口都需要用到 chrome.windows.Window 来计算。我不希望每个窗口都调用一次 chrome.windows.getCurrent,便想在整个页面初始化时获取 Window 对象,向下传递给子组件来使用。如果传递的层级很深,React 推荐的方式是使用 useContext4,但我觉得比较麻烦,而且不够灵活,于是引入了 zustand 来做全局状态的同步。下面是代码示例:

/* define app store hook */

export interface AppStore {
  chromeWindow: chrome.windows.Window|null
}

export const useStore = create<AppStore>()((set) => ({
  chromeWindow: null,
}))


/* use app store hook */

// options.tsx: update chromeWindow
chrome.windows.getCurrent().then(window => {
  useStore.setState({
    chromeWindow: window
  })
})

// WindowManager.tsx: get chromeWindow
const WindowItem = ({data, defaultId, onDataChanged, onDelete}: WindowItemProps) => {
  const chromeWindow = useStore(state => state.chromeWindow)
  const context = getContext(data.staticContext, chromeWindow!)
  ...
}

以上是一些开发中的心得和收获,如果你有更多兴趣,可以直接阅读源码。还有一些技巧不再赘述,以下是一个简单的列举:

  • 通过 chrome.action.setPopup 实现切换点击扩展按钮的行为(显示 popup 或触发 action click 事件) → code-0, code-1
  • 通过 chrome.windows.onBoundsChanged 监听窗口大小的改变,并控制事件的发生间隔 → code
  • 使用 expr-eval 进行数学表达式计算,避免使用 Chrome 扩展所不支持的 evalcode
  • 使用 data url 创建一个临时的窗口来显示错误信息 → code
  • 通过 key 属性值的变化使得设置了 defaultValue 的 input 元素在 rerender 时仍可以改变数值 → code

用法说明

Options

在安装了 Window Opener 之后,首次点击扩展按钮,会打开设置页面:

  • Icon action: 点击扩展按钮的行为,有两种模式,Open Default Window 会直接打开默认的窗口,Open Windows List 会打开窗口列表供选择。
  • Windows: 用户自定义的窗口,在这里进行添加、修改和删除
  • Backup and restore: 导出和导入扩展配置。点击 Export 会直接下载 JSON 格式的配置文件。若要导入,请先点击 Choose File 选择文件,再点击 Import 完成导入。

点击 Create 按钮,开始创建第一个窗口。下面的截图是我定义的用于满足最初需求的 Twitter 窗口。

参数说明如下:

  • Name: 窗口名称
  • URL: 窗口链接
  • Type: 窗口类型,normal 是平时使用的正常窗口,popup 仅有边框,没有地址栏和扩展按钮
  • Focused: 是否在打开后聚焦到该窗口
  • Default: 是否为默认窗口。仅能设置一个,需要先取消勾选才能更改为其他窗口。
  • left: 窗口到屏幕左边的像素距离
  • top: 窗口到屏幕顶部的像素距离
  • width: 窗口的宽度
  • height: 窗口的高度
  • Context: 用于参与 left, top, width, height 表达式计算的变量
    • windowWidth: 当前窗口宽度,动态数值
    • windowHeight: 当前窗口高度,动态数值
    • screenWidth: 屏幕宽度。静态数值,与窗口绑定(以下3个变量与此相同)
    • screenHeight: 屏幕高度
    • xOffset: 屏幕X轴的不可用宽度,比如 MacOS 的 Dock 放在屏幕左侧就会使得一部分空间对于窗口来说是不可用的。
    • yOffset: 屏幕Y周的不可用高度,比如 MacOS 的 menubar。

这里我希望 Twitter 在当前窗口的右侧,而我的屏幕将 Dock 放在左侧,因此新窗口距离屏幕左侧的距离 left 应该是 xOffset + windowWidth;与屏幕顶部的距离 top 可以简单设置为 0,Chrome 会考虑 menubar 所占用的空间,自动将窗口下移,也可以像我这样精确设置为 yOffset。宽度 width 要填满右侧可用空间,因此是 screenWidth - (windowWidth + xOffset);高度 height 则可以直接使用 screenHeight,与 top 同理,超出可用长度的部分会被自动处理,也可以填为精确计算的数值 screenHeight - yOffset。

Tips: 要实现一个宽 600px, 高 500px 的居中窗口,请参考以下参数

width=600, height=500, left=(screenWidth - 600) / 2, top=(screenHeight - 500) / 2

Popup

当 Icon action 设置为 Open Windows List 时,就可以打开 popup,界面如下:

蓝色 ★ 表示默认窗口,鼠标点击窗口条目即可打开。

下方 Settings 是设置页的链接。点击 Create from current window 会基于当前窗口的 URL, left, top, width height 创建新的窗口。

目前 Popup 存在 accessibility 上的问题,应该使所有按钮可以通过 tab 键切换 focus,实现仅用键盘导航和打开窗口。

Keyboard shortcut

Window Opener 默认的快捷键是 ⌘ ⌃ T, 也可以在 chrome://extensions/shortcuts 进行自定义。

结语

开发 Chrome 扩展越来越成为我的一大爱好。浏览器是我们在赛博世界赖以生存的基本工具,能让它变得更好用,意味着我可以用更短的时间做更多的事,并享受更好的体验。开一个新的 side project,可以让我短暂离开主线任务和生活中的琐事,专注在具体明确的目标上,不仅是精神上的放松,也是对开发技术的淬炼。

希望你能喜欢 Window Opener :)


  1. Moom 是我使用多年的窗口管理工具 https://manytricks.com/moom/ ↩︎

  2. https://developer.chrome.com/docs/extensions/reference/windows/ ↩︎

  3. chrome.system.display 可以获得所有屏幕的数据,但无法知道当前窗口所在的是哪一个屏幕,而 Window Opener 需要在 background 中运行,此时是无法使用 DOM 的 window.screen 对象的,因此最终将 screenWidth, screenHeight 设计成了绑定在每个用户添加的 window 上的静态数值,但可以在编辑界面动态更新。 ↩︎

  4. Passing Data Deeply with Context ↩︎

用 AI 工具快速撰写分享型推文

2023年3月11日 11:44

前几天刷到一个 YouTube 视频,内容是斯坦福大学一位教授介绍它对 AI 的看法和 “AI Thinking” 思维观,感觉很有收获,于是就一如既往地想把我的所得分享到 Twitter 上。以往我都是自己来写推荐语,或许是受视频主题的影响,我便想到,要不要试试用 AI 来帮助我完成这次内容创作呢?

由于近期 ChatGPT 的火爆,我早已经安装尝试了许多基于 ChatGPT API 的工具,甚至自己开发了一些,因此也没有花功夫去寻找其他的,直接使用已有的工具来完成创作流程。

第一个也是最重要的工具是 Glarity,它是一个 Chrome 扩展,为许多网站提供使用 ChatGPT 提取摘要的功能,比如 Google、YouTube、GitHub 等,效果很好,非常实用。Glarity 支持 OpenAI API 和 ChatGPT Webapp 两种后端,前者需要用户自己提供 API key,后者只需要登录 ChatGPT 网页版即可使用,完全免费。Glarity 的开发者来自中国,近期他们在 Product Hunt 发布了产品,大家可以多多点赞支持。

Glarity 在 YouTube 播放器右侧注入了一个小组件,点击 “Ask ChatGPT to summarize” 即可用当前视频的字幕生成摘要,效果如下:

Use Glarity to summarize a YouTube video.

由于字幕是英文,因此总结内容也是英文,我使用 BobOpenAI Translator 插件将摘要翻译成了中文:

Use Bob to translate Glarity summary into Chinese.

看着效果还不错,读起来也很通顺,但是内容太长了,如果想发表到 Twitter 上,就要受到 280 个字符(140 个汉字)的限制,需要再精简一下。用我自己写的 Drafts 插件 ChatGPT Conversation,通过定义 system message 创建了一个专门用于生成社交媒体内容的笔记,让它基于上一步的翻译结果进行创作:

Use Drafts to generate social media content.

结果为:

斯坦福AIRE项目主任李江指出,未来许多工作将被人工智能代替,因此向下一代传授AI思维尤为重要。AI思维包括理解AI运作方式、与其合作完成任务及区分人类和人工智能的能力。江建议通过教授设计思维培养创造力和创新精神。他鼓励将人工智能视为强大工具,利用它创新并产生新想法。让我们一起学习AI思维,迎接未来挑战吧!

只需稍稍修改便是一个合格的文案了。不过其实我是自己把第二步的翻译结果进行了重编排,推文如下:

Twitter

斯坦福教授李江讨论了人工智能对就业和教育的影响,他强调 AI Thinking 的重要性,这包括了解AI如何工作、区分人类和机器,并与AI合作完成任务。他还建议通过斯坦福 Design Thinking 方法论来提高孩子们的创造力、共情与定义问题的能力,帮助他们在未来竞争中取得优势。https://t.co/n6yy0v1nLg

— Reorx (@novoreorx) March 7, 2023

总结

本文探索了使用 AI 技术将有趣的内容总结成文案后发布到社交网络上的流程。用到了以下工具:

虽然是一个简单的任务,但有 AI 的帮助确实加速了生产力,如果我从头看完视频 + 自己写,至少需要 30 分钟,AI 则可以将这个步骤压缩在 5 分钟内完成。

当然了,这对我来说只是一个实验,我并不想完全以这种方式来创作,一则我并非内容农场,我只创作我认为有价值的内容;二则使用 AI 生成并不能帮助我去思考或深入了解问题。写作是一个创造性的过程,我享受它所带来的成就感,甚至挫败感,它们都能使我得到成长。但我依然非常喜欢 GPT AI,因为它会持续优化我的生产力,帮助我分担非创造性劳作,让我能投入更多时间在创造性工作上。这又让我想起那句话,技术本身是无罪的,只看你怎样使用它。任何结果都是使用者的选择,而不能将原因归咎在完成这件事的工具上。

A Message to GPT-API Product Makers

2023年3月8日 15:37

I regularly check new GPT-APT-based products for my awesome list every day. Recently I found Zeeno.ai and Monica. They both look promising but lack the ability to customize API keys, so I left some comments on Product Hunt asking if this feature could be supported. Luckily, they both responded with a positive attitude.

Additionally, the maker of Monica replied:

I’m a bit curious, is using a personal API because the daily free quota is not enough?

This is an interesting question because I initially asked for this feature solely from a developer’s point of view: utilizing my own key would decrease expenses and provide more autonomy. But for product makers, it’s not worth creating a feature for a small group of people with technical background when the majority of users are normal individuals. That seems to be the case, and the common practice for GPT-API-based products is to build a subscription model around the usage amount—a reseller of the upstream API. So why bother telling the user that you can bring your own key if we want them to think highly of our technology?

Well, there are still reasons to do so. Let me explain:

  1. What the user truly cares about is the usability of your product. Although some may not be aware of OpenAI, the number of competitors in the market continues to grow rapidly on a daily basis. Therefore, providing this option will not have a negative effect on how normal individuals think about your product.
  2. The developer’s favor is a cost-free marketing promotion. As a developer, I appreciate transparent and customizable products. I will spare no effort to promote your product for free through writing articles or posting tweets, as long as I find it powerful and customizable, and I believe other developers are the same as me. YouTubers may also appreciate the opportunity to create tutorials teaching their viewers how to use great products for free by utilizing “certain technologies”. We will spread its popularity.
  3. Those willing to invest in a tool are less concerned about API keys, because they prioritize convenience and time over cost. Consequently, we can assume that conversion rates for subscriptions are constant. The more users a product has, the higher the likelihood of gaining subscribers. It’s straightforward logic, isn’t it?

I am aware that, in many cases, backend logic can be too complex to allow for this feature. However, where possible, I recommend enabling the customization of API keys, which would be a mutually beneficial solution for both of us. So, what is your opinion on this?

谈谈我对 ChatGPT 应用的 prompt 的看法

2023年3月5日 23:08

上一篇更新中我讲到自己基于 ChatGPT API 做了一个校对和润色文字的 Popclip 插件,叫做 Popclip Proofreader。由于 ChatGPT API 本身非常简单,这个插件的核心价值其实是我调教出的可以稳定、准确完成润色这一任务的 prompt。

在见识过越来越多的新产品后,我越发认定,prompt 是一个 ChatGPT 应用的灵魂,甚至未来 prompt 本身就可以成为应用。并且 prompt 关系到用户所输入的信息如何被使用和上传(到 OpenAI),因此我衷心希望所有基于 ChatGPT API 的产品都能向用户公开其所使用的 prompt,保持透明,尊重用户隐私。更何况 prompt 的使用交流无论是对于用户还是开发者都很有价值,任何人都不必敝帚自珍。

我们正身处一场技术发展和下放所产生的时代变革之中,作为一个开发者,应该清醒地认识到,我们开发的应用的价值是由所有参与到生成式 AI 与大型语言模型的学术研究、数据训练、软件开发的学者和公司所赋予的。应带着感激和尊敬,挖掘和普及这项技术的价值,使它能够应用在更多的场景中,为更多的人带来便利。这是我关于开发 ChatGPT API 工具的使命和初心。

Prompts for proofreading

说回正题,我来分享下 Popclip Proofreader 所使用的 prompt:

system: I want you act as a proofreader. I will provide you texts and I would like you to review them for any spelling, grammar, or punctuation errors.

user: Proofread the following content and give me the result without extra delarations or comments:

你可能会好奇,user message 似乎已经包含了足够多的信息,为什么还要有个 system message 呢?我的初衷是通过 system message 传递所有的任务要求,我给了它两个指示,1. 检查文字中的拼写、语法、标点错误并更正;2. 返回的信息不要包含额外的评论和声明。第二点的目的是为了避免输出中包含 “Here’s the corrected sentence:” 这样的无用信息,但结果却不甚理想,这种声明仍然时不时会出现。

于是我查阅了 OpenAI 的文档1,发现这样一段话:

Many conversations begin with a system message to gently instruct the assistant… In general, gpt-3.5-turbo-0301 does not pay strong attention to the system message, and therefore important instructions are often better placed in a user message.

也就是说,ChatGPT 对 system message 的遵从程度没有那么高,一些重要的指示最好还是放在 user message 中。所以我最终将第二个指令放在了 user message 中。但其实这么看来,system 更适合在较长的对话中定下基调,而单条信息的查询只用一条 user 来表达是最好的。

下面我收集了一些同样目的的 prompt 供读者参考和尝试。

可选项 1,来自 ChatGPT Grammar Check PopClip Extension

user: Please correct the grammar and polish the following sentences, do not provide any translation, comments, or notes, and use the same language as input

可选项 2,来自读者 Yu Bai

user: Rewrite the text in authentic English

可选项 3,来自 OpenAI Polisher Bob Plugin,括号部分可以去掉

user: Revise the following sentences to make them more clear, concise, and coherent (Please note that you need to list the changes and briefly explain why)

Other people’s thoughts

其实除了 prompt, ChatGPT API 的其他参数 (parameters) 也影响最终结果的产生,因此在公布 prompt 的同时,也应该将这些参数囊括进来。推友 @mr_easonyang 甚至认为参数应该成为自定义选项,我认为这是很好的提议:

Twitter

OpenAI 的 API 没太多可调的参数,其中比较好玩的应该是 temperature 和 top_p 二选一做微调,说白了就是定制 AI 的严谨(正经)程度。

几番测试下来,我觉得 temperature 0.5~0.8 时比较适合知识型问答、1.2 左右比较适合聊天瞎侃。

希望各位工具开发者们考虑下允许用户对这两个参数做自定义配置。 pic.twitter.com/svfMYgTF6B

— Eason Yang (@mr_easonyang) March 4, 2023

推友 @daydayuuup 也表达了对 prompt 无法控制的担忧:

Twitter

在想一个问题,大家在谈「咒语」(prompt)对于回复质量的重要性,我们在使用CHATGPT的时候是自己主动输入「咒语」,如果使用开发者创建的应用,比如翻译,那么翻译的提示语质量可高可低,我们还控制不了,所以要谨慎选择开发者,选择质量高的应用。否则可能会影响翻译质量。对不? https://t.co/cZHeaCKHsq

— 天天 (@daydayuuup) March 5, 2023

推友 @xxm459259 说 ChatGPT 类产品很容易被下游开发者复刻,我深以为然。一开始大家可能都会想把 prompt 作为壁垒,但对想要破解的人来讲,看一眼可能就猜个七七八八,自己调试下说不定效果还更好了…所以我觉得 prompt 还是公开的好,更快推动 Prompt Engineering 的发展,作为开发者也一定能在更好的环境中获利。

Twitter

其实我这段时间也用 ChatGPT 写了很多小工具,能尝试的 demo 几乎都尝试了一遍,甚至中间一度也想找一个合适的方向搞一个相对成型的产品,而非 Demo。
现在让我踌躇不前最主要的原因其实是壁垒,我觉得下游开发者复刻太容易,即便不知道你的 Prompt 看一眼产品也能猜个七七八八。。

— Micro 小熊猫 (@xxm459259) March 5, 2023

推友 yeaphgel 说到细分行业融入工作流组织化才能形成真正的壁垒,我很认可这个观点,这样的市场环境是我希望看到的。一个有追求的产品应该在工具的深度上做竞争,而不是急功近利地圈地抢人,如果你做的东西不够好,用户最终还是会流失掉。

The “Open Prompt” project

在写这篇文章的时候,我产生了一个想法——发起一个名为 Open Prompt 的开源项目,由社区参与者共同收集和维护各种 AI 产品的 prompt,附带详细的说明,供用户和开发者审阅和参考。与 Awesome ChatGPT Prompts 不同的是,每个产品的 prompt 都会有一个单独的页面,大家可以在这里看到 prompt 版本的变化,提交反馈和参与讨论,使这里成为 AI 爱好者们学习和交流的平台。如果读者们对此项目感兴趣,或有不同的想法,欢迎与我联系和讨论。

The market value of AI prompt

上文中提到的,未来或许 prompt 可以直接成为应用,今天就看到了 PromptBase 这个网站,看来 “Prompt as a Service“ 已经指日可待。这个网站可以搜索和买卖各类 AI 产品的 prompts,除了 ChatGPT 和 GPT-3,还有之前备受关注的 text-to-image 领域的 Stable Diffusion, DALL-E, Midjourney 等等。我随便查看了几个 ChatGPT 的,有教你如何赚钱的 “Eary Money Now”, “The Business Genie”,也有自媒体博主非常需要的 “Never Run Out Of Instagram Ideas”, “Social Media Weekly Content”,价格大都在 $1.99 到 $4.99 之间,卖得好的已经有上千次出售。

看来 prompts 真的有很大的商业价值,或许我关于 prompts 与参数透明化的想法太简单了,但我仍然相信开放才是进步的源动力,就像 OpenAI 完全可以给出更高的定价,完全可以不将 API 开放,继续让更多的人购买 ChatGPT Plus,但他们却把 AI 技术下放,使人人都可低价使用和获利。这种做法的背后即便是为了更大更长远的商业利益,也是我所欣赏和赞扬的,并在事实上推动了文明的进步。

ChatGPT Proofreader extension for Popclip

2023年3月2日 23:36

昨天 OpenAI 在博客中介绍了新的 ChatGPT API 1, 并且已经发布上线。这个消息令所有关注生成式 AI 开发者为之狂热,我也不例外。我有一大堆点子想要通过 OpenAI 的接口实现,而 ChatGPT API 几乎解决了旧 API 存在的所有问题。

但一个好的开发者应该是务实的,这些点子想要实现还要费一番功夫,有什么地方是马上可以用 ChatGPT API 得到改善的呢?这时我看到了 Popclip ChatGPT extension 的推文 2,并立刻发现这是个非常好的点子。Popclip 是个运行在 MacOS 上的软件,它会在选中的文字上方展示一个提示栏,点击其中的按钮就可以对文字进行快捷操作,如复制、粘贴、查询字典,用户也可以安装更多扩展以实现更多的功能。

Popclip ChatGPT extension 实现了将选中的文字作为 prompt 发给 gpt-3.5-turbo 模型并将返回结果粘贴在下方的功能,并能保存上下文。不过我的评价是,这很 cool,但没啥用,不如直接打开 ChatGPT 网页对话。我想到我经常使用 ChatGPT 的需求之一——校对和润色文字,于是我基于这个扩展做了一个新的扩展,取名为 ChatGPT Proofreader.

功能和用法

ChatGPT Proofreader 提供了中英文两种语言的润色功能,用法如下:

  • 选择英文后,在 Popclip 中点击钢笔图标,润色后的英文将被粘贴在选中文字的下方。
  • 选择中文后,在 Popclip 中点击「润」图表,润色后的中文将被粘贴在选中文字的下方。

安装和配置

首先你需要运行 Popclip,然后拖拽鼠标选中下方完整的代码片段,在 Popclip 中点击 “Install Extension ChatGPT Proofreader” 即可完成安装。之后扩展会提示你输入 OpenAI 的 API Key,点击确认便完成了配置。

// #popclip extension for ChatGPT
// name: ChatGPT Proofreader
// icon: iconify:fluent:calligraphy-pen-24-regular
// language: javascript
// module: true
// entitlements: [network]
// options: [{
//   identifier: apikey, label: API Key, type: string,
//   description: 'Obtain API key from https://platform.openai.com/account/api-keys'
// }]

const prefixes = {
    "native": "Paraphrase the following sentences to make it more native:\n",
    "revise": "Revise the following sentences to make them more clear concise and coherent:\n",
    "standard": "Correct this to standard English:\n",
    "polish": "Please correct the grammar and polish the following sentences, do not provide any translation, comments, or notes, and use the same language as input:\n",
    "authentic": "Rewrite the text in authentic English:\n",
    "ielts": "Rewrite the text using IELTS standard:\n",
}
async function chat (input, options, lang, prefixName) {
  const openai = require("axios").create({
    baseURL: "https://api.openai.com/v1",
    headers: { Authorization: `Bearer ${options.apikey}` },
  });

  let messages
  switch (lang) {
    case "en":
      messages = [
        {"role": "system", "content": "I want you act as a proofreader. I will provide you texts and I would like you to review them for any spelling, grammar, or punctuation errors."},
        {"role": "user", "content": `Proofread the following content and give me the result without extra delarations or comments:\n\n${input.text}`},
      ]
      break;
    case "zh":
      messages = [
        {"role": "system", "content": "你是我的写作助手,检查接收到的文字的拼写、语法错误,对其进行润色,向我提供修改后的文字。"},
        {"role": "user", "content": `修改和润色下面的文字,直接输出修改后的结果,不需要额外的声明:\n${input.text}`}
      ]
      break;
  }
  if (prefixName) {
      messages = [{"role": "user", "content": `${prefixes[prefixName]}${input.text}`}]
  }

  const { data } = await openai.post("/chat/completions", {
    model: "gpt-3.5-turbo",
    messages,
  });
  const result = data.choices[0].message;
  return input.text.trimEnd() + "\n\n" + result.content.trim();
};

exports.actions = [
{
  title: "ChatGPT: proofreader en",
  after: "paste-result",
  code: async (input, options) => chat(input, options, "en"),
},
{
  title: "native",
  icon: "text native",
  after: "paste-result",
  code: async (input, options) => chat(input, options, "", "native"),
},
{
  title: "revise",
  icon: "circle revise",
  after: "paste-result",
  code: async (input, options) => chat(input, options, "", "revise"),
},
{
  title: "polish",
  icon: "square filled polish",
  after: "paste-result",
  code: async (input, options) => chat(input, options, "", "polish"),
},
{
  title: "ChatGPT: proofreader zh",
  icon: "square filled 润",
  after: "paste-result",
  code: async (input, options) => chat(input, options, "zh"),
},
];

Alternative Prompts

本扩展所使用的 prompt 如下:

system: I want you act as a proofreader. I will provide you texts and I would like you to review them for any spelling, grammar, or punctuation errors.

user: Proofread the following content and give me the result without extra delarations or comments:

你可以尝试以下 prompt,或使用自己调整的内容。

可选项 1,来自 ChatGPT Grammar Check PopClip Extension

Please correct the grammar and polish the following sentences, do not provide any translation, comments, or notes, and use the same language as input

可选项 2,来自读者 Yu Bai

Rewrite the text in authentic English

可选项 3,来自 OpenAI Polisher Bob Plugin,括号部分可以去掉

Revise the following sentences to make them more clear, concise and coherent (Please note that you need to list the changes and briefly explain why)

其他:

Native

Paraphrase the following sentences to make it more native:

Standard

Correct this to standard English:

ChatGPT API 的优点

ChatGPT API 打开了我们对工具的想象力,但要着手去拿它做些什么,首先需要了解其特性。我在 Twitter 发表了一个 thread 3来总结其优点,希望能帮助你节省一些时间。以下是完整内容:

ChatGPT API 相比 ChatGPT 的一些优点。

  1. 显式定义角色。在新的 ChatGPT API 中,消息增加了 role 属性,表示其所属的角色,其中 system 用于定义接口的行为,比如「你是一个写作助手」, userassistant 则用于区分用户输入和模型输出。这使得创造特定需求的助手变得更加清晰明确。
  2. 选择性地去掉会话中的信息。ChatGPT 最大可以存储 4096 个 token(大约 16384 个英文字母)的上下文,当一个会话的内容超出这个数量,最前面的信息就会被遗忘。而 ChatGPT API 每次都要将完整的上下文传递过去,这意味着我们可以选择保留重要的信息,选择性地去掉一些无用的以避免超出限制。
  3. 返回多个结果供选择。通过传递 n 参数,可以一次性返回多个不同的结果,适合文字润色、短内容生成等场景,避免多次重复问询。
  4. 使用 logit_bias 参数调整特定词汇(token)在结果中出现的可能性,实现特定词汇屏蔽的功能。
  5. 使用 temperature / top_p 参数调整结果的相关性和准确性。当我们需要发散思维、拓宽脑洞时,可适当调低结果的相关性;而当提供的上下文足够多,需要做精确的分析时,则可调高相关性,减少 编造内容的比重。

  1. Introducing ChatGPT and Whisper APIs ↩︎

  2. From twitter.com/PopClipApp/status/1631040246841319427:

    PopClip ChatGPT extension snippet — updated to use the new GPT 3.5 chat API, so you can actually chat with it.https://t.co/xo8xaGABHs pic.twitter.com/gM916fL7io

    — PopClip for Mac (@PopClipApp) March 1, 2023
     ↩︎
  3. From twitter.com/novoreorx/status/1631250035852861440:

    Thread🧵: ChatGPT API 相比 ChatGPT 的一些优点。

    1. 显式定义角色。在新的 ChatGPT API 中,消息增加了 role 的属性,表示其所属的角色,其中 system 用于定义接口的行为,比如「你是一个写作助手」, user 和 assistant 则用于区分用户输入和模型输出。这使得创造特定需求的助手变得更加清晰明确。 pic.twitter.com/35Yzb4sE4Z

    — Reorx (@novoreorx) March 2, 2023
     ↩︎

思考生活与生命在英语中的区别

2023年2月11日 14:41

写笔记时想到一句话:

掌控时间不仅能使我的生活变得更好,同时也是使我的生命更有价值

尝试翻译成英文:

Being in control of time not only makes my life better, but also gives my life more value.

这里「生活」与「生命」都用了 life,如何体现汉语意思中的区别呢?

在中文里:

  • 生活:强调的是一个日常的、持续的状态。当我们谈及生活,大多有着较为具体的感受。
  • 生命:是一种对自身存在意义和价值在精神层面的评判,相较「生活」更加抽象。

我将这个问题发到了 Twitter 上1,并询问了身边的人,陆续收到了一些改进的建议:

  • 推友 @Sunset16094839 提出生活可以翻译为 “my days”:

    Being in control of time not only makes my days better, but also gives my life more value.

  • 表妹(英语老师)的建议:
    • 将生活换成 living;makes 换成 leads to;value 换成 valuable:

      Taking control of time leads to not only a better living but also a more valuable life

    • 不要刻意去区分,直接合并

      Taking control of time leads to a better and more valuable life

  • 女友的建议:
    • 在翻译「生活」时加上一个修饰词,如 daily/working 等。这符合「生活」更加具体的涵义。

      Being in control of time not only makes my daily life better…

    • 根据 “Life” - 生活 vs 生命 vs 人生 | Ci Shifu Vocabulary Mastery Course - YouTube 视频里的讲解,「生活」可以用 to live 来表达:

      Being in control of time not only makes me live better, but also gives my life more value.

  • 推友 @7id 同样认为生活可以用 living 来替代,并且给了很多深入而专业的解读,让我大呼过瘾2:

    关于“掌控时间”的说法,可能你想强调的是时间管理或自律这个概念吧,单说字面翻译 DeepL 更好一些,master 更接近 manipulate 的含义。二是“生活”如果是指物质层面,英语一般习惯用 standard of living 描述,精神层面 quality of life 用得多。

    再就是“生命价值”这个概念,中文里可以说价值多还是少,而英文里 life value 更偏重价值观,对应的是忠诚、正义这样的词,所以这个翻译不是很恰当。然而 value of life 也不行,这个词是形容一条命值多少钱的。我觉得 self actualization 更符合这个语境。

    最后就是两个细节,回到汉语的思维模式,“使我的生活变得更好”显然没有“改善我的生活”更地道,英语也是一样的,像 improve/enhance 这些词自带改变的含义。还有中文的“不仅/还”是隐含的递进关系,强调后面的部分,字面对应的 not only 在关系强调上要若一些。

我自己在写出了「生活」与「生命」的中文意义时,就有了一个想法,那就是这个翻译的原文是可以优化的。汉语思维中我们习惯了说让生活变得更好,但在英文里可以扩展到更具体的意思——到底是生活的哪个方面变得更好了?作为出题人,我很快就想起,当时是因为读到时间管理对工作效率的提升,才感慨其能使生活变得更好。这也非常符合英语写作中不断推导来完善证据链的技巧。

综合各种建议,我最终把翻译修改为:

Being in control of my time not only improves my productivity at work, leading to a better life, but also enables me to reach higher levels of self-actualization.

也许还有一些错误,但翻译的结果其实已经不重要了,由这一句话延伸出的学习过程才是最有价值的。


  1. From twitter.com/novoreorx/status/1623978507327127553:

    英语怎么区分生活和生命?比如我想说「掌控时间不仅能使我的生活变得更好,同时也是使我的生命更有价值」。生活强调的是一个日常的、持续的状态,而生命是一种自身存在意义的评判。图片是 DeepL 的翻译,我也写不出更好的,但感觉并没有表达出我想要的意思。 pic.twitter.com/FQJfPvx29A

    — Reorx (@novoreorx) February 10, 2023
     ↩︎
  2. From twitter.com/7id/status/1623989480062599169:

    生命用 life 比较好,生活可以用 living 来替代。

    对于表达不到位这个事我有个猜测,你用英语的文法习惯在构思,但找不到恰当的词。“构思”说的是你拿“掌控时间”这样一个抽象概念做主语,汉语里习惯是用具象事物做主语。英语里要区分近义词,要么换个词根,要么换个表达方式,生命和生活就是后者。

    — 唐僧 (@7id) February 10, 2023
     ↩︎

Some random thoughts on Generative AI

2023年2月8日 21:40

ChatGPT 带来的震动一次又一次出圈,最近也在不断地看到各类基于 GPT 的新产品。科技圈里,Google 刚刚宣布了 Bard 1的消息没两天,Microsoft 便发布了与 ChatGPT 结合的 Bing 和 Edge 2,两大巨头在生成式 AI 领域的大型科技与商业竞争正在白热化。在这个人人都在谈论 AI 的时刻,我也有几个观点想要分享一下。

What’s next for GPT-based products?

目前我认为基于 GPT 的 Generative AI 最适合做的事情也是最强的功能之一就是 summarization——生成摘要。群聊 tldr (https://chat-simplifier.imzbb.cc/) 是个好点子,但是缺少 integration ,使用上的不便使其节省时间的价值大打折扣。

我设想一个 telegram chat tldr 工具,它会持续同步聊天记录的 archive。当我一觉起来看到一个群 999+未读,我可以向它询问最近12小时的聊天摘要;或者工作了一下午,看到300+条未读,我也可以指定要最近300条的摘要。它会先查询消息再格式化后输入给 OpenAI API 得到结果。

与其他短文本的信息服务整合也同理,如 twitter archive(过去一周我在关注什么?心情如何?吃了什么?);twitter timeline(这几天中英文 tl 分别在讨论什么?);rss news headlines(概述一下过去一个月的热点事件)。

应用到笔记软件,可以把一段时间的 daily notes 生成出 weekly/monthly summary,适合雇员写周报,或者PKM实践者做总结。

现在 GPT 虽然可以给个人用,却都是基于通用的数据集,只能短暂而有限地使用个人数据,比如 ChatGPT 所能保存的上下文最多为3000词。下一步突破,我希望是做到人人都可以用个人的大量数据(浏览器历史记录、所有看过的文字)持续输入,训练出专属的 AI,届时我们便能得到“上周我看过一个关于xx的网页或者推文,帮我找找是哪个”这种问题的答案了,或可诞生 GPT-powered personal search engine 这样的产品。

新技术的应用时机

OpenAI 和 ChatGPT 的成功,以及仅仅月余便百花齐放的 GPT-based products 给我的另一个启发是:当一个新技术成就了革命性的产品,技术便会下放,此时才是它被广泛应用的最好时机

收藏工具 mymind 发布时,距离 ChatGPT 的出现还有一两年时间。mymind 一开始就给自己贴上了 AI 的标签,标榜为新一代的智能收藏工具,做了 AI 分析、摘要、搜索等功能,但整体的市场反响却平平。为何 mymind 没有得到足够多的关注呢?我认为主要是因为在 ChatGPT 之前,AI 被喊了太多年,就像「狼来了」的故事,人们早已失去了开始的好奇心,见怪不怪了。而彼时 AI 技术的确没有突破性的价值,加上过于滥用,最终成了廉价的噱头,在国内科技界尤其如此。

直到 ChatGPT 横空出世,带来了十倍、百倍于之前的 AI 产品的提升,这才重使人们燃起对 AI 的兴趣和热情。而 OpenAI 提供的 API 使任何产品都可以方便地接入,实现接近 ChatGPT 的 AI 功能,大大降低了 AI 技术的应用门槛。此时虽然利好 AI 相关的产品,但 mymind 所宣传的功能不再具有壁垒,未来也没法有很强的竞争力。因此我认为 mymimd 在 AI 上投入是创新的、值得肯定的,但从收益上来看是个失败案例。

《风暴英雄》对我的意义

2023年1月20日 21:52

《风暴英雄》培养了我与人竞争的能力。

从小我就是个胆子比较小的人,遇到与人正面对抗的事情就会犯怵,人多的时候说话都不利索。但接触电脑游戏后,我渐渐察觉到对抗的乐趣。一开始我玩的游戏对手都是 AI,由于它们不是「人」,卸下了我潜意识里的恐惧。我独自玩可以玩得非常好,因为我其实并不怕失败和尝试,每次重来我都能学到新的东西,而电脑程序的逻辑是有穷尽的、不会进化的,最终都会被我攻克。但我的性格仍旧没有改变,一旦我知道对手是人,就会下意识地想要回避、担心失败,发挥不出原本水平的一半。在玩 FPS、RTS、MOBA 游戏时都是如此。

刚开始玩风暴时,由于其出色的美术效果,和暴雪人物所带来的亲切感,我很快就喜欢上这个游戏。但我只敢玩「合作模式」,即匹配 5 个玩家和 5 个 AI 对局。就这样玩了一年多,我对每个英雄的每个天赋都熟悉到倒背如流,所有的组合都已经试过无数遍,合作彻底失去了乐趣,我这才怀着忐忑的心情迈出了和真正的玩家对战的第一步。我先是玩了很久「非排名模式」,这个模式顾名思义,胜负不会计算分数和排名,但和排位赛一样需要通过 Drafting 1 阶段选择英雄。同样没有胜负压力的还有「快速模式」,并且可以直接选择你要玩的英雄,更加自由。我又把这两个模式玩了一年,当时我还使用了 HOTSLogs 这个工具上传我的对局 Replay,根据它的算法,我的 MMR 都已经到大师段位了。这时我已经彻底代谢掉了与人对抗的恐惧,开始享受起来。于是我迈出了最后一步,开始参与排名模式,一个人单排打到了钻石段位,这期间认识了许多好朋友,包括不少百强的宗师玩家,都乐于和我组队,甚至还和职业代打组队,帮他们打「单子」,做黄金联赛替补,留下了很多很多美好的回忆。

现在的我不再惧怕任何形式的与人竞争,并且喜欢上了竞争,肯定竞争的意义和价值。这些都与《风暴英雄》这个游戏有着密不可分的关系。当我写代码累了,或遇到不顺心的事情,总是喜欢在风暴里来一场紧张刺激的对抗,让肾上腺素的分泌激活我的精神。

敢于竞争是一种勇气,这与自己的水平高低无关。但要乐于竞争,还需要足够支撑竞争的能力和自信。这是另一个我从《风暴英雄》中获得的东西。我的反应速度、英雄操作只是中等偏上,但在《风暴英雄》中,我发现自己的观察力和对全局的分析和把握能力能起到更关键的作用,左右对局的胜负。比如在一个局部战斗结束后,无论击杀或被杀,我都能第一时间分析起接下来一分钟甚至到下个「机制」2开启之前要做的正确的事情。这听起来似乎不难,但对大多数玩家而言,一次战斗过后,几秒甚至几十秒大脑可能都在无意识地发呆,有的人甚至从来没有从宏观的角度思考过赢下一场游戏的策略。但对我来说,这种思考方式在刚开始和人对战后不久就察觉到了,仿佛是一种与生俱来的能力。依仗这种能力,加上经常打完后看录像复盘、经常看职业比赛视频和顶级选手的直播,我才能够单排打到较高的段位,得到最顶尖的一波玩家的肯定。

《风暴英雄》使我发现,在一个游戏中,我所擅长的事情完全可以转化成为我自己的优势,但前提是对规则足够的了解。现实中的竞争也可以看做游戏,同样有其规则和各种信息,因此只要发挥主观能动性,去思考和分析,就一定能把自己独特的优势发挥出来,取得出人意料的战果。这样的心态使我对任何形式的竞争都能怀着自信步入赛场。

还有 3 天《风暴英雄》连同暴雪全家桶的国服就要关闭了,这篇文章算是与风暴的正式告别。再见,《风暴英雄》,还有我们一起度过的 6 年难忘的时光!


  1. 国内玩家喜欢称之为 BP,即 Ban/Pick 的缩写,双方队伍互相禁止对方可以选择的英雄并确定己方英雄的过程。 ↩︎

  2. 全称是「地图机制」,是每个地图特有的中立道具或事件,在一场比赛中可以开启多次,类似 Dota 中的 Roshan 和 LOL 中的 Nashor,玩家通过争夺机制获得攻打对方防御塔的助力。《风暴英雄》正因为这一特性而具有所有 MOBA 端游中最频繁的团战节奏。 ↩︎

The debut of Substance: A HTML-to-Markdown extractor

2023年1月18日 00:00

In the past week, I’ve been creating a tool to extract the main content of the current web page and convert it to Markdown for archiving purposes. Currently, I’ve finished a Web app as a Proof-of-Concept or a preview version before the final release. Its only feature is to extract and download Wikipedia articles to markdown files. So here’s the link:

substance.reorx.com

To put it simply, the goal of the product is to be an alternative to MarkDownload with more extensibility. MarkDownload has been an excellent help for archiving content from the web, but it does not always work well on every website. Every now and then, I found it gives bad results for some websites such as Wikipedia (that’s why I take it as an example to work on at the very beginning).

After releasing this web app, I’ll focus on developing the extension and writing documents for the product. The code is open-sourced here though it has no README by far, but you can give me your feedback on the issues or reply here if you like.

In the next post, I’ll give a detailed introduction about what Substance really is and how it works.

Until next time, don’t forget to subscribe to my fresh-made newsletter to get the latest update.

「荒木型」与「三浦型」创作者

2023年1月13日 15:18

最近看 JoJo 石之海的时候,看到替身的战斗中竟然化用了莫比乌斯环和广义相对论的概念,又一次被荒木老师丰富的知识储备和想象力所震惊。之后在 B 站刷到一则荒木最近接受访谈的视频1,精神和皮肤都非常好,谈起自己的工作的样子充满热情,整个人都仿佛笼罩在一层光芒中。昨天又看到一则新闻,JoJo 第九部的主角设计已经公布,看来再画个二三十年也不成问题。

而我另一位喜欢的漫画家,《剑风传奇》的作者三浦健太郎,前阵子刚刚因病去世。在惋惜的同时,我也一直关注着后续的进展。这两天看到一篇文章2讲到,三浦对画作有着非常严格的要求,因此一直以来都没有培养自己的助手团队,直到 2019 年身体每况愈下才逐渐让助手介入到创作中,但一直到死前,他都负担着 90% 以上的创作工作。想到荒木的采访,他虽然坦言自己不擅长和陌生人交流,但和工作室团队却有着非常好的分工与配合。

荒木和三浦都是最出色、最具艺术追求的漫画家,但他们的性格和处事风格却截然不同。荒木良好的 work-life balance 使他拥有远低于实际年龄的年轻外观,粉丝们常以「荒木老妖」、「Jo 级生物」调侃;三浦却一直身体欠佳,最终因「主动脉夹层」而英年早逝。在和他人共事方面,荒木不仅有着多年培养的工作室团队协助漫画创作,使得 JoJo 系列问世 30 多年依旧保持稳定地产出,还与动画公司(大卫社)在动画创作中保持紧密地联系,如亲自挑选每一个 ED 的歌曲;而三浦习惯一个人战斗,随着年龄的增长更新速度越来越慢,直到去世也没能将《剑风传奇》完结,成为所有读者的遗憾。

我在他们身上看到两种开发者(创作者)的影子。荒木型开发者同时有着对生活的热爱和专业的追求,他们不仅能够开发出色的软件,在兴趣爱好上也有持续的投入和收获。由于思想的开放,他们也具有一定的管理能力和产品能力,这使他们能够在需要的时候组建和领导团队,把自己的作品带向更好的生产循环。

三浦型开发者如同独狼,或许是多年持续维护一个硬核软件的开源工作者,或许是隐匿在地下室的超级黑客,大家对他们的了解只有固定使用的 id。他们有着非常高超的技术,能够在一个事情上做到极致,但在现实中却缺少归属感,导致生活节奏混乱或行为孤僻。

希望我能成为一个荒木型的开发者。

“Moving away from UUIDs”, Really?

2022年11月23日 16:20

Recently I saw an article called Moving away from UUIDs – Neil Madden on Hacker News. The title immediately got my attention since I use UUIDs a lot in various projects, no matter personal or commercial. Whether I’m using it right or wrong is a vital concern from an engineering perspective, so I read it thoroughly and carefully. This article is my summary and thoughts on it.

Overall, the author gives an opinion that using UUIDs for unguessable random string like session tokens or cookies is a bad practice, the reasons are as follows:

  1. UUID is insecure in cryptography. In some situations, an attacker can take only 35 minutes to brute-force guess a valid result.
  2. UUID is inefficient in storing data. Because of its hexadecimal format and the use of extra dashes, a UUID takes 36 characters to represent 16 bytes of data.

As a replacement, the author suggests to use a 20 bytes random string that is URL-safe base64-encoded. Here’s an example comparing with an UUID string:

20 bytes base64 random: Xl3S2itovd5CDS7cKSNvml4_ODA
UUID                  : 5a097fe7-1720-457c-8363-8d660a65bab2

The advantages over UUIDs are:

  1. A 20 bytes random value is almost impossible to guess in a reasonable time.
  2. The length of the string is just 224 characters, resulting in much less storage space than UUIDs.

Generally speaking, I think although the conclusion of not using UUIDs for tokens is correct, the assumption is totally wrong. UUID (Universally unique identifier) as the name says, is an ID which should not be used for cryptographic purposes in the first place. The proper scenario for UUIDs is using it as primary keys in distributed systems, in which it prevents collisions without relying on a centralized identity generator. In contrast, random string has no way to achieve that.

I did learn something new from this article, but it failed to give me anything useful upon my understanding of how UUIDs should be used.

Do not write clickbait posts, as being neutral and accurate is a virtue for engineers.

离开国产 SaaS

2022年10月29日 17:31

昨天看到 @dingyi 的推文说:

国产软件真的没必要轻易尝试了……

原因是蜘蛛笔记发布了一个公告1,这款诞生不到半年的国产笔记服务因「战略调整」即将停止运营,甚至没有走出测试阶段,非常令人唏嘘,推友对此的猜测大都集中在行业萧条和内容审查上。

虽然不再尝试国产软件说得有些重,但我非常理解这种心情,因为我也认为,现在国内已经不再适合小公司的 SaaS 生存了。 在国内内容审查持续变严的大背景下,个人和小团体开发者将日渐减少,因为它们根本无法负担起内容审查的时间和金钱成本。 不久之前内容销售平台面包多暂时关停,随后创始人 DK 发布了公告2,表示在恢复后平台的敏感度和审查度都会大幅提高,以确保面包多可以在日益严苛的监管条件下存活下去。

站在行业生态的角度,我对国产应用总是抱有着鼓励和期盼,但站在个人用户的立场,我选择从现在开始尽可能不把重要数据放在国内小公司开发的线上服务上。毕竟数据的重要性高于一切。在服务随时可能关停、数据随时可能因审查而消失的情况下,真的没办法安心使用下去。

下面盘点一些我正在使用的包含重要数据的工具服务,并给出关于迁移方式和替代产品的建议:

Category Service Migration Alternatives
网页保存 Cubox Cubox 国际版 Pinboard Upgrade
Mem.ai
Raindrop.io
wallabag
HamsterBase
待办清单 滴答清单 国际版: TickTick Todoist
书影音记录 Douban 豆伴:豆瓣账号备份工具 NeoDB
生词本 Eudic 生词本同时存在于本地和云端,
导出也非常方便3,因此无需担心
目前还没见过其他
有云同步功能的字典软件
笔记 石墨/语雀/wolai等 我在这些服务上的文档很少,手动下载即可 Notion, Obsidian

Defeat VSCode Tab Bar

2022年10月18日 21:39

A while ago, I found my VSCode tab bar (or tool bar) UI was quite unstable. The reason was that extensions may add icons called “action button” on the right side of the tab bar according to the type of the file, so when switching back and forth between different kinds of files, these buttons will either show or hide, making the available spaces for tabs changing constantly. This may not be noticable when you just open a few tabs, but if you have tabs that are enough to take up the spaces of the tab bar, the whole tab bar will be a clown fiesta with the appearace and disappearance of the action buttons. Tabs are flickering due to their width change, the last one could even be pushed to the next line. It’s just so eyes-hurting and annoying.

After realizing who was the culprit, I immediately started to search for a solution. Sadly, VSCode itself did not provide a way to customize the side bar, but I was luck to find an extension called Customize UI, which allows me to inject css into the application, so that hack the UI whatever I want. I installed the two extensions and add the following lines of code in settings.json and boom, the world was quiet again.

{
    "customizeUI.stylesheet": {
        ".editor-actions": "display: none !important;",
    }
}

But this solution is not perfect, because Customize UI relies on the Monkey Patch Extension to achieve UI hacking, which always requires me to restart the VSCode immediately after I open it. That’s fine, I told myself, as long as I can get ride of those tab bar icons.

Today, I updated VSCode and was surprised to find that VSCode finally implemented a feature called Hide actions from tool bars, it says:

You can now hide actions from tool bars. Right-click on any action in a tool bar and select its hide command or any of the toggle commands. Hidden actions are moved to the … More Actions menu and can be invoked from there.

This means I can hide those buttons without th need of using the clumsy extensions. I tried to right click on an action button and select “Hide …”, it disappeared as expected. Then I uninstalled Customize UI and hide all the action buttons one by one. So yeah, this is the story of how I defeat the VSCode tab bar.

真正的好作品只能靠自己去发现

2022年10月10日 23:06

最近闲暇时间在看一本网络小说,名叫《异仙列传》,作者是流浪的蛤蟆。

蛤蟆是一位非常老资历的网文作者,我从 2008 年接触他的小说《蜀山》开始,不定期关注他的更新。蛤蟆的小说总是给人天马行空的感觉,想象力非常独特,情节自然流畅,主角大都洒脱不羁,仙气和痞气并存,有种与众不同的魅力。虽然许多书都没有完本(俗称「太监」),但读起来总是手不释卷,活泼泼让人快乐起来。

但这样一位有才华的作者,却一直没有多少读者认识他,为什么呢?

在网络小说发端的蒙昧时期,创作难,受众小,许多作者虽然创造出了很好的作品,但受限于市场,他们得到关注和回报都非常有限。后来,以唐家三少、我吃西红柿、天蚕土豆、梦入神机为代表的一批年轻作者,依靠简单的文笔、俗套但有爽点的剧情、极高的更新速率,让网络小说真正走进了普罗大众的生活中。网络小说的创作和阅读门槛被大大降低,与通俗文学渐行渐远,逐渐形成了现在无脑、爽文的刻板印象。

网络小说的发展史,就是一个劣币驱逐良币的过程。好的作品不会过于套路化,因为有追求的创作者总是在尝试突破和创新,因此不仅生产效率不会太高,还需要读者有一定的耐心和智力才能渐入佳境。但大众读者所需要的,其实不是文学意义上的好作品,而是一种工业品,即通俗易懂、能在尽可能短的时间里带来刺激和反馈的大量文字,并能持续稳定的获得。这些需求与好作品的创作条件背道而驰,不愿改变的作者自然难以成为主流。

这让我再次感受到,大众认为好的不一定对自己而言是好的。被大众认可,意味着要满足普适性的需求。要通俗易懂——简单无脑;要政治正确——阉割思想;要适合传播——利用人性的弱点,消费用户而不是被用户消费。在文学、影视、音乐、游戏这些由个人品味和主观感受来判断好坏的领域,普适很可能是对自己而言的不适。

所以,不要去看什么豆瓣电影 Top 250、网易云音乐热门推荐、Steam 畅销周榜了, 真正的好作品只能靠自己去发现。保持好奇心和对美的追求,它们会带你走向精神世界的探索之路。

P.S. 我关注的老资历而鲜有人知的网文作者,还有兰帝魅晨。另外,读者如果想了解早期优秀的网络小说,可以去搜索「网络文学十年盘点」。

我用过的位置追踪应用

2022年10月7日 01:18

国庆接连几天都在外面游玩,拍了许多照片,于是产生了一个想法:

有没有哪个 app 可以导入一堆照片然后把其中的地理位置信息连成一个轨迹?

在 Twitter 和 Telegram 上都被推荐了「一生足迹」这个 app,让我回忆起了之前用过的几款类似的应用,于是把它们一起记录如下。

  • Moves

    接触的第一款位置追踪应用,也是最喜欢的一个。除了有些耗电,各方面都几乎完美。我从 2014 年开始重度使用了一两年的时间,最后因为厌倦了手机发热而放弃。

    2018 年 Moves 被 Facebook 关闭,成为 FAANG 资本化的又一个牺牲品 https://about.fb.com/news/2018/07/hello-tbh-moving-on/

    In 2014, we bought the fitness app Moves. It records your daily activity — including walking, cycling and running. We’re deprecating the Moves app and Moves API on July 31.

  • Gyroscope

    2016 知道这款应用,一开始觉得很漂亮,但很快就审美疲劳了。Gyroscope 并非纯粹的位置记录工具,而是包含了各种 quantified-self 数据,却又做的非常社交化。我对分享自己的隐私数据没有什么兴趣,一年的会员结束后就卸载了。

  • Arc

    Moves 关闭没多久,出于对它的怀念,我找到了 Arc。客观的讲,Arc 也是一个足够优秀的应用,功能简单直接,没有什么多余的东西,但设计上平平无奇,没有了最初 Moves 给人的惊艳感受。

  • 一生足迹

    出自国内开发者之手,最大的特点是把省电这件事研究到了极致,果然优秀的创作者自己才是真正的用户。经过简短的试用,我确定这是目前最好的同类产品。

    不过一生足迹虽然可以导入照片中的位置信息,却没法将其连成轨迹,我只能继续寻觅其他 app,或者想办法自己实现了。

  • Rond

    2023-06-08 发现,官网 rond.azurewebsites.net

    Location-based life records, 不仅记录轨迹,还有丰富的统计功能,可视化查看时间花费的位置分布

浅谈 Chrome Manifest V3 的优缺点

2022年9月29日 23:39

自从 2018 年 Google 发布 Chrome 新的 Extension API Manifest V3 以来,不时会看到反对的声音,认为它限制了扩展的能力,降低了对用户隐私的保护云云。我自己虽然是一个 Chrome 插件开发者,也第一时间使用了 Manifest V3,却没有感受到具体的变化,因此一直对这些观点所描述的问题缺乏实感。

最近阅读了一篇文章,是 AdGuard 开发基于 Manifest V3 新插件的技术性回顾:
AdGuard publishes the world’s first ad blocker built on Manifest V3

这篇文章以 AdGuard 自身作为范例,对比 Manifest V2/V3 的差异,让我终于理解了 V3 API 变化的影响之所在。

简而言之,Manifest V3 对 AdGuard 造成了如下影响:

  • 规则数量限制

    • 每个扩展的静态规则数量被限制在了 30000 个以下,所有扩展的静态规则之和不能超过 330000 个。

      For static rules, Chrome set a minimum guaranteed limit of 30,000 rules per extension and a total limit of 330,000 rules for all extensions installed by a single user (this also takes into account the limit of 1,000 regexp rules per extension).

    • 预设的过滤器最多 50 个,用户自定义过滤器和规则之和不得超过 5000 个。

      blockers must use pre-set filters (no more than 50), and we have to be very selective about which filters will be available to users. Of course, you can still set your own filter manually. But don’t forget the 5000 rule limit on all custom filters and user rules.

  • API 变更: onBeforeRequestdeclarativeNetRequest

    • onBeforeRequest 是 Chrome Extension 中的事件回调函数,广告屏蔽插件的工作原理就是在这个函数中使用自己的一套逻辑来判断是否要中断当前的连接。从 Manifest V3 开始,这个函数就被废除了,取而代之的是 declarativeNetRequest,因此 AdGuard 必须将原来的私有动态规则转换成使用 declarative rules 所描述的新规则。

    • 而受 declarative rules 的语法所限,一些 AdGuard 原有动态规则的功能将无法被实现

    • rulesets(一组 declarative rules)只能在扩展安装和更新时加载,导致 AdGuard 失去了动态更新规则的能力

      declarative rules must be combined into rulesets

      Rulesets are specified in the manifest.json file and are loaded only when the extension is installed or updated.

  • 无法获得统计信息

    • 由于 Chrome 自己实现了一套拦截机制,Manifest V3 不会将请求的统计信息开放给生产环境下的扩展,仅能在开发模式下使用。这意味着 AdGuard 将无法真正确认到动态规则的执行情况,只能通过模拟运行来猜测规则是否可能被应用了,对于规则的维护者来说是灾难性的打击。

      Chrome itself now blocks requests and shares statistics only with extensions unzipped and installed in Developer Mode

      only show which rules may have been applied.

  • Service Workers 不能保证常驻,需要主动唤醒

    • AdGuard 的 cosmetic rules(在网页上屏蔽元素的规则)原本运行在 background.js 中,这个机制在 Manifest V3 中被 Service Worker 取代,由于不能常驻后台,cosmetic rules 有一定无法生效。

      When the browser stops the service worker, the extension goes into a kind of sleep mode: the declarative rules work, but the cosmetic rules that are loaded dynamically do not.

可以看出,AdGuard 这款新的 Manifest V3 插件在各方面都受到了「阉割」,无怪乎文章传达出了一种艰难求存、呼吁支持的态度。

在我看来,其实 Manifest V3 的核心非常明确,就是限制扩展对系统资源的使用。一直以来高资源占用都是 Chrome 为人诟病的痛点。相信每个用户都遇到过 CPU 或内存飙升的情景,许多人甚至说 Mac 没有 16G 以上的内存不要用 Chrome。而扩展由于在后台运行,如果出现问题,更是难以定位和管理,我想这就是 Google 的工程师想要做出改变的原因。

虽然增加了诸多限制,但我确实感受到 Manifest V3 的一些有益之处:

  • Service Worker 使得扩展不再能常驻后台,让扩展所占用的资源可以被回收,降低了浏览器整体的开销
  • 限制规则的数量,相当于控制了单一扩展在规则计算方面的资源使用上限
  • 以无法在运行时更新为代价,declarative rules 实现了更高效的动态规则
  • 不再提供请求底层的统计信息,缩短了请求生命周期中的调用链,提升了处理效率

这些变化可以让 Chrome 变得更加流畅,对于 99% 的用户来说都是好事。

未来的方向,也许 Chrome 可以提供一个 IPC 或者其他更有效率的通信方式,让另一个进程参与到请求的处理流程中,这样 ad blocker 插件的最大开销——规则匹配——就不再是效率和资源的瓶颈了。就像 LSP 的工作模式,把复杂的事情分离出来让独立的组件来做,说不定会诞生一个用 Rust 开发的新一代 rule filter engine 呢。

希望这篇文章能够帮助你理解 Chrome Manifest V3 的变化,辩证地看待它。

后记

补充一些本文完成后,与 Manifest V3 有关的趣闻

2022-09-29

昨天 Chrome 官方博客发布了一个声明 (More details on the transition to Manifest V3),将 Manifest V2 的废除时间从 2023 年 1 月向后推迟了一年:

Starting in June in Chrome 115, Chrome may run experiments to turn off support for Manifest V2 extensions in all channels, including stable channel.

In January 2024, following the expiration of the Manifest V2 enterprise policy, the Chrome Web Store will remove all remaining Manifest V2 items from the store.

再来看看两年前对废除 Manifest V2 的声明:

January 2023: The Chrome browser will no longer run Manifest V2 extensions. Developers may no longer push updates to existing Manifest V2 extensions.

从原本的斩钉截铁,变成现在的含糊和留有余地,看来强如 Google,想要执行一个影响全世界 65% 1 互联网用户的 breaking change,也不是那么容易呀。

2022-10-03

著名单文件网页存档插件 SingleFile 作者依照 Manifest V3 标准重构了原版插件,命名为 SingleFile Lite,并用宣传产品升级的语气描述了新插件的 “features” 和 “benefits”,非常讽刺,可以说是一场代码行为艺术了。

Feel the power of the Manifest V3. The future, right now!

Notable features of SingleFile Lite

  • unreliable auto-updates
  • no auto-save
  • save time limited to 5 minutes max.
  • no “Referrer” header injection
  • limited support for fonts dynamically loaded with the FontFace API
  • and more to come!

Benefits of the Manifest V3

  • none

Hacker news: Feel the power of the Manifest v3 | Hacker News

为什么人们在黄图群喜欢聊哲学

2022年9月29日 01:12

偶然看到一张网图,内容如下:

人类真矛盾,不管进啥群,最后都是搞黄色。反而进了黄色群,最后聊三观、聊实事。

以前也看过类似的说法,都一笑置之了,这次忽然觉得背后可能有着一定的心理学逻辑。

让我们来看看马斯洛需求层次 (Maslow’s hierarchy of needs) 这个广为人知的心理学理论。(中文世界里一般是五种,英文维基百科里有八种,这种差别让我忍不住想起 OSI 模型的四层与七层)

需求 Need 说明
生理 Physiological 级别最低、最急迫的需求,如:食物、水、空气、睡眠、
安全 Safety 对人身安全、生活稳定以及免遭痛苦、威胁或疾病、身体健康以及有自己的财产等与自身安全感有关的事情。
爱和归属 Love and social belonging 较高层的需求,常称为“社交需求”,如对友谊、爱情以及隶属关系的需求
尊重 Esteem 成就、名声、地位和晋升机会等。尊严需求既包括对成就或自我价值的个人感觉,也包括他人对自己的认可与尊重。
自我实现 Self-actualization 最高层的需求,包括针对于真善美至高人生境界获得的需求

以马斯洛需求层次来分析,许多人长期生活在性的需求得不到满足的状态下,「生理」这项需求占据了思想的主导,导致无心探讨群聊原本的话题,最终有意无意地开始性有关的交流。这就是为什么会产生「一切群最终都会变成黄图群」的情况。当然,这只是一种玩笑和夸张的说法,不能代表全部。

而黄图群则不同,由于摆明车马就是来搞黄色的,成员中一定有精通此道的行家,他们在性这一方面有能力得到满足,需求层次就会上升到社交和认知相关的区域,在群里聊起哲学话题就不让人意外了。

说到这里,我忽然想起朋友对技术群的吐槽。有些技术群虽然不搞黄色,却会有很多争论——哪个语言更好,哪个框架不好,为什么用A而不是用B——并且总是会上升到划分阵营、形成鄙视链的程度。用马斯洛的理论来看,或许是为了「尊重」这一需求吧:站定一个立场,维护自身的正确性,来获得他人的认可。不过我觉得要警惕这一行为,因为通过「做」而不是「说」来获得尊重和认同更有价值一些不是吗?

❌
❌