202603 最近の仕事&感想メモ

目的

いろいろ今の仕事をしていて勉強になったことが多いので備忘録的なメモ

今やってること

  • 新規機能のPM 兼 開発
  • 新規機能(LLM系)のリード
  • 新規機能(データ連携系)のリード
  • 既存機能の保守
  • 基盤チームの開発

何が学びになったか

1. 多国籍のソフトウェアエンジニアと働くこと

一つのチームが、自分とインドのソフトウェアエンジニアの3人チームになってる。

一個わかったことは、お互いが同じコンテキストを持っていれば、多少英語話せなくても、テキストコミュニケーションできるからあんまり困らなかった。

実際に、半年ぐらいでほぼ彼らに開発はお願いして、自分はレビューだけすればいい状態になってて、日本人と働く時と大して変わらなかった。

一点違うのは、日本のソフトウェアエンジニアと比べて、インドのソフトウェアエンジニアは雑なところが多い気がする。速度を重視して、とりあえずのPRを出してくる感じがある。

ただ、自分のチームは幸運なことに、新規事業っぽい感じなので、今のところプラスになってる。

元々、アジア圏の人とは何回か働いたことあったけど、自分のチームメンバーとして見るのは初めてだった。

が、いろいろ国による違いはあるだろうけど、個人差の方がでかいなと知ることができていい経験だった。

2. LLMを通したUI

SaaSの機能のPoCを作っているのだが、UIについて、いろいろ試行錯誤した結果、 Claude Coworkに、管理画面にログインさせて、APIを直接叩かせるSkillを作るのが一番体験としてよかった。

マニュアルもSkillsで提供できてるから不要だし、曖昧な指示でも汲んでくれる。

もちろん変な操作をされるリスクはあるから、ドメインを変えておいた方が安全かもしれないが。

もちろんLLMでサクッとフロント作るのはできるけど、SaaSのPoCって結局機能性の評価がメインになるから、UIがあるとそっちに気を取られて、そっちのフィードバックがたまにくる。

それを防げるのは良いのでは?と思った。何より、機能への質問に関しても、Claudeが回答してくれるし。

3. 基盤開発と新規事業の相性の良さ

Closedβとして、いくつか開発していたが、途中ぐらいで問題が発生した。

それはClosedβでうまくいっても、それを本番運用させるためには作り直しが必要ってことだった。(既存機能とRPAっぽい感じで繋いでるところがいくつかあったため)

で、結局Closedβのまま、売られ続けて自分の保守する機能だけが増えていくって感じ。

そこで上司に相談して、基盤開発チームと兼務させてもらうことにした。

基盤のAPIを提供してもらえれば、それを使って開発することができるわけで、なおかつ綺麗なインターフェースで作ってもらえるのではと仮説を持ってた。

その結果、今までは既存機能との連携部分がその機能を作ってた自分の知見によるものだったため、自分しかできなかった作業が、

API化されたことで、新規事業の開発は他メンバーに任せることができて、より基盤側に時間を割くことができた。

基盤側に入ってわかったのだが、基盤側も他のチームのニーズがわからないから優先度を決められないという問題があったらしく、いい関係を構築できた。

まとめ

ここ半年かけて、LLMの進化で、いろいろな仕事が並列でできてありがたい。

今月から、PM という名前もついたのでそこを頑張っていきたい。

PMはやったことないけど、やっぱりまずは売るために、営業さんに商品を売れると思ってもらうことが大事だろうから頑張りたい。

まだ始めたばかりだけど、デモを準備しておくと、営業さんの受けがよくて、商談に呼んでもらいやすい気がする。

こんな感じで、どうやったら売れるのかをもっと学ばないとなー

2025年の学びメモ

目的

GPTとかClaudeに教えてもらったり、いろいろ調査したことで、勉強になったことをメモして、将来見返す。

FQ制限

直近1時間以内に送った人には、配信をしないみたいな制約がたまにある。

これは、Redis(SET NX EX) で配信直前に、存在有無の確認を行う。TTLは1時間にしておく。

そこまで、高負荷でない & 運用楽にしたい場合は、DynamoDBの ConditionalCheck でOK。

in-flight request deduplication

例えば、DuckDB の インスタンスを、処理の中で作りたいけど、そこに並列で同じようなリクエストが来て、作成が競合するってことがある。

そんな時に以下の実装をすると、最初のリクエスト以外は、最初のリクエストが終わるまで待機して、終わったら最初のリクエストが作ったインスタンスを使うみたいなことができる。

let p = loadingTables.get(key);
if (!p) {
  p = (async () => { await createTable(entry, csvPath, tableName); })()
        .finally(() => loadingTables.delete(key));
  loadingTables.set(key, p);
}
await p;

テナント移動時のアラート

SaaSで複数テナントを持つユーザーが、二つのタブを開いていて、片方のタブでテナントを変更した場合、もう片方のタブでの表示は、変更前のテナントが表示されて、運用ミスるってことがよくある。

その場合、BroadcastChannel を使って、別タブに状態を伝えるのが手っ取り早い。すごく便利。

export const useAuthChannel = () => {
  // 認証状態を管理するstate。実際のアプリではContextやReduxなどから取得する
  const [isAuthenticated, setIsAuthenticated] = useState(true);

  // ログアウト処理
  const logout = useCallback(() => {
    console.log('ログアウト処理を実行し、メッセージを送信します。');
    // ここでAPIサーバーへのログアウト通知などの本来の処理を行う

    // 他のタブにログアウトを通知
    channel.postMessage('logout');

    // このタブの認証状態も更新
    setIsAuthenticated(false);
  }, []);

  useEffect(() => {
    // 他のタブからメッセージを受信したときの処理
    const handleMessage = (event) => {
      if (event.data === 'logout') {
        console.log('別タブからログアウト通知を受信しました。');
        setIsAuthenticated(false);
      }
    };

    // イベントリスナーを登録
    channel.addEventListener('message', handleMessage);

    // コンポーネントがアンマウントされるときにクリーンアップする
    return () => {
      console.log('クリーンアップ:イベントリスナーを削除します。');
      channel.removeEventListener('message', handleMessage);
      // 注: アプリケーション全体で一つのチャンネルを使い続ける場合、
      // ここで channel.close() を呼ぶと他のコンポーネントで使えなくなる可能性があります。
      // アプリが完全に閉じられるときに一度だけcloseするのが理想です。
    };
  }, []); // 空の依存配列で、初回レンダリング時に一度だけ実行

  return { isAuthenticated, logout };
};

DynamoDBの注意点

  • 書き込みが同一Partitionだと、1,000 WCUs/秒が限界

  • 読み込みも、同一Partitionだと、約3,000 RCUsが限界

例えば、上記のConditionalCheckのように、partitionKeyが分散できるものは便利だけど、SaaSで提供している場合、siteIdでPartitionKeyを設定したいと言った時に設計が難しい。

KafkaConsumerの高速化

KafkaのMessageを処理する際は、1件づつ取得する方法もあるが、もちろんBatchで取得して、Promise.all で並列処理すれば大体早い。

けど、それでも足りない場合がある。外部のAPIにアクセスする時など。

その場合は、外部のAPIにアクセスする部分だけを、await をつけずに渡して、次のメッセージを処理しにいくと早い。

ただ、これの問題として、サーバーが落ちた時に、後ろに溜まった APIアクセス部分が落ちたり、メモリが増加したりするデメリットがありうる。

また、APIアクセスをテスト環境で、Sleepなどで代替しようとすると、並列処理数が、実際のAPIアクセスの時と異なる値になる可能性が高い。

そのため、同時並列数を抑えるか、実APIにアクセスできるようにするなど、パフォーマンステストの方法を考える必要がある。

Kafkaの失敗時

Kafkaで処理失敗時に、ロジック内のリトライで済めば良いが、そうでない場合が難しい。

GPTに聞いたところ、DLQに入れて、後から再処理というのが一般的とのことだが、順序が決まっているものであれば、失敗したからと言ってRetryしていいわけでもない。

この辺り、考えたがいい答えが出なかったけど、難しいことだけはわかった。

カラム数の増加によるSQL速度の違い

TiDB に対して、Update対象のカラム数を変更して、速度を測ってみた。

一度に1000行更新するとして、カラム数が1違うとだいたい40msぐらい違った。

TiDBは、特にカラムが増えると、複数レプリカに書き込みが必要であったり、TiDB→TiKVへのネットワーク通信があるので、影響が大きいとのこと。

BatchUpdateするときは、カラム数を抑えた方が良さそう。

レコメンド機能を作る時のDB選定について

基本的に、いろんな種類のアイテムカタログから、レコメンド機能を作る際は、ElasticSearch がお勧めされる。

最初は、1インデックスで、複数のアイテムカタログのデータを保持して、検索をするのが最初はおすすめっぽい。

機会があったので、S3 + DuckDB + ECS(インメモリにDuckDBをキャッシュして速度あげる)を試してみた。

結論、カタログサイズが小さくて、更新頻度が1日に1回程度であれば、問題なく動いた。

メモリの問題さえ対処できれば、ここ以外でも有効に使えそうな感じがあった。

LLMの使い方

RAGみたいなめっちゃ分かりやすい使い道を除くと、あんまりいい使い方ができなかった。

一方で、Mastraのworkflowみたいに、ルールベースのロジックと組み合わせながら作ってもらったら、一定利用に耐えうるものはできてきた。

ただ、やはり処理速度の問題があるため、業務システム以外での使い道が難しい。

例えば、LLMで文字列をベクトル化して、近似値をとってレコメンド条件を柔軟にするみたいなのは良さそうだけど、突き詰めればここも機械学習でいいじゃんってなるから、難しい。

React-router / Remix

Remixを使ってみたけど、使ってすぐReactRouterに変わることがわかり、LLMを使って1日で変更した。

やはり、LLMがあるので、フレームワークの変更や、バージョンアップ作業自体はすぐ終わる。

一方で、そのときはリリース前だから良かったけど、今だとフロントの自動テストがもう少し充実しないと、怖くてできない。

テストが大事。ただ、フロントのE2Eは、あんまり評判よくないと聞いた。なんかいい方法がないんだろうか。

コードからドキュメント作り、ドキュメントからテスト作り、テストからマニュアル作り、マニュアルとドキュメントをRAGで検索みたいなことできないかな。

負荷テストのconsole.log

負荷テスト時に、1秒あたりの同時実行数に制限かけながら、console.logで実行ログだして、それをCloudwatch Insight で計測して、秒間実行数を計算してた。

が、Node.js で CPU 100% になった時に、想定と違った挙動を示した。

おそらくだが、これは console.log が CPU 100% になったことにより、CPUの実行待ち行列に入り、ログが出力される時間がずれたからではないかと考えられる。

最後

GPT-5が端的にアドバイスくれるので、短時間ですごく知識が伸びてる気がする。

コード書くのはClaudeCodeに任せるけど、もっぱら質問はGPT-5にしてる。

知識と経験は、LLMのおかげで、方向性もあんま違えなくなったため、やれば伸びてくから、体力が一番大事と感じてきた...

今のコーディングエージェントに対するメモ

geminiと一緒に作ったやつをメモしとく 来月には変わってそう(泣

これで自分の能力の最大化は一旦飽きてきたから、次はこっち方面やる https://zenn.dev/tesla/articles/3abc9e61da0b1e

コーディングエージェント時代のソフトウェア開発戦略

はじめに

AIによるコーディングエージェントは、ソフトウェア開発の現場に大きな変革をもたらしつつあります。本レポートでは、現状(2025年6月時点)を踏まえたコーディングエージェントの最適な活用法、開発プロセス、組織への影響について、筆者の見解を述べます。

筆者は日本のSaaSを提供する企業に勤務しており、新規の技術開発よりも、既存の技術やソリューションを効果的に組み合わせ、自社のビジネスに最適化することに主眼を置いた業務特性を持っています。 また、小規模チームでの開発経験が多いです。 あくまでその背景からの意見となります。

1. 現時点での最適な活用と開発スタイル

現状、特に開発タスクに集中して取り組むエンジニアにとって、ClaudeCodeのような対話型コーディングエージェントが最も実践的かつ効率的と考えます(2025年6月時点)。その理由として、特定のタスクに対するインタラクティブな試行錯誤が容易で、人間が主導権を持って開発を進めるサイクルに適している点が挙げられます。

具体的なスタイルとしては、二つのウィンドウを開き、一つは開発タスクをClaudeCodeに実行させ、一つは人間がClaudeCodeの出力をレビュー・修正していき、終わり次第、ウィンドウの役割を交換するという進め方が、自分にとって現時点で最も高い効率を実現できています。

また、コーディングエージェントを確実に介することで、最初の出力がコーディング規約などをより遵守するようになるため、オンボーディング期間の削減や、人間の記憶の節約に繋がります。

2. バックグラウンドAIエージェントの課題と可能性

Devinのような、より自律的にバックグラウンドで大規模なタスクを遂行しようとするエージェントに関しては、現時点では人間の認知負荷やレビュー・修正速度がボトルネックとなります。エージェントがどれだけ速くコードを生成・修正しても、人間がその内容を理解し、必要なレビューや修正を行う速度が律速段階となるため、完全なバックグラウンド自律実行のメリットを十分に享受できていない側面があります。 そのため、UX面で、出力の途中確認や、修正レビューのフローの流れが容易なClaudeCodeなどの対話型に軍配が上がると考えます。

しかし、これらのバックグラウンドAIエージェントは、日々の機能開発といったメインフロー以外の場面で有効性を発揮すると考えられます。例えば、本番障害発生時の原因調査、プロジェクトマネージャーからのコードに関する質問への対応など、エンジニアの主務ではない、突発的・非定型的なタスクにおいて、裏で情報収集や分析を行うサポート役として活用することで、人間の負担を軽減できる可能性があります。

3. コードレビュープロセスの変革

コーディングエージェントが生み出すコードは、レビュープロセスを変化させます。まず、AIが生成したコードに対する人間の初期レビューはClaudeCodeを使った開発であれば確実に行われているはずで、これに加えてCoderabbitのような自動レビューAIツールを併用することで、コード規約や一般的なバグパターンのチェックは自動化できます。これにより、開発した人以外の細かいコード記述に関するレビューは不要となり、レビュー効率が大幅に向上します。

一方で、システム全体の整合性、ビジネスロジックの妥当性、想定されるエッジケースへの対応など、仕様の深い理解に基づくクリティカルな部分の抜け漏れ確認は、引き続き人間の重要な役割となります。

4. 開発プロセスの転換:コード先行とドキュメント生成

コーディングエージェントの能力を活かすには、開発プロセスを「ドキュメント作成 → コード実装」から、「コード実装 → コードからのドキュメント生成 → レビューを受けて作り直し」へと変えることが有効です。コードはドキュメントより記述の自由度が低く、具体的であり、学習対象も多いため、コードというプログラミング言語を出力してもらうことで、仕様の曖昧さや潜在的な問題に早期に気づきやすくなります。

そして、一度実装されたコードから、コーディングエージェントがAPIドキュメント、設計概要、実装詳細などのドキュメントを自動生成します。これにより、ドキュメント作成の工数が削減され、コードとドキュメントの一貫性も保ちやすくなり、開発サイクル全体が高速化します。

5. テスト戦略とアーキテクチャへの影響

この新しい開発プロセスでは、E2E(End-to-End)自動テストも、開発対象のコードと同じリポジトリに配置し、同時並行で作成することが自然な流れとなります。これにより、継続的なテスト実行と品質確保が可能になります。

開発単位が小さく、テストしやすい設計が求められることから、システムアーキテクチャは必然的にマイクロサービス寄りの思想になると考えられます。マイクロサービスにおいては、各サービスの「境界面(インターフェース)」の明確な定義が極めて重要であり、これは人間のアーキテクトによる高度な意思決定が不可欠な部分です。ただし、Devinのようなマルチリポジトリ対応エージェントは、サービス間の依存関係分析などを支援し、アーキテクトの素早い意思決定に貢献できる可能性があります。

6. 組織構造と成果物の定義の変化

コーディングエージェントの普及は、エンジニア、プロジェクトマネージャー(PjM)、QAエンジニアといった従来の職能の境界を曖昧にするでしょう。将来的には、一つのGitリポジトリを中心に、仕様定義、コード実装、テストコード作成、デプロイ設定まで、開発に関わる全員がプログラムという共通言語で直接的に貢献していく形になる可能性があります。

この新しい体制において、最も重要な開発成果物は、品質を保証する自動テストコードと、実検証環境、実際に動作するコードそのものとなります。それ以外のドキュメントは、コードやテストからコーディングエージェントが必要な時に都度生成するものとして位置づけが変わります。

7. 必要なドキュメンテーションとその管理

全てのドキュメントが不要になるわけではありません。特に、開発内容をモジュール単位に分け、それぞれの役割、設計の意図、そして特定の意思決定が行われた理由など、人間の判断や背景を示す重要な情報は引き続き必要です。 これらは、対象コードと紐づけてリポジトリ内でバージョン管理し、エージェントによる参照や更新が容易な形式で保持することが望ましいでしょう。

8. AIコーディング時代の新たな課題:人間の疲労と開発スタイルの再考

コーディングエージェントの高速なコード生成は効率を向上させる一方で、人間が時間あたりにレビュー・確認する必要のあるコード量が飛躍的に増加させ、新たな疲労や認知負荷を生む可能性があります。 そのため、一部の人間しか適応できない可能性があると思います。 おそらく、コード全体を追うのではなく、ビジネスロジックの核心や既存システムとの連携部分など、重要な箇所に絞って読む「国語の試験」のような効率的なコードリーディングスキルを持つ必要が必須かもしれません。

ただ、一時的な負荷増は、チャットツール導入時のような過去の経験と同様に、人間の順応によって慣れていく側面もあると考えられます。しかし、単に慣れに頼るだけでなく、チームでの開発スタイルを導入することも考えてみたいです。

例えば、「毎日リファインメント」を軸に、午前中は前日のAI生成コードのレビューに集中し、午後にはチームで次のタスクを相談し、AIに依頼準備を行うといった、人間の「レビュー/修正」とAIの「生成」のサイクルを意図的に分けたスタイルを提案します。 このように個人単位でなく、チーム単位で負荷を制限するという形が必要になるかもしれません。

結論

コーディングエージェントは、ソフトウェア開発のプロセス、アーキテクチャ、そして組織構造を大きく変容させています。人間の役割は、コードの細かい記述から、AIが生み出すコードをレビュー・修正・統合し、仕様やアーキテクチャに関するクリティカルな意思決定を行うことへとシフトします。テストとコードを最重要成果物とし、ドキュメントはコードから生成するスタイルに移行し、一つのリポジトリを中心に職能横断的に連携する。このような新しい開発スタイルに適応し、AIの力を最大限に引き出すことが、今後の競争力を左右する鍵となります。

202505 最近の仕事&感想メモ

目的

いろいろ今の仕事をしていて勉強になったことが多いので備忘録的なメモ

今やってること

  • 社外向け新規機能の実装
    • バックエンド、フロントエンドの実装
    • 不明な仕様をPMに質問して、チームメンバーに依頼する。
    • 新規機能のクラウド構築
    • 開発メンバーへの依頼タスク準備
    • デザイナーさん、PMとのMTG設定
    • 基盤チームへの相談(負荷テストとか、機能追加依頼とか)
    • 自チームに収まらないクラウド変更のSREチームへの作業依頼及びやりとり
    • QAチームへの作業依頼及びやりとり
  • 社内向けツールの実装
    • 社内チームへの要件ヒアリング及びに仕様相談
    • 新規実装
    • 修正依頼対応
    • 情シスへの作業依頼及びやりとり(zoomとかGoogle Workspace連携などあるので)
  • 担当者不在の社内ツールの撤廃

何が学びになったか

1. 社内ツールの進め方

何の因果か、新しい社内ツールを作りながら、全く関係のない別の既存社内ツールを撤廃するという2つの業務を同時にしてる。

基本はどこかのチームからの依頼で作る訳だが、そのフロントとなる人 or チームのOKRにそのツールがどう寄与するかを最初に定義するのが一番大事だと感じた。

目標設定自体は、ちゃんと話し合ってOKRとして使える数値となって出ているはず。

これを達成するためにどういったツールが適切かを話し合うという流れにしないと、まず効果測定ができない。

そして、なによりフロントとなる人に遠慮なく協力を依頼できるし、議論がしやすい。(その分こちらへのスケジュール要求も高くなるが)

これがないツールを今撤廃しているわけだが、なくするかどうかの意思決定を誰もできないという問題が起きてる。(逆に言えば、重要度が低くて放置しても良いものなのだが、困った時の質問が自分にくるようになってしまったので少しづつ整理してる。)

まとめると社内ツールの作成の際は以下を意識しないといけない。

  • どこのチームのどの測定指標に寄与しているのか?
  • その測定指標がどれだけ上がれば良いのか?
  • その指標が、依頼チーム側の担当者のOKRに紐づいているか?
  • どうやってその指標を定量的に測定していくか?
  • どうやって今後の機能改善を進めていくか?(社内ツールは、相手チームが協力的であれば、コアな要求だけを満たした不完全な状態で提供を始められるので、きっちり仕様を決めるより継続的に改修していく方が良い認識)

結構当たり前なことだけど、基本的に自分がアサインされる時はその辺りも曖昧なことが多いし、予算もまだ確保できてなくて、自分一人でまずやってみてって感じになるから、自分はここを意識するのがすごく大事だと学んだ。

2. チームメンバーへの依頼方法

今までチームリーダーとしてタスクを依頼したことをやったことあったけど、それは今やってる自分の仕事を渡して、自分は誰も社で取り組んだことない仕事を始めるためという形がほぼだった。

なので、チームメンバーも正社員で、今の仕事を引き継いで、より改善してもらうみたいな感覚で、相手のスキル次第でサポートの強弱つけながら、チームメンバーが自分のタスクを決められるというところを目指してやってた。

だが、今回は業務委託さんに作業を依頼するという形になっている。

これが上手くできなかったし、今もまだあんまりよくわからない。相手に期待しすぎというフィードバックを受けてしまった。

基本的には依頼は正社員、業務委託関係なくこんな感じになるはず。

  1. 最初はちゃんと仕様と作業を明確にして、ただタスクをしてもらうだけにする。
  2. もし、可能なら、少しづつ作業の抽象度を高くしていく。
  3. もし、可能なら、少しづつ仕様の抽象度を高くしていく。

自分はこの1から2になるタイミング判断がめっちゃ早いっぽい。

なので、対策としては、自分以外のアドバイザーに入ってもらうのが良いはず。

今後は、経験がある方にPjMとして入っていただいたので、継続も含めてタイミングを判断してもらい、一般的な感覚というのを掴むのが良さそう。

しかしながら、LLMによってその感覚もどんどん変わっていきそうだけど...

3. その他

3年ぐらい今の会社で働いているから、ビジネス側もコーポ側もプロダクト側も、ほぼ全てのチームに知り合いがいて、依頼しやすい環境ができてるし、みんな快く対応してくれる。

この今やってるいろんなチームにお願いしながら進めないといけない仕事は、これが一番やりやすさに繋がってる気がする。

自分はほぼリモートでやってるから、ここは出社か否かとかはあんま関係なくて、まず最初はやっぱり相手から相談をしてもらいやすいポジションにいることが重要な気がする。

まとめ

LLMによって、作業時間自体はどんどん短縮できてるから、一人でスイッチしながら複数のタスクができるようになってきてる。

また、技術に関しても、DeepResearchに質問すれば、各界隈の専門の方の技術ブログとかに行き着いて、いい感じの実装ができてる気がします。

となった時に自分がもっと慣れるべきなのは、目標設定と、人に依頼する時のコミュニケーションだなと感じる今日この頃です。

202504 Github Copilot使い分け&感想

概要

会社で、Github Copilotの Agent mode, Edit mode, Next Edit Suggestion(NES) を紹介しつつ、自分が普段どのように、使い分けて開発をしているか共有する機会があった。

せっかくなので自分のブログにもメモ。

自分の使い分け

機能名 使い所 良い点 悪い点 使用感
Copilot Agent どう作るかイメージが湧かないとき 曖昧な指示でもいい感じのコードを作ってくれる。 自分より優れたコードも多い。 他と比較して狙った通りの出力が難しい。 レスポンスが遅い。 後輩エンジニアに仕事を任せてる感じ。
Copilot Edit 実装イメージがしっかりあって、真似できるコードがあるとき 単純にコーディング速度だけで言えば、一番早い。 あらかじめ、ちゃんと実装に必要なファイルを渡す必要がある。 後輩エンジニアとペアプロしてる感じ。
Copilot NES 実装イメージがふわっとしてしかない時
実装イメージあるが真似できるコードがない時
今までの仕事の仕方と同じイメージで進められる。 劇的に早くはならない。 今まで通り。

現状

Copilot Edit >= Copilot Next Edit Suggestion(NES) >>>>> Copilot Agent

  • 何故、Copilot Agentを使っていないか
    • 以前は、既存の改修(Copilot NES & Edit)と、新規開発(Copilot Agent)で同時並行で行っていた。(新規開発は特殊で、プロダクションに乗せないコードだから書き捨てでよかった)
    • が、新規開発以外ではCopilot Agentに全部任せるのは厳しい & 新規開発の割合が少なくなってきた。
  • だんだん、ボトルネックがコード理解 & コードレビューになってきた。
    • そこで、Copilot Agentを使うと、合ってるか分からない大量のコードが生成されることになり、それをチェックする無駄な仕事が増えた。そのため、Copilot Editの方で少しづつレビューしながら作ってもらうようになった。

今後の展望

自動テストは、カバレッジという明確な指標を持てるし、

フロントエンドは、既に自分よりLLMの方が綺麗なコード書いてくれる + BrowserUse等のテスト自動生成も見えてきてるので、

MCPの流れに乗りつつ、その辺りはレビューさえもほとんど不要にしていきたいな。

Sagemaker AIでもGemma3を動かしてみた

概要

Sagemaker AIで動かしたのでメモ。

流れ

  1. Sagemaker AI を開いて、Domain作る
  2. jupyter notebookを開く
  3. 必要なライブラリをInstall
  4. hugging faceにログイン
  5. modelをダウンロードする
  6. 質問する

実行スクリプト

インスタンスタイプは、ml.g4dn.2xlarge を今回は使った。(g5系が空きがない?らしく使えなかった)

# CUDAのversionをチェック
!nvidia-smi

# CUDA 12.4だったので、必要なライブラリをインストール(https://pytorch.org/get-started/previous-versions/)
!pip install torch==2.5.1 torchvision==0.20.1 torchaudio==2.5.1 --index-url https://download.pytorch.org/whl/cu124
!pip install transformers==4.50.0

# huggingfaceにログイン
import os
from huggingface_hub import login
login()

# gemma3はhugging face上で利用申請的なものが必要なので注意。

# modelをダウンロード
from transformers import AutoTokenizer, AutoModelForCausalLM
import os

model_id = "google/gemma-3-4b-it"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, device_map="auto")

ここまでできたら、あとは質問するだけ(Geminiに作ってもらった)

import torch
# --- 前のコードでロード済みの tokenizer と model がある前提 ---
# tokenizer = AutoTokenizer.from_pretrained(...)
# model = AutoModelForCausalLM.from_pretrained(...)

# 1. プロンプト(質問)を定義
prompt = "日本の首都はどこですか?" # ここに質問や指示を入力します
# 他の例:
# prompt = "AI技術の未来について教えてください。"
# prompt = "Pythonで簡単なWebサーバーを立てるコードを書いてください。"

print(f"Prompt: {prompt}")

# --- Instruct/Chatモデルの場合の推奨される方法 ---
# GemmaのInstructモデル(例: gemma-*-it)は特定のチャット形式を期待します。
# これにより、モデルは質問応答や指示追従をより正確に行えます。
# トークナイザーがチャットテンプレートをサポートしているか確認し、適用します。
try:
    # メッセージのリストを作成 (ユーザーからの質問)
    messages = [
        {"role": "user", "content": prompt}
    ]
    # チャットテンプレートを適用してプロンプトを整形
    # tokenize=False: 文字列として整形, add_generation_prompt=True: モデルが応答を生成しやすいように末尾にマーカーを追加
    chat_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    print(f"\nFormatted Chat Prompt (for Instruct model):\n{chat_prompt}") # 整形後のプロンプトを確認

    # 2. トークナイズ (整形後のプロンプトを使用)
    # return_tensors="pt": PyTorchテンソルとして返す
    # .to(model.device): モデルがロードされているデバイス(GPU/CPU)に入力テンソルを送る
    inputs = tokenizer(chat_prompt, return_tensors="pt").to(model.device)

except Exception as e:
    # チャットテンプレートが未定義などの場合は、元のプロンプトをそのまま使う (Baseモデルなど)
    print(f"\nCould not apply chat template (perhaps not an Instruct model?): {e}")
    print("Using the original prompt directly.")
    chat_prompt = prompt # 整形せずに元のプロンプトを使う
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

# 4. テキスト生成 (model.generate)
print("\nGenerating text...")
with torch.no_grad(): # 推論時には勾配計算は不要
    # max_new_tokens: 新しく生成するトークンの最大数 (元のプロンプトの長さは含まない)
    # do_sample=True: ランダムサンプリングを有効にする (より多様な応答)
    # temperature: サンプリングの際のランダム性を調整 (低いと決まった応答、高いと多様な応答)
    # top_p: Nucleus sampling の閾値
    outputs = model.generate(
        **inputs,
        max_new_tokens=150,  # 生成する応答の長さを調整
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        pad_token_id=tokenizer.eos_token_id # EOSトークンをパディングに使うことが多い
    )
print("Generation complete.")

# 5. デコード
# outputs[0]: 生成されたテンソル (バッチサイズ1なので最初の要素)
# skip_special_tokens=True: <bos>, <eos> などの特殊トークンを除去して表示
# 注意: Instructモデルの場合、整形後のプロンプトも含まれて出力されることが多い
generated_ids = outputs[0]
generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)

# Instructモデルの場合、入力プロンプト部分を取り除くと見やすい場合がある
# response_only = generated_text[len(chat_prompt):] # 簡単な除去方法 (テンプレートによっては調整が必要)

print("\nGenerated Text (Raw Output):")
print(generated_text)

# print("\nGenerated Text (Response Only - Attempt):")
# print(response_only.strip()) # 前後の空白を除去

メモ

  • AWSでのGPUインスタンスは g系 となる。(他にもあるけどめっちゃ高い)
  • 1時間あたりの料金(東京リージョン)
    • g4ad.xlarge: 0.51
    • g4dn.xlarge: 0.71
    • g5.xlarge: 1.459
    • g5g.xlarge: 0.5669
    • g6.xlarge: 1.1672
    • g6e.xlarge: 2.699
  • g5g.xlargeで、月6万ぐらい。
  • ml.g4dn.2xlarge で、gemma3 4Bはサクッと動いた。gemma3 12Bはめっちゃ結果出るまで時間かかったけど動いた。

Bedrockについてメモ

カスタムモデルインポート機能があり、OSSのモデルなどをBedrockでhostingしてサーバーレスの形式で提供してくれる。

制限1: 今はまだ東京リージョンで使えない。

制限2: 今はまだMistralやLlamaなど、指定のものをベースにしたモデルしか使えない。

aws.amazon.com

今はまだ、東京リージョンのBedrock Claude3.7は使えない。

Claude3.5 v2 は使えるんだけど、シドニークロスリージョンしてるので、会社によっては使えない...(国外にデータが出ちゃうからSaaSで使うと、セキュリティチェックちょいちょいひっかかりそう。)

Amazon Nova Proとかも同様にクロスリージョンしてるっぽい。

docs.aws.amazon.com

さらにメモ

gemma 1b モデルでいいなら、普通にM1 Macで動いた。Ollama使わなくてもいけるんだな...(ということは、GPU インスタンスじゃなくても動きそう)

from transformers import AutoTokenizer, AutoModelForCausalLM
import os
import torch

model_id = "google/gemma-3-1b-it"
tokenizer = AutoTokenizer.from_pretrained(model_id)
model = AutoModelForCausalLM.from_pretrained(model_id, low_cpu_mem_usage=True, device_map="auto")

# 1. プロンプト(質問)を定義
prompt = "スケジュール調整してください。と顧客から会った時に、簡単にスケジュール調整する方法を教えて" # ここに質問や指示を入力します
# 他の例:
# prompt = "AI技術の未来について教えてください。"
# prompt = "Pythonで簡単なWebサーバーを立てるコードを書いてください。"

print(f"Prompt: {prompt}")

# --- Instruct/Chatモデルの場合の推奨される方法 ---
# GemmaのInstructモデル(例: gemma-*-it)は特定のチャット形式を期待します。
# これにより、モデルは質問応答や指示追従をより正確に行えます。
# トークナイザーがチャットテンプレートをサポートしているか確認し、適用します。
try:
    # メッセージのリストを作成 (ユーザーからの質問)
    messages = [
        {"role": "user", "content": prompt}
    ]
    # チャットテンプレートを適用してプロンプトを整形
    # tokenize=False: 文字列として整形, add_generation_prompt=True: モデルが応答を生成しやすいように末尾にマーカーを追加
    chat_prompt = tokenizer.apply_chat_template(messages, tokenize=False, add_generation_prompt=True)
    print(f"\nFormatted Chat Prompt (for Instruct model):\n{chat_prompt}") # 整形後のプロンプトを確認

    # 2. トークナイズ (整形後のプロンプトを使用)
    # return_tensors="pt": PyTorchテンソルとして返す
    # .to(model.device): モデルがロードされているデバイス(GPU/CPU)に入力テンソルを送る
    inputs = tokenizer(chat_prompt, return_tensors="pt").to(model.device)

except Exception as e:
    # チャットテンプレートが未定義などの場合は、元のプロンプトをそのまま使う (Baseモデルなど)
    print(f"\nCould not apply chat template (perhaps not an Instruct model?): {e}")
    print("Using the original prompt directly.")
    chat_prompt = prompt # 整形せずに元のプロンプトを使う
    inputs = tokenizer(prompt, return_tensors="pt").to(model.device)

# 4. テキスト生成 (model.generate)
print("\nGenerating text...")
with torch.no_grad(): # 推論時には勾配計算は不要
    # max_new_tokens: 新しく生成するトークンの最大数 (元のプロンプトの長さは含まない)
    # do_sample=True: ランダムサンプリングを有効にする (より多様な応答)
    # temperature: サンプリングの際のランダム性を調整 (低いと決まった応答、高いと多様な応答)
    # top_p: Nucleus sampling の閾値
    outputs = model.generate(
        **inputs,
        max_new_tokens=150,  # 生成する応答の長さを調整
        do_sample=True,
        temperature=0.7,
        top_p=0.9,
        pad_token_id=tokenizer.eos_token_id # EOSトークンをパディングに使うことが多い
    )
print("Generation complete.")

# 5. デコード
# outputs[0]: 生成されたテンソル (バッチサイズ1なので最初の要素)
# skip_special_tokens=True: <bos>, <eos> などの特殊トークンを除去して表示
# 注意: Instructモデルの場合、整形後のプロンプトも含まれて出力されることが多い
generated_ids = outputs[0]
generated_text = tokenizer.decode(generated_ids, skip_special_tokens=True)

# Instructモデルの場合、入力プロンプト部分を取り除くと見やすい場合がある
# response_only = generated_text[len(chat_prompt):] # 簡単な除去方法 (テンプレートによっては調整が必要)

print("\nGenerated Text (Raw Output):")
print(generated_text)

# print("\nGenerated Text (Response Only - Attempt):")
# print(response_only.strip()) # 前後の空白を除去

まとめ

思った以上に、LLMをAWSに自分でホスティングして動かすのは簡単そうだった。

けど、シンプルにインスタンスが高い...普段Web APIとかばっかだからドキドキしながらインスタンス立ち上げることになって心臓に悪い...

やっぱりBedrockは保守とかセキュリティの面でとても有難いのは当然として、サーバーレス提供が費用面でも本当にありがたい。

Gemma3のFineTuningをやってみる

目的

ローカルLLMに学習させる方法を調べてみたら、案外サクッと行えたのでメモ。

使ったもの

unsloth : LLMファインチューニングのための軽量ライブラリ

docs.unsloth.ai

コード

unslothのサンプルをそのままcolabで実行した。

https://colab.research.google.com/github/unslothai/notebooks/blob/main/nb/Gemma3_(4B).ipynb#scrollTo=ZgcJIhJ0I_es

成果物

https://huggingface.co/Okadak/gemma-3-ft-demo

colabでFineTuningして保存

  1. 必要なライブラリをinstall
  2. modelと、tokenizer を初期化、設定
  3. 学習に必要な dataset を生成、tokenizerに合わせて最適化
  4. huggingface の Trainer を初期化、設定
  5. レーニング!!
  6. huggingfaceに保存!!

保存のところだけメモ

from huggingface_hub import login
login()

model.push_to_hub("Okadak/gemma-3-ft-demo", token = "...")
tokenizer.push_to_hub("Okadak/gemma-3-ft-demo", token = "...")

colabで実行

このブログ通りに作りつつ、モデルだけさっき作ったものに変えればOK

Gemma 3 を Google Colab で試しました|owlet_notes99.9

感想

とりあえずやってみただけなのだが、すごく簡単だった。

ただこれでどのくらい回答がよくなるのかわかってないので、今度実タスクで試してみる。