Normal view

There are new articles available, click to refresh the page.
Before yesterdayCode & Tech

在 M1 Mac 上运行 macOS 虚拟机

By: prin
26 November 2021 at 18:30

Apple M1 芯片问世一年有余,时至今日,在 M1 Mac 上运行 Windows、Linux 虚拟机的方法都已经比较成熟了。然而 macOS 本身的虚拟化却并非如此:直到 Monterey 发布,于 M1 Mac 上运行 macOS 虚拟机才成为可能。

最近有几个小实验需要在 macOS 虚拟机上跑,本来以为去 Parallels Desktop 上开一个就完事了,搜了一下才发现,其实事情没那么简单……实际配置过程中也是踩了几个坑,所以顺带记录一下。

前提

目前想要在 M1 Mac 上运行 macOS 虚拟机,有以下要求:

  • Host OS 和 Guest OS 都必须是 Monterey
  • 安装镜像必须是 IPSW 格式

为什么 Big Sur 不行?因为在 Virtualization framework 中运行 macOS 虚拟机是 Monterey 才加入的功能

那以前怎么就能虚拟呢?因为 ARM 架构的 M1 Mac 在引导上用的其实是 iOS 那一套,不是传统的 UEFI,所以苹果官方没提供 bootloader 的话自然没戏。黑苹果也是一样的道理,只能说且用且珍惜吧。

至于 .ipsw 文件,这玩意其实就是 iOS 固件的格式……真就大号 iPad 呗!

IPSW 镜像文件可以在这里下载:Apple Silicon M1 Full macOS Restore IPSW Firmware Files Database – Mr. Macintosh

Veertu's Anka

这个是我目前最推荐的一种方法,所以放在第一个说。

Anka 是什么?根据官网的介绍,Anka 是一个专门用来管理 macOS 虚拟机的软件,可以与现有的基于容器的 DevOps 工作流集成,为 iOS 应用的构建与测试提供 CI/CD 自动化支持。再看下其开发者 Veertu,也是做 iOS CI 和 macOS 云这一块的。

并且今年十月发布的 Anka 3.0 (beta) 已经支持在 M1 Mac 上创建 macOS 虚拟机了,正是我们所需要的。

下载 Anka M1 beta 版,安装后打开,就可以直接通过图形界面创建虚拟机了:

anka-m1-beta

或者,你也可以使用命令行创建虚拟机(相关文档在这里):

anka create --ram-size 4G --cpu-count 4 --disk-size 80G \  --app ~/Downloads/UniversalMac_12.0.1_21A559_Restore.ipsw 'macOS 12'

运行虚拟机:

anka start -uv

Anka 默认将虚拟机存储在 ~/Library/Application Support/Veertu/Anka 目录下,可以参考这里修改保存位置,或者干脆做个软链接也行。虚拟机的配置文件也在同目录下的 config.yaml 文件中,有些图形界面不提供的配置项可以在这里修改。

也可以使用命令行修改,比如修改虚拟机的分辨率和 DPI:

anka modify 'macOS 12' display -r 2560x1600 -d 220

另外,Anka 提供的 Guest Tool 会自动打开虚拟机内 macOS 的自动登录、SSH 并且阻止系统休眠(应该都是为了自动化服务的),并且提供了剪贴板共享、anka cp 文件复制,以及可以直接在虚拟机内执行命令的 anka run 等功能。

anka-macos-vm

不过有一个需要顾虑的是 License 的问题,在 beta 期间可以免费使用 Anka 没问题,但不知道正式版发布以后如何。不过原本 Veertu 家面向个人开发者的 Anka Develop 就是免费的,所以或许并不需要担心。

或者,你也可以使用本文最后提到的开源方案,体验也是不错的。

Parallels Desktop

毕竟是 Mac 虚拟机行业名声最响的,其实我第一个想到的也是 PD。

查了一下,macOS 作为 Guest OS 是 PD17 才支持的功能(前略,天国的 PD16 用户),然后 17.1 更新添加了 Parallels Tools 的支持,还提了一嘴「虚拟机默认磁盘大小从 32 GB 增加至 64 GB」。

我最开始还不知道这有什么好拿出来说的,后来才知道原因:你在 PD 中甚至无法调整 Mac 虚拟机的磁盘大小。不仅是磁盘,CPU 核心数、内存大小、网络连接方式都不能改,可配置项为零(至少无法在图形界面中配置),完完全全就是个半成品。

如果你确实想安装,这里是官方教程:Install macOS Monterey 12 virtual machine on a Mac with Apple M1 chips

点「新建虚拟机」以后,安装助手里就有直接下载 macOS 的选项。看起来很友好,然而……

pd-vm-installation-failed

啃哧啃哧下载了半天,最后提示「安装系统时出错」,也不知道为什么。查了下官方 Knowledge Base,貌似也不是个例:Inability to create a macOS Monterey 12 VM on Mac computers with Apple M1 chips

后来我找到了这篇文章:Customizing MacOS guest VMs in Parallels 17 on Apple Silicon,按照其中的介绍,通过命令行创建虚拟机,竟然就可以运行了……

/Applications/Parallels\ Desktop.app/Contents/MacOS/prl_macvm_create \  ~/Downloads/UniversalMac_12.0.1_21A559_Restore.ipsw \  /Volumes/xxx/Parallels/macOS\ 12.macvm \  --disksize 80000000000

我之前用 17.0.1 版本的时候也尝试用 prl_macvm_create 创建虚拟机,但是在进度到 90% 的时候失败了,提示「内部虚拟化错误。安装失败」。升级到 17.1.0 后虽然安装助手还是「安装系统时出错」,但命令行是可以正常创建虚拟机的。

命令行启动虚拟机:

/Applications/Parallels\ Desktop.app/Contents/MacOS/Parallels\ Mac\ VM.app/Contents/MacOS/prl_macvm_app \  --openvm /Volumes/xxx/Parallels/macOS\ 12.macvm

安装完成后,在 PD 控制中心可以导入 .macvm 格式的虚拟机文件,导入以后就可以从图形界面启动了。

pd-macos-vm

作为一个商业虚拟机软件,且不说快照、Suspend,连最基本的 VM 管理功能都欠奉,我也是无话可说了。想知道还有哪些功能是目前还不能用的,可以查看 Known issues and limitations

MacVM

MacVM 是一个开源项目,基于 Virtualization framework(当然啦,大家都是用的这个),提供了简单的图形界面用于配置虚拟机。

因为作者并没有提供编译好的程序,所以需要自己使用 Xcode 从源码编译。

下载源码,用 Xcode 打开 MacVM.xcodeproj,在 Signing & Capabilities 中修改为自己的开发者证书:

macvm-xcode

点击运行,会跳出来一个文件选择框,不用管先叉掉。

然后菜单栏 File -> New,新建虚拟机。输入 CPU 核心数、内存和磁盘大小后点菜单栏 File -> Save 保存,会生成一个 .macosvm 包。之后虚拟机的虚拟磁盘镜像也会保存在这个 bundle 中,所以要留意选择保存的位置。

macvm-new-vm

然后点 Select IPSW and Continue 按钮,选择之前下载的镜像文件,点 Install,等它安装完就好了。(最开始的版本还要自己生成磁盘镜像,然后拷贝到应用容器中,还要用 Apple Configurator 2 手动装系统,相比起来现在已经友好很多了)

安装完成后,窗口会整个儿变黑,此时就可以点右上角的启动按钮启动虚拟机了。

macvm-success

用这种方法优点是开源,有啥不爽的都可以自己改,包括没有提供配置项的地方。缺点就是要自己编译,毕竟不是谁都装了 Xcode 的。

跑起来以后和上面两种基本没差别,因为实际的虚拟机创建、安装和运行都是 Virtualization framwork 实现的,整个项目的代码其实并不多。

GitHub 上还有一些类似的项目,这里也列出来供参考:

最后

以上三种方法,其实底层大家都是一样的,就看在此之上谁做得更完善了。综合来看,目前感觉 Anka 的使用体验是最好的。

关于 M1 Mac 运行 macOS 虚拟机的一些参考链接:

UTM 有一个 dev-monterey 分支,我还没有尝试,不知道以后会不会推出支持 macOS Guest 的版本。

听说还有人使用 OSX-KVMDocker-OSX 跨架构在 M1 上运行了 x86 的 macOS,但是性能很糟糕(simulation 嘛)。

另外,以上的这些虚拟机方案都不支持快照恢复,有点麻烦。不过好在我用来放虚拟机的移动硬盘是 APFS 格式的,支持写时复制 (Copy on write),所以直接把镜像整个儿复制一份就好了,很快,也不会占用多余的存储空间。

在 M1 Mac 上构建 x86 Docker 镜像

By: prin
17 February 2021 at 05:38

今天闲来无事,数了一下服务器上在跑的东西,打算把它们都扔到 Docker 里面去。第一个开刀的就是之前写的 Google Analytics 博客阅读量统计,很简单的一个 Node.js + Express 程序。

写完 Dockerfile 测试好,正准备 push 上去时,我才突然想起来:

我现在用的是 M1 MacBook,丫的默认 build 出来的镜像是 arm64 架构的呀!

好在 M1 Mac 上的 Docker Tech Preview 也支持使用 buildx 构建多架构的镜像,稍微设置一下就可以了。

题外话,M1 MacBook Air 真的很好用,建议早买早享受(

启用实验性功能

Docker 的 buildx 还是实验性功能,需要在 Docker Desktop 设置中开启,具体位于 Preferences > Experimental Features > Enable CLI experimental features

新建 builder 实例

Docker 默认的 builder 不支持同时指定多个架构,所以要新建一个:

docker buildx create --use --name m1_builder

查看并启动 builder 实例:

docker buildx inspect --bootstrap
Name:   m1_builderDriver: docker-containerNodes:Name:      m1_builder0Endpoint:  unix:///var/run/docker.sockStatus:    runningPlatforms: linux/arm64, linux/amd64, linux/riscv64, linux/ppc64le, linux/s390x, linux/arm/v7, linux/arm/v6

其中 platforms 就是支持的架构,跨平台构建的底层是用 QEMU 实现的。

构建多架构 Docker 镜像

使用 buildx 构建:

docker buildx build \  --platform linux/amd64,linux/arm64  --push -t prinsss/google-analytics-hit-counter .

其中 -t 参数指定远程仓库,--push 表示将构建好的镜像推送到 Docker 仓库。如果不想直接推送,也可以改成 --load,即将构建结果加载到镜像列表中。

--platform 参数就是要构建的目标平台,这里我就选了本机的 arm64 和服务器用的 amd64。最后的 .(构建路径)注意不要忘了加。

构建完 push 上去以后,可以查看远程仓库的 manifest:

docker buildx imagetools inspect prinsss/google-analytics-hit-counter
Name:      docker.io/prinsss/google-analytics-hit-counter:latestMediaType: application/vnd.docker.distribution.manifest.list.v2+jsonDigest:    sha256:a9a8d097abb4fce257ae065365be19accebce7d95df58142d6332270cb3e3478Manifests:  Name:      docker.io/prinsss/google-analytics-hit-counter:latest@sha256:bb7f3a996b66a1038de77db9289215ef01b18e685587e2ec4bb0a6403cc7ce78  MediaType: application/vnd.docker.distribution.manifest.v2+json  Platform:  linux/amd64  Name:      docker.io/prinsss/google-analytics-hit-counter:latest@sha256:94ea08ac45f38860254e5de2bae77dee6288dd7c9404d8da8a3578d6912e68e7  MediaType: application/vnd.docker.distribution.manifest.v2+json  Platform:  linux/arm64

可以看到已经是支持多架构的镜像了。

参考链接

English speaking skills for PTE test

I shared my excitement about the PTE test result in the previous post. In this post, I will detail the English-speaking skills I learned during the three-month learning journey, which specifically meet the PTE test criteria.

PTE’s criteria

Let’s take a look at the two key criteria in the PTE’s speaking component: Pronunciation and Fluency.

Pronunciation and Oral fluency criteria of PTE test

Based on these criteria, I would like to highlight these key points:

Pronunciation:

  1. Vowels and consonants;
  2. Word stress;
  3. Sentence-level stress;
  4. Assimilation and deletions.

Fluency:

  1. Rhythm and phrasing;
  2. No hesitations.

As we can see, the PTE’s test criteria clearly show concepts we must fully understand and perfectly present if we want to achieve a higher score. In the following text, I will present my comprehension of these concepts, supported by related online resources.

Pronunciation

Vowels and consonants

In everyday conversations, sightly mispronounced words often do not significantly disrupt the flow of our discussion. But thanks to modern technology, PTE’s scoring is based on algorithms and is implemented by computers, which can easily detect each mispronunciation. Therefore, the ability to pronounce words clearly and accurately is crucial.

I have tried numerous methods to improve my pronunciation and reduce Chinese accent including speaking loudly, having more emotion, and directly imitating local accents. However, it didn’t work as expected, it did not meet my expectation, resulting in a low score in PTE practice.

Changes occurred the time I met Sun’s tutorials and BBC Learning English collection on YouTube. These pronunciation videos elaborate on vowel and consonant details, with vivid body language and emotion.

As non-native speakers who want to pronounce concisely, we must focus on these particular points:

Mouth Shape
We can try to imitate the mouth shape that vowels and consonants request. For example:

  1. When pronouncing /æ/ sounds like Agriculture and Activity, we should open our mouths as large as possible;
  2. When pronouncing /i:/ sounds like These and Feed, the corners of our mouths should be as far apart as possible.
Mouth Shapes of /æ/ and /i:/

Tongue Position
We should also pay attention to the tongue position. For example:

  1. The /θ/ sound requires us to extend the tongue forward;
  2. The /r/ sound requires us to pull the front part of the tongues back and up and keep the back stable, making our tongues fatter and thicker.
Tongue Position of /θ/ and /r/

Breath
We should also carefully control our breath, ensuring vowels and consonants are presented appropriately. For example:

  1. When pronouncing /θ/ like Through and Thesis, we should ensure that the airflow passes through the gap between our teeth and lips;
  2. When pronouncing /b/ and /p/ like Big and Picture, we should ensure that the airflow is completely blocked and then released suddenly.

Focusing on these points helps us ensure our study paths are on the right track, and consistently improve our pronunciation. In addition, there are two tips:

  1. Don’t try to imitate accents. The PTE test does not expect you to have a perfect British or American accent. For beginners, attempting to achieve such an accent is time-consuming and pointless.
  2. Never compare pronunciations between English and Chinese. For instance, the Cantonese pronunciation 士多啤梨 is often equated with Strawberry in English, which overlooks many nuances. Therefore, it is important to acknowledge that the pronunciation of English and Chinese words are completely different.

Online Resources:

Word stress

Each Chinese character has only one syllable, whereas English words typically consist of two or more syllables.

For example, the word water consists of two syllables: wa-ter, and phenomenon consists of five syllables: phe-nom-e-non. It depends on how many vowel sounds the word includes.

Syllable numbers of the word “the”, “water” and “phenomenon”

Furthermore, an English word consisting of two or more syllables includes both stressed syllables and unstressed syllables. These are indicated by the phonetic symbols found in dictionaries.

For example, the phonetic symbol of agri-cul-ture is /ˈæɡrɪ-kʌl-tʃər/, we can see the stress mark /'/ is placed in the first syllable /ˈæɡrɪ/, which known as a stressed syllable, while others are unstressed syllables.

Stressed syllable and unstressed syllables of the word “agriculture”;

An interesting rule to note is that the stressed syllable can be vary within the same word depending on its function in a sentence. For example:

  • When Project is acting as a noun, it is pronounced: /ˈprɑːdʒekt/
  • When Project is acting as a verb, it is pronounced: /prəˈdʒekt/

What should we do?
Pronounce each syllable with different efforts:

  1. Stressed syllables should be pronounced longer and louder;
  2. Unstressed syllables should be pronounced shorter and thinner, or take place from the Schwa (we will discuss it later.)

When training pronunciation, I strongly recommend exaggerating these nuances to ensure we are on the right track and fully comprehend this concept. Eventually, it should sound natural and require less effort.

Properly presenting word stress is key to making our speech more like English, and it can significantly help in shedding “Chinglish” tendencies.

Online Resources:

Sentence-level stress

To meet this criteria, there are two concepts we should understand: Content/Grammar Words and Stressed Words.

In the English world, there are two types of words within a sentence: Content words and Grammar words.

  1. Content words like nouns, verbs, adjectives, and adverbs play an important role in sentence structure and convey main information.
  2. Grammar words like prepositions, auxiliary verbs, and articles are used to link content words to make a complete sentence, we can’t understand a sentence that only includes grammar words.
In the sentence “I would like to read books,” the content words are “I”, “like” “read” and “books”; while the grammar words are “would” and “to.”

In the sentence “I would like to read books,” the content words are I, like, read and books, they should be pronounced clearly and accurately, while would and to are grammar words, and they should be pronounced more softly than those content words.

The second point is to decide which words should be stressed. This is an easy-to-understand concept but hard to implement when we are facing a complex sentence. I will demonstrate it through Chinese examples:

Two examples of stressed word.

We stress some special words in our mother tongue subconsciously, furthermore, and this often influence the meaning.

For instance, we can express “There are beautiful flowers in the park.” in these different ways:

  1. There are beautiful flowers in the park.” We are pointing out the location;
  2. “There are beautiful flowers in the park.” We are emphasizing the flowers, not buildings or trees;
  3. “There are beautiful flowers in the park.” We are emphasizing the place where the beautiful flowers are located.

I suggest following general rules to avoid the potential risk of making mistakes and mispronunciation because the given text is unpredictable when we are sitting at the PTE test. Here are two steps for consideration:

1. Understanding the text.
Rather than speaking without consideration and comprehension, we should first grasp what ideas the writer attempting to convey before we open our mouths. Furthermore, analyzing the elements and structure of the sentence is crucial, including subjects, verbs, objects, content words, grammar words, and clauses.

2. Marking stressed words and unstressed words.
Generally, we stress one word in a phrase, choosing from a range of words, including objects, gerunds, passive verbs, adjectives, and adverbs. Below are several examples from an actual test:

Globalisation refers to a set of changes rather than a single change.
In the sentence “Globalisation refers to a set of changes rather than a single change,” the stressed words are “set” and “single.”

Stress what authors attempt to emphasize. In this case, the author is declaring it is a set, not a single change.

You will be introduced briefly to the discipline of child psychology.
In the sentence “You will be introduced briefly to the discipline of child psychology,” the stressed words are “introduced,” “discipline,” and “child.”

We always stress adjectives and adverbs that modify a noun. If they are connected, stress the first one. Similarly, when facing a compound noun, we stress the first noun generally.

Although choosing the stressed words is subjective, they should be chosen from an appropriate scope that I mentioned before.

Online Resources:

Assimilation and deletions

These two concepts were the most interesting part of my learning journey. They make our English speaking vivid and dynamic.

Some voices can be transformed in specific circumstances. Below are several examples:

  1. Using the Schwa. Grammar words and articles can be reduced to a “Schwa sound”, such as “to” /tu/ becoming /tə/, and "than" /ðæn/ becoming /ðən/;
  2. Linking vowels. When a word ends with explosive sounds like /t/, /k/, and /p/ and the following word begins with a vowel sound, we link them together, such as that element becoming tha-telement;
  3. Inserting new sounds. When a word ends with a vowel sound and the following word begins with a vowel sound, we often insert a connecting sound. For instance: many of can be many jof.

There are lots of variations in pronunciation that we need to learn and practice. While this might feel overwhelming for some beginners, it is a vital part of speaking like a native and sounding natural. Keep learning from online resources and practice consistently until you feel comfortable.

Some voices can be dropped in specific circumstances. Below are several examples:

  1. Combining sounds. Link the same consonant sounds that are adjacent, such as in big garden, two /g/ sounds becoming one /g:/ sound but a bit longer;
  2. Dropping sounds. In a rapid speech, we sometimes drop explosive sounds between two consonants, such as an important role becomes an importan role;
  3. Holding sounds. Similarly, we hold back explosive sounds without fully releasing them when these sounds between a vowel and a consonant, such as that person becomes tha-person;
  4. Reducing /h/ sounds. When a word ends with explosive sounds and the following word begins with the /h/ sound, we always reduce the /h/ sound, such as "might have" becomes "migh-t(h)ave" and "an hour" becomes "a-nour."

Online Resources:

Fluency

Rhythm and phrasing

These two concepts are related to the term “Thought groups.” When speaking English, we always separate the sentences into several groups by their meanings, emotions, structures or lengths. Here is an example from the real test:

Two examples of separating sentences.
  1. Many papers you write in college / will require you to include quotes / from one or more sources.
  2. The speaker reminisces about his views / of the English Revolution / when he was a student.

In particular, we break sentences down before prepositions such as “of”, “in” and “that.” Importantly, we should NEVER separate compound words like “the English Revolution.”

Furthermore, I suggest breaking the sentence into smaller fragments for practice, like this:

An example of using high pitch and low pitch.
Many papers / you write in college / will require you / to include quotes / from one or more sources.

However, the PTE test would perfer a longer phrase, so I suggest that each group should have 4 to 7 words.

Now we know what is the term “Thought groups” and how to divide a sentence, the next step is to learn how to present it well. This related to the term ‘intonation and it means words pronounced in a high or low pitch accordingly and intermittently.

Intonation can bring rhythm to speaking, however, it is hard to handle and can cause trouble easily for beginners.

An example of using high pitch and low pitch.

What should we do?

  1. Always present a low pitch to the last word of a thought group and the sentence;
  2. Carefully present a slightly high pitch to the adjectives or adverbs that modify a noun or a noun phrase.

No hesitations

PTE test can detect any hesitation or mispronunciation which can negatively influence our final score, especially in the Read Aloud and Repeat Sentence module.

Despite numerous challenges on test day, such as being disrupted by other test-takers or encountering unfamiliar words, I strongly recommend speaking slowly and confidently to avoid potential risks and maintain fluency.

This strategy is crucial: when we face a word or phrase that is difficult to express and may cause hesitations unavoidably, this may affect our scores in both Pronunciation and Fluency. However, if we express these challenging words slowly and confidently, maintaining a natural flow, it might primarily affect our Pronunciation score.

This is why I strongly advocate for speaking confidently, even when making mistakes.

Summary

This article discusses the knowledge I gained on my English learning journey, including methods to improve pronunciation and an understanding of the PTE speaking module criteria.

Additionally, I’ve decided to update posts in English from now on. It may contain numerous grammatical errors, awkward phrasing and word-choice issues, it’s still a necessary step forward. ‘Practice makes perfect’ is the key lesson from this journey.

我在 2023 年结束之前完成了一件大事

PTE 八炸达成,我的 2023 年大圆满了

一位戴着大眼镜的卡通小男孩坐在书房中的书桌前,正在专注地使用笔记本电脑。桌上摆放着几本书和一支笔,而背景中是装满书籍的书架和温暖的室内照明。

风暴中心

在 2023 年初的时候,我拿到一个深圳互联网公司的 Offer,当时给出了一个让我无法拒绝的待遇,我毅然加入了深圳千万打工人中的一员。

入职之后是让人感到惊喜的。公司地理位置优越,能看到深圳湾、春笋大厦和人才公园;公司楼下有一大片鸟语花香的草坪和水池等景观,人与自然能够和谐相处;公司不用打卡,同事之间结构扁平简单,很好沟通。这是我目前为止职业生涯中最好的一家公司了,待遇、环境、人际、交通全部没有可以投诉的地方。还有一只粘人小猫可以天天摸。

公司的猫咪

直到年中,事情发生了变化。

在 6 月底 7 月初的时候,我发现好像每周都会零星消失一些人,时不时会发现桌子突然清空了。事情开始不对劲。

裁员风暴终于席卷了全公司。从 8 月份开始,公司前台每天都堆满了归还的显示器和笔记本,IT 老哥拿小推车推走一批又一批。坐在我前面两排的产品/运营同事逐渐消失,公司人数从我刚入职时 500+ 骤减到 300+。

公司前台的照片。展示了一排被裁员工交还的的十多台苹果银色笔记本和黑色的 21 寸显示器。
员工交还的笔记本电脑和显示器

到了 8 月份,在一个微妙的时间点 — — 还有 2 周就呆满半年的时候,终于轮到我了。

本来应该是周一通知我的,因为要面谈的人太多了,拖到了周三 🤦。现场情况就是在小会议室里聆听 HR 的裁决,然后平静地签字。

第一次直观感受到这么大的裁员风暴,多少感觉到是有点震撼的。

一个决定

我曾经想过在那里做到退休,但无奈只能接受现实。我无法改变 “大环境”,也改变不了那天在会议室里的裁决,我反而在想:

在互联网公司中做设计刚好 5 年了,我好像一直在上班 / 下班、入职 / 离职、准备作品 / 面试,就没做过其他事情了,不如趁这个机会,认真做下自己想做的事情吧。

正好当时我有在因应公司的需要而在自学英文,只是进度有点缓慢,毕竟只能在非工作时间的时间进行。眼下反正当下也没工作了,干脆就放手一搏,全职学几个月,看看能到什么水平,所以最开始的目的是:

  1. 战胜童年阴影。我在学生时代英文应该是没有及格过的,单词不认识、语法看不懂、音标全不会;
  2. 为职业生涯铺路。如果真能学出点什么名堂出来,说不定还能对之后的工作有些帮助(可能也没帮助,但做了可能有,不做一定没);

之后进行更多的了解之后,发现如果拥有一份受西方世界认可的英语成绩,除了能达到上面两个目的,甚至可能会影响我的人生轨迹:有一个之前从未想像过的 “可能性”。

于是,更加坚定了我的信心。虽然我的基础是很菜,过去的事情已经没法改变了,但抱着《没有人生下来就会》的心态,以及这几年在职场里面学习到了设计内/外的很多东西,我是对自己的学习能力是有信心的,认为这个结果会是乐观的。

最终我下了决定要考 PTE Academic。因为如果我想达到我在前文提到的一些目的,将这件事情利益最大化,我的成绩需要是听说读写这四个科目同时在雅思 8 分(对标 PTE 是 79 分)以上。

而在雅思的口语和写作想要拿到 7+ 难如登天。我干脆来一手曲线救国,PTE Academic 虽然不像雅思那样全球通用,但至少在一些西方国家如澳洲、新西兰、英国和加拿大是一样受到政府机构 / 高校认可的。

Pearson 品牌宣传横幅,标语为 ‘Be yourself in any language.’,上方有 @pearsonlanguage 的社交媒体账户名。右侧有三位开心交谈的女士图像,背景为蓝色。

过程感受

于是乎就开始了漫长的备考路。单词不会?积累;语法不会?问 GPT;哑巴英语?跟读,学音标…

这段三个月全职学 PTE Academic 的历程和我之前还在职的时候学设计、学编程很相似:投入了时间和精力研究某项东西之后,我会喜欢观察里面的一些细节和对比不同之处。随着更多的了解,越来越觉得英文世界很有意思:

  1. 我会很乐意看到一些熟词生义。 比如:interest [n. 利益、股份]、current [n. 电流] 和 lead [n. 铅] 等;
  2. 我会经常向 ChatGPT 提问。 比如问它时态的选择、句子结构的分析和近义词之间的选择等,甚至一些不常见的词根我会问它的来源;
  3. 我会很喜欢去了解并尝试做到一些英语发音中的微妙细节。 比如 Flap T / True T, Light L / Dark L 等。当然也包括考试中很看重的发音现象 Linking 和 Reduction。

在整个过程中我几乎没有感到太多难受的地方,没有很沮丧的情绪,比如 “唉,还有这么多题要刷”。反而,我想的是 “明天需要早起半小时多练下 xx”。

海量的学习资源(B站、小红书、YouTube、ChatGPT 以及各大 PTE 备考平台),这让我很难没有进步,在那段时间我似乎每天都有新东西进脑子,甚至还需要排期进脑子:“今天先研究下单词重音,明天再学下怎么用 Schwa”。

屏幕截图展示了两个自学英语资源的网页,右半部分为 YouTube 频道 ‘Accent’s Way English with Hadar’,突出了频道主 Hadar Shemesh 和她的口号 ‘Speak English Like Yourself And Freaking Rock it!’;左半部分显示了 Bilibili 平台上 ‘Belinda的英语学习日记’ 的内容预览,包括多个英语学习视频的缩略图。

三次考试

首考

我在 9 月底开始了解并练习 PTE 这门考试,当时定了目标是练 2 个月看看能到什么水平,根据成绩调整练习方案,所以报名了 11 月 27 号在广州首考试试水。

2023 年 11 月 27 日首考成绩。听力 70,阅读 69,口语 73, 写作 78,总分 72。

在拿到首考成绩单的时候我是惊喜的,原来我的口语也没那么烂(即便有浓重的粤语口音)。分析成绩之后大概能感受到我离目标还差什么:

  1. 阅读差的比较多。 RW、R 需要投更多时间去练,需要有更准的语法判断和分析句子成份能力。而 RO 则需要多做题掌握技巧和对关键词保持敏感;
  2. 而口语和听力则是需要对 RS 投入更多时间,对校园场景和一些常见短语需要有较高的熟练度。RA 继续维持原本练习节奏;
  3. 写作方面已经很接近目标了,所以不需要花太多时间,把时间留给其他题型。

二战

第二次考试我定在了 12 月中旬,18 号。我是希望在 2023 年把这件事结束的,如果这次还没过的话,还有多一两个礼拜能继续复习和再考。

第二次备考只有 20 天的时间,所以我加大了练习强度:

  1. 整个下午都分配给了口语练习,每天基本上练到嗓子冒烟才停止,还特意买了两瓶枇杷膏缓和一下;
  2. 压缩睡眠时间 1~1.5 小时,换取更多的阅读练习时间。因为我觉得阅读需要有足够多的输入,才能从容面对考试中的各种情况。
有趣的是,即便这样,我这段并没有感觉特别困或没精神,反而特别有干劲儿,可能是多巴胺或内啡肽之类的在起作用了。

在进入考场前我是对自己有信心的,甚至有预感这次应该能过。因为我能通过平常的练习和模考中感受我比第一次考试的状态强了很多。

2023 年 12 月 27 日第二次考试的成绩。听力 77,阅读 76,口语 79, 写作 89,总分 80。

好吧,第二次的结果是很明显有提升,口语和写作达标了,但阅读和听力距离 79 的目标还差了一丁点,已经离我的终极目标非常接近了。

从这两次考试成绩给了一个很明显的信号是:我的备考策略是可行的,继续练阅读RW,R,RO,口语继续练 RS,减少写作的投入时间。剩下的就是运气和临场发挥的事情了。

所以我立刻就报了下周的考试,这应该是最后一个机会了,如果拖到 2024 年,那么做这件事情的代价实在太大了。

三战

最终在 12 月 26 号的考试中我稳定发挥,没什么做得特别好的地方,也没太拉跨的表现,我拿到了理想的结果。

2023 年 12 月 27 日第三次考试的成绩。听力 89,阅读 80,口语 79, 写作 90,总分 86。

在考了三次之后达到了目标分数。由于本身就有将一件无聊的事写成长篇大论的能力,这一次写作拿到了喜人的满分 90 分。小小遗憾是听力差 1 分满分,如果有双满分这张成绩单会好看很多 😭。

我总共备考 PTE 的时间是刚好满 3 个月, 这应该是一个不短的时间,尤其对于成年人来说,少工作一天就少一天收入。只是很庆幸备考过程中不断的收到一些正向反馈,这些都能刺激我继续做这件事情。比如前两次成绩的对比,我花了更多精力做的事情,能在数据上有所体现;又比如一些发音技巧,掌握之后口语流利度能明显提升;或者语法知识点,多熟练掌握一个,考试中或许就能又少一处错误。

达到一个这样的成绩实际上离能够流利和老外对话,或者书写中不犯任何语法错误都是仍然有很大一段距离的。但至少我至少战胜了童年阴影,能说出别人听得懂的英文、用简单的书面语言表达自己的想法和看法,看互联网上的英语材料没那么费劲了,现在也不是所有东西都要翻译了。

此外,更重要的是,并且这份成绩给了我一个 “可能性”。

那么,代价是什么?

对我而言,学英语、考 PTE、拿高分,这几件事同时做到的话代价是比较高的。

金钱

这种英语考试可不便宜,我考了三次一共 ¥5,268;
而且做官方的模拟考试是需要收钱的,这里一共 ¥896;
口语的提升离不开口语老师的反馈,我找过不止一个老师,多角度评判我的口语水平,这里是 ¥1,289;
以上大额开销总盛惠 ¥7,453

如果算上其他交通费、考试后的 “奖励自己” 开销和护嗓枇杷膏等小额开销,加起来应该在 ¥7,700 左右。

不过钱倒是还好,总是能挣回来的,毕竟要继续打三十多年的工,这里区区几千块算不了什么。

时间

时间方面是比较要命的,毕竟时间可是买不回来的。

我从 8 月中被裁员,到 12 月底达到目标,这中间隔了 4 个月,后续重新找工作还需要整理简历、投简历、面试等。至少半年没在工作,这种现象是会遭到未来用人单位的盘问以及质疑 (甚至唾弃?) 的。

视力

我的视力受损了。这段时期每天对着电脑的时间比在职的时候长的多。

在职时好歹晚上差不多到点了就回家,为了第二天早起工作还会早点睡觉,周末还有休息。而我这段时间是没有周末,从早上 8 点多一直对着电脑到晚上 12 到 1 点的,一直在看着密密麻麻的英文字母,每周眼睛面对电脑屏幕的时间应该接近翻倍。

一个明显感受是之前在家里玩 Xbox 我是能看清电视上的字的,现在是完全看不清的,必须坐近一点。去医院眼光之后近视度数多了 75 度。

值得吗

尽管代价不菲,但最终能在 2023 年结束前做到了,我坚定地认为 — — 至少当下认为 — — 是绝对值得的。2024 年我能做出新的规划,不用再被这件事纠缠着了。

虽然这段时间我的职业生涯是按下了暂停键,但获得了那个 “可能性”。3 年后我可能会对自己说 “还好当初做了这个决定,不然就…”;或者 “早知道被裁之后立马找工作了,那段时间真是浪费”,我觉得前者的可能性大一些,具体回答是什么,到时候揭晓吧,反正我能让我的网站一直运行到那个时候。

预告

由于这段时间的备考对我的改变最大的是口语 — — 从一个音标都不会的哑巴到到能正常地说出英语,而且英语口语中确实有很多有意思的地方,下一篇博客我会记录下我这段时间对于英语口语这块的认识和分享一些对我有帮助的学习资源。

记录一次 AIGC Hackathon 参赛经历和收获

AIGC Hackathon 主题海报,由公司品牌设计师陶老师和 Stable Diffusion 的共同创作

公司在 5 月上旬的时候举办了一场 AIGC Hackathon 比赛,鼓励内部员工基于公司相关业务,利用 AI 技术进行创作,重塑生产力。

这么有意思的活动我当然也要掺一脚,在同事的怂恿和牵线之下,召集到了另一位设计师,以及一位研发大佬。所以加上我总共是 3 个人,2 个设计,1 个研发。但是严格来说应该是 1.5 个设计,1.5 个研发。因为我会参与到前端界面的实现,这也是我想参与这次活动的其中一个主要原因。

先放上我们开发的小工具的最终效果,其实是通过提前录入 SQL 语句规则,然后用自然语言询问 AI,让 AI 帮忙生成一个可用的 SQL 语句:

过程

想法

因为当前我们公司的业务是研发管理平台,所以我想到能通过 AI 帮助我们的主要是提高效率,减少出错,诸如此类的方向。

尤其是我在刚来到公司的时候有一段时间给我学习体验公司产品。中途就看到有个地方生成图表的话,可以选择各种筛选条件,也能用 SQL 来快速筛选,当时我就觉得如果用 SQL 来筛选会比手动一个个条件添加来的快,但我又不会写 SQL 语句,所以就想到一个主题:「输入自然语言,通过 AI 生成一段 SQL 语句」,产品名称就被定为「SQL-Helper」。

最开始我们讨论到有几种实现方式:

  • 产品原生:在公司的产品中搭建一个开发环境,在里面进行对话交互功能的设计和开发。
  • 外部网页:搭建一个网页,在网页里实现对话。
  • 谷歌插件(Popup):在浏览器右上角上添加一个 Popup 入口,点击后打开一个浏览器提供的容器,在上面进行对话交互。
  • 谷歌插件(嵌入网页):在公司的产品当中嵌入一个小卡片,在上面实现对话和配置等,会遮盖部分网页内容。
用树状图展示了三种实现方式的取舍选择方式的示意图。

最终选择了谷歌插件(嵌入网页)的方式,因为这样算是能比较融入到产品当中去,不需要来回切换网页这么麻烦,毕竟是一个对话的产品。而且 Popup 有一个致命问题是,如果鼠标点击到了网页的任何地方,Popup 就会收起,这种体验不适合用在聊天式的产品当中。而直接基于公司产品的开发环境中进行开发,这种做法成本比较大而且难以协作,毕竟团队中只有一个正经的研发。

团队合作

得益于大佬的用爱发电,他花了一个休息日的时间把架子搭起来了,技术栈是:

  • React
  • Ant Design Manifest V3(支持 Chrome 96)
  • Stylus 样式结构支持

最初搭建好的框架。是能够在网页中嵌入一个按钮,点击后打开一个弹窗:

最初搭建好的框架。图片展示了一个浏览器右下角有一个橙色的沙漏图标,点击后会打开一个出现在屏幕中央的弹窗。

这两个地方就是我们作品的主要载体。我们希望点击悬浮窗之后能打开一个聊天小窗口,能在里面填入 SQL 语句相关的 Key 和 Value,然后填入用户的 API Key,并实现与 AI 实时对话。

所以很快,在第一天的时候我们就有了一个大概的原型,中途通过每日下班前的简短碰面来一起决定增加/减少哪些内容。

最开始的原型。在 Figma 中进行的,由方框和简单文案组成的原型图。分为消息模块和配置模块,每个模块的原型图下方列举了一些必做的功能清单。

然后也确定了我们三人小组的分工:

  • 我:前期负责设计界面、实现静态界面。后期参与 Prompt 调试。
  • 另一个设计师:负责界面设计、图标设计、PPT 制作/包装。
  • 研发大佬:负责功能实现,比如表单提交,发送和接收消息,接口调试等。

然后我们就各司其职,我这边按照大佬给的流程,每天把自己做好的部分提交 Pull requests,并标记清楚完成了哪部分,哪部分的功能需要研发大佬来实现等等。

Github 仓库的 Pull requests 截图。展示了 SQL-Helper 仓库中部分 Pull requests 记录,并且用带有圆角的绿色方框高亮由我提交的部分 Pull requests。

整体的时间表如下图(只有五个工作日,时间紧迫。):

时间表截图。记录了三个成员在周一至周五各自的工作,完成的工作会用绿色打勾图标来标记。

一些细节

初试 Ant Design 的感受

平常在设计稿中使用基于 Ant Design 组件的经验有很多,但是自己亲自在代码里使用该组件还是第一次。

一个强烈的感受是,Ant Design 组件库确实很丰富,很多常用的组件都有封装好,主题色更改的功能也很强大。但是想要基于 Ant Design 组件库来做一些客制化的改动,实在是太费劲了(也可能是因为我太菜...)。

比如当时我想实现一个 TextArea 根据容器高度来自动填满高度的话,我的样式代码是如此之多:

在深色主题下 Visual Code 中的代码截图。展示了我使用大量重复的样式来让一个 TextArea 组件自适应高度。

另一个坑是我们的聊天窗口是嵌入到网站当中的,而且原本我们公司的产品也是基于 Ant Design 组件库搭建的,所以会出现很多样式的冲突。

比如我在自己的界面中使用了 Button 组件,而被嵌入的网站中如果也用了 Button 组件,那么样式将会被原本网页覆盖掉。

如果在这种情况下想要自定义样式,需要在 CSS 中通过多种手段来提升样式的优先级。这里是我比较频繁用到的两个提高优先级的方法:

  • 使用选择器提高 Class 的优先级
  • 使用 !important
在深色主题下 Visual Code 中的代码截图。展示了我使用选择器和 !important 的方法来提升所编写的 Class 层级。
CSS 选择器的优先级相关内容可以参考张鑫旭大神的名著《CSS选择器世界》,写得特别详细。

所以后面回想起来,我在改造 Ant Design 组件库花的时间还是太多了。这个组件库可能在做复杂后台的时候好用,但在做这种小应用,发挥创意的地方,我觉得还限制还是太多了。我觉得我们最开始应该选择使用有更高扩展性的组件库,比如前段时间看到的 Radix UI

从文档来看,Radix UI 会组件里面每个 DOM 都提前命名好相关的 ClassName,那么用户在想自定义样式的时候,就方便很多了。

Radix UI 组件库的截图。展示了 Alert Dialog 组件的代码,并且用带有圆角的绿色方框高亮了代码中的四处 className。

对比起 Ant Design,引用一个组件进来后,会生成巨多个没有打标记的 DOM。每个 DOM 的样式巨难选择,想要改组件内某个元素的样式,没有熟练的 CSS 选择器功夫还真搞不定。

但还是得具体试一试才知道 Radix UI 是不是真的好用,或许也会暗藏某些坑。或许我应该拿自己的个人网站来试一试 🤔。

改造弹窗:拖拽和操作

由于这是一个嵌入在网页当中的对话窗口,本着提高一个易用性的初心,我觉得需要对弹窗组件进行魔改,支持以下两种特点:

  • 支持拖拽:允许用户拖拽弹窗标题区域,改变弹窗的位置,以免挡住原本页面中的元素。
  • 移除遮罩:不展示弹窗背后的黑色遮罩,并且弹窗出现的时候,不影响用户操作原本的网页。

仅仅是改造弹窗这块,足足花了我一天。严重影响了我调试 Prompt 的时间,所以感觉很有必要把它记录下来,即便我的做法绝非最佳实践。

拖拽

拖拽这块我是用了一个名为 DragM 的开源库来实现了,因为看到示例里面效果很不错,所以直接用上了。效果确实也很不错,强烈推荐。

过程有点丢人,就不详细描述了。大概是我参照示例代码做到了弹窗能够实现拖拽,但弹窗的关闭按钮却失效了,后面还是得靠研发大佬来修复。

允许操作背景

这个特点的实现简直是噩梦。Ant Design 文档是提供了一个 API 是能够允许我们隐藏掉黑色的遮罩。

Ant Design Modal 组件的 API 截图。用带有圆角的绿色边框高亮 Ant Design Modal的 API。该 API 控制是否展示 Mask,默认是展示的。

但是这只是让我们看不见遮罩,打开弹窗时,原本页面的元素仍然是无法点击的。所以我试了一些方法,比如说将每一个疑似是遮罩的 DOM 设置为 display: none;,然而并不奏效。

最后生效的方法是用 CSS 选择器选中遮罩层,并将其调整为跟弹窗一样大。那么实际上遮罩还是存在的,但已经被修改为和弹窗一样大,并且叠在弹窗的后面,其实谁都感知不到这个透明遮罩的存在。

所以对于弹窗尺寸以外的部分,是没有被遮罩所覆盖的,自然能够正常响应鼠标操作。

这种做法无法适应弹窗高度会动态变化的情况,但我们这次设计的产品中,弹窗的宽高是固定的,所以即便做法比较不成熟,但确实是达到效果了。

结果

比赛最终有点遗憾,距离拿奖仅一步之遥。获得我们组目标奖项的是一个单人成组的高手。 很可惜的是我们就差一点点,因为最后颁奖的时候大佬也提到了我们的作品,说是如果能更好用一些,比如将生成的 SQL 语句自动填进输入框,或者自动应用然后直接生成图表,就有更大的机会获奖了🤦。

确实我们在演示的时候没有将这个这个产品最强大的功能发挥出来。因为本身产品也有些缺陷,限制了我们的演示范围。

最开始我们设想是演示的时候用自然语言多问几个问题,然后 AI 每次回复都很准确,凸显了自然语言生成 SQL 语句的便利性,而且还想演示输入了几个错别字,AI 仍然能够正确地识别我们的意图,回复正确的 SQL 语句。

但实际上我们最终的产品还是有一个明显的 BUG,在连续聊天时,消息队列不正常,AI有时候会回复我问的上上一个问题,而不是紧接着我的提问来进行回答。

这使得我们演示的时候只能问一个问题,简单带过,没有办法将自然语言带来的便捷性完全地展现出来,实属遗憾。

收获

虽然没能获奖,但参与这次公司内部的 AIGC Hackathon 比赛对我来说有三个特别重要的收获。

参赛经验

简单来说就是产品设计和功能开发的边界,需要根据演示需要来进行规划。

之前我在样式实现中花了太多时间(职业病🤦),比如输入框的聚焦效果,聊天气泡出现的动画,发送消息加载中的动画等等,而实际上在演示的时候,这些可能只展示了 1~2 秒,甚至更少。

因为这些内容大概率不会是评委所在意的东西,反而在过程中怎么调试 Prompt,调试过程中遇到什么挑战,怎么解决,怎么选择模型等等,显得团队很沉浸于与 AI 接口进行交互,通过这个项目积累了挺多经验,可能这些才是大佬们有兴趣了解的地方。

我应该早点完成产品界面还原,然后尽快投入到调试 Prompt 当中的。 如果之后再次参与类似的活动,我觉得我的所有工作都会围绕最后一天的演示来进行,其他东西能减就减。

首次参与代码 PR,并且与真正的研发同事合作

这次是我第一次将代码 PR 到其他成员的仓库当中,并且解决代码冲突,编写改动点,同步最新代码等等。这种团队协作的感觉,让作为设计师的我感觉很新鲜和奇妙。

而且各司其职,每个人都做出了超出自己以往经历的内容,然后每天短会同步下今天的进度、明天的工作内容。尤其是路演前一天晚上一起调试 Prompt,然后把最佳表现录屏,录屏中还出现了几次滑稽的意外...这种协作的经历让我感觉很充实,富有激情。

而且我每天都会遗留一两个事项给峰哥来解决,比如:

  • 我做完了一个表单填写框和提交按钮,我就告诉峰哥让他来做表单提交的功能;
  • 我做完了聊天气泡(由AI、用户、系统发出的消息)的三种样式,也是一样让峰哥来做相关的展示逻辑...

真正参与产品代码贡献对我来说是一直想去做的事情,因为我一直觉得如果能由做界面/交互设计的人,来亲手将其创作内容实现出来,是一件很酷的事情,也能够减少很多沟通成本。

参与到 AI 的浪潮之中

我最开始是在去年年底的时候,用 ChatGPT 来帮我解决我重构个人网站时的一些实现问题,中途因为好奇心驱使,也问了很多奇奇怪怪的问题。然后最近也在高频使用 ChatGPT,来作为我学习英语的助手。

而这次比赛不仅给了我一个机会去参与创造一个跟 AI 相关的产品(包括代码也有不少是 ChatGPT 参与的),还给了我推强大的动力去了解 AI 在商业中的场应用景。除了在比赛中看到其他参赛者的创意之外,现在还了解到比如 Atlassian 已经有相关的功能前瞻了。

从 Atlassian 在 5 月 4 日发布的股东信当中,也透露到该公司已经在用 AI 来来为客户提供服务了。

"Now that generative AI has reached consumer-grade maturity with LLMs, we can create magical new experiences for our customers."现在,随着大型语言模型(LLMs)带领生成型人工智能达到了消费级的成熟,我们可以为我们的客户创造魔术般的新体验。

除了 Atlassian,另一个互联网巨头:Shopify 也将 AI 能力应用到其产品当中。比如在商品详情页的编辑界面当中,我们可以输入几个关键字,让 AI 帮忙生成一段商品介绍的文本,还能够调整文本的风格。

Shopify 商品详情页 AI 功能截图。画面是一个卡片叠加在商家管理后台的商品详情页的富文本输入框上,该卡片左侧是一个纵向排列控件的配置面板,第一个是输入框,输入一些关于商品的关键字;第二个是调整文本风格的下拉菜单,第三个是额外的备注。然后右侧是由 AI 生成的文本,用淡紫色背景的文字来区分,并且提供赞和踩的反馈。卡片右下角是一个 Pointer 样式的鼠标指针,选停在保存按钮上方。

Shopify 将同样也是利用 LLM 来实现的生成文本能力,这被官方称为「Shopify Magic」。除了在商品详情页生成商品描述,也能在邮件当中根据活动类型或产品关键字生成邮件标题,或者在主题编辑器中根据要求来生成标题等等。

微软就不必多说了,也已经在 Microsoft 的办公软件上集成了很多 AI 功能。

代码仓库

这里放上我们产品的代码仓库吧,记录这段热血时光:

SQL-Helper · Github

最后,通过这次 AIGC Hackathon 活动,我真正参与到了产品的代码贡献,同时也看到了许多 AI 能力的应用场景。听说公司有产品在研究 AI 能力在业务中的应用,希望之后自己也有机会在商业项目上进行更多相关的实践吧!

近况

太久没发博客了,记录下我最近的状态吧。应该没有人想知道,但若干年后的我可能会想知道。

  1. 近沉迷于学习英文,所以会导致网站、博客的更新会比之前慢一些。
  2. 发现英文还挺有意思的,但在学生时代是看见英文就想睡觉。以前的我应该是比较喜欢数学和历史。
  3. 小目标是今年内能出一篇全英文的博客。全程用英文书写,然后在发布之前先给 ChatGPT 帮忙检查和润色一下。
  4. 个人网站 LRD.IM 正在开发支持切换语言至「英语」的功能。
  5. 有一个前端相关的分享会正在准备进行,是面向部门内产品、本地化运营和设计同事的。
  6. 在手机上开通了 ChatGPT Plus。直观感受是 GPT-4 的回答比 GPT-3.5 靠谱很多很多,应该会持续订阅。
  7. 正在尝试一种新的作息,再多体会一段时间后,可能也会在博客上记录。
❌
❌