Git - TECH PLAY - TECH PLAY

TECH PLAY

Git

イベント

マガジン

技術ブログ

本記事は 2026 年 3 月 4 日 に公開された「 Embed AWS Quick chat agents in enterprise applications 」を翻訳したものです。 対話型 AI を導入する組織は、2 つの大きな課題に直面する場合があります。1 つ目は、ユーザーが別のツールではなく、普段利用している CRM やサポートコンソール、分析ポータルの中で答えを得たいという点です。2 つ目は、セキュアなチャットをアプリケーションに埋め込むには、認証、トークン検証、ドメインのセキュリティ、グローバル配信のインフラストラクチャを作り込む必要があり、開発に数週間かかることもあるという点です。 AWS Quick の 埋め込みチャット は、1 つ目の課題の解決に役立ちます。対話型 AI をアプリケーションに直接組み込めるので、ユーザーはツールを切り替えずに、構造化データへの問い合わせ、ドキュメントの検索、アクションの実行ができます。 本記事では、2 つ目の課題を解決する方法として、Quick の Embedding SDK を利用してチャットエージェントを企業ポータルに埋め込む、ワンクリックでデプロイできるソリューションを紹介します。 ソリューションの概要 このソリューションは、埋め込みチャット用のセキュアなウェブポータルをデプロイします。グローバルなコンテンツ配信には Amazon CloudFront 、 OAuth 2.0 認証には Amazon Cognito 、REST API エンドポイントには Amazon API Gateway 、サーバーレスな API 処理には AWS Lambda 、Quick との ID 連携には OpenID Connect (OIDC) フェデレーションを利用します。 セキュリティは多層防御で実装しています。CloudFront での DDoS 対策、 オリジンアクセスコントロール によってフロントエンドのアセットへの直接アクセスを防ぐプライベートな Amazon Simple Storage Service (Amazon S3) バケット、API Gateway での AWS WAF によるレート制限、そして Amazon Cognito の公開鍵を利用した JSON Web Token (JWT) の署名検証です。署名を検証したうえで、最小権限の AWS Identity and Access Management (IAM) 権限で、ユーザーごとの有効期限付き埋め込み URL を生成します。 ソリューションのアーキテクチャを次の図に示します。 処理の流れは次のとおりです。 ユーザーがウェブポータルの URL にアクセスすると、リクエストは CloudFront にルーティングされます。 CloudFront はオリジンアクセスコントロールを利用して、プライベートな S3 バケットから HTML、CSS、JavaScript のファイルを取得します。 ウェブアプリケーションは有効な認証トークンがあるかを確認し、未認証のユーザーを OAuth 2.0 ログイン用の Amazon Cognito のホストされた UI にリダイレクトします。 ユーザーが Amazon Cognito のログインページで認証情報を入力すると、Amazon Cognito が認証情報を検証し、1 回限り有効な認可コードを付けて CloudFront の URL にリダイレクトします。 アプリケーションは認可コードを取り出し、API Gateway の API を HTTPS で呼び出します。この呼び出しは AWS WAF のレート制限を通過します。 API Gateway は認可コードを渡して Lambda 関数を呼び出します。 Lambda 関数は Amazon Cognito の OAuth トークンエンドポイントをサーバー間の HTTPS で呼び出し、認可コードを JWT (ID トークン、アクセストークン、リフレッシュトークン) と交換します。 Lambda 関数は、Amazon Cognito の公開鍵である JSON Web Key Set (JWKS) を利用して ID トークンの暗号署名を検証します。Lambda 関数は、JWKS をスレッドセーフな方法でキャッシュします。 デコードした JWT の例を次に示します。 {"at_hash": "abcdefifB5vH2D0HEvLghi", "sub": "12345678-abcd-1234-efgh-123456789012", "email_verified": true, "iss": "https://cognito-idp.us-east-1.amazonaws.com/us-east-1_EXAMPLE123", "cognito:username": "12345678-abcd-1234-efgh-123456789012", "origin_jti": "abcd1234-5678-90ef-ghij-klmnopqrstuv", "aud": "1a2b3c4d5e6f7g8h9i0j1k2l3m", "event_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890", "token_use": "id", "auth_time": 1704063600, "exp": 1704067200, "iat": 1704063600, "jti": "abcdef12-3456-7890-abcd-ef1234567890", "email": "user123@example.com"} Lambda 関数は、検証済みの ID トークンを利用して AWS Security Token Service (AWS STS) の AssumeRoleWithWebIdentity API を呼び出し、IAM ウェブ ID ロールを引き受けて一時的な AWS 認証情報を受け取ります。 Lambda 関数は一時的な認証情報で Quick の ListUsers API を呼び出してユーザーの存在を確認し、続いて GenerateEmbedUrlForRegisteredUser API を呼び出して、ドメイン制限付きのセキュアな埋め込み URL を生成します。 Lambda 関数は、 クロスオリジンリソース共有 (CORS) ヘッダー付きの JSON レスポンスで埋め込み URL を返します。レスポンスは API Gateway を経由して CloudFront に届きます。埋め込み URL の例を次に示します。 {"ChatEmbedUrl": "https://us-east-1.quicksight.aws.amazon.com/embedding/abcdefe827dd4ef8b4e1fb921db046c4/quick/chat?code=Abcdef....&identityprovider=quicksight&isauthcode=true", "user": "user123@example.com"} CloudFront 上のアプリケーションは、Quick の Embedding SDK で埋め込みコンテキストを作成し、セキュアなクロスオリジン通信を利用して HTML の iframe 内にチャットインターフェイスを表示します。 ソリューションは、大まかに次の手順でデプロイできます。 AWS Cloud Development Kit (AWS CDK) でサーバーレスインフラストラクチャをデプロイする。 Amazon Cognito と Quick にユーザーをプロビジョニングする。 Quick のアセット (チャットエージェントと、関連する接続やナレッジベース) を共有する。 ウェブポータルにアクセスして Quick のチャットエージェントを利用する。 前提条件 本記事のソリューションをデプロイするには、次のものが必要です。 AWS アカウント 認証方法が パスワードベース または シングルサインオン の Quick のサブスクリプション AWS CDK CLI AWS SDK for Python (Boto3) ソリューションのデプロイに必要な権限を持つ AWS CLI プロファイル 。Quick の名前空間の一覧取得、IAM ロールの作成、および CloudFront ディストリビューション、S3 バケット、API Gateway REST API、AWS WAF ウェブアクセスコントロールリスト、Lambda 関数、Amazon Cognito ユーザープールといった AWS リソースの作成の権限が必要です。 Node.js 20 以降 jq 1.7 以降 起動済みの Docker Desktop AWS CDK でサーバーレスインフラストラクチャをデプロイする 次の手順で、AWS CDK を利用してサーバーレスインフラストラクチャをデプロイします。 GitHub リポジトリ をクローンします。 git clone git@github.com:aws-samples/sample-quicksuite-chat-embedding.git cd sample-quicksuite-chat-embedding インフラストラクチャをデプロイします。 ./setup.sh AWS リージョンコード、AWS CloudFormation のスタック ID とポータルのタイトル、AWS CLI プロファイルの入力を求められます。 Amazon Cognito と AWS Quick にユーザーをプロビジョニングする 次の手順で、Amazon Cognito と Quick にユーザーをプロビジョニングします。 Amazon Cognito ユーザープールに Amazon Cognito ユーザーを作成します。 python scripts/create_cognito_user.py --profile <aws-profile> <cognito-user-email> Quick にフェデレーションユーザーを作成します。 python scripts/create_quicksuite_user.py --profile <aws-profile> <cognito-user-email> AWS Quick のチャットエージェントを共有する 次の手順で、Quick のチャットエージェントを共有します。 Author Pro ロールを持つ認証情報で Quick のコンソールにサインインします。 ナビゲーションペインで チャットエージェント を選択します。 共有したいエージェント (例: AnyCompany Ecom order assistant) を選択し、 共有 を選択します。 先ほど作成したユーザー名 (例: user123@example.com) を検索します。 共有 を選択します。 エージェントをすべての機能で利用できるようにするには、エージェントの共有後に、エージェントに紐付いた各リソースも個別に共有する必要があります。 ウェブポータルにアクセスして AWS Quick のチャットエージェントを利用する 次の手順で、ウェブポータルにアクセスしてチャットエージェントの利用を開始します。 Amazon Cognito から送信される検証メールで、一時パスワードを確認します。 ウェブブラウザで CloudFront の URL にアクセスし、ユーザー ID と一時パスワードでログインします。 初回ログイン時に、パスワードの変更を求められます。 ログインに成功すると、チャットインターフェイスに My Assistant が表示されます。 接続先のリージョンを選択し、Quick のカスタムチャットエージェントに接続します。 自分に共有されたチャットエージェントを表示するには、 Filter の下にある Shared with me を選択します。 利用するエージェントを選択して、チャットを開始します。 次のスクリーンショットは、カスタマーサービス担当者がチャットで操作する例です。担当者は、電話で本人確認を済ませたお客様の依頼を受けて、サンプルのオンライン注文を追跡し、返品を処理しています。 クリーンアップ 次のコマンドで、デプロイした AWS リソースを削除します。 ./cleanup.sh まとめ このソリューションは、対話型 AI を大規模に埋め込む際の主な課題に対応します。具体的には、世界各地から同時にアクセスする数千人のユーザーの認証をセキュアに処理し、充実した監査証跡を備えたエンタープライズレベルのセキュリティを保ち、インフラストラクチャのプロビジョニングを自動化してデプロイを簡単にします。ポータルのブランディングのカスタマイズ、セキュリティポリシーの調整、既存の ID プロバイダーとの統合もできます。従量課金のまま、数千人の同時ユーザーまで自動でスケールします。 このソリューションを試すには、 GitHub リポジトリ をクローンし、ワンクリックでインフラストラクチャ一式をデプロイして、Quick のチャットエージェントを埋め込んでください。 著者について Satyanarayana Adimula AWS Generative AI Innovation & Delivery の Senior Builder です。20 年以上にわたるデータと分析の専門知識を活かし、エージェント型 AI システムの構築を専門としています。大企業が複雑なワークフローを自動化し、意思決定を速め、測定可能なビジネス成果を上げられるよう支援しています。 翻訳は Solutions Architect の 守田 凜々佳 が担当しました。
開発2部の内原です。 自分の観測範囲では、最近Claude CodeやCodex CLI、Gemini CLIといったAIツールのCLIを使うケースが増え、以前に比べてターミナルソフトを使う機会が多くなったように感じます。 ただ、日常的に使っているとしても、このソフトウェアが何をしているかを正しく理解しているとは言えない状態でした。ターミナル、シェル、ttyの区別が曖昧だったり、Ctrl-Cでプログラムが停止するのはなぜか、文字の色付けは誰が担当しているのか、といったことを人に説明できるだけの知識は持てていないという自覚がありました。 そこでこの記事では、ターミナルソフトの挙動について改めて学んでみます。 なお、ターミナルソフトは本来MacやWindows上で動作するGUIアプリケーションとして実装されるものですが、今回の目的はあくまで内部の挙動の把握であるため、描画やフォントといったGUI部分は扱わず、その裏側の機構のみを対象とします。POSIX環境を前提に、C言語で動作を確かめる方針です。 用語の整理 まず、ターミナルを使っているときに関わってくる要素を整理します。 ターミナルエミュレータ 普段ターミナルソフトと呼んでいるGUIアプリケーション キー入力の受付と画面の描画を担当 pty(擬似端末、pseudo tty) カーネルが提供する、双方向なバイト列のやり取りに用いられる仕組み masterとslaveの2つの終端を持ち、master側にターミナルエミュレータ、slave側にシェルがそれぞれ接続される カーネル内の端末処理層(line discipline) ptyのmasterとslaveの間に位置する加工層 行編集、エコー、シグナル生成などを担当 シェルや各プログラム ptyのslaveを標準入出力として読み書きする データの流れを図にすると以下のようになります。 ターミナルのデータの流れ 両端のターミナルエミュレータとシェルはユーザー空間のプロセスで、その間のptyとline disciplineはカーネル内に存在します。 ttyという名前はteletypewriterに由来します。かつてはコンピュータにシリアル回線で物理的な端末(VT100など)を接続しており、カーネルはその回線の先にいる端末とのやりとりをttyとして扱っていました。物理端末の役割をソフトウェアで再現したものがターミナルエミュレータで、シリアル回線の代わりを務めるのがptyです。 自分のシェルがどの端末につながっているかは tty コマンドで確認できます。 $ tty /dev/ttys003 ps でTTY列を表示すると、プロセスと端末の対応が見えます。 $ ps -o pid,tty,comm PID TTY COMM 1029 ttys000 -zsh 14861 ttys000 tmux 1041 ttys001 -zsh 43561 ttys008 vim ターミナルのウィンドウやタブ、tmuxのペインごとに、別々のptyが割り当てられていることがわかります。 ptyを開いてシェルを実行 ターミナルエミュレータの中核は、ptyを確保してシェルを起動し、そのあとはバイト列を中継し続けることです。 以下の minipty.c は、 script(1) コマンドとほぼ同じ動作をする、画面描画を持たない簡易的なターミナルの実装です。GUI部分こそ存在しませんが、ターミナルソフトとやっていること自体は同じと言えます。 #define _GNU_SOURCE #include <fcntl.h> #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <sys/ioctl.h> #include <termios.h> #include <unistd.h> static struct termios orig; static void restore ( void ) { tcsetattr (STDIN_FILENO, TCSANOW, &orig); } int main ( void ) { // 1. ptyのmasterを確保し、対応するslaveのパスを取得 int master = posix_openpt (O_RDWR | O_NOCTTY); grantpt (master); unlockpt (master); char *slave_name = ptsname (master); pid_t pid = fork (); if (pid == 0 ) { // 2. 子:新しいセッションを生成し、slaveを制御端末にしてfd 0,1,2に接続 setsid (); int slave = open (slave_name, O_RDWR); ioctl (slave, TIOCSCTTY, 0 ); dup2 (slave, 0 ); dup2 (slave, 1 ); dup2 (slave, 2 ); close (slave); close (master); execl ( "/bin/sh" , "sh" , NULL ); _exit ( 1 ); } // 3. 親:自分の端末をrawモードに変更し、キー入力を加工なしで素通しする if ( isatty (STDIN_FILENO)) { tcgetattr (STDIN_FILENO, &orig); atexit (restore); // 終了時に元の設定へ戻す struct termios raw = orig; cfmakeraw (&raw); tcsetattr (STDIN_FILENO, TCSANOW, &raw); } // 4. 親:「キーボード → master」「master → 画面」の中継をくり返すのみ struct pollfd fds[] = {{STDIN_FILENO, POLLIN, 0 }, {master, POLLIN, 0 }}; char buf[ 4096 ]; for (;;) { poll (fds, 2 , - 1 ); if (fds[ 0 ].revents & POLLIN) { ssize_t n = read (STDIN_FILENO, buf, sizeof buf); if (n <= 0 ) fds[ 0 ].fd = - 1 ; // 入力が尽きたら以降は監視しない else write (master, buf, n); } if (fds[ 1 ].revents & (POLLIN | POLLHUP)) { ssize_t n = read (master, buf, sizeof buf); if (n <= 0 ) break ; // シェルが終了した write (STDOUT_FILENO, buf, n); } } return 0 ; } 説明のため、エラー処理は省略しています。処理は大きく4つに分かれます。 ptyの確保 posix_openpt(3) でmasterを開く grantpt(3) と unlockpt(3) で、対応するslaveを使用可能にする ptsname(3) でslaveのデバイスパス( /dev/ttys005 など)を取得 子プロセスの準備 setsid(2) で新しいセッションを作成し、元の端末から切り離す slaveを開いて制御端末にする dup2(2) でslaveをfd 0, 1, 2に複製してから、 execl(3) でシェルを起動 親プロセスの端末設定 自分自身の端末をrawモードに設定 中継 poll(2) で2つのfdを監視し、キーボードから来たバイト列はmasterへ、masterから来たバイト列は画面へ書き出す dup2(2) による複製は、 以前の記事 で扱ったリダイレクトと同じ仕組みです。 準備が終わった時点で、fdは以下のような状態になっています。 minipty実行時のfdの接続 実行して、子のシェルで tty を打ってみます。 $ tty /dev/ttys003 $ cc minipty.c -o minipty && ./minipty sh-3.2$ tty /dev/ttys005 sh-3.2$ exit exit $ 子のシェルは、元の端末( /dev/ttys003 )とは別のpty( /dev/ttys005 )につながっています。 親プロセスの処理は、2つのfdの間でバイト列を転送しているだけです。プロンプトの表示も、打った文字が画面に出るのも、シェルや次の章で扱うline disciplineの仕事であるため、このプログラム自体はデータの中身を一切解釈していません。 GUIのターミナルソフトは、この中継部分の画面側を自前の描画に置き換えたものだと考えることができます。 line disciplineとtermios ptyのmasterとslaveの間には、line disciplineという加工層が存在しています。普段は意識しませんが、ターミナルの基本的な挙動の多くはここで実現されています。 line disciplineの処理 現在の設定は stty -a で確認できます。以下はmacOSでの出力の抜粋です。 $ stty -a lflags: icanon isig iexten echo echoe -echok echoke -echonl echoctl iflags: -istrip icrnl -inlcr -igncr ixon -ixoff ixany imaxbel -iutf8 oflags: opost onlcr -oxtabs -onocr -onlret cchars: discard = ^O; dsusp = ^Y; eof = ^D; eol = <undef>; eol2 = <undef>; erase = ^?; intr = ^C; kill = ^U; lnext = ^V; ... 主な項目の意味は以下のとおりです。 icanon canonicalモード 入力を行単位で蓄積し、Enterが押された時点でプログラムに渡す Backspaceによる削除もこの段階で処理される echo 受け取った文字をmaster側へ送り返して画面に表示する isig intr = ^C などの特殊文字を受け取ったら、シグナルを生成する Ctrl-Cを押したとき、ターミナルソフトは0x03の1バイトを送るだけ line disciplineがそれをSIGINTに変換し、フォアグラウンドのプロセスに送ることでプログラムが停止する onlcr 出力の改行 \n を \r\n に変換する rawモード vimやless、AIツールのCLIのようなTUIアプリケーションは、line disciplineの加工をほとんど無効にしたrawモードで動作しています。1キーごとに反応し、カーソル移動や補完、エコーの表示方法を自前で制御する必要があるためです。 line disciplineの設定はtermios構造体で表され、 tcgetattr(3) と tcsetattr(3) で読み書きします。 cfmakeraw(3) を使うと、icanon、echo、isigなどの加工をまとめて無効にできます。 前章のプログラムで親プロセスが自分の端末をrawモードにしていたのも同じ理由です。このプログラムではline disciplineが2つ直列に並んでいます。 親プロセスと本物の端末の間のline discipline masterとslaveの間のline discipline(子のシェル側) 親側をrawモードにしないと、Ctrl-Cは親側のline disciplineでSIGINTに変換され、子のシェルではなく親自身が終了してしまいますし、エコーも二重になります。親側をrawにして素通しすることで、加工は子のシェル側のline disciplineだけが担当するようになります。 エスケープシーケンス line disciplineを通過した出力は、最終的にターミナルソフトに単なるバイト列として届きます。 文字の色やカーソルの移動は、バイト列の中に埋め込まれた制御用の文字列、エスケープシーケンスによって実現されています。 以下のコマンドでは赤文字で red という文字が出力されます。 $ printf '\e[31mred\e[0m\n' ESC( \e )、 [ 、数字、終端文字、で命令を表します。 上の例では ESC [31m で文字色を赤にし、 ESC [0m で装飾を元に戻しています。 以下はよく使われるエスケープシーケンスです。 シーケンス 意味 ESC [31m 文字色を赤に設定 ESC [0m 装飾をリセット ESC [2J 画面全体を消去 ESC [5;10H カーソルを5行10列目に移動 ESC [A カーソルを1行上に移動 ターミナルソフト側の処理 ターミナルソフトは、受け取ったバイト列を状態機械によってパースし、画面バッファを更新しています。 ターミナルソフトの出力処理 パーサ 通常の文字を受け取ったら、カーソル位置のセルに書き込んでカーソルを進める ESCを受け取ったら、エスケープシーケンスの読み取り状態に遷移 [ に続く数字をパラメータとして蓄積し、終端文字( m 、 H 、 J など)を受け取った時点で命令を実行 画面バッファ 行数 × 列数のセルの配列 各セルは文字と属性(文字色、背景色、太字など)を保持 描画 画面バッファの内容をフォントで描画する(今回は対象外) TERMとterminfo エスケープシーケンスは端末の種類によって異なる場合があります。そこでvimやless、AIツールのCLIのようにシーケンスを出力する側のプログラムは、環境変数 TERM で出力先の端末の種類を知ります。vimやlessは、さらにterminfoというデータベースから対応するシーケンスを引いています。 TERM 自体は、ターミナルソフトがシェルを起動する際に自身の端末種類を伝えるために設定しておくものです。 $ echo $TERM xterm-256color $ infocmp xterm-256color ... clear=\E[H\E[2J, cup=\E[%i%p1%d;%p2%dH, ... clear は画面消去、 cup はカーソル移動、などを表しています。 なおncursesなどの端末用ライブラリを利用すればこの定義を自動的に参照してくれるため、アプリケーション側でシーケンスを直接記述することを避けられます。 isattyとパイプ ここまでは、プログラムの標準入出力が端末につながっている場合の話でした。しかし標準出力の行き先はパイプやファイルの場合もあり、多くのプログラムは出力先が端末かどうかで挙動を変えています。 判定には isatty(3) を使います。 #include <stdio.h> #include <unistd.h> int main ( void ) { if ( isatty (STDOUT_FILENO)) { printf ( " \033 [32mstdout is a terminal \033 [0m \n " ); } else { printf ( "stdout is NOT a terminal \n " ); } printf ( "line 1 \n " ); sleep ( 2 ); // 端末なら1行ずつ、パイプならまとめて出力されることを確認するための待機 printf ( "line 2 \n " ); return 0 ; } 同じプログラムを3通りの方法で実行してみます。 $ ./isatty_demo stdout is a terminal line 1 line 2 $ ./isatty_demo | cat stdout is NOT a terminal line 1 line 2 $ ./minipty sh-3.2$ ./isatty_demo | cat stdout is NOT a terminal ... sh-3.2$ ./isatty_demo stdout is a terminal ... 端末に直接出力すると緑色で表示され、 | cat を付与すると色付けはなくなります。前述のminiptyの中で実行した場合も、子のシェルの標準出力はpty slaveなので端末として判定されます。 ls --color=auto や git も同じように判定しています。 ls --color=auto 端末に出力するときだけ色を付ける git log など 端末に出力するときはページャ(less等)を起動し、パイプ先では起動しない AIエージェントがコマンドを実行するとき この挙動は、AIエージェントにコマンドを実行させるときにも関わってきます。 試しにClaude Codeに tty と、シェル自身の標準入出力を確認するコマンドを実行させてみました。 $ tty not a tty $ lsof -p $$ -a -d 0-2 COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME zsh 549 uchihara 0r CHR 3,2 0t0 336 /dev/null zsh 549 uchihara 1w REG 1,14 18 8392275 /private/tmp/claude-502/.../tasks/bryiflukh.output zsh 549 uchihara 2w REG 1,14 18 8392275 /private/tmp/claude-502/.../tasks/bryiflukh.output 自分の環境では、標準入力は /dev/null 、標準出力と標準エラー出力はファイルに接続されており、ptyは使われていませんでした。このため、端末上で人間が実行した場合とは以下のような違いが生じます。 色付けが行われない isatty(3) が偽になるため、 ls や git はカラー無効とする 出力が一括で行われる 行単位ではなくブロック単位でバッファリングされる 時間のかかるスクリプトの途中経過が見えないことがある 対話的なコマンドは利用不可能 標準入力が /dev/null に接続されているので、入力を求めるコマンドはすぐにEOFを受け取って終了する パスワード入力のように /dev/tty を直接開くコマンドは、制御端末がないため失敗する 標準入力がパイプのまま開いている実装では、入力を待ち続けて固まることがある まとめ ターミナルソフトが担当しているのは、以下の処理でした。 pty masterの読み書き キー入力をバイト列に変換してmasterに書き込む masterから届いたバイト列のエスケープシーケンスを解釈して描画する それ以外の、行編集やエコー、Ctrl-Cによるプロセスの停止はカーネルのline disciplineが、コマンドの解釈はシェルが担当しています。日常的に使っているターミナルソフトも、中身は数十行のCで再現できる程度の単純な機構の組み合わせであることがわかりました。
自己紹介とインターン参加の理由 自己紹介 公立はこだて未来大学 情報システム科学部 情報アーキテクチャ学科3年の阿部大晟と申します。 大学では主に情報デザインについて学んでおり、現在は数年後の未来で使われるアプリを制作するプロジェクトに携わっています。開発においては、フロントエンド側のUI/UX設計や、チームの進捗管理を担当することが多いです。 趣味はゲームや小説、アニメ鑑賞など日本のサブカルチャー全般です。 サイオスのインターンを選んだ理由 大学での説明会をきっかけに参加を決めました。目的は以下の3点です。 実際の現場で必要な能力の把握と習得 AI駆動開発の正しい手法の習得 (特に「Claude Code」を使用できる点) 就職活動に向けた実践的な学習 加えて、社員の方々の人柄が良く、「一度お話してみたい」と感じたことも大きな決め手でした。 開発したプロダクト:学習支援アプリ「メタゼロ」 制作物の紹介 私たちAチームが10日間で開発したのは、大学生の「メタ学習時間」をゼロに近づける学習支援アプリ 「メタゼロ(MetaZero)」 です。 「メタ学習時間」とは、学習を始める前に資料を探したり、勉強のスコープを定めたりする「探す・計画する・管理する」時間を指す私たちの造語です。 メタゼロは複数の大学サイトに分散した課題・試験情報を自動巡回して1枚の日程表に統合し、そこからAIとの対話で「いつ・何を・どれくらい」やるかの学習スケジュールを決定します。メタ学習時間削減により浮いた時間は、アップロードした資料から自動生成される模擬試験や一問一答に充て、学習そのものに集中してもらう狙いがあります。 私たちは 13画面と、AR実験機能まで実装 しました。 自分の役割 私は フロントエンド側を中心に担当し、UIデザインの方向性も決定 しました。 具体的にはタイトル画面や各種ロードマップの作成・UI調整、模擬試験などの画面実装です。 特にこだわったのは科目別ロードマップで、 「試験本番までの道のりをハニカム(六角形)で見せ、蜂がその日のセルを移動する」という演出 を作り込みました。 成果発表までの道のり ハッカソン形式で行われ、初日はアイスブレイクやセキュリティ研修がメインでした。その後、ワイヤーフレームを作成してイメージを共有しながら開発を進めました。 開発の軸となったのは、 「仕様書を唯一の正とする」こと 。仕様書をもとに「実装→レビュー→テスト」のループを回し続ける手法により、非常にスムーズに開発を進めることができました。 チーム開発を通じて気づいたこと、学んだこと コミュニケーションの大切さとコンフリクトからの学び 開発当初、「ワイヤーフレーム通りに実装すれば大丈夫だろう」と高を括り、大した相談もせず開発を進めていたため、コンフリクト(コードの衝突)が頻発してしまいました。また、途中でフロントエンドとバックエンドで担当を分けた際にも、連携の難しさを実感しました。 そこでチームでの進め方を見直し、 「バックエンド1人・フロントエンド2人」の層(レイヤー)で分担し、両方が触るデータの形(API)だけ事前合意した上で、それ以外は「〇〇を直す。影響ある人がいたら教えて」の一報だけで進めるルールに変更 しました。これにより承認待ちで手が止まることがなくなり、リモートでも開発スピードを維持できるようになりました。 この経験を通して、「短くても的確に伝わるコミュニケーション」の重要性を痛感しました。チームが連動して動くことで、結果的により良いものが生まれます。 コミュニケーションは他人のためだけでなく、「自分のため」でもあるの だと強く感じました。 「1コミット1関心事」とAIを活用した仕組みづくり 開発面では、 コミットを「1コミット1関心事」に徹底 し、メッセージの型(feat:, fix:など)と理由をGitフックで機械的に検査しました。また、AIエージェントを活用し、コードをレビューする役目と、テストの期待値を決める役目を実装者から切り離しました。 「指摘する人と直す人を分ける」「コードを1行も読まずに期待値を先に決める」仕組み により、期待値が実装に引きずられて甘くなるのを防ぐことができました。  SIOS Values(チームワークとパッション)の実践 サイオスが掲げる5つの指標のうち、私は特に 「チームワーク」と「パッション」を意識 しました。常に「チームワークが大事だ」と念頭に置き、環境づくりにも注力したことで、開発そのものも円滑に進むと感じました。 チームメンバーは優秀な方ばかりで、助けられてばかりでした。実装で手が止まった時や意見を求めたい時には必ず丁寧にサポートしてくれました。特に感心したのは、皆さんの物腰が柔らかく、自然と話しやすい環境になっていったことです。笑いながら作業できる和やかさがありつつ、決してたるんでいない、素晴らしい環境でした。 彼らがいなければこのアプリは完成しなかったと思います。  認識共有の難しさと「ほうれんそう」 開発で一番苦労したのは「認識の共有」です。 人が持つイメージは本人にしか100%理解できません。しかしチーム開発では複数人のイメージをすり合わせる必要があります。 これが想像以上に難しく、きちんと共有できていないと、いつの間にか見当違いの方向へ進んでしまうことが多々ありました。 人は間違えるからこそ、見当違いを防ぐ仕組みづくりが必要だ と学びました。 また、Claude Codeのような便利で作りやすいAIツールがあるからこそ、一歩立ち止まって考える時間が必要だと痛感しています。私は「これがあったら喜ばれるだろう」と良かれと思って独断で機能を追加実装してしまい、結果として重めのバグを残してしまうという失敗をしました。 便利だからと勢いで進めるのではなく、 仲間がいる以上、最低限の共有(ほうれんそう)は絶対に欠かしてはいけない と猛省しました。 最大の学びと、これからの目標 サイオスのインターンシップを通じて「エンジニア」のイメージが大きく変わりました。 最大の収穫は、 「エンジニアに一番大切なのはコミュニケーション能力である」と知れたこと です。 もともとエンジニアの仕事は、「頭をフル回転させて一人でコードを実装する」という イメージでした。 しかし実際は、時間の大半は「要件定義」に費やされます。またコードの実装自体も、Claude Codeなどのツールで代行できるケースが今後さらに増えていくからこそ、 最終的に差がつくのは人との対話力だと感じました。 私の中で、エンジニアとは 「プロジェクトのゴールを達成するために、仲間と話し合い、コミュニケーションを通じて物事を解決していく職業」 へと大きく変わりました。 私にとってこのエンジニア像のアップデートは、非常に大きなものでした。 今後の展望 今回のインターンを通じて、今後の自分に向けた明確な目標が2つできました。 1つ目は、 英語の学習 です。 コミュニケーションツールとして英語ができれば、国境の壁を越えてさまざまな人と円滑に連携が取れると感じたためです。 2つ目は、 セキュリティの学習 です。 開発速度が劇的に上がるこれからの時代だからこそ、セキュリティを深く理解していれば、より安心して技術を活用できるのではないかと考えています。 これからインターンを受ける後輩へ向けたアドバイスや応援の一言! サイオスのインターンシップは、オンライン開催でありながら、社員の方々とのコミュニケーションの機会が豊富にあり、非常に有意義な10日間でした。 これからのIT業界で必要なスキルを、確実に学べるインターンだと思います。 今後参加される方は、ぜひ 「大いなる熱意」 をもって臨んでみてください。必ず素晴らしい経験になるはずです。 ご覧いただきありがとうございます! この投稿はお役に立ちましたか? 役に立った 役に立たなかった 0人がこの投稿は役に立ったと言っています。 The post コンフリクトの頻発を乗り越え「報告・連絡・相談」でチームが団結! first appeared on SIOS Tech Lab .

動画

書籍