【AIコーディング】「意図通りのコードが出ない」を防ぐコンテキスト管理術|プロンプトエンジニアリングの基礎
はじめに
「AIにコードを書かせようとしたけど、全然違うものが返ってきた」「結局自分で書いた方が早かった」
AIコーディングツール(Cursor, GitHub Copilot, Gemini Code Assistなど)を使っていて、このような経験はありませんか?
AIの回答精度が低いとき、多くの開発者は「プロンプト(指示の仕方)」を工夫しようとします。「あなたは優秀なエンジニアです」と役割を与えたり、命令口調を変えてみたり。
しかし、実務レベルの複雑なコード生成において、「どう頼むか(Instructions)」よりも圧倒的に重要なのが「何を渡すか(Context)」 です。
この記事では、AIコーディングの精度を劇的に向上させるための「コンテキスト管理術」について解説します。
「意図通りのコードが出ない」原因とは?
AIが期待通りのコードを書けない最大の原因は、「AIがあなたのプロジェクトの文脈(仕様、依存関係、コーディング規約)を知らないから」 です。
| 原因 | 症状 | 対策 |
|---|---|---|
| 文脈不足 | 架空のライブラリを使う、既存の設計を無視する | コンテキスト注入 (今回解説) |
| 指示の曖昧さ | 一般論しか返ってこない、コードが動かない | ゴールの明確化 |
| モデルの限界 | 最新のライブラリを知らない | Web検索の活用 |
AIはどれほど高性能でも、渡された情報(プロンプトウィンドウ内の情報)しか処理できません。「このプロジェクトの雰囲気で」といった暗黙知は通用しないのです。
コンテキスト管理の3つの鉄則
AIに適切なコンテキストを与えるための、具体的なテクニックを3つ紹介します。
1. 関連ファイルを「明示的」に渡す
チャット形式のAIツールを使う際、現在開いているファイルだけが自動的に読み込まれることが多いですが、それだけでは不十分です。
依存関係にあるファイルや、参照すべき型定義ファイルなどを明示的にコンテキストに含めましょう。
-
Cursorの場合:
@Filesで関連ファイルを指定 -
GitHub Copilot Chatの場合:
#fileで参照ファイルを指定
良い例 (Cursor):
@auth_service.ts と @user_model.ts を参照して、
ユーザーログイン時に LastLoginAt を更新するメソッドを追加してください。
2. 「正解データ」としての参考コードを見せる
AIにゼロから考えさせるのではなく、「このプロジェクトではこう書いている」という手本(Few-Shot Prompting)を見せることが最も効果的です。
特に、独自のアーキテクチャや命名規則がある場合、言葉で説明するよりも、既存の似たような機能の実装ファイルを渡す方が確実です。
プロンプト例:
@existing_feature_controller.ts の実装パターンを参考にして、
新たに @new_feature_controller.ts を作成してください。
エラーハンドリングとログ出力の形式は既存ファイルに合わせてください。
3. ルールファイル(.cursorrules / instructions.md)の活用
プロジェクト固有のルール(言語バージョン、ライブラリ、命名規則など)を毎回入力するのは非効率です。
多くのAIツールは、プロジェクトルートに特定の設定ファイルを置くことで、すべてのプロンプトに自動的にコンテキストを追加できます。
-
Cursor:
.cursorrules -
Github Copilot:
.github/copilot-instructions.md
記述すべき内容の例:
例えば、Next.jsプロジェクトの .cursorrules は以下のようになります。これだけで、AIは「React Server Components」を意識したコードを書くようになります。
# Project Context
- Framework: Next.js 14 (App Router)
- Language: TypeScript
- Styling: Tailwind CSS
- State Management: React Context + SWR
# Coding Standards
- Use Functional Components with named exports
- Use `interface` for prop definitions
- Avoid `useEffect` where Server Actions can be used
- All asynchronous functions must use `async/await`
# Important Files
- Database Schema: @prisma/schema.prisma
- Auth Utility: @lib/auth.ts
実践フロー:機能追加時のコンテキスト戦略
実際に「ユーザーのプロフィール画面に、最終ログイン時間を表示する」というタスクをやってみましょう。
悪い例(コンテキストなし)
プロンプト:
ユーザーモデルに
lastLoginAtを追加して、UIに表示して。
これだと、AIは勝手に User クラスを作ったり、使っていない axios をインポートしたりします。
良い例(コンテキストあり)
プロンプト:
@schema.prisma に
lastLoginAtカラムを追加し、@user-profile.tsx に表示ロジックを追加してください。
日付フォーマットは @date-utils.ts のformatDate関数を使用してください。
生成されるコードのイメージ(差分):
// prisma/schema.prisma
model User {
id String @id @default(cuid())
+ lastLoginAt DateTime?
// ...
}
// components/user-profile.tsx
import { formatDate } from '@/lib/date-utils'; // 既存のユーティリティを再利用できている!
export function UserProfile({ user }: { user: User }) {
return (
<div>
<h1>{user.name}</h1>
<p>Last Login: {user.lastLoginAt ? formatDate(user.lastLoginAt) : 'Never'}</p>
</div>
);
}
関連ファイルを明示することで、AIは「既存のユーティリティ関数の存在」に気づき、DRYな(重複のない)コードを生成できます。
おわりに
AIコーディングにおいて、「プロンプトエンジニアリング」とは、巧みな言い回しを考えることではなく、「AIが正しく推論するために必要な情報を過不足なく集める(コンテキストマネジメント)」 ことです。
次回は、このコンテキスト管理術を応用し、最も難易度の高いタスクの一つである「レガシーコードのリファクタリング」に挑戦します。
Next Step:
Part 2: 【リファクタリング】「触るのが怖い」レガシーコードをAIと安全に改善する実践ステップ
Discussion