ラベル 技術 の投稿を表示しています。 すべての投稿を表示
ラベル 技術 の投稿を表示しています。 すべての投稿を表示

2026年5月28日木曜日

ぼくの中での emacs は死んでしまったのかもしれない

ぼくの中での emacs は死んでしまったのかもしれない

さらば emacs …

結論

  • emacs は完全に AI 関連で遅れを取っている
  • Copilot や OpenAI の拡張はあるが「そうじゃない」感がすごい
  • VSCode + copilot の使い勝手に勝てない

以下詳細に綴ります

emacs 拡張を使えば各種 AI と連携はできるができるだけで公式レベルには勝てないと判断した

emacs にも copilot.el などの拡張があるので emacs から各種 AI と連携することはできます

ただ連携できるだけでエージェントや MCP 連携 (最近は MCP もほぼ使わない) がほぼ無力な印象です
ただチャットに質問しているだけで自動で編集してくれたり作成してくれたりはしません

これを執筆しているときはほぼ emacs は使っていないので何ともですが自分が copilot.el を試した時期では少なくともエージェントのような機能はありませんでした
今であれば拡張も充実してエージェント機能も充実しているかもしれませんがそれでも VSCode + copilot のエージェント機能には及ばない気がします

公式が出しているエディタに付属している公式が出しているエージェントが公式の LLM を使うという当たり前の正解

基本 AI コーディングには LLM は必須です
OpenAI や Claude、Gemini などバックエンドに専用の LLM が必要です
もちろんローカルでも OK です

すべての LLM に言えることとしてすべての LLM は挙動が違うと思っています
OpenAI だとこう答えるが Gemini だとこう答えるといういわゆる「クセ」みたいなのがあると思っています

LLM に対して質問するのは基本的にエージェントです
そのエージェントが LLM の挙動を知っているか知っていないかは AI コーディングにおいて非常に重要な点かなと思っています

例えば VScode + copilot でローカル LLM を呼び出すとしましょう
するとどうでしょうか普通に使っている copilot とは全く挙動が違って全然思い通りのコーディングをしてくれなかったなんて経験はないでしょうか
自分はかつて試したことがあり全く期待通りじゃなかったことを実感しています
もちろんローカル LLM の選択やマシンのスペックが低かったなど考えられる要因は様々あります

つまりエージェントには適した LLM があり間違った組み合わせのエージェントと LLM では AI コーディングの精度に大きな差が生まれるのです

ここまで説明すればだいたいわかると思いますが同じ公式が作ったエディタ+エージェント+LLMの組み合わせのほうが確実に良い AI コーディングができるに決まっているのです

それなのに自力でエージェントを作ってサードパーティなりローカルの LLM を呼び出し AI コーディングしてもらうのは (決して間違ってはいませんが) DRY や KISS の法則的には反しているような気が自分はしています

与えられたものを素直に使うのが良いということです

独自の AI エージェントを作る場合は別

上記の件に関して独自の AI エージェントを作る場合は別かなと思います

エージェントは自作する必要がありますし LLM もどこか選定し API をコールする必要があります
そのエージェントに何をさせるかはわかりませんが自分だけの自分専用のエージェントであれば作りましょう

だたその場合も CLI で動作するエージェントはたくさんあるのでもし自分にあった既製品があるのであればそれを使うほうが精度としては良くなることがあるかもしれません (もちろん自作のほうが良い挙動になる可能性もあります)

エージェントが賢くなりすぎて最近は MCP すらいらない気がする

かつてはエディタやエージェントから MCP にアクセスする必要がありプロセスを立ち上げていました
MCP は Ubuntu などの Linux 環境では容易に立ち上げられるし管理も用意だったのでその点では emacs との親和性はありました

しかし最近のエージェントではデフォルトでブラウザが扱えたりローカルに対してはコマンドを叩けたり外部リソースにも簡単にアクセスしたりとかつて MCP が必要だった機能をエージェントが補完してくれています (内部的に MCP をエージェントがコールしてくれているケースもありますが)

要するにエージェントが賢くなりすぎていろいろできるようになってしまったがゆえに MCP も独自で立てる必要がなくなった気がしています

もちろん MCP が不要とはいいませんが昔ほど AI コーディングにおいて MCP が必須なコンポーネントであるとは現在では思えません

RAG や他の AI プロトコルについては今回は触れませんが RAG に関しても自分は同等の意見です
また Agents.md などのコンテキストというかシステムプロンプト的なものも最近では書く必要を感じなくなりました

そうじゃない感について

自分は主に copilot を使うので比較対象が copilot になってしまうのですが copilot には「Copilot CLI」という CLI 環境で動作するエージェントもあります
それを使って emacs の拡張で足りない部分を補いことはできるかもしれませんが個人的にはそれが「そうじゃない感」を生み出している点です

開発するときには基本的にはエディタや IDE と向き合います
かつて自分が emacs で開発しているときは開発 VM に ssh しそこで tmux を立ち上げその tmux バッファないで emacs を -nw で emacs を立ち上げて開発していました

確かにその環境は素晴らしかったです
ssh して tmux バッファを開けばすでに emacs でコーディングもできるし他のバッファでアプリのビルドやテストができる状態でした
その環境自体は自分は今も好きです

ただ AI コーディングをするとなるとエディタや IDE だけで完結する必要があります (必要があるというかその方がはるかにストレスレスだと思っています)
AI に質問するのにわざわざバッファを切り替えたり AI に質問したあとでわざわざ手動でコピペするというのは今の時代だとかなりナンセンスな方法です
一昔前であればブラウザでチャッピー (ChatGPT) に質問しその結果をエディタ側にコピペしてという開発手法でしたがコンテキストの与え方を考えたりトークンの数を減らすために RAG を構築したりとかなり大変でした (それは今もやってるかもしれませんが)

AI コーディングするにあたってあっちこっち切り替えてコピペしては簡単な作業とは言えかなりストレスが溜まります
それが VSCode + copilot ではすべてその場で解決します
質問すればすべて回答しその回答結果を即時に勝手にコードに反映してくれます
もしかすると一部コピペする必要があるかもしれません
もしかすると一部のコマンドの許可を与えるためにボタンを押す必要があるかもしれません
それでもアプリを切り替えてあっちこっちいったりする必要はなく VSCode ないだけでほぼ確実に完結します

これが個人的に感じている AI コーディングにおける「そうじゃない感」です

勝っている点があるとすれば

正直ないような気もします
本当に一部のケースで(慣れの問題もありますが) emacs のほうが素早く作業できる場合はあります

  • インストール -> どちらも homebrew できる
  • CLI 環境 -> emacs は -nw、VSCode は code-server or SSH トンネルなど
  • 拡張 -> どちらもあり (elisp or JavaScript)
  • コード実行 -> どちらもあり
  • オープンソース -> どちらも
  • キーバインド -> どちらもできる

などなど emacs にしかできないことはほぼないような気がします (もちろん探せば全然ありますが表面上の利用用途としてはほぼなさそう)

それでも一部の作業はやはり emacs を使う

自分が今でも emacs を使っているケースがあります
それは単純なメモを取るときです

VSCode + copilot は何でもかんでも AI でやろうとするのでただメモを取りたいだけなのにコードサジェスチョンをしようとします
タイプ中に関係ないことをサジェスチョンされるのは嫌なのでプレーンテキストメモするときは emacs を使っています

また VSCode は基本的に 1 プロジェクトのみを開くことを対象にしているので現在開発しているプロジェクト以外を編集/閲覧したい場合には emacs を使います
一応 VSCode にも workspace という機能があり 1 つのウィンドウで複数のプロジェクトを開くことができるのですが AI のノイズになったり単純に邪魔になったりするので自分はあまり使っていません

emacs は今後も生き続けるはず

それでも emacs は今後も生き続けるはずです
emacs 自体の発展や拡張の発展が AI コーディングにより期待できるからです

先ほども記載しましたが tmux + emacs の環境は今でも好きだし使っています

またキーバインドによるショートカットや矩形選択などの編集などはまだ emacs のほうが速く作業できるケースもあるのでケースバイケースかなとも思っています
ただメインのアプリケーション開発においてはもう VSCode しか使っていません

もちろん emacs キーバインドは大好きで VSCode でも他のエディタや IDE、ターミナルソフトでも emacs キーバインドを使っています

最後に

AI エージェントが搭載されていないアプリはエディタや IDE だけではなく今後なくなってくるのかもしれません
また AI の肝はエージェントの実装と言っても過言ではないので優れたエージェントを持つアプリが生き残っていくのかもしれません

今回はその例がたまたま emacs vs VSCode だったというわけです

その他メモ

  • トラブル時にサーバに SSH ログインして作業するときも vim があれば十分なケースが多い
  • Microsoft 製のソフトウェアで唯一使っているソフトかもしれない

2026年2月4日水曜日

軽量ローカル LLM でコーディングするのは不可能ではないがやめたほうがいい

軽量ローカル LLM でコーディングするのは不可能ではないがやめたほうがいい

不可能ではないがフラストレーションがえぐいだけ
誰でも動かせる LLM じゃあ商売上がったりなんでそりゃ無理だよねって話

結論

  • copilot レベルの精度を出すには軽量 LLM ではほぼ不可能
    • 学習および機能不足すぎる
    • 軽量 LLM では tools/thinking がない
    • 単純に学習量が少ないのか回答が的はずれななケースが多くフラストレーションが高い
  • ただしマシンスペックが潤沢 (RTX 4090/5090、メモリ VRAM+RAM が128GB 以上) なら話は別
    • 大規模 LLM が動かせれば copilot と同等の UX が得られるかも
    • 「かも」なのは実際に試せていないため

試したエディタ/エージェント環境

  • VSCode 1.108.2
  • copilot chat プラグイン (標準装備)

使ってみた LLM

  • phi4-mini:3.8b (agent)
  • llama3.1:8b (agent)
  • llama3.2:3b (ask)
  • codegemma:7b (ask)
  • gemma3:4b (ask)
  • qwen3:8b (ask)

以下それぞれ使ってみた感想です
細かい精度は違いますが大枠は変わらなかったのでモデルごとではなくまとめて記載します

感想/所感

  • thinking がないから命令を丁寧にしなければならない
    • どのコンテキストに対して何をどうやって修正すればいいのか
    • 命令の意図を解釈するフェーズが LLM にないので可能な限り丁寧に命令しなければならない
    • copilot は雑な質問でも命令を解釈しコンテキストを読み込み意図を解釈してくれる
  • Agent が使えないケースが多い
    • 使えても使い物にならない
  • 結論としては大規模 LLM が必要
    • ローカル LLM を使って copilot 相当のことをしたいのであれば大規模 LLM が必要
    • 最低でも 20b ほどだが現実ラインでは 120b レベルが必要 (gpt-oss:120b など)
    • 実際それを動かすとなると推論に必要なグラボと何よりも VRAM + RAM が必要になる
    • 120b なら合わせて 128GB ほどメモリ領域が必要になる
    • deepseek-v3.1:671b などは現実レベルでローカルで動かすのはほぼ不可能ということになる
  • 使い方が悪かった可能性もある
    • 命令やコンテキストの与え方が悪い
    • 命令が抽象的にすぎる (が本当は抽象的に質問してもやってほしい)
    • コンテキストが多すぎて LLM が解釈できない
    • 逆にコンテキストが少なすぎて無視してしまう
  • トンチンカンな回答例
    • コンテキストで与えたコードを無視して回答する
    • 単純なサンプルコードだけ提示して終了
  • 適切な LLM の設定
    • Continue などはどのフェーズでどの LLM を使用するか設定できる
    • copilot にも auto があるがローカル LLM だと使えない (はず
    • Ask/Edit/Plan で使用する LLM をカスタムエージェントで設定できるのだろうか (おそらくできない

最後に

大規模 LLM が動作するマシンが30万だとすると現在の Copilot Pro は 1500 円くらいなので

  • 300000/1500=200

で200ヶ月分は使えるので素直に Copilot Pro に課金するのがいいかもしれません
ローカルで大規模 LLM は無料と言えば無料ですが本当に無料ではありません (電気代とか)
他にゲームやマイニングとかで用途があればあってもいいかもですが

2026年1月14日水曜日

AI エージェントを作ってみた感想

AI エージェントを作ってみた感想

これまでにBotはいくつか作っていました

https://blog.kakakikikeke.com/2025/10/no-life-no-bot.html

それもエージェントと言えばエージェントなのかもしれませんがもう少しエンジニアっぽい AI エージェントを作ってみたので感想やらつらつら残しておきます

作ったもの

  • 不正プロセス監視エージェント
  • 不正ログ監視エージェント
  • 株価予測エージェント

不正プロセス監視エージェントのソースコード

#!/usr/bin/env python3
import json
import os
import subprocess
import time
from collections import deque

from google import genai
from slack_sdk import WebClient

GEMINI_API_KEY = "xxx"
SLACK_TOKEN = "xoxb-xxx"
SLACK_CHANNEL = "#general"
CUSTOM_BASE_URL = "https://your-llm-domain/endpoint"

# バッチ処理の設定
BATCH_MODE = (
    os.getenv("BATCH_MODE", "true").lower() == "true"
)  # デフォルトはバッチモード
BATCH_SIZE = int(os.getenv("BATCH_SIZE", "5"))  # 一度に判定するプロセス数
BATCH_TIMEOUT = int(os.getenv("BATCH_TIMEOUT", "10"))  # タイムアウト時間(秒)
MONITOR_INTERVAL = int(os.getenv("MONITOR_INTERVAL", "30"))  # プロセス取得間隔(秒)

print(f"Mode: {'BATCH' if BATCH_MODE else 'SINGLE'}")
print(
    f"BATCH_SIZE: {BATCH_SIZE}, BATCH_TIMEOUT: {BATCH_TIMEOUT}s, MONITOR_INTERVAL: {MONITOR_INTERVAL}s"
)

client = genai.Client(
    api_key=GEMINI_API_KEY,
    http_options=genai.types.HttpOptions(base_url=CUSTOM_BASE_URL),
)

slack = WebClient(token=SLACK_TOKEN)

# 既に通知済みのプロセスを追跡(重複通知を避けるため)
notified_processes = set()


# --- プロセス情報取得 ---
def get_processes():
    """
    ps コマンドでシステム上の全プロセスを取得する。
    戻り値: [{"pid": "...", "user": "...", "cpu": "...", "mem": "...", "cmd": "..."}, ...]
    """
    try:
        # aux フラグで詳細情報を取得
        cmd = ["ps", "aux"]
        result = subprocess.run(
            cmd,
            stdout=subprocess.PIPE,
            stderr=subprocess.PIPE,
            text=True,
            check=True,
        )

        processes = []
        lines = result.stdout.strip().split("\n")

        # ヘッダースキップ
        for line in lines[1:]:
            parts = line.split()
            if len(parts) >= 11:
                processes.append(
                    {
                        "user": parts[0],
                        "pid": parts[1],
                        "cpu": parts[2],
                        "mem": parts[3],
                        "vsz": parts[4],
                        "rss": parts[5],
                        "cmd": " ".join(parts[10:]),
                    }
                )

        return processes

    except Exception as e:
        print(f"Error getting processes: {e}")
        return []


# --- LLM にプロセスの異常判定を依頼 ---
def analyze_process_single(process_info: dict) -> bool:
    """
    単一のプロセスを LLM で判定する。
    怪しいなら True を返す。
    """
    prompt = f"""
あなたは Linux サーバーのセキュリティ監視 AI です。
以下のプロセス情報が不審かどうか、以下の形式で答えてください。

プロセス情報:
- PID: {process_info['pid']}
- ユーザー: {process_info['user']}
- CPU使用率: {process_info['cpu']}%
- メモリ使用率: {process_info['mem']}%
- VSZ: {process_info['vsz']}
- RSS: {process_info['rss']}
- コマンド: {process_info['cmd']}

判断:
- 不審なら "SUSPICIOUS"
- そうでなければ "OK"

通常のシステムプロセス(kernel threads、systemd関連など)は OK と判定してください。
考察や理由は出力しないでください。
"""

    response = client.models.generate_content(
        model="gemini-2.5-pro",
        contents=prompt,
    )

    if response.text is None:
        print("No response text from LLM")
        return False

    result_text = response.text.strip().upper()
    return "SUSPICIOUS" in result_text


def analyze_processes_batch(processes: list) -> dict:
    """
    複数のプロセスを一度に LLM で判定する。
    戻り値: {pid: bool} の辞書
    """
    if not processes:
        return {}

    # プロセス情報をまとめてプロンプト作成
    processes_text = "\n".join(
        [
            f"{i+1}. PID: {p['pid']}, User: {p['user']}, CPU: {p['cpu']}%, Mem: {p['mem']}%, Cmd: {p['cmd']}"
            for i, p in enumerate(processes)
        ]
    )

    prompt = f"""
あなたは Linux サーバーのセキュリティ監視 AI です。
以下のプロセス情報が不審かどうか、それぞれを判定してください。

プロセス一覧:
{processes_text}

判定結果を以下の JSON フォーマットで出力してください:
{{
  "1": "OK",
  "2": "SUSPICIOUS",
  "3": "OK",
  ...
}}

JSONのキーはプロセス番号、値は "SUSPICIOUS" または "OK" です。
通常のシステムプロセス(kernel threads、systemd関連など)は OK と判定してください。
"""

    response = client.models.generate_content(
        model="gemini-2.5-pro",
        contents=prompt,
    )

    if response.text is None:
        print("No response text from LLM")
        return {p["pid"]: False for p in processes}

    # JSON を抽出して解析
    result_dict = {}
    try:
        # レスポンスから JSON を抽出
        json_str = response.text
        # JSONブロックの開始と終了を探す
        start_idx = json_str.find("{")
        end_idx = json_str.rfind("}") + 1
        if start_idx >= 0 and end_idx > start_idx:
            json_str = json_str[start_idx:end_idx]
            parsed = json.loads(json_str)

            # プロセスごとに判定結果をマッピング
            for idx, process in enumerate(processes, 1):
                result = parsed.get(str(idx), "OK").upper()
                result_dict[process["pid"]] = "SUSPICIOUS" in result
        else:
            # JSON が見つからない場合は全て False
            result_dict = {p["pid"]: False for p in processes}
    except json.JSONDecodeError as e:
        print(f"JSON parse error: {e}")
        result_dict = {p["pid"]: False for p in processes}

    return result_dict


# --- Slack 通知 ---
def notify(process_info: dict):
    message = f"""
:rotating_light: *Suspicious process detected!*
PID: {process_info['pid']}
User: {process_info['user']}
CPU: {process_info['cpu']}% | Mem: {process_info['mem']}%
Command: {process_info['cmd']}
"""
    slack.chat_postMessage(channel=SLACK_CHANNEL, text=message)


# --- フィルタリング関数 ---
def should_check_process(process_info: dict) -> bool:
    """
    監視対象外のプロセスをフィルタリング
    """
    cmd = process_info["cmd"]
    user = process_info["user"]

    # self プロセスは除外
    if "process_monitor_agent" in cmd:
        return False

    # root 関連の明らかに安全なプロセスは除外
    safe_keywords = [
        "kernel",
        "[",
        "]",
        "systemd",
        "sshd",
        "agetty",
        "bash",
        "sh",
        "grep",
        "ps",
        "vim",
        "nano",
        "sudo",
        "root",
        "ls -l",
        "sd-pam",
        "unbound",
        "cron",
        "sleep",
        "less",
        "/sbin/init",
        "canonical-livepatch",
        "snapd",
        "ubuntu-advantage/timer.py",
        "multipathd",
        "journalctl",
        "bpftrace",
        # 開発ツール関連
        "tmux",
        "mysqld",
        "redis-server",
        "code-server",
        "mosquitto",
        "ollama",
        "nginx",
        # postfix 関連
        "pickup -l -t unix -u -c",
        "tlsmgr -l -t unix -u -c",
        "qmgr -l -t unix -u",
        "/usr/lib/postfix/sbin/master",
        # docker 関連
        "dockerd",
        "docker-proxy",
        # apt 関連
        "apt-get",
        "packagekitd",
        "/usr/lib/update-notifier/apt-check",
        "tar -df alternatives.tar.0 -C /var/lib/dpkg alternatives",
        # EDR 関連
        "cbram",
        "cybereason-sensor",
        "cybereason-activeconsole",
    ]
    if any(keyword in cmd for keyword in safe_keywords):
        return False

    return True


# --- プロセス監視メイン処理 ---
def monitor_processes():
    """
    定期的にプロセスを取得して監視
    """
    print("Process monitor started.")

    if BATCH_MODE:
        # バッチモード
        monitor_processes_batch()
    else:
        # 単発モード
        monitor_processes_single()


def monitor_processes_single():
    """単発モード: 各プロセスを即座に判定"""
    while True:
        try:
            processes = get_processes()

            # フィルタリング
            filtered_processes = [p for p in processes if should_check_process(p)]

            for process in filtered_processes:
                try:
                    suspicious = analyze_process_single(process)
                    if suspicious:
                        pid = process["pid"]
                        if pid not in notified_processes:
                            print(f"[!] Suspicious process: {process}")
                            notify(process)
                            notified_processes.add(pid)
                    else:
                        print(f"[OK] {process['cmd']}")

                except Exception as e:
                    print(f"Error analyzing process: {e}")

            time.sleep(MONITOR_INTERVAL)

        except Exception as e:
            print(f"Error in monitor loop: {e}")
            time.sleep(MONITOR_INTERVAL)


def monitor_processes_batch():
    """バッチモード: プロセスをバッチでまとめて判定"""
    process_buffer = deque()
    last_batch_time = time.time()

    while True:
        try:
            current_time = time.time()
            processes = get_processes()

            # フィルタリング
            filtered_processes = [p for p in processes if should_check_process(p)]

            # バッファに追加
            for process in filtered_processes:
                process_buffer.append(process)

            # バッチ処理の条件: BATCH_SIZE に達したか、BATCH_TIMEOUT を超えたか
            should_process = len(process_buffer) >= BATCH_SIZE or (
                len(process_buffer) > 0
                and current_time - last_batch_time >= BATCH_TIMEOUT
            )

            if should_process or current_time - last_batch_time >= MONITOR_INTERVAL:
                try:
                    # バッファ内のプロセスを一度に処理
                    processes_to_process = list(process_buffer)
                    process_buffer.clear()
                    last_batch_time = current_time

                    if processes_to_process:
                        results = analyze_processes_batch(processes_to_process)

                        for process in processes_to_process:
                            pid = process["pid"]
                            is_suspicious = results.get(pid, False)

                            if is_suspicious:
                                if pid not in notified_processes:
                                    print(f"[!] Suspicious process: {process}")
                                    notify(process)
                                    notified_processes.add(pid)
                            else:
                                print(f"[OK] {process['cmd']}")

                except Exception as e:
                    print(f"Error analyzing processes: {e}")

            time.sleep(1)  # CPU負荷を減らすため1秒待機

        except Exception as e:
            print(f"Fatal error: {e}")
            time.sleep(MONITOR_INTERVAL)


if __name__ == "__main__":
    while True:
        try:
            monitor_processes()
        except Exception as e:
            print(f"Fatal error: {e}, restarting in 5 seconds...")
            time.sleep(5)

これを systemd 配下で動作させています

簡単に流れの紹介

  1. ps aux の結果を取得
  2. LLM (gemini) に投げて各プロセスが怪しいか判定してもらう
  3. 怪しい場合は Slack に通知

他のログ監視も同じような仕組みです
株価予測エージェントは上記の流れとだいぶ違うのは違うのですが LLM に過去の株価情報を渡して予測してもらうだけです

感想

以下不正プロセス監視エージェントを使ってみた感想や所感です

お金がかかるので呼び出し方を工夫しなければならない

  • LLM にわたす情報を複数行にまとめて API をコールする
  • 1行ずつだと時間もかかるしお金もかかる
  • ローカル LLM が一つの解決策だがそれなりのマシンスペックは必要

判断するまでの速度

  • API (ネットワーク) + LLM の推論の時間が必ずかかるので判断までに数秒かかる
  • 完全なリアルタイムを実現するのは難しい
  • 一瞬で消えてしまうプロセスをキャッチできないケースがある
  • 速度もローカル LLM で解決できそうだが結局高スペックマシンでもそれなりの推論時間がかかるので数ミリ秒レベルのレスポンス速度は期待できない

精度が曖昧

  • 本当にアウトな場合のテストができない
  • アウトじゃないけどアウトと判定してしまう
  • アウトじゃないプロセスは無視するような仕組みが必要になる (コード内参照)
  • それ(特定のプロセス無視/ホワイトリスト形式)でいいのかという問題もある (同一プロセス名だと攻撃されても検知できなくなる)
  • 前はセーフだったけど次はアウトにしちゃう
  • 「怪しい」という LLM への命令が曖昧すぎるが故の過剰検知とハレーション

そもそもAI エージェントの具体的なユースケースは

  • 人間には難しい「判断」や「解釈」が必要なタスク+状況に応じて行動を変える仕事
  • ログから「人間レベルの洞察」を出す
  • 毎日の情報を「読む → 取捨選択 → 解釈 → 提案」する
  • インシデント予兆検知
  • コードのレポート・改善案
  • などなど、とにかく得意なのは「判断」と「解釈」あと「生成」

(現状は)何かに特化した AI エージェントしか作れない

  • 本当にやりたいことは「何でも」やってくれる汎用 AI エージェント
  • でも現状はやりたいことだけをやってくれる特化 AI エージェントしか作れない
  • もっというと「やりたいこと」すら AI エージェントが見つけて勝手にやってほしい
  • 特化エージェントを組み合わせて汎用っぽく見せかける

AI を使わなくてもいい問題

  • 今回作成した AI エージェントのようなログ監視やプロセス監視の製品はある
  • 判断が静的 (特定の文字列を含む場合にアラート/CPUが90%以上になったらなど) だがそれで十分なケースが多い

そもそも「AI エージェント」の定義は

  • 広義には「人の代わりに作業をやってくれるツール」だと思っている
  • 自分は今回のようなプロセス版のエージェントも VSCode の Copilot Chat のようなエディタもエージェントだと思っている
  • エージェントにもいろいろなタイプがあるので言葉に惑わされてはいけない

既製品を使ってもいい

ブースティング

  • 複数の LLM に判断させてその結果で最終的な判断をする
  • エヴァンゲリオンのマギシステム的なイメージ
  • ただしお金が倍々に増えていくので注意

最後に

AI もあるので AI エージェント自体を作ることは非常に簡単ですが本当に有用なものを作るのは難しいと言うか無くても何とかなるというのが正直なところです

株価予測エージェントは評価機能もあるので評価の結果が良ければ実践投入してどうなるか試してみようかなと思います
売買も自動でできると更に良いかなと思っています

2025年10月1日水曜日

ボットは絵も動画もブログも作成できるようになった

ボットは絵も動画もブログも作成できるようになった

人生とはボットを育てることなのかもしれない

これまで

https://blog.kakakikikeke.com/2025/04/kakakikikeke-die-twice.html

自分のツイートを学習させて自分っぽいツイートができるようになりました

アカウント

https://x.com/kakakikikekebot

現在のボットにできること

2025/09/26 現在ボットは成長し以下ができるようになりました

  • ツイートをする
  • ブログを書く
  • 絵を書く
  • ショート動画を作る

以下それぞれの仕組みを詳細に説明します

共通点

  • 動作マシンはローカルの M2 Pro mac mini
  • バックエンドでは「CPU50%のマイニング」「Ollamaサーバの起動」「SD-webui」が稼働している
  • 生成時は MPS にすべて対応しているものを採用
  • Python で実装
  • 生成時間は動画以外数分で完了 (絵、文章系)

ツイートをする

Twitter API + Ollama API (gemma3)

  • 自分のツイートを500件準備、そこから20件をプロンプトのコンテキストとして与える
  • ツイートを生成してもらうようなプロンプトを入力
  • レスポンスをパース、サニタイズなど調整してからツイート
  • ハッシュタグやリンクなどは含めないようにしている
  • プロンプトは以下
def build_prompt(past_tweets, sample_size=20):
    examples = "\n".join(random.sample(past_tweets, sample_size))
    return f"""以下は過去のツイートです:

----ここから過去のツイート----
{examples}
----ここまで過去のツイート----

これらの文体や話題を参考にして、あなたらしい新しいツイートを1つ生成してください。
日本語でかつ文章の意味がわかるような自然なツイートにしてください。
ツイートに「https://」から始まるようなURLは絶対に含めないでください。
ツイートの長さは50文字以内にしてください。
ツイートを作成した理由などは説明しないください、ツイートのみ生成してください。
"""

ブログを書く

Twitter API + Blogger API + Ollama (gemma3)

  • テーマを与えてそのテーマに基づいたブログ記事を書いてもらう
  • 出力は必ずHTMLになるようにプロンプトで調整、デフォルトがMarkdownっぽいので可能な限りMarkdownにならないようにする
  • テーマはランダムで100件保持、100件分のテーマを消費すると停止する
  • テーマはChatGPTに生成してもらっている
  • プロンプトは以下
def build_prompt_for_blog_post(theme):
    return f"""以下はブログ記事のテーマです。

----ここからテーマ----
{theme}
----ここまでテーマ----

このテーマを参考にして、あなたらしい新しいブログ記事を1つ生成してください。
ブログ記事内では Markdown 記法は使えません、Markdown 記法は自動で HTML に変換されません。絶対に Markdown 記法を使わないでください。
ブログ記事は Markdown 記法を使わずに生成してください。
ブログ記事内では HTML タグが使えます。
コードなどシンタックスハイライトが必要な場合は HTML タグを使ってください。
ブログ記事に画像は含めないでください。
ブログ記事内に StyleSheet や CSS の記述を含めることは可能ですがブログ本体の StyleSheet や CSS に影響を与えないようにしてください。
ブログ記事を作成した理由などは説明しないください、ブログ記事の内容のみ生成してください。
"""

絵を書く

Twitter API + Stable Diffusion WebUI API (model はランダム)

  • SD-Web UI を API モードで起動してそれをコールする
  • SD-Web UI の命令はそのまま JSON のペイロードとして API に送信できるので楽
  • 生成された画像のサイズは896x1152
  • モデルは固定ではなく10個ほど保持しておりそれを順番に使用していくように Python 側で制御
  • プロンプトもランダムになるようにしておりベース、性別、向き、服装、背景など要素ごとに毎回ランダムで生成するようにしている
def generate_prompt():
    # プロンプトの定義
    base = "masterpiece, best quality, ultra-detailed, 8k, cinematic lighting"
    gender = "".join(
        ("1 beautiful japanese young women, fair skin, light makeup, realistic face")
    )
    viewpoint = get_random_viewpoint()
    posing = get_random_posing()
    hair_cut = get_random_hair_cut()
    hair_color = get_random_hair_color()
    hair = f"{hair_color} {hair_cut}"
    face = get_random_face()
    background = get_random_backend()
    tops = get_random_tops()
    bottoms = get_random_bottoms()
    clothes = f"{tops}, {bottoms}"
    return "".join(
        f"{base}, {gender}, {viewpoint}, {posing}, {hair}, {face}, {background}, {clothes}",
    )

ショート動画を作る

Twitter API + Stable Diffusion WebUI API + LTX-Video (txv-2b-0.9.8-distilled)

  • text-to-video ではなく image-to-video の手法
  • 先に SD-WebUI を使って画像を作成してもらう
  • その画像を使って LTX-Video で動画にする
  • 256x384の4秒動画を作成 (num_frames=121、frame_rate=30)
  • 4秒以上の動画を生成する場合は192x256にすれば生成できる
  • 本当は896x1152で生成したいがリソースが足りない
  • 動画の生成はほぼマシンリソースをすべて使うので生成前にマイニングおよび ollama の runner を停止している
  • 各種プロセスを停止しないとメモリが枯渇してほぼ100%クラッシュする
  • 1動画生成するのに20-60分ほどかかる
  • 生成時間にムラがありおそらく入力している画像の影響だと思われるが安定しない
  • LTX-Video をサブプロセスとしてコールしているのでその作りが微妙
def convert_image_to_video(
    img_filename: str, width: int, height: int, prompt: str = ""
) -> Optional[str]:
    # 環境変数からパスを取得、設定がなければデフォルト値を使用
    work_dir = os.getenv("LTX_WORK_DIR", "/Users/kakakikikeke/Documents/work/LTX-Video")
    img_dir = os.getenv("LTX_IMG_DIR", "/Users/kakakikikeke/data/repo/videobot")

    # 仮想環境の Python を直接指定
    venv_python = os.path.join(work_dir, "env", "bin", "python")

    # 実行コマンド
    command = f"""
    cd {work_dir} && \
    PYTORCH_MPS_HIGH_WATERMARK_RATIO=0.0 \
    {venv_python} inference.py \
    --prompt "{prompt}" \
    --conditioning_media_paths {img_dir}/{img_filename} \
    --conditioning_start_frames 0 \
    --height {height} \
    --width {width} \
    --pipeline_config configs/ltxv-2b-0.9.8-distilled.yaml
    """

今後追加したい機能

  • 音楽の作成
    • AudioGen
    • RVC
  • 長めの動画
    • 今のマシンだと画像サイズを落としてフレーム数を上げるしかない
    • もっと軽量なモデルを探す
    • 本当はWan2.2やVeo3レベルの動画を生成したい
  • 俳句
    • gemma3 でプロントプト換えるだけ
  • 音楽+動画
    • 上記で生成した音楽と動画を何かしらで組み合わせる
    • 最終形はこれなのかもしれない
  • 昔の写真や自分で書いた絵を動画にしたい
    • 過去の復元
    • 手書きの絵 -> アニメ化 -> 動画化
  • NanoBanana 連携
    • 有料だとかなり強力
    • というかお金を使う前提ならローカルで全部動かさないで Google AI Studo API や Colab でガンガン動かして高品質、高画質、長尺コンテンツを作りまくれる
    • ただマネタイズ考えないと大変なことなりそう
    • 動画にするだけならVeo3でもいける

課題

  • Twitter API Free プランの上限
    • 500ツイート/1日にほぼ当たっている
    • 有料プランにすれば解決するがどうするか
    • コンテキスト(メディア系、テキスト系)にアカウントを分割するか、管理は面倒になる
  • Ollama で動作させるモデル
    • gemma3 はだいぶ軽量なので動作する
    • gpt-oss のような大規模LLMは動作しない (動作はするがスワップだらけでかなり遅くなる)
    • openhermes など試したがハッシュタグや無駄に絵文字を使うのでやめたりした
    • いいモデルを探すのが難しい
  • プロンプトだけで制御するのには限界がある
    • 含めないでと命令しても含めてくる
    • モデルに合ったプロンプトを調整しなければいけないのが辛いのでなんとかしたい (プロンプト最適化など)
  • プロンプトのパターン追加
    • テーマなど手動の部分があるので自動化したい
    • 絵を描く際のプロンプトのランダム抽出の候補を追加する
    • そもそも完全ランダムにする方法を考える
  • 絶望的にマシンスペックが足りない
    • やはり AppleSilliocn だとかなり辛い
    • 基本は RTX などの CUDA が使えるグラボがあることが前提 (xformers動作など)
    • 動画生成は特に VRAM を消費するので VRAM モリモリのグラボがないと厳しい (Wan2.2など)
  • ディスクも足りない
    • ローカルにモデルを置きまくっているのでディスクが枯渇する

最後に

多少の知識はいるが生成系は本当に楽な時代になった

2025年9月10日水曜日

NanoBanana の API を試す

NanoBanana の API を試す

ちゃんとこういうテック記事も投稿しないとダメだなと思い試しました
Python を使っています

環境

  • macOS 15.6.1
  • Python 3.12.11
    • google-genai==1.35.0
    • pillow==11.3.0

準備

必要なライブラリをインストールします

  • pipenv install google-genai
  • pipenv install pillow

google-generativeai は Deprecated なので注意してください
google-genai を使いましょう

API キーの取得

Google AI Studio (ここ)から生成しましょう

サンプルコード

比較的新しめの技術に関しては AI 先生に聞いても教えてくれないから公式ドキュメントを見るのが一番なんですよね

ファイルをアップロードして変換してもらうだけの簡単なスクリプトなのでリファクタなどはしていません

xxx の部分は上記で取得した API キーを入力してください

from io import BytesIO

from google import genai
from PIL import Image

client = genai.Client(api_key="xxx")

prompt = """
  背景を宇宙に変更してください。
"""
image = Image.open("reforg.jpeg")

# チャットクライアントの作成
chat = client.chats.create(model="gemini-2.5-flash-image-preview")
# イメージとプロンプトの送信
response = chat.send_message([prompt, image])

# 結果(テキストと画像)の取得、一応 pyright のエラーも対応
if response.candidates is None:
    raise

if response.candidates[0].content is None:
    raise

parts = response.candidates[0].content.parts
if parts is not None:
    for i, part in enumerate(parts):
        if part.text is not None:
            print(part.text)
        elif part.inline_data is not None and part.inline_data.data is not None:
            image = Image.open(BytesIO(part.inline_data.data))
            image.save(f"generated_image_{i}.png")

で入力した画像がこれで

出力された画像が以下です

すばらしい
特にレスポンスの速さがすばらしい

最後に

Google AI Studio の UI からだと制限があったりしてうまく画像が生成されないことがあるようなのでそんな場合は API を使いましょう

ただこれだとチャットというよりかは一問一答の会話なのでチャット的な感じでインタラクティブな感じにしたいのであればもう少し手を加える筆意ようがありそうです

それでもこれが無料で使えるのは本当に素晴らしいことだと思います

参考サイト

2025年4月25日金曜日

これでボクがいなくなってもボクっぽいツイートは生き続けるでしょう

これでボクがいなくなってもボクっぽいツイートは生き続けるでしょう

便利な時代になったもんだ

アカウント

現在は1日1回ツイートするようにしています

仕組み

ローカルの M2 Mac で動かしています
開発当初は Fine-Tuning方式 (LoRA) にしようかなと思ったのですが面倒なのと精度も微妙なのでやめました
たぶん ollama が動作すればいいのでスペックはそこまで必要ではないかもしれません

概要

仕組みは非常に簡単で過去の自分のツイートを LLM にコンテキストとして与えてそれを元に新しいツイートを生成してもらっているだけです

前処理も含めて流れの全体は以下です

  1. ツイートのアーカイブ (tweets.js) からランダムに500件抽出
  2. 500 件のうち 20 件をランダムに抽出し LLM のコンテキストとして与える
  3. ツイートを生成してもらう
  4. ツイートする

というかなり簡単な流れになっています

LLM モデル

特に理由はないですが日本語に対応していて可能な限り軽量な LLM を採用しています

これを ollama で起動して API をコールしてツイートを生成しています

  • ollama run openhermes

ツイートを抽出するスクリプト

さすがに全ツイートを食わせるのは無理なので先に何件か抽出します
とりあえず今は500件抽出しています
ここの抽出条件をもっと自分っぽいツイートだけに絞ったら文章も良くなるのかもしれないです

ここで生成されたツイートを LLM に食わせます

import json
import random

with open(
    "/path/to/twitter_archive/tweets.js",
    "r",
    encoding="utf-8",
) as f:
    raw = f.read()
    # 最初の JavaScript 部分を除去
    json_data = json.loads(raw.replace("window.YTD.tweets.part0 = ", ""))
    # "http://" または "https://" を含まないツイートだけに絞る
    filtered_tweets = [
        t["tweet"]["full_text"]
        for t in json_data
        if "http://" not in t["tweet"]["full_text"]
        and "https://" not in t["tweet"]["full_text"]
    ]
    tweets = random.sample(filtered_tweets, min(500, len(filtered_tweets)))

with open("tweets_500.json", "w", encoding="utf-8") as f:
    json.dump(tweets, f, ensure_ascii=False, indent=2)

LLM によるツイート生成

先ほど作成したツイートファイルを展開しプロンプトを作成します

ここのプロンプトを変えるだけで生成されるツイートが大きく変わります (これが世に言う「プロンプト最適化」)

以下が現状使っているプロンプトですが Openhermes だと「URL 含めないで」と言っても平気で含めてくるので別途正規表現で削除する処理を追加しています

Twitter の API は v2 でないとダメなのでご注意ください

import json
import os
import random
import re

import requests
import tweepy

# 過去ツイートの読み込み
with open("tweets_500.json", "r", encoding="utf-8") as f:
    past_tweets = json.load(f)

# プロンプト作成
# openhermes の場合は可能な限り質問を具体的にしないと自然な文章にならない
# chatgpt はだいぶ自然なツイートを作成してくれるから本当は OpenAI API を使いたい
examples = "\n".join(random.sample(past_tweets, 20))
prompt = f"""以下は過去のツイートです:

----ここから過去のツイート----
{examples}
----ここまで過去のツイート----

これらの文体や話題を参考にして、あなたらしい新しいツイートを1つ生成してください。
日本語でかつ文章の意味がわかるような自然なツイートにしてください。
ツイートに「https://」から始まるようなURLは絶対に含めないでください。
ツイートの長さは50文字以内にしてください。
ツイートを作成した理由などは説明しないください、ツイートのみ生成してください。
"""

# Ollama API にリクエストを送信
# stream: false にしないと一文字ずつ処理しないといけないので false にする
response = requests.post(
    "http://localhost:11434/api/generate",
    json={"model": "openhermes", "prompt": prompt, "stream": False},
)

# 出力を整える
tweet = response.json().get("response", "(ツイート生成失敗)").strip()
# URL が含まれる場合があるのでここで削除、ドメインが追加されたら随時対応
tweet = re.sub(r"(https?://\S+|pic\.twitter\.com/\S+|t\.co/\S+)", "", tweet).strip()
print("自動生成されたツイート:")
print(tweet)

# Twitter (X) にツイート投稿
# 各種キーとトークン(環境変数などから取得するのが安全)
# OAuth 2.0 User Context 用クレデンシャル(環境変数から取得)
bearer_token = os.getenv("TWITTER_BEARER_TOKEN")
consumer_key = os.getenv("TWITTER_CONSUMER_KEY")
consumer_secret = os.getenv("TWITTER_CONSUMER_SECRET")
access_token = os.getenv("TWITTER_ACCESS_TOKEN")
access_token_secret = os.getenv("TWITTER_ACCESS_SECRET")

if all(
    [bearer_token, consumer_key, consumer_secret, access_token, access_token_secret]
):
    client = tweepy.Client(
        bearer_token=bearer_token,
        consumer_key=consumer_key,
        consumer_secret=consumer_secret,
        access_token=access_token,
        access_token_secret=access_token_secret,
    )
    try:
        response = client.create_tweet(text=tweet)
        print("ツイートを投稿しました")
        print(f"ツイートURL: https://twitter.com/user/status/{response.data['id']}")
    except Exception as e:
        print(f"ツイート投稿に失敗しました: {e}")
else:
    print("Twitter API キーが設定されていません。環境変数を確認してください。")

定期実行

Mac なので launchctl で行っています

生成されるツイートについて

詳細はアカウントを直接見てください
結果としては50点くらいです

課題

以下で改善点を上げます

文章が自然ではない

  • モデルが悪いかも
    • 日本語特化なモデル (Elyza や LLM-jp など) に変更すると良くなるかも
    • 試した中で一番自然だったのは OpenAI API だったので本当はそれがベスト
  • プロンプトが悪いかも
    • URL はいらないと命令しても URL が含まれたりする
    • 説明はいらないと命令しても説明が含まれたりする
    • プロンプトを構造的にして質問したほうがいいのかもしれない
  • そもそもコンテキストとして渡しているツイート自体が自然な文章ではないのが原因の可能性はある
    • つぶやきは文章ではない
    • そこから意図や感情を読み取るのは LLM でも難しいのかも
    • 渡すツイート情報を変更するのはあり
    • そもそもツイートを渡すのではなく「こういうツイートをして」とかにしたほうがいいのかも
    • そうなると自分がどういうツイートをしているのかを分析する必要があるが
  • コンテキストとして渡すツイートの精査をする
    • ランダムではなく「それっぽい」ツイートだけを抽出するようにする
    • その抽出作業自体を LLM に任せればいいのかも

なぞのURLとハッシュタグを含んでしまう

  • URL は正規表現で別途対応
  • ハッシュタグは含まないように命令したほうがいいのかも

リファクタリング

  • しません

定期実行が止まったらわからない

  • 通知機能をつけてもいいかも
  • あとはローカル実行ではなく適当なクラウドサーバに移行してもいいかも
  • Twitter の API が v2 から v3 になったり非互換になったりトークンが期限切れになった止まる

変なツイートしないか心配

  • Fワード系
  • 個人情報系
  • 急に誰かにメンションするとか

その他思うこと

LLM について思うことをつらつら書いていきます
ちなみに過去に思ったことはこちらです

Fine-tuning やら RAG やら MCP やらあるが正直オーバーエンジニアリングな気はする

  • LLM 自体がかなり賢くなっている
  • トークンの制限さえなくなれば「プロンプト最適化」が一強になるはず

AI を使うために AI を使う開発をするのは今後必然になる気がするが本末転倒な気がしなくもない

  • 例えば dspy や LangChain、MCP Server といったツールを使って LLM を操作する技術は今後必須になってくる
  • しかし結構大変なイメージ
  • 特に Fine-tuning や RAG や MCP は作るのも大変 (それすら AI 先生に助けてもらえるが)
  • やりたいことは AI のための開発ではなくあくまでもプロダクトの開発
  • そしてせっかく作ったものが必ずしも開発の役に立つ、手助けしてくれるレベルになるとは限らない

完全に AI のみで済むような世界観は実はまだまだ先の未来なのかもしれない

  • とは言え開発に LLM がないという状況は今後考えにくい
  • LLM はあくまでも「お手伝いロボット」で生成してくれたコードなどは結局自分たちでテスト、検証しなければならない
  • 本当はそこまで全部やってほしい
  • 極論「新機能でこういう機能がほしいから実装しておいて」とか「既存のコードでここにバグがあるから直しておいて」と言えば全部勝手にやってくるのが最高だがその未来は遠そう
    • Agent など実現するためのツールは揃っているがそれらを使って実現するスキルが自分には足りない
    • 結局「生成してもらう」->「コピペ」->「テスト」のループを繰り返すのが一番効率がいいのかもしれない

エージェントについて

  • 本当にいるのだろうかと思っている
  • もちろん自分の分身レベルでエージェントが勝手に動いてくれるならほしい
  • でもそこまでのエージェントを作るのはかなりの労力と技術がいると思っている
  • 現状のエージェントはいわゆる「作業の自動化」でそれは別にエージェントを使わなくても問題ない
  • これまで通りやっていた自動化を LLM を使って実現すればいいだけの話になる
  • つまりエージェントレベルではいらない (それをエージェントと呼ぶのであれば話は別だが)
  • 強化学習でいうところのエージェント+LLM の世界観
  • エージェントには意思があると思う

最後に

AI 先生は中身がブラックボックスすぎて何をしているのかわからなさすぎて怖いです
だがそれがいい

2024年12月3日火曜日

自分はレビューいらない派です

自分はレビューいらない派です

ChatGPT 先生と相談しやはりいらないという結論に至りました

レビューの意義やメリットについてはここでは触れません、またそれらも人それぞれの感性があることを前提に進めます

この記事は全面的にレビューを批判する記事になっています

自分のレビューに対する考え要約

  • レビューでバグは潰せない
    • それならばE2Eなテストを増やそう
    • ちなみにテストを書くにも嫌い (特にユニットテスト)
  • 情報共有の意味があるならレビューではなく直接タスクを与えたほうが価値がある
  • 静的解析、テストなどシステムにすべてまかせるような仕組みを作るべき
    • それがないから人間(レビュー)に頼ってしまう
  • 作ってくれた人は全面的に信頼している
    • どうしてもその人がレビューしてほしいと思うのであればやるべき
    • 作ってくれたコードに文句を言いたくない、文句があるなら自分で直せばいいだけ
  • そもそもレビューは同レベルの知識を持つ人でないと意味がない
  • バグのないアプリは存在しない
    • 自分は常にそう思っている
    • バグがある箇所や時期を踏まれていないだけでどんなアプリにも必ずバグはある
    • もしくは全く使われていないだけか
  • 上記踏まえて時間の無駄、逆にやらないことによる時間を有意義に使える

ChatGPT先生との相談ログ

以下先生に相談したログです
フォーマットなどは崩れていますが可能な限り読みやすいようにしています

Q: コードレビューについて質問です。自分はコードレビューは意味がないと思っているのですがあなたはどう思いますか?

A:

コードレビューが意味がないと感じる理由を教えてもらえますか?その理由によっては、コードレビューの意義を再考したり、効率を改善したりするアプローチが見つかるかもしれません。

コードレビューは多くの現場で以下のようなメリットをもたらすと言われています:

品質の向上
バグの発見や設計の見直しが行われ、コードの品質が向上します。複数の目で見ることで、自分では気づけないミスや改善点が見つかることがあります。

ナレッジ共有
他の開発者がコードを読むことで、プロジェクト全体の理解が深まり、属人化を防ぐことができます。

スキル向上
レビューを受けることで、自分のコードスタイルや設計に対するフィードバックが得られ、スキルが向上します。レビューする側も他人のコードを読むことで学びを得ることができます。

規律の向上
他人に見られることを前提にコードを書くため、コードの可読性や設計を意識するようになり、結果としてチーム全体の基準が引き上がります。

ただし、意味がないと感じるケースも理解できます。例えば:

形式的で深掘りしないレビュー: 表面的なコメントや惰性で行われるレビューでは、実際の改善につながりにくい。
過度な批判や不毛な議論: レビューが対立の場になると、効率が下がるだけでなく、チームの雰囲気を悪化させる。
時間がかかりすぎる: レビューが長時間に及び、進捗が遅れる。
もし具体的な課題があるのであれば、それに応じた解決策を一緒に考えられます。たとえば「短時間で効果的なレビューの方法」や「建設的なフィードバックの仕方」などについて話すこともできます!

Q: 自分がコードレビューが意味がないと感じる理由は以下です「コードレビューをしてもバグがゼロになるわけではない」「コードレビューに情報共有の意味があるのならコードレビューで情報共有しないで直接コードを修正するようなタスクを与えて情報共有したほうが効果もある」「時間の無駄」が主な理由です

A:

コードレビューが「意味がない」と感じる理由、納得できる部分もあります。それぞれのポイントについて考察と改善のアイデアをお伝えします。


1. コードレビューをしてもバグがゼロになるわけではない
考察: 確かに、コードレビューをしてもバグがゼロになる保証はありません。バグは多くの場合、コード以外(設計、要件定義、使用環境など)に起因することもあります。また、レビューでは見逃しが発生する可能性も高いです。

改善アイデア:

レビューの目的を明確化する: 「バグをゼロにする」ことを期待するのではなく、「主要な設計ミスや重大なバグを減らす」「可読性やメンテナンス性を向上させる」ことに焦点を当てると現実的です。
ツールを活用: 静的解析ツールやテストフレームワークで自動的にバグを検出できる部分を先に排除し、コードレビューの負担を減らす。


2. コードレビューより直接修正タスクを与えた方が効果がある
考察: 「修正タスクを与えることで学ぶ」というアプローチは効果的です。ただし、修正を行う本人がプロジェクト全体の背景や他の影響を理解していない場合、不適切な修正が増えるリスクがあります。

改善アイデア:

ペアプログラミング: 修正タスクを与える代わりに、タスクを行う開発者とコードの知識を持つレビュー担当者が一緒に作業する。これにより効率的な情報共有が可能。
モブプログラミング: チーム全体で特定のコードを修正しながら、全員で学び合う場を作る。
事前の知識共有: タスクを与える前に、該当箇所に関連する設計や背景知識をしっかり共有しておく。


3. 時間の無駄
考察: 時間がかかりすぎたり、効果を実感できないコードレビューは、無駄と感じやすいです。特に、形式的で建設的な議論が生まれないレビューは、コストに見合うリターンが得られません。

改善アイデア:

軽量レビューの導入: 大規模なコードレビューではなく、小さな変更を頻繁にレビューすることで、時間を節約しつつ質を保つ。
レビュー基準の明確化: 「何を確認すべきか」をチームで明確にして、効率的なレビューを目指す。
ツールの導入: GitHubやGitLabなどのプラットフォームを活用して、変更箇所のみをピンポイントでレビューする。


代替案:コードレビューを廃止した場合のリスク評価
もしコードレビューを完全に廃止した場合、以下のリスクが考えられます:

コードの品質や一貫性の低下
属人化の進行
他メンバーのスキル向上や学習機会の減少
これらのリスクを受け入れられる場合は、より直接的なアプローチを選ぶのも1つの方法です。ただし、チームメンバー全員の合意と運用ルールの徹底が必要です。

Q: コードの品質や一貫性の低下はユイットテストやエンドツーエンドテストで補えると思います。属人化の進行はレビューではそもそも補えません、実際にコードを書けるレベルになって初めて属人化の解消になります。他メンバーのスキル向上や学習機会の減少、これもレビューでは補えません。属人化の部分の回答と同じです

A:

なるほど、非常に筋が通った主張ですね。コードレビューのメリットとして挙げられがちな点が、他の方法で代替可能であるという視点は重要です。以下の観点であなたの意見を深掘りしつつ、代替アプローチについて考えてみます。


1. コードの品質や一貫性について
あなたの言う通り、ユニットテストやエンドツーエンドテスト(E2Eテスト)は、バグ検出や動作保証の観点でコードレビュー以上に効果を発揮することがあります。さらに、コードの一貫性についても、自動化ツール(PrettierやESLint、StyleCopなど)を使えば、レビューに頼る必要がほとんどなくなります。

代替アプローチ:

テストカバレッジの向上: ユニットテストやE2Eテストの割合を高め、レビューではチェックしない項目をカバー。
CI/CDの整備: 自動テストとコードフォーマットチェックをCI/CDパイプラインで実行し、レビューを通さずに品質を担保。
注意点: ただし、レビューはコードの「意図」や「設計」にフォーカスできますが、自動化ツールはその部分を補えないことがあります。テストで検出できない設計ミスや冗長な処理を防ぐための仕組みが必要です。


2. 属人化の進行について
属人化の解消には、「コードを書けるレベルになる」ことが不可欠であるという点に同意します。コードレビューだけでは、確かにそれが実現しない場合も多いです。

代替アプローチ:

ペアプログラミングやモブプログラミング: 直接的にコードを書きながら学ぶことで、知識共有とスキルアップを同時に実現できます。
ローテーション制度: 特定のタスクや機能領域について定期的に担当者を変える。これにより、複数のメンバーが同じ部分を理解する機会を得られます。
技術共有会: チームでコードや設計について定期的に議論し、属人化を減らす。


3. スキル向上や学習機会について
コードレビューはスキル向上の一部を担うことがありますが、必ずしもそれが最適な手段とは限りません。レビューのコメントが表面的だと学びは少なく、実際にコードを書く経験には遠く及ばないという意見は非常に妥当です。

代替アプローチ:

トレーニングタスクの設定: 開発者が新しい技術や設計パターンを学ぶための小さなタスクを与え、学びの機会を明確化。
メンター制度: 経験豊富な開発者が他のメンバーを指導し、実践を通じて学ぶ環境を整える。
レビュー以外のフィードバック: リアルタイムでのコード解説や設計レビューセッションを設ける。


結論
あなたの意見の核は「レビューだけではこれらの課題は解決しきれない」という点にあり、それは正しいです。加えて、レビューは一つの手段に過ぎず、それがチームにとって非効率であるならば、他のアプローチを導入すべきです。

チームとして以下のようなプロセスを試してみる価値があるかもしれません:

コードレビューの目的を再定義: レビューを行う場合でも、レビューの対象や方法を限定し、設計や意図に焦点を絞る。
自動化と実践型アプローチを組み合わせる: テストやCI/CDの自動化に投資しつつ、学びの場としてペアプログラミングや技術共有会を導入。
コードレビューを必須にする必要はありません。チームの状況や課題に合わせて柔軟にプロセスを設計することが最も重要です。

Q: ではコードをチェックできる自動化された環境がありかつチーム内で属人化を解消するコードの知識を共有する体制になっているのであればレビューは不要という認識でいいでしょうか?

A:

その通りです!コードをチェックする自動化された環境が整っており、かつ属人化を解消するためのコード知識共有体制が機能しているなら、コードレビューは不要と言えるでしょう。

以下にその条件を整理します:


1. 自動化されたチェック環境
静的解析ツール: コーディング規約の遵守や潜在的なバグ検出を行うツール(ESLint、SonarQube、Checkstyleなど)が適切に設定されている。
テスト自動化: ユニットテスト、統合テスト、E2Eテストが十分にカバーされており、CI/CDパイプラインで実行される。
コードフォーマットツール: PrettierやBlackなどでコードのスタイルを自動整形し、見た目のレビューを不要にする。
セキュリティチェック: 依存関係の脆弱性チェックやコードのセキュリティスキャンを自動化している。


2. 属人化を解消する仕組み
ナレッジ共有の文化: ドキュメント化や共有会、コードベース全体の可視性が確保されており、誰でも理解しやすい体制になっている。
ローテーションやペアプログラミング: 特定のタスクや機能領域が特定の人に依存しないように、メンバーが交代でコードを触る機会がある。
実践を通じた学び: 他のメンバーが既存のコードを理解しやすいタスク設計やコーディングの実践が行われている。
コードの意図を記録: 関数やクラスのコメント、設計ドキュメント、コミットメッセージで十分にコードの意図が伝わるよう工夫されている。


3. レビュー不要とする前提条件
以下の質問に「はい」と答えられる場合、レビューは不要と考えられます:

バグの発生率が許容範囲内か?
→ テストや静的解析でほぼ十分にバグが防げている。
チーム全員がコードの目的や設計を理解しているか?
→ 共有体制が機能し、メンバーがコードを理解している。
新メンバーが独立して貢献できるまでの時間が短縮されているか?
→ ドキュメントやナレッジ共有によって属人化が解消されている。
チーム内の信頼が高いか?
→ メンバー間の信頼が高く、他人のコードが問題なく利用できる。


検討ポイント
レビューを廃止する前に以下も確認すると良いでしょう:

トライアル期間を設ける: レビューを完全に廃止する前に、レビュー頻度を下げたり、特定のケースでのみ行う運用を試してみる。
データの計測: レビューをしないことで、バグの発生率やリリース速度がどう変化するかを測定する。


結論
「自動化されたチェック環境」と「属人化を解消する仕組み」が整備されているのであれば、レビューは不要であるという認識で問題ありません。それどころか、その環境が機能している場合、レビューを行うことが逆に非効率になる可能性もあります。

重要なのは、チームにとって最適なプロセスを柔軟に設計し、定期的に見直すことです。このアプローチがあなたのチームにとってベストな選択肢になることを願っています!

相談した結果感じたこと

レビューは一つの手段に過ぎず

確かにこれには同意した
強制するものではなくあくまでも「手段」であるということ
そういう意味でも自分はレビューという手段の優先度はかなり低い

あなたのチームに合ったプロセスを模索するのが重要

確かにこれも同意した
チーム内でレビューに重きを置いていることを上も理解してそこにコストを割くことを容認してくれている場合はじっくりやっていいと思う

ただそれでもコードの内容を知らない人にレビューしてもらっても何も意味がない気がする

データの計測: レビューをしないことで、バグの発生率やリリース速度がどう変化するかを測定する。

これも同意
むしろデータ計測やサービス効果測定みたいなものは初めからあってもいいと思う
ただどうやるかと何を評価項目にするかとどう抽出するかはかなり難しい気がする

最後に

最後は ChatGPT 先生を言いくるめたような感じなってしまいましたが最終的には自分の意見に同意してくれたのでよかったです

参考文献