跳至主要內容

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

· 2 分鐘閱讀

我做了一個可以和多個 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 無伺服器打造雲端儲存服務

· 4 分鐘閱讀

はじめに

自分専用のファイル共有システムが欲しいと思い、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

· 1 分鐘閱讀

下載 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

關於最強壓縮方式的調查

· 5 分鐘閱讀

結論

先說結論。

調查了對總計 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

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

· 2 分鐘閱讀

執行環境

以下環境確認過可用:

  • 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

· 1 分鐘閱讀

匯入

請將 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 當作伺服器公開網站

· 2 分鐘閱讀

在 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。

設定畫面

按「完成設定」。

在 AlmaLinux 10 (Raspberry Pi 5 / GNOME / aarch64) 上安裝 Mozc 的方法

· 1 分鐘閱讀

下載 rpm 檔案

  • mozc
  • mozc-gui-tools
  • ibus-mozc

請搜尋上述套件,並從 rpmfind.net 下載。

確認架構是否正確。

  • Raspberry Pi 5 則為 aarch64
  • 一般 PC 則為 x86_64

rpm 檔案範例

  • mozc-2.31.5810.102-160000.1.2.aarch64.rpm
  • mozc-gui-tools-2.31.5810.102-160000.1.2.aarch64.rpm
  • ibus-mozc-2.31.5810.102-160000.1.2.aarch64.rpm

安裝

指定下載的 rpm 檔案,使用 dnf 指令安裝。

cd ~/Downloads
sudo dnf install ./mozc-2.31.5810.102-160000.1.2.aarch64.rpm ./mozc-gui-tools-2.31.5810.102-160000.1.2.aarch64.rpm ./ibus-mozc-2.31.5810.102-160000.1.2.aarch64.rpm

登出

先登出一次。

設定

打開「設定」→「鍵盤」,然後按此順序新增:

  • Japanese (Mozc)
  • Japanese

設定完成!

透過 API 呼叫 GPT-5

· 1 分鐘閱讀

GPT-5 已經公開了,所以我用 PowerShell 試著呼叫它的 API。

程式碼

$uri = "https://api.openai.com/v1/chat/completions"
$headers = @{
"Authorization" = "Bearer $env:OPENAI_API_KEY"
"Content-Type" = "application/json"
}

$body = @{
model = "gpt-5"
messages = @(
@{
role = "user"
content = "筆記本和鉛筆合計是100元。鉛筆比筆記本便宜40元。鉛筆多少錢?"
}
)
} | ConvertTo-Json -Depth 2

$response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body

foreach($choice in $response.choices){
$choice.message.content
}

輸出結果

30元

理由:
- 設筆記本為 x 元、鉛筆為 y 元,則
- x + y = 100
- y = x - 40
- 代入得 2x - 40 = 100 → x = 70 → y = 30
- 驗算: 70 + 30 = 100,且鉛筆比筆記本便宜 40 元。
標籤: