動かざることバグの如し

近づきたいよ 君の理想に

ヨーロッパ過酷な14時間フライトに役立った機内グッズまとめ

概要

2025年にヨーロッパ旅行に行った。概略は以下

第1日目:9月14日(日)

  • 12:45:羽田空港 出発(AZ793便)
  • 20:30:ローマ 到着
  • 21:25:ローマ 出発(AZ078便)
  • 23:15:バルセロナ 到着(到着後ホテルへ)

第2日目:9月15日(月)

  • 08:30:バルセロナ市内観光 開始
  • 14:30:市内にて解散、自由行動

第3日目:9月16日(火)

  • 朝:ホテル出発(バス)
  • 11:40:バルセロナ 出発(AZ077便)
  • 13:25:ローマ 到着、午後市内観光
  • 18:00:ホテル到着

第4日目:9月17日(水)

  • 終日:自由行動(ローマ)

第5日目:9月18日(木)

  • 早朝:ホテル出発(バス)
  • 08:35:ローマ 出発(AZ316便)
  • 10:45:パリ 到着、午後市内観光
  • 20:30:ホテル到着

第6日目:9月19日(金)

第7日目:9月20日(土)

  • 終日:自由行動(パリ)

第8日目:9月21日(日)

  • 朝:ホテル出発(バス)
  • 11:40:パリ 出発(AZ319便)
  • 13:45:ローマ 到着
  • 14:55:ローマ 出発(AZ792便)帰国の途へ

第9日目:9月22日(月)

さて、問題はフライト時間である。

つまりフライト時間は14時間。新青森まででも4時間近くかかって長く感じたが、今回はそれどころではない。 しかも海外の航空会社である。そこで今回の海外旅行にあたって実際に役に立ったグッズを、機内で使うものに絞って紹介していく。あくまで個人の感想なので、その点は悪しからず。

機内で便利だったもの

着圧ソックス

マジで良かった。個人的には今回のMVPアイテムだ。 機内は狭く、長時間同じ姿勢でい続けることになるので、どうしてもエコノミークラス症候群(重力の影響で血液が足に溜まりやすくなる状態)のリスクがある。 特に今回乗ったITAエアウェイズは座席がかなり狭かったから尚更だ。

その点、着圧ソックスは足首からふくらはぎに適度な圧力をかけて、血液を心臓へ戻す手助けをしてくれる。 科学的な根拠もしっかりある対策グッズとして非常に優秀だ。ただ、具体的にどの商品がベストかは、正直なところ人によるとしか言いようがない。

自分は太ももがむくみやすく効果を最大限に得たかったので太ももまであるタイプを購入した。

デメリットとして機内では脱ぎ履きできないのでトイレの個室で頑張るしかない。以下のようなふくらはぎまでカバーするといったものもある。

メディケンシリーズはメディキュットよりちょっと安い。まず試すならこっちのほうがいいかもしれない。一度履いた状態で過ごしてみたりベッドで寝て自分に合っているか確認することをオススメする。

蒸気でホットアイマス

14時間フライトで夜に到着し、翌日からすぐ観光!!!となると、体力を最大限回復させて旅行本番に温存しておきたい。 とはいえ機内は人も多く、何だかんだで照明も明るいので、意外と眠れない。だからこそ、視界を強制的に暗くできるグッズを持っていくのは大事だと思う。

別に温かくならなくてもいいなら、100均などで売っている目隠しでも十分なのかもしれない。でもこれはケチる要素じゃないと感じたので、結局ちゃんとしたものを持っていった。 開封してから30分くらいは温かさが続くし、

無香料の方がいい。今は商品名が「めぐりズム」に変わっているけれど、仕様は同じ。ドラッグストアのセール時のほうが安いかもしれない。

マスク(ネクスケア)

機内は湿度が10%くらいなんじゃないかと思うほど、とにかく乾燥している。だから寝るときはマスクをしていた。 使ったのは「ネクスケア マスク 保湿タイプ」。これはコロナ禍でマスクがなかなか買えなかった時期に、唯一ヨドバシで手に入った商品で、名前の通り本当に保湿してくれる。 夏に買った当時は失敗したかもと思って結局使っていなかったのに、こういう場面でしっかり役に立った。別に普通のマスクでも全然違うと思う。

それに、フライトに限らず体調不良のときに薬局でマスクを買おうとすると、おそらくかなり高い。だから機内対策としてだけではなく、そもそも持っていったほうがいい。

USB-C to USB-Aケーブル

まず、モバイルバッテリーは必須。ただし、飛行機によっては座席シートにUSBポートが用意されていて、ケーブルを挿せば充電できる。 これは非常に助かるのだが、問題はケーブルの種類だ。普段はタイプCのUSBケーブルしか持ち歩いていないけれど、こういうときのポートは基本的にタイプAしかないので、USB-C to USB-Aケーブルが必要になる。

余談だが、そのケーブルがあれば空港の待機所でも無料で充電できるのでおすすめ。

イメージ画像。こんなに短かったら意味ないので1m以上は欲しい。国内と違って断線したり紛失しても気軽に買えないので予備は必須。

picture 1

サンダル

長時間靴を履き続けるのは厳しいので、サンダルを履くことにした。これがなかったら相当きつかったと思う。 今回自分が買ったサンダルは100均で買った安いやつで、海外旅行先でもホテルの部屋の中で履いていて、便利だった。帰宅後は後ぐされなく捨てたw

ウルトラストレッチアクティブジョガーパンツ

楽な服装がいいと聞いたのでユニクロでジャージを購入した。軽くて履きごごちはいいしホテルで寝るときもこれ履いていた。

www.uniqlo.com

ポッケタブルUVカットパーカ

機内は基本的に適温だがたまに涼しく感じる時がある。繰り返すが機内は乾燥しており、肌寒いと感じたままいると普通に体調不良になる。そこで、軽く羽織れる上着としてユニクロの「ポッケタブルUVカットパーカ」を購入した。

www.uniqlo.com

この商品の最大の特徴は使わないときは折り畳んで小さくできる点。

picture 0

あと防水なので簡易カッパとしても持っていった。実際フランスの最終日は雨降ったがカッパとして役に立ったので持って行って良かったと思っている。

エアークッション

今回は以下のヨック株式会社「ヨックション」を持っていった。Amazon楽天だと中華製品がほとんどだが、現地で購入ができない以上国内製品にこだわった。

離陸後に息を膨らまして大きくするタイプ。まず手荷物の量が限られているので使わないときは小さくできるタイプにしたのは正解だと思った。 個人的には座面が硬いと寝れないタイプだったので助かった。ただし今回のフライトで鍛えられたので次回持っていくかは要相談。

ほかに持っていったもの

リスト形式で紹介していく。

  • モバイルバッテリー: 必須
  • 歯ブラシ、歯磨き粉: フライトが長いのでトイレの中で軽く歯を磨いたりしていた。
  • 本: スマホだけだと長時間の暇つぶし厳しいので持っていった。文庫本は体積、重さの割に時間潰せるので良い。あとは地球の歩き方で予習するのも良い。
  • 目薬: 私はドライアイなので、普段から使っている目薬を持参している。
    • 航空会社のルールにもよるが、ジップロックに入れておかないと機内持ち込みできない気がするので注意してほしい。
    • 医薬品はOKだと書いている記事もあるものの、入国審査官がNGと言ったら結局持ち込めない(ノートパソコンで理不尽な目に一度遭った)。
    • 備えあれば憂いなしということで、目薬を持ち込むときは基本的に小さいジップロックに入れておくことを推奨する。
  • リップクリーム: 乾燥しているので必須。普段からよくなくすので一番安い緑のメンソレータムを使用している。今回の海外旅行でも予備で2本持っていった。
  • 化粧水: 乾燥しているので必須
  • ネイルオイル: 乾燥しているので必須
  • クリーム: 乾燥しているので必須 手が思った以上に乾燥する
  • 乳液: 乾燥しているので必須

持っていったがあんまり活躍しなかったアイテム

逆に持っていったはいいもののあんまり活躍しなかったアイテムも紹介する。次の旅行には持っていかないだろう。

のどぬーるぬれマスク

機内はよく乾燥すると聞いていたので、「のどぬーる ぬれマスク」を持っていった。 実際に一度使ってみたが、これは普通のマスクとは違って「ぬれフィルター」がセットされている。

そのおかげで通常のマスクより保湿度は上がる一方、つけ心地は普通のマスクと変わってくる。結果としていまいち寝付けなかったし、ネクスケアのマスクが最強すぎてそれで事足りた。 とはいえ、のどぬーる ぬれマスク自体がクソってわけではない。旅行前には一度は試しておくべきだ、という教訓を得た。

S字フック

機内で物ぶら下げるように購入したが、基本床に置くので使わなかった。

minioからRustFS乗り換え勢用のdocker-compose.yml

環境

  • rustfs/rustfs:1.0.0-alpha.81

やりたいこと

minioを使い続けるのが無理になったのでRustFSに乗り換えることにした。

rustfs.com

いままではminioをDockerで動かしていたのでRustFSもDockerで動かしたい。

RustFSのdocker-compose.yml紹介するぜ

公式サイトのdocker-compose.ymlサンプルコードを参考に自分用にシンプルに作成した。

rustfs:
  image: rustfs/rustfs:latest
  environment:
    RUSTFS_ACCESS_KEY: myuser
    RUSTFS_SECRET_KEY: mypassword
    RUSTFS_CONSOLE_ENABLE: "true"
  command: /data --console-enable --address ":9000"
  ports:
    - "9000:9000"
    - "9001:9001"
  volumes:
    - ./data:/data
  healthcheck:
    test:
      [
        "CMD",
        "sh", "-c",
        "curl -f http://127.0.0.1:9000/health && curl -f http://127.0.0.1:9001/rustfs/console/health"
      ]
    interval: 30s
    timeout: 10s
    retries: 3
    start_period: 10s
createbuckets:
  image: minio/mc:latest
  depends_on:
    - rustfs
  entrypoint: >
    /bin/sh -c "
    mc alias set myrustfs http://rustfs:9000 myuser mypassword;
    mc mb myrustfs/gitlab || true;
    exit 0;
    "

やってることはほぼ「minio + mcで初期バケットを作る」構成そのままだ。

  • rustfs サービスがS3互換の本体。/data をデータディレクトリとして起動して、S3 API:9000 で待ち受ける
  • RUSTFS_ACCESS_KEY / RUSTFS_SECRET_KEY はMinIOでいうところの MINIO_ROOT_USER / MINIO_ROOT_PASSWORD 相当の管理者キー
  • RUSTFS_CONSOLE_ENABLE: "true"9001:9001 はWebコンソール用。UIにアクセスするポート。
  • volumes: ./data:/data はホストの ./data を永続化領域にする。後述するがパーミッションエラーに注意
  • healthcheck9000 側の /health と、9001 側のコンソールヘルスを叩いて死活監視している。

で、MinIOでよくある「起動したら最初にバケット作っとく」問題は、RustFS本体にそういう機能がないので createbuckets を別コンテナでやっている。

  • 使ってるのは minio/mc(MinIO Client)で、RustFSのS3互換APIに対して普通に操作できる
  • mc alias set myrustfs http://rustfs:9000 myuser mypassword で接続先を登録している。docker-composeの同一ネットワーク内なので rustfs:9000 で届く
  • mc mb myrustfs/gitlab || truegitlab バケットを作る。すでに存在してても落としたくないので || true にしている
  • 最後に exit 0 してるので、このコンテナは「一回だけ実行して終わる」使い捨てジョブ扱いってわけ

起動すると無駄に格好いい管理画面UIが登場する。Alpha版で本番投入は不可の状況なのに日本語化もすでにされている。

picture 0

以下詰まったところ

自動でバケット作る機能はRustFS自体にはないの?

ない Issueもちょいちょい経っているがminio同様にコア機能ではなく自前で実装するか外部スクリプトでやるべきというスタンス

github.com

起動時にパーミッションエラーになる

初回起動時に以下のようなエラーが出た

Server encountered an error and is shutting down: Io error: Permission denied (os error 13)

公式Dockerfileソースコードを確認すると、

RUN addgroup -g 10001 -S rustfs && \
    adduser -u 10001 -G rustfs -S rustfs -D && \
    mkdir -p /data /logs && \
    chown -R rustfs:rustfs /data /logs && \
    chmod 0750 /data /logs

となっていた。そこでホスト側で

chown -R 10001:10001 ./data

を実行して再起動することで解決した。

GitLab CIでスクリプトからアーティファクトファイル名に環境変数を渡したい

環境

  • GitLab 18.6

やりたいこと

GitLabCIを使ってビルドして成果物をアーティファクトとして保存したかった。

保存するときにバージョンを取得しファイル名に組み込みたかったのだが、

script:
- yarn install --frozen-lockfile
- yarn build
- VERSION=$(node -p "require('./manifest.json').version")
- echo $VERSION
artifacts:
name: "my-chrome-extension-v$VERSION" # ここに値が入らない
paths:
- dist/

なぜかダウンロードしたときのファイル名は「my-chrome-extension-v.zip」となってしまいVERSION環境変数の値が反映されない。

対策

GitLab 16.4より $GITLAB_ENV環境変数を渡すことで script セクションから artifactscache セクションに環境変数を渡すことができるようになった。

今回のケースだと以下のようになる。

script:
- yarn install --frozen-lockfile
- yarn build
- VERSION=$(node -p "require('./manifest.json').version")
- echo "VERSION=$VERSION" >> $GITLAB_ENV # ← これを追加
- echo $VERSION
artifacts:
name: "my-chrome-extension-v$VERSION"
paths:
- dist/

これが該当のMR

gitlab.com

やったね

LiteLLM Proxyで会話数(messages)を制限する方法

環境

  • litellm v1.80

やりたいこと

現在、LiteLLMをLLMプロキシサーバーとして利用し、ロールプレイアプリを実装している。

ロールプレイという性質上、チャットのターン数は急増しやすく、50ターンに達することも珍しくない。しかし、生成に必要な文脈は直近の会話だけで十分なケースが大半である。50ターン分すべてを送信してしまうと、コストが増大するだけでなく、不要な情報によりコンテキストがブレる原因にもなってしまう。

LiteLLM Proxy側で会話数を制限したい場合、本来であればクライアント実装側で過去ログを間引くのが定石である。しかし、今回は諸事情により「アプリ都合でそれができない(クライアント側で会話数を制限できない)」という制約がある。

そこで、指定したターン数以上を切り捨てる処理をProxy側で実装することで対応した。

コード

from litellm.integrations.custom_logger import CustomLogger  
from litellm.proxy.proxy_server import UserAPIKeyAuth, DualCache  
from typing import Any, Literal  
  
class MessageTruncationHandler(CustomLogger):  
    def __init__(self, max_messages: int = 10):  
        self.max_messages = max_messages  

    async def async_pre_call_hook(self, user_api_key_dict: UserAPIKeyAuth, cache: DualCache, data: dict, call_type: Literal[
            "completion",
            "text_completion",
            "embeddings",
            "image_generation",
            "moderation",
            "audio_transcription",
        ])  -> dict: 
        # メッセージリストを取得  
        messages = data.get("messages", [])  
          
        # メッセージ数が制限を超えている場合、直近のmax_messages件に絞り込む  
        if len(messages) > self.max_messages:  
            # systemメッセージは保持し、直近のmax_messages-1件を追加  
            system_messages = [msg for msg in messages if msg.get("role") == "system"]  
            other_messages = [msg for msg in messages if msg.get("role") != "system"]  
              
            # 直近のメッセージを取得  
            recent_messages = other_messages[-(self.max_messages - len(system_messages)):]  
              
            # systemメッセージと直近メッセージを結合  
            data["messages"] = system_messages + recent_messages  
              
            print(f"Truncated messages from {len(messages)} to {len(data['messages'])}")  
          
        return data  
  
# インスタンスを作成  
message_truncation_handler = MessageTruncationHandler(max_messages=8)

LiteLLM Proxyには callbacks という仕組みがあり、リクエストを上流LLMに投げる直前に async_pre_call_hook() を挟める。ここで data(OpenAI互換のリクエストボディ)が渡されるので、data["messages"] を書き換えれば「直近Nターンだけ送る」が実現できる。

このコードがやっていることは以下である。

  • data["messages"] を取り出す
  • max_messages を超える場合だけ間引く
  • systemは残し、それ以外のメッセージを末尾から必要数だけ残す

ポイントは、LiteLLM Proxyが受け取る /v1/chat/completions のpayloadはだいたい {"messages": [...]} なので、ここを削るだけでprompt tokenが減り、コストとコンテキストブレを同時に抑えられる点だ。

設定

実際にProxyに組み込むには、Pythonファイルとして /app/message_truncation_callback.pyに配置して、proxyの config.yaml から参照する。形式は ファイル名.インスタンス名 である。

litellm_settings:
  # ファイル名.インスタンス名 で指定
  callbacks: ["message_truncation_callback.message_truncation_handler"]

Rails generateコマンド比較表

環境

Rails ジェネレーター種類大杉問題

rails scaffold 以外にもcontroller、model、さらにはscaffold_controllerやresourceなんてものもある。

scaffoldが一番使う機会が多いがすでにあるテーブルに対して追加でコントローラー生成したいということも少なくない。

いい感じの比較表が昔はあったんだが見つからなかったので自作した。

ジェネレーター Model Migration Controller Views Routes Model Test Controller Test Helper System Test JSON Views
scaffold
scaffold_controller
resource
controller
model
migration

controller は、引数でアクション名を渡したときだけ Views と Routes を自動生成するため △ にしている。

例えば rails g controller Users index show のようにアクションを指定すると、app/views/users/index.html.* などのテンプレートが作られ、同時に get "users/index" のようなルーティングも config/routes.rb に追記される。

一方で rails g controller Users のようにアクションなしで実行すると、作られるのはコントローラー本体とテスト類だけで、Views と Routes は増えない。

参考リンク