跳至主要內容

AI 代理人的「技能」是什麼?淺顯說明其運作原理

將「技能」加入 AI 代理人,就像在應用程式安裝功能擴充一樣,可以增加它能做的事情。本文解說 Agent Skills 的運作方式,以及代理人在內部如何處理任務。

AI 代理人是什麼​

首先作為前提,AI 代理人是指「接受指示並能自主執行任務的 AI 程式」。

與像 ChatGPT 那種「問了就回答」的 AI 不同,代理人可以做以下事情:

  • 讀寫檔案
  • 執行程式碼並檢查結果
  • 呼叫外部 API 或工具
  • 自行判斷並執行多步驟流程

技能是什麼​

Agent Skills 是一種「用來為代理人新增能力或專業知識的機制」。

比喻成人類的話,就像把「新的工作手冊」交給對方一樣。閱讀了手冊(技能)的代理人就能理解該任務應如何進行,並據此行動。

無技能: 「寫一篇部落格」→ 代理人隨便寫
有技能: 「寫一篇部落格」→ 按照手冊步驟,以一定品質寫出文章

技能主要以 Markdown 檔案(SKILL.md)描述,通常包含以下要素:

  • 步驟說明:要做哪些事、按什麼順序做
  • 腳本:想自動化的處理流程
  • 範例與設定:代理人會參考的資源

為什麼需要技能​

AI 代理人能力很強,但不會預先具備你專案特有的知識。

例如:

  • 「這個團隊要如何撰寫提交訊息」
  • 「這個部落格的 front matter 要如何寫」
  • 「部署流程要用哪個指令」

這類資訊如果不以技能方式提供,代理人無從得知。有了技能,代理人就能在知道「正確做法」的前提下執行任務。

代理人使用技能時的處理流程​

接下來看看代理人在內部如何運作。

重點如下:

1. 載入技能​

代理人一開始會載入技能。技能內容成為輸入給 LLM 的一部分(提示)。LLM 讀到後會理解「這個任務的正確做法」。

2. 任務拆解​

LLM 根據收到的步驟,將任務拆成小步驟。例如「先閱讀既有文章 3 篇」「接著決定檔名」「寫 front matter」等。

3. 呼叫工具​

在每個步驟中,視需要呼叫工具:讀檔、搜尋網路、執行程式碼等,依照技能定義的流程執行。

4. 結果回饋​

工具執行的結果會再次傳回給 LLM。LLM 根據結果判斷「下一步要做什麼」,並持續迴圈直到任務完成。

技能指令​

技能可以透過斜線指令(/命令名)來呼叫。

呼叫指令時,對應的 Markdown 檔內容會被展開為提示,代理人就會開始依該步驟執行。

技能的應用範圍​

Agent Skills 的格式是 由 Anthropic 開發並開放的,目前支援許多工具。

工具是否支援
Claude Code✅
GitHub Copilot✅
Cursor✅
Gemini CLI✅
OpenAI Codex✅
VS Code✅

同一套技能能在多個工具間重複使用,這是很大的優勢。

總結​

  • 技能是把專業知識或操作步驟傳給代理人的機制
  • 只要撰寫 Markdown 檔(SKILL.md)描述步驟與規則,就能建立技能
  • 代理人把技能當成提示給 LLM,LLM 會解讀並按步驟執行
  • 這是個開放且跨工具的標準格式,如 Claude Code、Cursor、GitHub Copilot 等均可共用

善用技能可以省去「每次都要向 AI 解釋同一件事」的麻煩,讓代理人以一致的品質完成任務。

在 AWS 無伺服器環境下自建部落格留言 API

想要在這個部落格加入留言功能,因此在 AWS 無伺服器架構下自建了一個 API。介紹其設計與實作。

架構​

請求以以下流程處理。

瀏覽器 (www.hikari-dev.com)
↓ HTTPS
API Gateway
├── GET /comment?postId=... → 取得留言
├── POST /comment → 發表留言
└── PATCH /comment/{id} → 管理(切換隱藏)
↓
Lambda (Node.js 20 / arm64)
↓
DynamoDB(儲存留言)
+ SES v2(通知管理者)

程式碼以 TypeScript 撰寫,並以 SAM(Serverless Application Model)管理基礎設施即程式碼(IaC)。Lambda 採用 arm64(Graviton2)以稍微節省成本。

DynamoDB 的資料表設計​

資料表名稱為 blog-comments,分區鍵為 postId,排序鍵為 commentId。

鍵型別說明
postIdString文章的識別值(例: /blog/2026/03/20/hime)
commentIdStringULID(可時序排序的 ID)

因為在排序鍵使用 ULID,使用 QueryCommand 取得的留言會自動按發表順序排列。這也是為什麼沒有使用 UUID 的原因。

垃圾留言過濾​

在將留言寫入 DynamoDB 之前,會與 keywords.json 中定義的關鍵字比對。

若命中關鍵字,會以 isHidden: true 自動隱藏,並附加 isFlagged: "1"。若未命中則立即公開。

isFlagged 作為稀疏 GSI(Sparse GSI)的鍵使用。未命中的留言不會帶入此屬性,因此不會在 GSI 中增加多餘的分區,對成本與效能都有利。只要在 DynamoDB Document Client 設定 removeUndefinedValues: true 就能達成。

通知管理者的郵件​

每次有留言發表時,會用 SES v2 發信通知自己。郵件內容包含發言者名稱、內容、評分、IP 位址以及是否被標記(flag)等資訊。

郵件發送採非同步進行,即使發送失敗也會忽略錯誤(不影響使用者流程)。這是為了避免影響留言發表的回應速度。

隱私考量​

雖然會在 DynamoDB 中儲存 IP 位址與 User-Agent,但不會把它們包含在 GET 端點的回應中。我們在型別定義層就進行分離。

安全性​

層級對策
網路使用 AWS WAF 設定每個 IP 每 5 分鐘 100 次的速率限制
CORS僅允許 https://www.hikari-dev.com
管理 APIAPI Gateway 的 API Key 認證(X-Api-Key 標頭)
垃圾留言關鍵字過濾自動隱藏

管理端點(PATCH /comment/{id})只要在 SAM 模板設定 ApiKeyRequired: true 即可啟用 API 金鑰認證。無需自行實作 Lambda Authorizer,較為簡單。

總結​

採用無伺服器架構後不需管理伺服器,DynamoDB 的按需付費讓流量低的個人部落格也能把成本降到最低。

程式碼以 SAM + TypeScript + esbuild 打包,僅需執行 sam build && sam deploy 即可部署。

製作可使用多個生成式 AI 代理人的 VSCode 聊天擴充套件 Hime

我做了一個可以和多個 AI 供應商聊天的 VSCode 擴充套件 Hime (HikariMessage)。

採用 BYOK(Bring Your Own Key),只要有各 API 提供者的 API 金鑰即可使用。

什麼是 Hime​

Hime 是一個整合在 VSCode 側邊欄的生成式 AI 聊天擴充套件。支援 Anthropic、OpenAI、Azure OpenAI、OpenRouter、Ollama,並可透過下拉選單輕鬆切換提供者。

主要功能​

支援多個 AI 提供者​

支援以下提供者:

  • Anthropic (Claude)
  • OpenAI
  • Azure OpenAI
  • OpenRouter
  • Ollama

即時串流顯示​

回應會即時串流顯示,因此即使回覆很長也不會感覺到太久的等待。

MCP​

透過設定以 JSON 格式指定以下參數即可使用 MCP。

範例

{
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users"]
}
}

豐富的 UI​

  • Markdown 呈現
  • 程式碼區塊語法高亮
  • 程式碼區塊複製按鈕
  • 顯示 MCP 的輸出結果

聊天記錄持久化​

對話記錄會以 JSON 格式儲存在 ~/.hime/chats/。即使重新啟動 VSCode 也能從上次繼續對話。

自動系統提示​

工作區資訊、作業系統資訊與目前開啟的編輯器上下文會自動包含在系統提示中。只要說「請修正這個檔案」,AI 就能知道你正在查看哪個檔案。

安裝​

需要 Node.js 20 以上與 VSCode 1.96 以上。

git clone https://github.com/Himeyama/hime
cd hime
npm install
npm run watch # 開發時: Extension Host 與 Webview 同時監控

之後在 VSCode 按 F5 即可啟動擴充套件主機。API 金鑰可從側邊欄的設定面板輸入,並會使用 VSCode 的 SecretStorage 加密儲存。

總結​

Hime 的強項是在不離開編輯器的情況下即可與 AI 互動,並且可透過 MCP 執行工具。歡迎試用。

程式碼倉庫: https://github.com/Himeyama/hime

用 AWS 無伺服器打造雲端儲存服務

はじめに​

自分専用のファイル共有システムが欲しいと思い、AWS のサーバーレスサービスだけでファイルストレージサービスを作りました。

この記事では、設計で意識したポイントと、実際のアーキテクチャを紹介します。

何を作ったか​

制作した Web システムは、Web ブラウザからファイルのアップロード・ダウンロード・フォルダ管理ができるクラウドストレージサービスです。

主な機能​

  • ファイルのアップロード / ダウンロード
  • フォルダの作成・階層管理
  • 複数ファイル / フォルダの一括 ZIP ダウンロード
  • ユーザー認証(サインアップ・ログイン・パスワードリセット)
  • ユーザープロフィール管理

アーキテクチャ​

以下、構成図です。

認証の大部分は Cognito で行い、 ファイル転送は S3 の Presigned URL を Lambda で発行してクライアントと S3 が直接やり取りする仕組みです。

技術スタック​

レイヤー技術
バックエンドC# (.NET 8) / AWS Lambda
認証Amazon Cognito + Managed Login v2
APIAPI Gateway (REST) + Cognito Authorizer
ストレージAmazon S3

設計判断とその理由​

認証を Cognito で行う​

Cognito の OAuth 2.0 エンドポイントと Managed Login を活用し、認証機能を実現しました。

最終的に認証系の Lambda は TokenFunction 1 つだけになりました。

機能的にもセキュリティ的にも減らせるコードは減らすのが吉です。

AWS のサービスがやってくれることを自前で書く必要はありません。

Presigned URL によるファイル転送​

ファイルのアップロード・ダウンロードで Lambda を経由すると、いくつかの問題が生じます:

  • Lambda のペイロード上限に引っかかる
  • 大きなファイルを Lambda のメモリに載せるとコストがかかる
  • 転送時間が Lambda の実行時間としてカウントされる

Presigned URL なら、Lambda は URL を発行するだけで、実際のファイル転送はブラウザと S3 が直接行います。

Lambda の実行時間は数十ミリ秒で済み、ファイルサイズの制約も S3 の上限までとなります。

アップロードの流れ:
1. ブラウザ → Lambda: 「file.pdf をアップロードしたい!アップロード先の URL を送れ~」
2. Lambda → ブラウザ: 「アップロード先の Presigned URL だよ。ここに PUT してね~」
3. ブラウザ → S3: 「S3 に PUT するよ~」
4. ブラウザ → Lambda: 「アップロード完了したよ~」

4. フォルダの ZIP ダウンロード​

S3 にはフォルダごとダウンロードする機能がありません。

複数ファイルの一括ダウンロードは、Lambda 上で ZIP を生成して一時的に S3 に置き、その Presigned URL を返す方式にしました。

一時 ZIP ファイルは S3 のライフサイクルルールで 1 日後に自動削除されるので、ゴミが溜まることはありません。

セキュリティ​

対策実装
ブルートフォース防止Cognito 標準のロック機能 (5 回失敗: 15 分ロック)
API 保護Cognito Authorizer による JWT 検証
CORSAllowedOrigin を特定のドメインに限定
一時ファイル管理S3 ライフサイクルで不要なファイルを 1 日で自動削除

コスト​

サーバーレス構成なので、利用がなければコストはほぼゼロです。

  • Cognito: ESSENTIALS Tier は MAU 10,000 まで無料
  • Lambda: 月 100 万リクエストまで無料
  • S3: 保存量に応じた従量課金(GB あたり約 $0.025/月)
  • API Gateway: 100 万リクエストあたり $3.50

個人利用なら月額数十円〜数百円程度に収まります。

インフラのコード化​

インフラ全体を 1 つの template.yaml (AWS SAM) で定義しています。

Cognito User Pool、API Gateway、Lambda 3 関数、S3 バケット、CloudWatch アラーム、SNS — すべてのリソースを 600 行程度の YAML で定義しています。

在 WSL 2 上安裝 Rocky Linux 8.10

下載 WSL 2 映像檔​

https://dl.rockylinux.org/pub/rocky/8/images/x86_64/Rocky-8-Container-Base.latest.x86_64.tar.xz

從這裡下載。

參考: https://docs.rockylinux.org/8/guides/interoperability/import_rocky_to_wsl/

解壓映像檔​

將 tar.xz 解壓成 tar 檔。轉成 tar 後即可匯入到 WSL2。

cd ~/Downloads

xz -d Rocky-8-Container-Base.latest.x86_64.tar.xz

※Windows 上預裝的 bsdtar 無法解壓此檔。
※若沒有 xz 指令,可在 Cygwin64 安裝,或使用其他 WSL 發行版來解壓。

匯入映像至 WSL2​

將映像匯入到 WSL2。

wsl --import RockyLinux-8.10 $HOME .\Rocky-8-Container-Base.latest.x86_64.tar --version 2

確認映像​

wsl -l -v

新增使用者並設定預設使用者​

以使用者名稱 hikari 為例:

wsl -d RockyLinux-8.10 -u root -- dnf install sudo passwd -y

wsl -d RockyLinux-8.10 -u root -- adduser hikari

wsl -d RockyLinux-8.10 -u root -- passwd -d hikari

wsl -d RockyLinux-8.10 -u root -- usermod -aG wheel hikari

wsl -d RockyLinux-8.10 -u root -- sed -i 's/^# %wheel/%wheel/' /etc/sudoers

wsl -d RockyLinux-8.10 -u root -- echo -e "[user]\\ndefault=hikari" `| tee -a /etc/wsl.conf

啟動映像​

wsl -d RockyLinux-8.10

關於最強壓縮方式的調查

結論​

先說結論。

調查了對總計 34 GiB 的大量檔案進行壓縮與解壓時的時間與大小。

壓縮・建立封存檔​

方式指令realusersys壓縮後大小
tartar cf large-pkg.tar large-pkg1m19.449s0m6.702s0m48.121s26 GiB
tar.gztar czf large-pkg.tar.gz large-pkg11m33.942s11m18.811s0m55.573s5.5 GiB
LZ4tar cf - large-pkg | lz4 > large-pkg.tar.lz43m33.187s0m53.958s2m57.122s8.6 GiB
zstdtar cf - large-pkg | zstd -T0 -o large-pkg.tar.zst9m13.819s1m45.049s2m43.609s4.8 GiB
bzip2tar cf - large-pkg.1 | bzip2 > large-pkg.tar.bz237m22.743s28m40.329s3m31.000s4.3 GiB
xztar cf - large-pkg | xz > large-pkg.tar.xz125m31.447s124m10.523s5m18.330s3.3 GiB

展開 (解壓)​

方式指令realusersys
tartar xf large-pkg.tar2m11.793s0m6.906s2m4.183s
tar.gztar xf large-pkg.tar.gz3m39.544s2m1.189s2m58.317s
tar.gz(gzip)gzip -dc large-pkg.tar.gz | tar xf -3m40.416s2m0.272s3m0.043s
tar.gz(pigz)pigz -dc large-pkg.tar.gz | tar xf -3m53.711s1m38.147s4m42.893s
LZ4lz4 -dc large-pkg.tar.lz4 | tar xf -4m46.576s0m32.174s4m36.055s
zstdzstd -dc large-pkg.tar.zst | tar xf -3m46.419s0m46.533s3m34.668s
bzip2bzip2 -dc large-pkg.tar.bz2 | tar xf -11m31.287s9m52.644s4m17.974s
xzxz -dc large-pkg.tar.xz | tar xf -8m11.527s3m45.562s7m15.109s

準備大量小型檔案​

首先,作為大量小型檔案,使用 node_modules。

把 package.json 設成如下。套件隨意挑選。

package.json
{
"name": "large-pkg",
"version": "1.0.0",
"description": "",
"author": "",
"type": "commonjs",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"dependencies": {
"async": "^3.2.6",
"axios": "^1.13.2",
"bcryptjs": "^3.0.3",
"bluebird": "^3.7.2",
"body-parser": "^2.2.2",
"chalk": "^5.6.2",
"chalk-template": "^1.1.2",
"cheerio": "^1.1.2",
"chokidar": "^5.0.0",
"commander": "^14.0.2",
"cookie": "^1.1.1",
"core-js": "^3.47.0",
"cors": "^2.8.5",
"debug": "^4.4.3",
"dotenv": "^17.2.3",
"express": "^5.2.1",
"fast-glob": "^3.3.3",
"form-data": "^4.0.5",
"glob": "^13.0.0",
"got": "^14.6.6",
"inquirer": "^13.2.0",
"jsonwebtoken": "^9.0.3",
"lodash": "^4.17.21",
"mime": "^4.1.0",
"minimist": "^1.2.8",
"mkdirp": "^3.0.1",
"mkdirp-classic": "^0.5.3",
"mongoose": "^9.1.4",
"ms": "^2.1.3",
"node-fetch": "^3.3.2",
"ora": "^9.0.0",
"passport": "^0.7.0",
"prop-types": "^15.8.1",
"qs": "^6.14.1",
"react": "^19.2.3",
"react-dom": "^19.2.3",
"request": "^2.88.2",
"rimraf": "^6.1.2",
"semver": "^7.7.3",
"sharp": "^0.34.5",
"socket.io": "^4.8.3",
"supports-color": "^10.2.2",
"tslib": "^2.8.1",
"uuid": "^13.0.0",
"ws": "^8.19.0",
"xml2js": "^0.6.2",
"yargs": "^18.0.0"
},
"devDependencies": {
"@babel/cli": "^7.28.6",
"@babel/plugin-transform-runtime": "^7.28.5",
"@babel/preset-env": "^7.28.6",
"@babel/runtime": "^7.28.6",
"autoprefixer": "^10.4.23",
"ava": "^6.4.1",
"babel-core": "^6.26.3",
"babel-loader": "^10.0.0",
"chai": "^6.2.2",
"commitlint": "^20.3.1",
"concurrently": "^9.2.1",
"conventional-changelog": "^7.1.1",
"cross-env": "^10.1.0",
"css-loader": "^7.1.2",
"dotenv-expand": "^12.0.3",
"eslint": "^8.57.1",
"eslint-config-prettier": "^10.1.8",
"eslint-config-standard": "^17.1.0",
"eslint-plugin-import": "^2.32.0",
"eslint-plugin-jsx-a11y": "^6.10.2",
"eslint-plugin-node": "^11.1.0",
"eslint-plugin-prettier": "^5.5.5",
"eslint-plugin-react": "^7.37.5",
"husky": "^9.1.7",
"jest": "^30.2.0",
"less": "^4.5.1",
"lint-staged": "^16.2.7",
"mocha": "^11.7.5",
"nodemon": "^3.1.11",
"playwright": "^1.57.0",
"pm2": "^6.0.14",
"postcss": "^8.5.6",
"prettier": "^3.8.0",
"puppeteer": "^24.35.0",
"rollup": "^4.55.1",
"rxjs": "^7.8.2",
"sass": "^1.97.2",
"semantic-release": "^25.0.2",
"sinon": "^21.0.1",
"style-loader": "^4.0.0",
"stylus": "^0.64.0",
"supertest": "^7.2.2",
"tailwindcss": "^4.1.18",
"ts-loader": "^9.5.4",
"ts-node": "^10.9.2",
"typescript": "^5.9.3",
"vite": "^7.3.1",
"vitest": "^4.0.17",
"webpack": "^5.104.1",
"webpack-cli": "^6.0.1",
"webpack-dev-server": "^5.2.3",
"zx": "^8.8.5"
}
}

接著用以下指令建立 node_modules。

npm i

來看看容量。

$ du -h -d1
566M ./node_modules
567M .

這樣就知道已建立大量小型檔案。

複製 node_modules 以建立更多檔案。

for i in {1..59}; do
echo "node_modules.${i} をコピー中..."
cp -r node_modules "node_modules.${i}"
done

再看一次容量。

$ du -h -d1
...
34G .

變成相當大的容量。

tarball​

看看製作 tarball 的速度。

建立檔案​

$ time tar cf large-pkg.tar large-pkg

real 1m19.449s
user 0m6.702s
sys 0m48.121s

建立後 26 GiB

解壓​

$ time tar xf large-pkg.tar

real 2m11.793s
user 0m6.906s
sys 2m4.183s

打包與解壓都算快。

tar.gz​

接著用 tar.gz 測試。

tar 指令是用來打包的命令,gzip 是壓縮單一檔案的命令。把兩者結合所產生的就是 tar.gz 檔。現在 tar 指令本身就能壓縮與解壓 tar.gz(其他壓縮格式也同理)。

壓縮​

$ time tar czf large-pkg.tar.gz large-pkg

real 11m33.942s
user 11m18.811s
sys 0m55.573s

壓縮後:5.5 GiB

解壓​

$ time tar xf large-pkg.tar.gz

real 3m39.544s
user 2m1.189s
sys 2m58.317s

用 tar 與 gzip 解壓​

$ time sh -c 'gzip -dc large-pkg.tar.gz | tar xf -'

real 3m40.416s
user 2m0.272s
sys 3m0.043s

速度幾乎沒差。

並行解壓 (pigz)​

$ time sh -c 'pigz -dc large-pkg.tar.gz | tar xf -'

real 3m53.711s
user 1m38.147s
sys 4m42.893s

速度沒有太大改變。看起來檔案讀寫速度成為瓶頸。

bzip2​

壓縮​

$ time sh -c 'tar cf - large-pkg.1 | bzip2 > large-pkg.tar.bz2'

real 37m22.743s
user 28m40.329s
sys 3m31.000s

壓縮後:4.3 GiB

壓縮花了比較多時間,但壓縮率還不錯。

解壓​

$ time sh -c 'bzip2 -dc large-pkg.tar.bz2 | tar xf -'

real 11m31.287s
user 9m52.644s
sys 4m17.974s

解壓也花不少時間,但解壓率還不錯。

xz​

壓縮​

$ time sh -c 'tar cf - large-pkg | xz > large-pkg.tar.xz'

real 125m31.447s
user 124m10.523s
sys 5m18.330s

壓縮後:3.3 GiB

壓縮率極為優秀,但花的時間太多。

如果網路極度慢、儲存成本高、且多年才用一次的罕見情境,或許可以考慮。

解壓​

$ time sh -c 'xz -dc large-pkg.tar.xz | tar xf -'

real 8m11.527s
user 3m45.562s
sys 7m15.109s

LZ4​

壓縮​

$ time sh -c 'tar cf - large-pkg | lz4 > large-pkg.tar.lz4'

real 3m33.187s
user 0m53.958s
sys 2m57.122s

解壓​

$ time sh -c 'lz4 -dc large-pkg.tar.lz4 | tar xf -'

real 4m46.576s
user 0m32.174s
sys 4m36.055s

zstd​

Meta(前 Facebook)開發的高速壓縮方式。

壓縮​

$ time sh -c 'tar cf - large-pkg | zstd -T0 -o large-pkg.tar.zst'
/*stdin*\ : 18.57% (27492075520 => 5106239218 bytes, large-pkg.tar.zst)

real 9m13.819s
user 1m45.049s
sys 2m43.609s

解壓​

$ time sh -c 'zstd -dc large-pkg.tar.zst | tar xf -'
large-pkg.tar.zst : 27492075520 bytes

real 3m46.419s
user 0m46.533s
sys 3m34.668s

Libertouch ES(日文版)評測

Libertouch ES​

我入手了 Libertouch ES 的日文配列 (NC07902-B281-ES)。

不客氣地說,使用約一個月後的感想。

優點​

  • 打字手感
    在薄膜式鍵盤中屬於頂級。很貼合手指,有類似機械式的輕盈感,但觸感又不是機械式那種回饋。希望能一直堅持薄膜設計。
  • 堅固
    鋁製很堅固,幾乎可以當鈍器使用。
  • 鍵帽與 Cherry MX 相容
    能更換鍵帽這點很棒。

希望改善的地方​

  • 有時候按鍵不被識別…
    有些鍵按下去沒有反應,有時似乎還會出現多個鍵互換的情況。應該是韌體或軟體的問題。
  • 鍵帽容易脫落…
    可能是結構上的問題,空白鍵和 Enter 鍵特別容易掉,感覺不太穩定。
  • 希望有替換用鍵帽
    雖然有提供鍵位修改軟體這點不錯,但還是希望能有替換鍵帽。尤其左下的 Windows 鍵需求應該很大,特別想要。若能有 Home、End 也更好。
  • 希望更換或附加附贈線材
    附贈的是 Type-C to C 線。但電腦多數仍是 Type-A,所以希望能附 Type-A to C 線。
  • 價格偏高
    因為是試作品,8 萬日圓的高價可以理解。如果把所有問題都解決,售價落在 3 萬日圓左右我會考慮購買。若高於那個價格會比較難決定。

總評​

Libertouch ES 是一款在打字感與堅固性表現優異的高品質薄膜鍵盤。但在實用面仍有需改進之處,例如輸入識別不穩、鍵帽易脫落、附贈線材規格等。如果能增加替換鍵帽與線材選項,會更具吸引力。希望未來能有全尺寸或 80% 的選擇,這類需求可能比 65% 更大。

期待後續的改良與正式發售。

在 Raspberry Pi 上使用 mkcert 為 cockpit 建立證書,並設定到伺服器(cockpit)與瀏覽器的方法

執行環境​

以下環境確認過可用:

  • Raspberry Pi 5
  • AlmaLinux

各證書的意義​

  • raspberrypi.pem: 伺服器證書(公鑰)
    這個證書是針對主機名稱 raspberrypi 所簽發的 SSL 證書。像是網頁瀏覽器等用戶端會用它來驗證伺服器的正當性。(安裝於伺服器)
  • raspberrypi+1.pem: 伺服器證書(公鑰)
    這個證書是針對 raspberrypi <IP 位址> 這類主機名稱所簽發的 SSL 證書。同上
  • raspberrypi-key.pem:
    與 raspberrypi.pem 對應的私鑰。由伺服器端持有,用於 SSL 通信的加解密。絕對不可外洩。(安裝於伺服器)
  • raspberrypi+1-key.pem:
    與 raspberrypi+1.pem 對應的私鑰。由伺服器端持有,用於 SSL 通信的加解密。絕對不可外洩。同上
  • rootCA.pem: 本地根憑證(公鑰)
    由 mkcert 自動產生的本地 CA(憑證機構)憑證。將此憑證安裝到用戶端(例如瀏覽器)後,即可將 raspberrypi.pem 視為可被信任的憑證。
  • rootCA-key.pem: 本地 CA 的私鑰
    與 rootCA.pem 對應的私鑰,供 mkcert 用來簽署伺服器證書(例如 raspberrypi.pem)。這是 mkcert 內部使用的,通常不需要手動操作。

證書的產生​

使用 mkcert 指令來產生證書。產生後部署到伺服器。

mkcert raspberrypi <IP アドレス>

sudo cp raspberrypi+1-key.pem /etc/cockpit/ws-certs.d/raspberrypi.key
sudo cp raspberrypi+1.pem /etc/cockpit/ws-certs.d/raspberrypi.crt
sudo systemctl restart cockpit

確認本地根憑證的位置​

確認要安裝到電腦的根 CA 憑證所在位置。

mkcert -CAROOT

將根憑證複製到 PC​

將根憑證複製到電腦。

scp raspberrypi:/home/<USER>/.local/share/mkcert/rootCA.pem .
cp rootCA.pem rootCA.cer

在 Windows 上註冊憑證​

開啟 rootCA.cer,在憑證存放區將憑證新增到「受信任的根憑證機構」。

在 Android 裝置上註冊憑證​

將 rootCA.pem 移到裝置,然後從設定中安裝註冊。

如何用 podman 安裝 OpenStreetMap

匯入​

請將 japan-xxx.osm.bpf 下載到家目錄。 接著執行以下指令做準備。volume 選項末尾的 :Z 是在 SELinux 運作時要加上的。

請不要更改 /data/region.osm.pbf 的部分。

podman volume create osm-data

podman run -v <ダウンロードした osm.bpf ファイル>:/data/region.osm.pbf:Z -v osm-data:/data/database/ overv/openstreetmap-tile-server import

匯入範例​

podman volume create osm-data

podman run -v <ダウンロードした osm.bpf ファイル>:/data/region.osm.pbf:Z -v osm-data:/data/database/ overv/openstreetmap-tile-server import

執行​

使用以下指令啟動圖磚伺服器。

podman run -p 8080:80 -v osm-data:/data/database/ -v osm-tiles:/data/tiles/ -d overv/openstreetmap-tile-server run

設定防火牆後,網路上的其他裝置即可存取。

備份​

podman volume export osm-data > osm-data.tar

用 Raspberry Pi 當作伺服器公開網站

在 Raspberry Pi 上設定 nginx​

# 安裝並啟用 nginx
sudo dnf install nginx

# 編輯 /etc/nginx/nginx.conf
# sudo nano /etc/nginx/nginx.conf

# 啟動並設定 nginx 開機自動啟動
sudo systemctl start nginx
sudo systemctl enable nginx
sudo systemctl status nginx

編輯 /etc/nginx/nginx.conf​

在 http { server {} } 裡加入以下內容

location / {
return 200 'Hello, world!';
add_header Content-Type text/plain;
}

CloudFlare 設定​

  1. 前往 https://one.dash.cloudflare.com/。
  2. 開啟「網路」→「Tunnels」
  3. 按「新增隧道」

Cloudflare Tunnels

  1. 按「選擇 cloudflared」

選擇 Cloudflared

  1. 在「隧道名稱」輸入適當名稱,然後按「儲存隧道」

為 Cloudflare 儲存隧道名稱

安裝 cloudflared​

# 將 cloudflared.repo 新增到 /etc/yum.repos.d/
curl -fsSl https://pkg.cloudflare.com/cloudflared-ascii.repo | sudo tee /etc/yum.repos.d/cloudflared.repo

sudo dnf clean packages

# 安裝 cloudflared
sudo dnf install -y cloudflared --nogpgcheck

用 cloudflared 啟動服務​

sudo cloudflared service install xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

流量路由設定​

設定主機名稱的子網域與網域,以及服務類型和 URL。

設定畫面

按「完成設定」。