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?

AIを触り始めて3日で24時間動くものを作った ― そして8回間違えた

0
Posted at

私はAIの専門家ではありません。

3日前まで、エージェントという言葉の意味もよく分かっていませんでした。それでも、複数のAIに仕事を分担させて24時間動かす構成を作ることはできました。

そして、3日間で8回、自分の間違いを訂正しました。

この記事は、うまくいった話ではありません。初心者が何につまずくかの記録です。動くコードも出します。

1日目 ― 「動かす」の前に「止まる」を考えていなかった

最初にやったのは、データを集める処理を動かすことでした。動きました。数時間かかる処理です。

問題は、それが途中で止まったときに誰も気づかないことでした。

私は画面を見ていました。見ていない間に止まったら? そのときは、次に画面を開いたときに気づきます。それが何時間後かは、運次第です。

ここで最初の間違いをしました。生死を2値で考えたことです。

最初の実装   動いている / 止まっている

実際には3つ目があります。「分からない」です。

# 生死は3値で持つ。**不明を死に丸めない**
def state(job, now):
    if pid_alive(job.pid):
        return "RUNNING"
    if job.progress_age is None:          # 進捗の出所が読めない
        return "UNKNOWN"                  # ← **ここを DEAD にしない**
    if job.progress_age > job.stall_sec:
        return "STALLED"
    return "NOT_RUNNING"

「分からない」を「死んでいる」に丸めると、生きている処理を殺しにいきます。
逆に「生きている」に丸めると、死んだまま何時間も気づきません。
3つ目を持つだけで、両方が防げます。

進捗の出所は、ジョブ自身に宣言させる

見張りを作って、すぐ次の間違いをしました。

ログファイルの更新時刻で「進んでいるか」を判定したのです。

ところが、ログではなくデータベースに書く処理がありました。ログは静かなままです。
3時間無音に見えて、実際は正常に動いていました。

# ジョブごとに「進んでいる証拠がどこに出るか」を宣言させる
jobs:
  - id: collect
    progress: {kind: file_mtime, path: logs/collect.log}
    stall_sec: 1800
  - id: probe
    progress: {kind: sqlite, path: data/live.db, column: started_at}
    stall_sec: 900

宣言できないジョブは登録させないことにしました。
「どこを見れば進捗が分かるか」を書けないものは、そもそも見張れません。

2日目 ― 複数に分けたら、誰が何をしているか分からなくなった

作業を分担させました。データ収集、分析、検証。速くなりました。

同時に、「いま何が動いていて、何が終わったか」が分からなくなりました。
チャットの履歴をさかのぼって確認する。それ自体が作業になっていきます。

このあたりで、AIを増やすほど自分の仕事が増えるという奇妙な状態に気づきます。

答えは単純でした。状態を会話ではなくファイルに置く。

会話に置く   その会話を開いた人しか知らない。**閉じたら消える**
ファイルに置く 20分ごとに動く小さなプログラムが読める。**文脈が要らない**

3日目 ― 費用を測ってみたら、桁が違った

ふと、どれだけ使っているのかを調べました。

セッションの長さ    17時間
ターン数           469
読み直したトークン   108,575,962
→ 1ターンあたり     231,505

1回のやりとりごとに、23万トークン。

会話が長くなるほど、AIは毎回それまでの全部を読み返します。知らないうちに、1歩の値段が上がり続けていました。

もっと意外だったのはこちらです。

別のAIに作業を投げた場合   1回あたり   3,372 トークン
自分の会話で進めた場合     1ターン   231,505 トークン

約69分の1でした。

私は逆に思っていました。「AIを増やすと高くなる」と。だから自分で抱え込んでいた。
その日の作業の85%は、投げるべきものでした。

トークンを減らしたいなら、最初に見るのはプロンプトの長さではありません。セッションの寿命です。

監視を止めたら、その1時間後に対象が死んだ

長時間動く処理に、見張りをつけていました。

その処理を止めようとする過程で、私は先に見張りのほうを止めました。

1時間後、処理は別の理由で異常終了しました。誰も気づきませんでした。

13:25   異常終了
14:21   「進んでる?」と聞かれて、はじめて発覚
→ **57分間、何も動いていなかった**

止めたい対象こそ、止まった瞬間を知る必要がある。
当たり前のことですが、その場では分かりませんでした。

いま見張りは機械が持っています。人もAIも持ちません。

人が見張る    その人が止めた瞬間、見る目が消える
AIが見張る    費用がかかり、しかも会話が長くなるほど高くなる
機械が見張る  20分ごとに動く小さなプログラム。**費用はゼロ**

AIは、古い記録を疑わない

3日間で3回、同じことが起きました。

自分が前に書いたメモが古くなっていて、AIがそれを現在の状況として読んで、正しく推論して、間違った結論に着く。

AIは書かれていることを信じます。疑いません。

だから記録が古いだけで、そこから先の判断が全部ずれます。しかも一見もっともらしいので、気づきにくい。

対処は、記録と実物を機械が突き合わせることでした。

# 記録が「公開済み」と言っている記事が、本当に公開されているか
for rec in published_records():
    live = fetch_title(rec["url"])          # 実物を取りに行く
    if live != rec["title"]:
        print("記録と実物が食い違う:", rec["file"], "|", rec["title"], "", live)

生成物ではなく、記録のほうが先に古くなります。

後日談 ― 自分の「できました」を数えてみた

ここまでの間違いには共通点があります。どれも「対処済み」と自分で記録していました。

そこで、「できました」と言った回数そのものを数える台帳を作りました。

要点はひとつです。申告した時点で「どうすれば嘘だと分かるか」を書かせる。

$ python claims.py add --by 自分 --kind done \
    --claim "記事を公開した" \
    --check "APIの一覧に当該記事が含まれる"      # ← これが無いと登録できない

$ python claims.py add --by 自分 --kind done --claim "テスト" --check ""
却下: --check(どうすれば嘘だと分かるか)が空。
  **反証条件を書けない申告は、申告として数えない。**

初日の結果です。

申告 6件(真3 / 偽1 / 未検証2)
  的中率 75%    確かめたもののうち本当だった割合
  検証率 67%    そもそも誰かが確かめた割合

偽が1件出ました。「記事を公開した」と記録した直後、反証条件どおりに機械が確認したら、
実際には公開されていませんでした(投稿数の制限に当たって拒否されていた)。
私はそれを知らずに「公開しました」と報告する寸前でした。

**検証率のほうが大事だと思っています。**的中率だけを見ると、
「確かめやすいことしか確かめていない」状態を見逃します。

**この数字は上界です。**台帳に載るのは捕まった誤りだけなので、本当の誤り率はこれ以上あります。

同時に1つしか使えないものが、24時間運用の上限を決めた

複数を並列に動かそうとして、すぐ詰まりました。同時に1つしか使えない資源があったからです。

外部への接続、台帳への書き込み、公開。これらは並列にできません。
逆に言うと、それ以外は何本でも同時に動かせます。

排他        接続 / 台帳への書き込み / 公開      → 常に1本
それ以外    調べる・書く・検証する               → 何本でも同時

役割で分けるのをやめて、資源で分けることにしました。
「調査担当」「実装担当」と分けても、両方が同じ接続を使うなら並列度は1のままです。

実際、同じ資源を使う3つの部門を1つに統合しました。分けても速くならなかったからです。

ロックはファイル1つで足ります。

# 誰が握っているかと、そのプロセスが生きているかを見る
lock = read_lock()                      # {"pid": 1234, "owner": "collect"}
if lock and pid_alive(lock["pid"]):
    skip("%s が使用中" % lock["owner"])  # **絶対に起動しない**

**「ロックファイルがある」だけでは足りません。**握っていたプロセスが死んでいると、
ロックだけが残ります。pidの生死まで見ないと、永久に起動できなくなります。

使い回そうとして、初めて分かったこと

3日目に作った見張りを、別の作業でも使おうとしました。動きませんでした。

中を見たら、最初のプロジェクトの実パスが直接書かれていました。
ロックファイルの場所、データベースの場所、判定に使う条件。

書いてあったもの   C:\...\session.lock / C:/.../live.db / 特定の条件名
起きたこと         別の作業では、**存在しないパスを見に行った**

厄介なのは、それでも動いてしまうことです。
ファイルが無ければ「進捗が読めない」となり、「分からない」が「止まっている」に化けます。
最初に3値にしておいたのに、設定が間違っていれば意味がありません。

道具の側に、特定の作業の事情を1つも持たせない。
パスも資源名も条件も、外の設定ファイルから渡すようにしました。

道具   仕組みだけを持つ。**固有の値はゼロ**
設定   プロジェクトごとに1ファイル。**無ければその機能は黙って無効**

「どの設定で動いているか」が1行で出るようにもしました。
それが分からない状態が、いちばん危ないと思ったからです。

3日で分かったこと

1  会話が長いほど高い。**作業の区切りで切る**
2  任せたほうが安い。自分で抱えると桁が変わる
3  見張りは機械が持つ。人が持つと、人が止めた瞬間に消える
4  AIは記録を疑わない。だから記録を機械が点検する
5  並列度を決めるのは役割ではなく資源。**同じ資源なら分けても速くならない**
6  道具に固有のパスを埋めない。**使い回そうとした日に必ず気づく**
7  「できました」は測れる。**測るには反証条件を先に書かせる**

7つとも、始める前には想像していませんでした。

AIは速く作ります。問題は、作ったものが正しいかを確かめる側が追いつかないことでした。

だから私は、確かめるほうを機械にしました。
作るのはAI、確かめるのは機械、決めるのは人。

初心者が3日でたどり着いたのは、この分け方ひとつです。

次は、この申告の台帳を続けた結果を書きます。件数が増えたときに的中率がどう動くか、
そして測っていること自体が申告を変えてしまうか
を測ります。


この連載は、実際に測った数字だけで書いています。
同じことを試す人の役に立ちそうなら、ストックしておいてください。

この連載

AIエージェントを実際に回して、かかった費用と壊れた箇所を測って書いています。推測は書きません。実測値だけです。

  1. Claude Code のトークン使用量を実測したら1ターン23万 ― 節約に効いたのは「セッションを切る」だった
    https://qiita.com/manabu49-ai/items/2598a30d5140e4445ab6
  2. 57分間、死んだジョブを誰も見ていなかった ― AIエージェントの見張りを機械に渡す
    https://qiita.com/manabu49-ai/items/5c7506c94b5be58c040e
  3. AIに100通り試させたら、効果ゼロなのに5個が「有意」だった
    https://qiita.com/manabu49-ai/items/397a018948e1ab7ef29e
  4. 仕様書は2日前から手元にあった ― 読まずに実装した対処が、データを永久に失う設計だった
    https://qiita.com/manabu49-ai/items/9b947604d3dc4958fa36
  5. Qiitaで何が読まれるかを3,478本から探した ― 106通り掃いて、残ったのは6つだけだった
    https://qiita.com/manabu49-ai/items/d78a5aeaecf8d7f3f20b
  6. 本記事 ― AIを触り始めて3日で24時間動くものを作った ― そして8回間違えた

番外 ― Claude のモデル提供終了、猶予は告知から約60日 ― 過去9回を数えて、手元を検査するスクリプトを書いた
https://qiita.com/manabu49-ai/items/bc2a7be2a6ee4ceb6185

**無料記事は結論まで全部書きます。**出し惜しみはしません。

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?