0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

「次に試すこと」を推薦するCLI

0
Posted at

人生の目的が404なので、感情ログから「次に試すこと」を推薦するCLIを作った

人生の目的が分からない。何をして喜ぶのかも、正直よく分からない。

そこで、妙に元気な少年に相談した。彼の手段はだいたい、探すか、集めるか、強くなるか、の三択だ。

少年「人生の目的、どこだ! 探せば出てくるだろ!」

相方「出てくる話じゃない」

少年「じゃあ七つ集めればいいんだな!」

相方「集めるのはお前の妄想だ」

少年「一行書けって、それで人生わかるのかよ!?」

相方「わからない。でも一行ある方がマシだ」

meaning_of_life() を直接呼び出してはいけない

調子が悪い時ほど、でかい問いを投げてしまう。

meaning_of_life()

でもこの関数、要件定義が終わってない。返り値が大きすぎる。テストケースもない。疲れてる時に呼ぶと、いつまでも Loading... のまま固まる。

だから問いを小さくする。

what_should_i_try_next()

「何のために生まれたか」じゃなくて、何をしたあとなら、もう一回だけ試してもいいかな、と思えるかを記録する。

心理学から借りる三つの設計原則

1. 行動してから、反応を観察する

行動活性化は、活動と気分を見ながら生活の行動を少しずつ調整していく考え方。うつ病の心理療法として研究されているが、ここでは治療の代わりじゃなく、日常の小さな実験を設計するための発想だけ借りる。

少年「楽しいこと見つかってから動けばいいんだろ!」

相方「それだと今日は一生動けない」

少年「一生!? そんなバカな」

相方「腹減る前に飯は食えない。待つな。先に一口食ってみろ」

少年「一口!? そんなので強くなるのか!」

相方「強くなる話じゃない。楽だったか、見るだけだ」

2. 喜びを単一指標にしない

自己決定理論では、自律性・有能感・関係性の三つが大事だと言われる。

観点 日常語にすると
自律性 自分で選んだ感じがあったか
有能感 ちょっと前に進んだ感じがあったか
関係性 誰かや何かとつながった感じがあったか

気分だけじゃなく、この三つもログに入れる。

少年「つまんなかった日は全部0点でいいよな」

相方「それ、お前が勝手に付けてる点数だろ」

少年「じゃあ何を見ればいい」

相方「自律、有能、関係。三つ」

少年「三つ!? めんどくせえ! 一つにまとめろ!」

相方「一つにまとめたら『楽しかったか』だけになる。それがいちばんズルい」

3. 意志を条件分岐に変える

「明日から頑張る」は、まだ実装じゃない。

実行意図は、「状況Xになったら、行動Yをする」という if-then 型の計画。

if lunch_break_started:
    walk_for(minutes=5)

気合いを増やすんじゃなくて、行動が始まる条件を決める。

少年「よし、気合い入れた! 明日から本気だ!」

相方「その本気、何時に起動する」

少年「起動……? 朝だ!」

相方「朝の何時」

少年「……起きれた時」

相方「起きれた時に本気、は詐欺だ。昼休みに入ったら外を5分歩け。そこまで書け」

まずはJSONLに記録する

でかいAIはいらない。最初はCLIで十分。

from __future__ import annotations

from dataclasses import asdict, dataclass
from datetime import datetime
from pathlib import Path
import json

LOG_PATH = Path("life_events.jsonl")


@dataclass(frozen=True)
class LifeEvent:
    activity: str
    energy_before: int
    energy_after: int
    repeatability: int
    autonomy: int
    competence: int
    connection: int
    distress: bool = False
    created_at: str = ""

    def normalized(self) -> "LifeEvent":
        return LifeEvent(
            activity=self.activity.strip(),
            energy_before=clamp(self.energy_before),
            energy_after=clamp(self.energy_after),
            repeatability=clamp(self.repeatability),
            autonomy=clamp(self.autonomy),
            competence=clamp(self.competence),
            connection=clamp(self.connection),
            distress=self.distress,
            created_at=self.created_at
            or datetime.now().isoformat(timespec="seconds"),
        )


def clamp(value: int) -> int:
    return max(0, min(10, value))


def score(event: LifeEvent) -> int:
    delta = event.energy_after - event.energy_before
    return (
        delta * 2
        + event.repeatability
        + event.autonomy
        + event.competence
        + event.connection
    )


def append_event(event: LifeEvent) -> None:
    event = event.normalized()

    if event.distress:
        print("最適化を中断します。今は一人でログを解析する場面ではありません。")
        print("身近な人、医療機関、地域の緊急窓口へ接続してください。")
        print("相談先一覧: https://www.mhlw.go.jp/mamorouyokokoro/")
        return

    with LOG_PATH.open("a", encoding="utf-8") as file:
        file.write(json.dumps(asdict(event), ensure_ascii=False) + "\n")


def load_events() -> list[LifeEvent]:
    if not LOG_PATH.exists():
        return []

    events: list[LifeEvent] = []

    with LOG_PATH.open(encoding="utf-8") as file:
        for line in file:
            events.append(LifeEvent(**json.loads(line)))

    return events


def recommend(events: list[LifeEvent]) -> str:
    if not events:
        return (
            "候補を三つ試してください: "
            "5分歩く、何かを作る、誰かを少し手伝う"
        )

    best = max(events, key=score)

    return (
        f"次は「{best.activity}」を、"
        "前回より小さく再実行してください。"
    )


if __name__ == "__main__":
    append_event(
        LifeEvent(
            activity="昼休みに5分だけ歩く",
            energy_before=3,
            energy_after=5,
            repeatability=8,
            autonomy=7,
            competence=2,
            connection=1,
        )
    )

    print(recommend(load_events()))

スコアは雑でよい

ここで大事なのは精密な数理モデルじゃない。「なんか全部ダメだ」って巨大な認識を、観察できるイベントにバラすこと。

スコアが高い活動を人生の目的と呼ぶ必要もない。次は前回より小さく試すだけでいい。

少年「散歩、点数いちばん高いじゃねえか! これが答えだ!」

相方「答えって言うな。まだ早い」

少年「じゃあもっと歩けば近づくのか!」

相方「近づかない。同じところを小さく試せ」

少年「小さく!? 俺はもっと強く——」

相方「歩け。帰ってきたら一行書け」

7日間のMVP

毎日、一つだけ試す。記録する項目はこの七つで足りる。

項目 意味
activity 何をしたか
energy_before 開始前の気力
energy_after 終了後の気力
repeatability もう一度やってもよいか
autonomy 自分で選んだ感じがあったか
competence ちょっと前に進んだ感じがあったか
connection 誰かとの接点があったか

候補が浮かばない日は、次の三つから一つ選ぶ。

系統
体を使う 散歩、料理、片付け、入浴
渡す 人を手伝う、短い連絡をする、知識を共有する
作る 文章、コード、絵、植物の世話

フェイルセーフは必須

ログを取れないほどつらい時、自分を傷つけるかもしれない時は、アプリの推薦を止める。これは例外処理じゃなくて、主要機能。

if distress_detected:
    stop_optimization()
    connect_to_human_support()

この記事を読んでいる今、自分を傷つける可能性があるなら、記録や習慣化より安全を先に。日本にいる場合、差し迫った危険がある時は119。厚生労働省の「まもろうよ こころ」に電話・SNS・チャットの相談先がある。

まとめ

人生の目的を返すAPIはない。でも次の小さな実験を選ぶことはできる。

while alive:
    try_something_small()
    observe_response()
    keep_only_weak_signals()

少年「人生の目的、まだ分かんねぇ!」

相方「昨日もそう言ってた」

少年「今日は5分歩いた! 進んでるだろ!」

相方「進んでるのは歩数だけだ」

少年「……でも腹減った。飯食う!」

相方「そこは毎日確実だな」

注意

この記事は診断や治療を目的にしたものではない。日常生活が大きく崩れている時、何週間も何にも反応しない時、消えたい気持ちが続く時は、一人で最適化を続けず、医療機関や相談窓口へ。

参考文献・相談窓口

おもんな。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?