Claudeを使って業務支援アシスタントAIを開発してみた

未分類

仕事柄、複数のクライアントのタスクが平行に走る上、ツールもメールの他にMattemost、Slack、Backlogなど多岐にわたります。しかも、複数の企業名義で仕事をしていることもあり、SlackやBacklogなどには複数のアカウントがあり、それぞれにメッセージが届きます。
また、見積の作成や月末月初には請求業務などもあり、これらのタスクをどうにか管理する仕組みが出来ないかと、常々思っていました。

タスク管理で一番手軽なのはToDoアプリですが、情報のソースが様々なプラットフォームに分散しているため、出典と期日などをいちいち手でToDoアプリに登録するのも面倒で、なかなか習慣として定着しませんでした。

そこで、今回Claudeを使って業務支援アプリを作ってみよう、と思い立つに至りました。とりあえず業務で使えるレベルになったので、現状をご紹介したいと思います。

コンセプト

ローカルLLMで動く、業務用のAIアシスタント。様々なプラットフォームに分散したメッセージを読み、内容を分析して収集、タスク管理の他、私が認識漏れしているメッセージの通知やタスクの優先度の管理、作業の催促などを行ってくれる、そんなアプリです。
ついでにアバターを実装したので、必要な事項は声で報告もしてくれます。

業務データを扱いますので、全ての処理をローカル環境で完結し、AIもLLMを使うことで機密情報は一切外に出さず、PC内部で保持・処理する方法を採用しています。

バックエンド・技術スタック

バックエンド

言語Python 3.12
WebFastAPI 0.141 + Uvicorn 0.40
テンプレートJinja2 3.1(サーバーサイドレンダリング)
DBSQLite 3.45(単一ファイル。FTS5全文検索を使用)
数値計算NumPy 1.26(埋め込みのコサイン類似度、画像のアルファ処理)

AI

LLMOllama + qwen3.8:latest(27B級 Q4)/num_ctx: 8192
出力制御JSON Schema による制約デコード(Ollama format)
埋め込みbge-m3(1024次元。BLOBでSQLiteに格納)
音声合成Style-Bert-VITS2(別プロセス・別venv/Python 3.10/CPU実行)

フロントエンド

アバター描画three.js 0.185 + @pixiv/three-vrm(VRM 0.x)
モーションデータVRMA + Mixamo(FBXをJSONに変換して利用)
チャット画面JavaScript(フレームワーク不使用)+ AJAX

ハードウェア・OS

OSWindows 11 Pro
LLMエンジンOllama
ビデオカードnvidia RTX PRO 4500 Blackwell(VRAM 32GB)

なお、開発は全てClaudeのPROプラン($20/月)を契約し、Claude codeで行っています。開発期間は、ちゃんと動くのに1週間程度といった感じでしょうか。仕事で使いつつ余力をこちらの開発に回しているのですが、PROプランでも結構ちゃんとしたアプリが作れます。

機能一覧

  1. 情報収集
    Gmail、Mattermost、Googleカレンダーおよび見積・請求システムから3分ごとの自動収集
  1. 内容の分析
    届いたメッセージをLLMが解析し、要約・依頼・期限・宛先・返事待ちを抽出
  1. 質問への回答・通知
    私からチャットウィンドウで質問した内容に根拠つきで回答(根拠となるメッセージを提示)
    メッセージの中から期限や日数、催促などが有った場合に優先度を付け、コードが決定論的に重要な順に並べて通知
  1. 情報の検索
    LLMを利用し、自由な言い方で検索が可能。人・案件・期間・話題を同時に指定可能
    例:「2日位前に来た○○社の□□さんからのメッセージを探して」
  1. 音声による通知・ブリーフィング
    毎朝、業務開始時に音声でブリーフィングを実施、タスクを整理して提示してくれる
    急ぎの割り込みや会議直前の通知など、重要なものは音声および吹出表示でアラート
  2. 操作方法
    基本的にはチャットボックスの対話がそのまま操作になる
    チャットでToDoの登録も可能
    メッセージはすべて統合インボックスに優先順位を整理した上で表示されるので、対応中、対応不要などのボタンを押して整理可能
  1. チェック機能
    請求漏れチェック:受注済みで未請求のものを月初に通知
    見積の作り忘れ:「見積を送ります」と言ったのに経理システムに登録が無いもの
    サーバ監視:私に関係がある案件についてアラートの内容によって通知
    PCの状況:ディスクの空き容量、SMART情報のエラーなど
  1. アバター
    デスクトップにVRMアバターを表示。待機・所作・瞬き・視線・口パクなどのモーションあり
    緊急事項は吹出表示で通知
  1. 統合インボックス
    全てのメッセージを管理する機能。状態変更・メモ・判定理由の表示・元メッセージ(gmailやslackなど)へのリンク
    期日による緊急性の絞り込みなども可能

AIアシスタントアプリの特徴

チャットボットでも、ToDoアプリでも、統合インボックスでもなく、アバターとの対話がこのシステムの操作系そのもの。

この位置づけは途中で変わりました。当初はアバターはおまけ機能で、ブラウザベースのタスク管理ツールを予定していました。チャット型のインターフェースを実装する予定だったので、ついでに開発の最終フェーズに「表示クライアント」程度でのアバター実装を考えていましたが、開発を進めるうちに変化してきました。

先に述べたように、私がこれまでToDo管理に失敗した理由は2つあります。

  1. ToDoアプリに情報を転記する必要があった
  2. ToDoアプリから元アプリへの導線を手で設定する必要があった

特に複数の企業のアカウントを利用して仕事をしていることもあり、情報の確認元が複数あるため、ToDoアプリに手動で情報を集約して管理するのはかなり面倒でした。

しかし、対話が操作になれば、この2つは原理的に発生しません。 アバターからの報告に「それ対応不要」と言えば状態が自動的に変わり、アバターにチャットで「○○の件どうなってた」と聞けばLLMが情報をまとめて経緯を説明してくれます。依頼は元メッセージから生成されるので、参照元のリンクも添付してくれます。

つまり、おまけ機能で考えていた「アバターでも実装するか」と「ToDoが続かない」は、実は同じ要件の裏表だった、という訳です。

全体の処理の流れ

収集 ──→ 正規化 ──→ 経路判定 ──→ 解析(LLM) ──→ 状態 ──→ 対話 ──→ 発話

「事実はコードで集め、判断はコードで導き、言葉だけLLMに作らせる」 が全体を貫く方針を前提としています。これは、全てLLMで処理すると勝手な判断や抜け漏れ、間違いなどがどうしても発生するためです。

LLMに多値分類をさせない

開発を進めていると、LLMが顧客からの明確な依頼を「無関係」と判定したが、同じ回答の reason には「関連性が高い」と判定していた、という矛盾が発生しました。
そこでLLMには「依頼されているか、私宛のメッセージか」というような事実だけを問い、分類はコードで導出する形で処理を行っています。
判断をコードに置くと、なぜそう判定したかを常に説明できるメリットもあります。

原則は日付をLLMに解決させない

モデルが「8月30日」を2023年と出力したことがあり、原文の表現をそのまま抜き出させ、受信日時を基準にコードで計算する仕組みとしています。
ただし、LLMを利用するのが正解な場合もあります。それは「2営業日後」などのコードでは判定が難しい場合です。コードで計算するが、場合によってはLLMを併用する仕組みとしています。

通知は適合率を優先。迷ったら通知しない

誤通知が大量に来れば、人間、メッセージを読まなくなります。何でも通知すれば良いわけではなく、LLMの分析結果に基づいて適合率をコードで計算し優先度を決定しています。

主な機能

やることを挙げる

「やることを教えて」と質問すると、根拠つきで答えてくれます

まず□□の○○さんへの見積もり状況の確認に関する返信が4日止まってるから、
これを最優先で出しときな。次に○○氏への仕様書添付と、××社の素材送付手配も
待たれてるから、この3件を片付けたほうが流れが止まらないよ。

なんかぶっきらぼうな感じですが、読み上げ音声が妙にかわいらしいので許せます。
こんな感じで、やるべきことを優先的に教えてくれます。

案件の経緯を答える

「□□社の件どうなってる?」「○○の進捗について教えて」と質問すると、案件の中で同時に走っている仕事のまとまりをLLMが見つけ、 「いま『Wordpressの開発』と『TOPページのデザイン制作』が動いてる」 のように、どのタスクの話かが分かる形で答えてくれます。

自由な言い方で探す

「2日位前に来た○○社の□□さんからのメッセージを探して」

人 AND 案件 AND 期間 AND 話題を同時に指定して検索できます。 LLMに書かせるのはSQLではなく決まった枠の値で、LLMを使った検索なので自然言語で検索できるのが特徴。
条件に一致するものが見つからないときは、どの条件を外せば答えが出るかを教えてくれます。

2026-09-04〜2026-09-06/案件「○○社」/□□さんのものは無かったよ。
期間の指定を外すと出てくるよ(いちばん新しいので2026-09-01)。

見積・請求連携

  • 請求漏れ … 受注済みなのに請求日を過ぎて未送付のものを挙げ、月初に声をかける
  • 見積の作り忘れ … 自分が「見積を送ります」と言ったのに、見積・請求システムに見積が無いものを見つけ、自分の約束として登録する

請求漏れは基本的にはない(見積・請求システム上で送付状態のチェックなどを行っているため)のですが、抜け漏れがあるとまずいので連携機能を入れました。
むしろ重要なのが見積もりの作り忘れや、頻繁に見積内容が変わった場合の対応で、これは抜けてしまう場合があるので、LLMがメッセージを解析し、私が見積もりを送る旨の会話をしているけど見積・請求システムにまだ見積が登録されていないものについて、アラートを出すようにしました。

アバター

最初はおまけ機能だったけども、いつの間にか主要機能になっていたのがこのアバター。最初はStable Diffusionで絵を作って貼っていたのですが、動きがないのもつまらないので、BoothからVRMモデルを購入。自分で作るのは面倒なのでお金の力で解決しました。

はじめてBoothの中を見てみましたが、いやー、いろんな3Dアバターがあるんですね。探すだけでも大変。しかも可愛らしいキャラばかりで、業務システムのアバター用には難しそう…。動物アバターもあったのですが、モーションの入手が難しそう。ということで、ちょっとアンドロイドっぽい、見た目が気に入ったアバターを選んでみました。
I-s(アイズ) Ver.2.0というアバターなので、なし崩し的にアバター名もアイズで決定。

描画は画面外レンダラで描き、透過ウィンドウに合成しています。私の環境が100%スケーリングと125%スケーリングのモニターが混在しているので描画サイズの処理は少し手こずりました。
待機ループ・瞬き・視線・所作・口パクのモーションを持っているので、時々無駄に手を振ったりしゃがんだり、くるっと1回転したりします。

実装するにあたって、思いっきり脱線したのもこのアバターです。せっかくの3Dデータですので、いろいろ動かしたくなって、時々マウスを視線で追尾したり、Mixamoからモーションデータをダウンロードして追加したり、各モーションのつなぎ部分を滑らかにしたり…といろいろ拘っています。こだわりポイントが違いますね…

統合インボックス

ToDo、未着手、対応中、要対応、要把握・解析待ち、参考・bot通知の7つのセクションに分けてメッセージを一覧表示する画面です。状態はAIは提案するだけで、利用者が決めます。メッセージには必ず元メッセージへのリンクが付きます。

メッセージが大量にあると、1つずつ状態を処理するのは面倒ですので、例えばあるメッセージを完了にした場合、連続している一連のスレッドのメッセージをまとめて抽出し、一気に完了にすることも出来ます。
抽出はコードで行っていますが、LLMが分析し、関連しているか再度チェック、関連していない場合は除外することも可能です。

期日が近い・過ぎているものは絞り込むこともできますので、対応忘れなどが減ると思います。

開発で苦労した点

「もっともらしい間違い」がいちばん怖い

LLMに全ての判断を任せると、もっともらしい判断で間違えます。例えば、請求漏れチェックを作り、実データに当てたら「未請求15件・合計800万円」となりました。提示された根拠・数字としてはもっともらしいのですが、中身を確認すると、 15件すべてが「見積中」か「失注」でした。
これは、見積・請求システムで案件を作った場合、自動的に請求日が入るため受注していない見積も 「請求日を過ぎた未請求」に見えたのが原因です。

自分宛のメッセージを探す

メールの場合は、私宛の依頼はほぼto:に私のアドレスが入っていますが、Slackなどで難しいのは、一連のメッセージの中で依頼された業務です。メンションがあれば手がかりにできますが、メッセージを分析して追っていく必要があります。
addressed_to_user という機能で、メールアドレスなどを使って「本文の宛名が本人か」をチェックしており、機能としてはそれ自体は正しいものです。ただ、この機能だけでは私が処理する必要があるタスクが拾いきれませんでした。
宛名が自分でなくても自分が動かないと止まる仕事があるためです。これに対応するためには、宛名とは別の問いが必要でした。

この連絡について、利用者が動かないと止まることが残っているか。
残っているなら、次にやる一手を1行で。

これを1件ずつ問う層を追加することで対応しています。

ローカルLLMの制約

この辺はすべてClaudeにお任せでしたが、以下のような制約があり、試行錯誤が必要だったようです。

  • 制約デコード(JSON Schema)は上限に達した時点で文を途中で切る。 モデルが自発的に短くまとめるわけではない
  • 日本語のenumは信頼できない。ASCII識別子にして表示用の日本語と分ける
  • フィールドの並び順が推論の順序になる。根拠を先に書かせると精度が上がる

こちらが指示せずとも、問題を見つければ自動的に対応を考え、試し、解決してくれるので、エンジニアではない私でもプログラムを作るのがとても簡単になりました。

便利な点

転記が不要でメッセージ・タスク管理ができる

今まで失敗していたTodoアプリのような転記は一切不要。チャットウィンドウや統合インボックスの一覧の行からメッセージを確認し、直接状態を変えられます。統合インボックスとチャットウィンドウは連携しているので、アバターとの対話からも状態を変えられます。

「なぜ」が常に見える

やることの根拠、案件判定の理由、通知の出どころなどがすべて画面に出ます。LLMは間違える可能性もあるのでこれは重要。AIの判断を信じる必要がなく、メールやSlackなどの原本をすぐに確かめられます。

元メッセージまで1クリック

統合インボックス、チャットウィンドウ、立ち絵の吹き出し。どこからでも元のメッセージに直接アクセスできます。URLはシステムが収集時に作ったものだけを使うので確実です。

声で受け取れる

朝、PCに触ったときにその日の状況をブリーフィングしてくれます。 急ぎの新着があれば声をかけてjくれます。読む文章と聞く文章は適正な長さが違うので、画面の文はそのまま、読み上げる文だけを短くしています。

英字の読みは、そのままだとアルファベットを一文字ずつ読むので(headerを「エッチイーエーディー…」みたいに読む)辞書機能を入れ、読み方を調整できるようにしています。

これからの拡張予定

  • 助言機能
    現在はメッセージを「探す」機能のみですが、一歩踏み込んで「どう返すべきか」まで提示する機能です。検索層をLLMが活用できるようにしましたので、それを土台にして構築する予定です。
  • 案件を横断した話題の検索
    いまは案件を名指ししないと絞れないので、もう少し広いエリアを対象とした検索の実現です。
  • 返信の下書きの精度向上
    メールやSlackなどの返信の原案を作成する機能。現状は素材を並べるところまでなので、ビジネス向けのライティングが出来るようにしたいところ

最も重要な点

顧客情報を扱いますので、セキュリティが極めて重要です。このシステムはローカル完結で動作し、業務データは外部に送信されません。LLMを使う最大のメリットがここになります。

コメント

タイトルとURLをコピーしました