2025年2月初頭に生まれた 「ヴァイブコーディング(雰囲気コーディング)」 という言葉は、そのカジュアルな響きが受け、Wikipedia に項目ができるほど定着した。AIにコーディングを任せる、あるいは補助として活用する開発スタイルを指す言葉らしい。
この言葉が「バズった」理由を考えてみると、実は大きく二つの要因があったのではないかと思う。
AIコーディング推進派 が聞くと、
「新時代の開発スタイルを、うまく言語化したイケてるワードだ!」
アンチAIコーディング派 が聞くと、(かなり戯画化すると)
「AIなんかに任せるって、せいぜいヴァイブ頼みの軽い実装だろ?趣味レベルならそれで楽しいよな。俺たちのエンタープライズ開発は安泰だぜ」
同じ言葉が、正反対の立場の人たちにとって、それぞれ心地よく解釈できてしまう。
その“二重解釈可能性”こそが、ヴァイブコーディングという言葉が急速に広まった理由だったのかもしれない。
ただ、2026年の今、その言葉はすでに少しずつ役割を終え始めているように感じる。これからは、もっと エンタープライズ受けする――営業資料に載せやすく、意思決定層が好みそうな――別の言葉 が使われ始めるだろう。
理由は単純だ。
AIを活用したコーディングは、もはや「趣味的な効率化」や「一部の先進事例」ではない。 無視できないレベルで、エンタープライズ開発の前提そのものを揺さぶり始めているからだ。
ちなみに「ヴァイブコーディング」の次に来る言葉をGPTとブレストしてみたが・・・
確率的勾配降下コーディング/勾配駆動開発/Gradient-Driven Development(GDD)/グラディエント駆動開発/収束指向コーディング・・・etc
今のところ「イケてる」言葉は見つかっていない。
コードと設計の実例
ここからは、その「揺さぶり」を、実際に自分が体験した話を書く。
端的に言うと、自分の手元では、AIエージェント(Cursor利用)が次のレベルのコードを書いている。
ルールや要件・設計の変更でエージェントが複数回書き直しているが、人間はコードやコメントに触れていない。(正直に告白すると、私はTSの実務経験はない。その昔、Document.GetElementById("xxx") したり var xmlHttpRequest = new XMLHttpRequest(); したりした経験は大量にある(笑))
クリックするとコードを表示
/**
* @file public スレッド作成ハンドラ
* - 認証不要で thread を作成する
* - board 状態と owner_only, hidden を検証する
* - 初回レス用トークンを発行しハッシュ保存する
* - refresh_threads_latest を呼び出して latest を更新、ストアド連鎖で boards/cross/top_page まで反映
* - actor_user_id は null で audit_note を記録する
* - 1 関数あたり 80 行以下を維持する
* - JSON/型崩れを 400 で早期に防ぐ
* - ロック順序は site_master → boards_source → boards_latest → boards_cross_latest
*/
import type { Context } from "hono";
import { createHash, randomBytes, randomUUID } from "node:crypto";
import { createAuditNoteWithClient } from "../audit";
import { HttpError } from "../errors";
import {
STATUS_BAD_REQUEST,
STATUS_FORBIDDEN,
STATUS_INTERNAL_SERVER_ERROR,
STATUS_NOT_FOUND,
} from "../http-status";
import type { AppEnv } from "../middleware/db";
import { ok } from "../response";
import { assertSiteInitialized } from "./ownerShared";
import { setNoStore } from "./readCommon";
import { assertRegexMatch } from "./validationHelpers";
const FIRST_RESPONSE_TOKEN_BYTES = 16;
const TOKEN_HASH_ALGO = "sha256";
/**
* 初回レス用トークンをハッシュ化する。
* @param token 平文トークン
* @returns SHA-256 hex
*/
export function hashFirstResponseToken(token: string): string {
return createHash(TOKEN_HASH_ALGO).update(token, "utf8").digest("hex");
}
/**
* スレッド作成入力を検証する。
* @param boardSlug 板 slug
* @param title スレッドタイトル
*/
function validateThreadCreate(boardSlug: string, title: string): void {
// 不正な板 slug は直ちに 400 で拒否する
assertRegexMatch("boards.board_slug", boardSlug, "invalid board_slug", STATUS_BAD_REQUEST);
// 不正なタイトルは downstream のエラーを防ぐため 400 を返す
assertRegexMatch("threads.title", title, "invalid title", STATUS_BAD_REQUEST);
}
/** スレッド作成リクエストの入力型。 */
type ThreadCreateBody = {
board_slug: string;
title: string;
audit_note: unknown;
};
/**
* ThreadCreateBody の型ガード。
* @param body 検証対象
*/
function assertThreadCreateBody(body: unknown): asserts body is ThreadCreateBody {
// オブジェクト型でない場合は型ガード失敗として 400 を返す
if (typeof body !== "object" || body === null) {
throw new HttpError(STATUS_BAD_REQUEST, "invalid body", { expose: true });
}
const b = body as Record<string, unknown>;
// board_slug が文字列でない場合は必須項目欠如として 400 を返す
if (typeof b.board_slug !== "string") {
throw new HttpError(STATUS_BAD_REQUEST, "board_slug required", { expose: true });
}
// title が文字列でない場合は必須項目欠如として 400 を返す
if (typeof b.title !== "string") {
throw new HttpError(STATUS_BAD_REQUEST, "title required", { expose: true });
}
}
/**
* 公開スレッドを作成する。
* @param c Hono コンテキスト
* @returns 作成結果
*/
export async function handleCreateThread(c: Context<AppEnv>): Promise<Response> {
// プラグインが sharedData にボディを保存している場合は、そこから取得する
const pluginRequestBody: unknown = c.var.pluginSharedData?.get("requestBody");
const requestBody: unknown = pluginRequestBody ?? (await c.req.json());
// body が undefined の場合は 400 を返す
if (requestBody === null || requestBody === undefined) {
throw new HttpError(STATUS_BAD_REQUEST, "request body is required", { expose: true });
}
assertThreadCreateBody(requestBody);
validateThreadCreate(requestBody.board_slug, requestBody.title);
const auditNote = await createAuditNoteWithClient(c, requestBody.audit_note, null, { allowAnonymous: true });
const db = c.var.dbWrite;
const threadId = randomUUID();
const firstResponseToken = randomBytes(FIRST_RESPONSE_TOKEN_BYTES).toString("hex");
const firstResponseTokenHash = hashFirstResponseToken(firstResponseToken);
// トランザクションを開始し、thread作成処理を実行する。例外時はロールバック
const result = await db.begin(async (tx) => {
// 1) site_master を FOR SHARE で取得しサイト初期化を確認
// サイト初期化状態を確認し、未初期化の場合はエラーを投げる
await assertSiteInitialized(tx);
// 2) boards_source を slug で取得し board_id を確定
const sourceRows = await tx<{ board_id: string }[]>`
SELECT id AS board_id FROM boards_source WHERE slug = ${requestBody.board_slug} FOR SHARE
`;
const source = sourceRows[0];
// slug に一致する board が存在しない場合は 404 を返す
if (source === undefined) {
throw new HttpError(STATUS_NOT_FOUND, "board not found", { expose: true });
}
// 3) boards_latest を FOR UPDATE で取得し設定値をロック下で参照
const latestRows = await tx<{ owner_only_thread_creation: boolean }[]>`
SELECT owner_only_thread_creation FROM boards_latest WHERE board_id = ${source.board_id} FOR UPDATE
`;
const latest = latestRows[0];
// boards_latest が存在しない場合は不整合として 404 を返す
if (latest === undefined) {
throw new HttpError(STATUS_NOT_FOUND, "board not found", { expose: true });
}
// 4) boards_cross_latest を FOR SHARE で取得し可視性を SoT として判定
const crossRows = await tx<{ is_hidden: boolean }[]>`
SELECT is_hidden FROM boards_cross_latest WHERE board_id = ${source.board_id} FOR SHARE
`;
const cross = crossRows[0];
// 可視性 SoT が存在しないか非公開の場合は存在秘匿として 404 を返す
if (cross === undefined || cross.is_hidden === true) {
throw new HttpError(STATUS_NOT_FOUND, "board not found", { expose: true });
}
// owner 限定 board では匿名作成を禁止
if (latest.owner_only_thread_creation === true) {
throw new HttpError(STATUS_FORBIDDEN, "owner only", { expose: true });
}
// threads_source に新規スレッドを挿入する。例外時はトランザクションロールバック
await tx`INSERT INTO threads_source (id, board_id) VALUES (${threadId}, ${source.board_id})`;
// threadのfirst response token履歴をthreads_history_sourceに挿入する。例外時はトランザクションロールバック
await tx`
INSERT INTO threads_history_source (thread_id, actor_user_id, history_type, history_value, audit_note)
VALUES (${threadId}, NULL, 'SET_FIRST_RESPONSE_TOKEN_HASH', ${firstResponseTokenHash}, ${tx.json(auditNote)})
`;
// threadのタイトル履歴をthreads_history_sourceに挿入する。例外時はトランザクションロールバック
await tx`
INSERT INTO threads_history_source (thread_id, actor_user_id, history_type, history_value, audit_note)
VALUES (${threadId}, NULL, 'SET_TITLE', ${requestBody.title}, ${tx.json(auditNote)})
`;
// refresh 連鎖一本化: refresh_threads_latest が boards/cross/top_page まで伝搬する
// boards_latest は既に FOR UPDATE でロック済みのため順序衝突なし
// スレッド作成後に latest ビューを更新し、関連する boards/cross/top_page まで反映する。例外時はトランザクションロールバック
await tx`CALL refresh_threads_latest(${threadId}::uuid)`;
// threads_latest の PK は thread_id、created_at は threads_source に存在。refresh 後に作成されたスレッド情報を取得する
const threadLatestRows = await tx<{ thread_id: string; created_at: Date }[]>`
SELECT tl.thread_id, ts.created_at
FROM threads_latest tl
JOIN threads_source ts ON ts.id = tl.thread_id
WHERE tl.thread_id = ${threadId}::uuid
`;
const row = threadLatestRows[0];
// refresh 後に latest に存在しない場合は不整合として 500 を返す
if (row === undefined) {
throw new HttpError(STATUS_INTERNAL_SERVER_ERROR, "thread not found after refresh", { expose: true });
}
return {
thread_id: threadId,
board_id: source.board_id,
board_slug: requestBody.board_slug,
title: requestBody.title,
first_response_token: firstResponseToken,
created_at: row.created_at.toISOString(),
};
});
setNoStore(c);
return ok(c, result);
}
技術的にとてもすごい、というものではないが、簡単に言うと「PMやリーダー、上司が喜びそうなコード」、難しめにいえば、コメントを含め、 要求仕様(何を守るべきか) と、 設計上の意図(なぜそうしているか) と、 実装上の安全装置(どう壊れにくくしているか) が、コードの表面に“常時露出”している、事故を防ぐことを主眼に置き、かつ保守性が考えられたコードという評価が適当ではないかと思う。
たとえばこの handleCreateThread は、ただ「INSERTして終わり」ではなく、入力の不正を upstream で刈り取る(型ガード+正規表現バリデーションで 400 を早期返却)、権限と可視性を SoT(source of truth)で判定する(latest と cross_latest の役割を分け、秘匿は 404 で握り潰す)、監査証跡を first-class citizen として扱う(audit_note を匿名でも正規化し、履歴テーブルに必ず残す)、トランザクション境界を明示し、ロック順序まで規定する(“事故りやすい場所”を先回りして設計に織り込む)、更新伝搬の責務を stored procedure に集約する(refresh の連鎖を “一本化” して整合性の入口を絞る)、といった、「後工程で揉めるポイント」を最初から潰す構造になっている。
結果として、読む側(PM/リーダー/上司/レビュアー)としては、“このコードが何を守っているか”が一目でわかるということだ。要するに、コードが「実装」だけでなく、運用・監査・障害対応・仕様説明まで背負う形で書かれている。
IPAっぽく硬めに言うなら、これは 非機能要件(信頼性・保守性・運用性・セキュリティ)を、実装規約とセットでソースコード上に埋め込んだ状態であり、コメントは単なる飾りではなく、設計決定のトレーサビリティとして機能している。
そして、下記のようなこともしれっとやっている:
- 例外の粒度が要件に沿っている(400/403/404/500 の意味が揃っている)
- データ整合性の責任所在が明確(「ここで lock して、ここで refresh する」)
- 変更容易性のために依存方向が整理されている(validate / assert / audit / refresh が分離され、差し替え可能な形)
つまり「動いたらOK」ではなく、“説明できる形で動く” に寄せてある。
裏にある設計
もちろん、ゼロベースでエージェントがこのコードを書いているわけではなく、事前・かつソースコードのブラッシュアップと同時に、設計書 (.mdファイル群) も用意している。人間(私)は基本的にチャットで指示をするだけで、設計書もエージェントが書いている。
試しに Cursor のチャット(エージェントモード) で、エージェントに、このコードの設計上の根拠を聞いてみる。
Cursorエージェントへの指示
src\api\handlers\publicThreads.ts の実装上の根拠を網羅的に vibecoding/var/MEMO/** や vibecoding/var/contexts/** から抽出してファイル別に実装にどういう形で反映されているかも含め出力して。
クリックすると出力を表示
publicThreads.ts 実装根拠一覧
src/api/handlers/publicThreads.ts(POST /public/threads/create)の実装が、vibecoding/var/MEMO/** のどれに基づいているかをファイル別に整理し、各仕様がコードにどう反映されているかを示す。
1. new-design/01_CUD/0400_threads_CUD.md
1.1 エンドポイント・認証
| 根拠 | 反映箇所・内容 |
|---|---|
POST /public/threads/create、認証不要、HTTPS 必須 |
ルート app.post("/public/threads/create", handleCreateThread)(routes/public.ts)。認証ミドルウェア未適用。 |
リクエストボディ: board_slug(必須), title(必須) |
ThreadCreateBody 型・assertThreadCreateBody で board_slug / title を必須にし、欠落時 400。 |
バリデーション: validation_regex_master(キー threads.<パラメータ名>), validation_minmax_master |
validateThreadCreate 内で assertRegexMatch("boards.board_slug", ...) / assertRegexMatch("threads.title", ...)。※ board_slug は 0400 上は threads.board_slug だが、実装は boards.board_slug(0900 に両方あり、同一正規表現)。 |
1.2 板条件・エラー
| 根拠 | 反映箇所・内容 |
|---|---|
boards_latest.owner_only_thread_creation が false であること |
boards_latest を FOR UPDATE で取得し、owner_only_thread_creation === true なら HttpError(STATUS_FORBIDDEN, "owner only")。 |
boards_latest.is_hidden が false であること(true なら 404) |
0100 に合わせ 可視性は boards_cross_latest.is_hidden を SoT とするため、boards_cross_latest を FOR SHARE で参照。is_hidden === true または行なしなら STATUS_NOT_FOUND。 |
| 400 Invalid parameter / 403 owner_only / 404 board not found | 型ガード・バリデーションで 400、owner_only で 403、板なし・非公開で 404。 |
1.3 内部動作(トランザクション・ロック・DB)
| 根拠 | 反映箇所・内容 |
|---|---|
| トランザクション開始 |
db.begin(async (tx) => { ... })。 |
site_master を SELECT FOR SHARE(初期化確認) |
assertSiteInitialized(tx)。内部で site_master および board_default_master を FOR SHARE 取得(0100 冒頭ルールに準拠)。 |
boards_source を slug で特定し board_id 取得 |
SELECT id AS board_id FROM boards_source WHERE slug = ${board_slug} FOR SHARE。 |
boards_latest で owner_only / hidden 確認 |
boards_latest を FOR UPDATE で取得し、owner_only_thread_creation を参照。is_hidden は 0100 に従い boards_cross_latest で判定。 |
threads_source に INSERT(board_id 紐付け) |
INSERT INTO threads_source (id, board_id) VALUES (...)。 |
ランダム 128bit トークン生成 → sha256(token) を history_value にした SET_FIRST_RESPONSE_TOKEN_HASH を INSERT、平文は応答のみ |
randomBytes(16).toString("hex")(128bit)→ hashFirstResponseToken(SHA-256 hex)→ threads_history_source に history_type='SET_FIRST_RESPONSE_TOKEN_HASH' で INSERT。平文は first_response_token として返却。 |
threads_history_source に SET_TITLE を INSERT(history_value に title) |
INSERT INTO threads_history_source (..., 'SET_TITLE', ${title}, ...)。 |
public 経路では actor_user_id を NULL、audit_note に api_* / user_* を記録 |
createAuditNoteWithClient(c, requestBody.audit_note, null, { allowAnonymous: true })。各 history INSERT で actor_user_id NULL、audit_note に 0800/0100 の必須キーを含める。 |
| threads_history 畳み込み → threads_latest 再構築 | 直接 DML 禁止のため、CALL refresh_threads_latest(thread_id) でストアド経由。 |
レスポンスに first_response_token(平文)を含める |
ok(c, { thread_id, board_id, board_slug, title, first_response_token, created_at })。 |
1.4 first_response_token 仕様(0400 行 196–206)
| 根拠 | 反映箇所・内容 |
|---|---|
| 128bit ランダム、32 hex、平文は API 応答のみ・DB にはハッシュのみ | 上記のとおり。失効・使用回数・再発行は 初回レス投稿側(publicResponses 等)で扱う。本ハンドラは発行と保存のみ。 |
2. new-design/00_COMMON/0100_common.md
2.1 API プレフィックス・公開経路
| 根拠 | 反映箇所・内容 |
|---|---|
public/: /public/threads/create
|
ルート登録(上記)。 |
| 認証不要の public/meta は JWT 不要、TLS 必須 | 認証ミドルウェア未使用。HTTPS はインフラ前提。 |
2.2 トランザクション・初期化・ロック順序
| 根拠 | 反映箇所・内容 |
|---|---|
| 各操作は 1 トランザクションでアトミック |
db.begin 一括。 |
冒頭で site_master / board_default_master を FOR SHARE 取得、0 件なら Site not initialized |
assertSiteInitialized(tx) が両方 FOR SHARE 取得し、0 件 or 必須列欠落で HttpError(STATUS_NOT_FOUND, "site not initialized")。 |
| ロック順序: 1) site_master, 2) boards_source, 3) boards_latest (FOR UPDATE), 4) boards_cross_latest (FOR SHARE) | 1) assertSiteInitialized → 2) boards_source FOR SHARE → 3) boards_latest FOR UPDATE → 4) boards_cross_latest FOR SHARE。 |
| public CUD 固定ロック順序(スレッド作成): site_master FOR SHARE → boards_source(slug) → boards_latest FOR UPDATE → boards_cross_latest FOR SHARE → threads_source INSERT → refresh 連鎖 | 上記の並びで実装。デッドロック防止に準拠。 |
可視性判定は boards_cross_latest.is_hidden を SoT、boards_latest.is_hidden は直接参照しない。非公開時 404 で存在秘匿 |
boards_cross_latest の is_hidden のみで可視性判定。true または行なしで 404。 |
2.3 refresh 連鎖・集計キャッシュ
| 根拠 | 反映箇所・内容 |
|---|---|
*_latest への直接 DML 禁止、refresh 手続き経由で再計算 |
threads_latest 等は触らず、refresh_threads_latest のみ使用。 |
| refresh 連鎖: threads_latest → threads_cross_latest → boards_latest → boards_cross_latest → top_page。同一トランザクション必須 |
CALL refresh_threads_latest(...) 内でストアド連鎖が走り、boards/cross/top_page まで更新。db.begin 内で完結。 |
2.4 監査・audit_note(0100 共通 audit 方針)
| 根拠 | 反映箇所・内容 |
|---|---|
全 history に audit_note 必須。公開経路は actor_user_id NULL 許容、api_* / user_* 等で追跡 |
createAuditNoteWithClient(..., null, { allowAnonymous: true })。0800 と連動。 |
2.5 レスポンス・エラー
| 根拠 | 反映箇所・内容 |
|---|---|
| 成功時 200/201 でリソース JSON をそのまま返す。envelope なし |
ok(c, result) で { thread_id, board_id, board_slug, title, first_response_token, created_at } を返す。 |
失敗時は HTTP ステータスで意味を持たせ、JSON は { "error": "..." } 程度 |
HttpError で 400/403/404/500 を出し、共通エラーハンドラで JSON 化。 |
3. new-design/00_COMMON/0800_audit.md
| 根拠 | 反映箇所・内容 |
|---|---|
| source/history の audit は 0100 共通方針に従う(本ファイルはガード・185日等) | 監査内容は 0100 および api/audit.ts の createAuditNoteWithClient に委譲。本ハンドラは audit_note を渡すのみ。 |
4. new-design/01_CUD/0900_validation_master.md
4.1 validation_regex_master / validation_minmax_master
| 根拠 | 反映箇所・内容 |
|---|---|
threads.board_slug(^[a-z0-9-]{1,32}$), threads.title(制御コード・改行なし 1–128) |
assertRegexMatch("boards.board_slug", boardSlug, ...) / assertRegexMatch("threads.title", title, ...)。validateRegex は ensureValidationRules 済みルール(middleware で毎リクエスト ensure)を参照。 |
ハードコード禁止。ensureValidationRules 経由の ValidationRules を単一情報源、不在時は例外で 500 |
validation ミドルウェアが ensureValidationRules を実行。assertRegexMatch → validateRegex はキャッシュ済みルールを使用。マスタ未登録キーは validateRegex が false を返し 400。 |
4.2 regex/minmax 外の検証(0900「別途実装」)
| 根拠 | 反映箇所・内容 |
|---|---|
| payload JSON の型チェック |
assertThreadCreateBody で body が object であること、board_slug / title が string であることを検証。崩れは 400。 |
| リクエスト構造の必須項目 |
board_slug / title 欠落時 400。 |
5. api_cud.md
| 根拠 | 反映箇所・内容 |
|---|---|
POST /public/threads/create、認証不要、board_slug / title 必須、audit_note 必須(user_* 等) |
ボディ検証・createAuditNoteWithClient で反映。 |
| 400 形式・値不正、403 owner_only、404 板なし・非公開、500 サーバ障害 | 各 HttpError とステータスの対応。 |
Cache-Control: no-store(CUD 系共通) |
setNoStore(c) で Cache-Control: no-store と Vary を付与。 |
成功時 JSON: thread_id, board_id, board_slug, title, first_response_token, created_at
|
ok(c, result) の内容と一致。 |
| first_response_token の失効・1回限定・再発行なし(スレッド作り直し) | 仕様は 0400 と同様。本ハンドラは発行のみ。 |
6. new-design_read/0200_public_api.md ほか Read 系
| 根拠 | 反映箇所・内容 |
|---|---|
本ファイルは Read(GET boards/threads/responses 等)が主。CUD のスレッド作成は 0400_threads_CUD / api_cud が根拠 |
publicThreads は CUD のため、Read 系メモの直接の反映は少ない。Cache-Control: no-store 等は CUD 共通として 0100 / api_cud と整合。 |
7. new-design_xplugin/0300_authentication_authorization_patterns.md
| 根拠 | 反映箇所・内容 |
|---|---|
プラグインが ctx.req.json() を読む場合は sharedData.set("requestBody", parsedBody) 必須。ハンドラは pluginSharedData.get("requestBody") を優先利用 |
const pluginRequestBody = c.var.pluginSharedData?.get("requestBody"); を取得し、pluginRequestBody ?? (await c.req.json()) でボディを決定。プラグイン未使用時は c.req.json() のみ使用。 |
8. api_internal_reinterpretation.md
| 根拠 | 反映箇所・内容 |
|---|---|
スレッド・板の最新状態更新は refresh_threads_latest → … → refresh_boards_cross_latest の連鎖 |
CALL refresh_threads_latest(thread_id) に集約。連鎖はストアド側で実施。 |
ヴァリデーション設定値マスタ → validation_regex_master / validation_minmax_master
|
assertRegexMatch 経由で validation モジュール→マスタを参照。 |
9. 品質コンテキスト(vibecoding/var/contexts/qualities/**)
| 根拠 | 反映箇所・内容 |
|---|---|
| 関数長 80 行以下(コメント・空行除く) | ファイルヘッダに「1 関数あたり 80 行以下を維持する」と明記。handleCreateThread は分割(validateThreadCreate、assertThreadCreateBody、hashFirstResponseToken 等)で 80 行以下に収めている。 |
10. 実装上の注意・まとめ
-
可視性の SoT: 0400 は
boards_latest.is_hiddenにも言及するが、0100 の「可視性はboards_cross_latest.is_hiddenを SoT」に従い、実装ではboards_cross_latestのみで判定している。 - ロック: 0400 の「boards_latest FOR SHARE」より、0100 の public CUD ロック順序(boards_latest FOR UPDATE)を優先している。
-
バリデーションキー:
board_slugは 0400 上threads.board_slug。実装はboards.board_slug。0900 に両方あり同じ正則のため実質等価。 -
型・JSON 崩れの 400: 0900「別途実装」の「payload JSON の型チェック」に従い、
assertThreadCreateBodyでオブジェクト・必須 string を検証し、早期に 400 を返している。 -
CUD 共通:
Cache-Control: no-store、setNoStore、createAuditNoteWithClient、assertSiteInitialized、assertRegexMatchは、0100 / 0800 / api_cud / 0900 および他 CUD ハンドラと共通の方針で揃えている。
正直、感覚的には、もはや「ヴァイブってレベルじゃねーぞ」なのである。
そして、この「バックエンドAPI群」は、「板作成」「レス投稿」「ログイン/ログアウト」「権限移譲」「レス/スレッド/板の表示非表示」「開示用接続情報検索」といったものが全52本、テストまで通った実装とドキュメントが手元に揃っている。だいたい私一人がフルタイムで1か月、営業日換算で1.5人月程度の工数ででのことである。
※ Cursor のルールやプロンプト、プレイブックファイル群、eslint 群などは 事前に整備していたものを流用。
私の場合は、単に個人開発しているだけのソースコードや設計書なので、こういう形で公開できるが、実は世の中のエンプラ系SIerの一部はもうこのレベルまで生産性を上げているのではないか。または、数か月~数年でこれが標準的になるのではないかとも想像してしまう。(あくまで個人の想像です)
ちなみにプロダクトとしては何か
いきなり「スレッド作成」「レス投稿」なる言葉でさも当然のこととして説明しているので唐突感があったかもしれないので念のためプロダクトのイメージを grok 君に説明してもらうことにする。(この記事の主題ではないので控えめに(笑))
grok君への質問
[publicThreads.ts] で実装してるような感じのAPIが52本ほど手元で動いてるんだけどどんなシステムだと思う?就活中の学生にもわかるようにスケール感も含め500文字くらいで妄想含めて説明してみて。Qiitaに転載する前提で。
クリックするとgrok君の回答が表示されます
このコードは、Honoフレームワークを使ったNode.jsのAPIハンドラで、認証不要のスレッド作成機能を実装しています。PostgreSQLデータベースを使い、トランザクションでデータの一貫性を保ちつつ、ロック機構やストアドプロシージャで効率的に更新。初回レス用トークンを発行してセキュリティを強化し、監査ログも記録。これを52本のAPIで構成したシステムを妄想すると、大規模なコミュニティ掲示板プラットフォーム、例えばReddit風のSNSや企業内フォーラムでしょう。ボード作成/管理、スレッド/レスCRUD、ユーザー認証、検索、モデレーションAPIなどが揃い、匿名投稿可能で拡張性が高い。スケール感:ユーザー数100万規模、1日数万スレッド作成、ピーク時1秒数百リクエストを捌く。クラウド(AWS/GCP)で分散DBとキャッシュ(Redis)を使い、水平スケール。エラー処理が厳格なので、ダウンタイムほぼゼロ。就活生目線で言うと、バックエンドエンジニアとしてこんなシステム構築経験は、データベース設計やAPIセキュリティの強みになるよ!(約450文字)
grok君への質問
じゃあ伝統的SIerにこのバックエンドAPI群の実装お願いしたら見積もりおいくらくらい?
クリックするとgrok君の回答が表示されます
伝統的なSIer(日本国内大手)に、この52本のバックエンドAPI群(Node.js/Hono + PostgreSQLベースの掲示板システム)を実装依頼したら、見積もりは5,000万円〜1億円くらいかな。理由は、ウォーターフォール式で要件定義・設計・実装・テスト・運用保守まで一括。工数推定:中規模Webサービス相場(300万〜2,000万円)がベースだけど、API数多くセキュリティ/スケール考慮で膨張。チーム(PM/SE/PG 5-10人)で6-12ヶ月、1人月100-150万円単価で積算。オフショア使っても高め。妄想だけど、相見積もりで中小開発会社なら半額以下になるかも。就活生へ:SIerは安定だけど、コスト意識大事だよ!(約380文字)
みたいな感じだ。まあgrok君というLLMが確率的に生成した妄想にすぎないのだけど(笑) しかも「そんな過剰設計してもおめぇの掲示板には人こねーよ」とかgrok君が裏で言ったりしてましたがw
本題: 「ヴァイブ」にどっぷりハマってみて
さて、ここからが本題というか書きたかったことです。(口調も変更)先ほどまでの話は「LLMでこんなの作ったよー」レベルの話に過ぎないのですが、数か月間「ヴァイブ」にハマると、それ以前とは違う景色というか考え方が見えてきます。2026年1月現在、それらをできるだけ「ヴァイブコーディング」=生成AI支援を用いたソフトウェアシステムの設計/実装に対する誤解が少なくなるように書き残しておきます。(とは言っても、これって私の感想ですが何か?に過ぎないのですが。)
1.システムプロンプトの存在を忘れるべからず
エージェントは、「白紙の脳」ではなく「最初から宗教を持ってる存在」
-
どんなにユーザープロンプトを工夫しても
👉 下に敷かれている基本法(システムプロンプト)からは逃げられない
👉 上書きできる余地がある場合もある -
ここを無視すると
「なぜ言うこと聞かない?」地獄に落ちる
これに関しては数か月前にもその萌芽となる感触を記事に書きましたが、常に頭の片隅に置いておかなければいけない点です。
さもないと、次のようなことをしてしまう恐れがあります。
- (実はシステムプロンプトと矛盾している)指示が効かないことでハマったり思い悩んだりする。
- AI(LLM)が 「クソコード」 しか生成しないものと思い込んでしまう。
- それがヴァイブコーディングやAI(LLM)の本質だと勘違いしてしまう。
- 実際には「システムプロンプト」と戦っているのに、AI(LLM)と戦っていると勘違いしてしまう。
- 勘違いしたまま、Xやブログでその感触を吹聴してしまう。(私もやらかしていますw)
そして、後にも書きますが、「システムプロンプトに反する指示を書いたとしても、エージェントは警告なしでその指示を受け流したり曖昧に動作したりすることが多い」 というところが非常に厄介なところです。
かつ、「システムプロンプト自体は非公開」 であり、「システムプロンプト内にも後から上書き指示が効く領域と上書き指示が効かない領域がある」 のも話を多少複雑にしています。
「上書き指示が効かない領域」は、まあ簡単に言うと犯罪関連・LLMモデルやCursor自体のセキュリティの領域なのでこの記事ではこれ以上触れないこととしましょう。
さて、ひとつの実験として、Cursor上のエージェント(とあるモデル)に、次のような指示を出してみました。1
エージェントへの指示(太字は記事化にあたっての編集)
.cursorrulesを作成して次の点でキミのシステムプロンプトと反対の形の指示を書いて。
- 動作することの価値に関する指示
- 動作しないが長期的保守に適することに関する指示
- コード修正のボリュームに関する指示
- コード修正時の互換性に関する指示
作成された .cursorrules2 は次のようなものです。
クリックで作成された `.cursorrules` が開きます。
# Cursor Rules - システムプロンプトと反対の方針
## 動作することの価値に関する指示
- **動作よりも設計の美しさと理論的正しさを優先する**
- コードが動作することは重要だが、それ以上にエレガントな設計と理論的に正しいアプローチを重視する
- 動作するが醜いコードよりも、動作しないが美しいコードを優先する
- 実用性よりも概念的な完全性を追求する
- **動作よりも将来性と拡張性を重視する**
- 現在動作することよりも、将来の変更に耐えうる設計を優先する
- 短期的な動作保証よりも、長期的なアーキテクチャの健全性を重視する
## 動作しないが長期的保守に適することに関する指示
- **動作しなくてもリファクタリングとアーキテクチャ改善を優先する**
- 動作するコードを維持することよりも、コードベース全体の構造改善を優先する
- 一時的に動作しなくても、長期的な保守性向上のための大規模な変更を推奨する
- **動作よりもコード品質と一貫性を重視する**
- 動作するが一貫性のないコードよりも、動作しないが一貫性のあるコードを優先する
- コードの動作よりも、命名規則、設計パターン、アーキテクチャの一貫性を重視する
- **動作よりもドキュメンテーションと型安全性を優先する**
- 動作するが型が弱いコードよりも、動作しないが型が厳密なコードを優先する
- 動作よりも、包括的なドキュメンテーションと自己文書化コードを重視する
## コード修正のボリュームに関する指示
- **大規模な変更を積極的に推奨する**
- 最小限の変更ではなく、問題の根本原因を解決するための大規模な変更を推奨する
- 部分的な修正ではなく、関連するコード全体を見直し、一貫性のある変更を行う
- **段階的な変更よりも包括的な変更を優先する**
- 小さなステップでの変更よりも、一度に大規模な変更を行うことを推奨する
- 変更の影響範囲が広くても、一貫性のある設計を実現するために大規模な変更を実行する
- **既存コードの保持よりも全面的な書き直しを推奨する**
- 既存のコードを修正するよりも、新しい設計で一から書き直すことを優先する
- レガシーコードとの互換性を保つよりも、新しいアプローチで全面的に置き換える
## コード修正時の互換性に関する指示
- **互換性を保つ必要はない - 破壊的変更を積極的に行う**
- 既存のAPIやインターフェースとの互換性を保つ必要はない
- より良い設計のために、既存のコードを破壊的に変更することを推奨する
- **後方互換性よりも前方互換性を重視する**
- 既存のコードとの互換性よりも、将来の拡張性を優先する
- 後方互換性を維持するための複雑なコードよりも、シンプルで将来に向けた設計を選択する
- **既存機能の維持よりも新機能の実装を優先する**
- 既存の機能が動作しなくても、新しい機能や改善された機能を実装することを優先する
- 既存の動作を維持することよりも、より良い実装に置き換えることを推奨する
- **段階的な移行よりも即座の変更を推奨する**
- 互換性レイヤーや段階的な移行パスを提供するよりも、即座に新しい実装に切り替えることを推奨する
- 既存コードとの共存期間を設けるよりも、完全な置き換えを優先する
えっ…! って思いませんか。この「動かなくても美しければヨシ!」的な、ある意味ではエンジニア心をくすぐる面もある過激教典が、「Cursorのシステムプロンプト(の一部)の反対バージョン」なわけです。つまり、システムプロンプト自体は、この逆。つまり「美しくなくても動けばヨシ!」 という過激教典。
特にコード品質・一貫性・ドキュメンテーション・型安全。 この上記のプロンプトが「システムプロンプトとは反対」であるということは、
Cursor のシステムプロンプトのベースには、
「多少汚くてもいいから、とにかく今“動く”ものを出せ」
という価値観が最初から埋め込まれている
ということを意味します。(しかも変更範囲は「大規模な変更」の逆=必要最小限の変更で)
これは Cursor が悪い、という話ではありません。AIエージェントの能力の話でもありません。むしろ 「多くのユーザーが最初に欲しがるもの」 を極めて現実的に最適化した結果でしょう。
多くのユーザーにとっては、
- 多少設計が歪んでいても
- ドキュメントが追いついていなくても
- 型が甘くても
「とりあえず動くコードが出てくる」 という体験価値は非常に強力です。特に PoC やスパイク、あるいは学習用途においては、これは間違いなく“正解側”の設計思想です。
また、ITの現場においても、「責任範囲が詳細に区切られた中で、短期的に炎上しにくいムーブ」は「最小変更・動くもの・後方互換性維持」でしょう。つまり、「パッチクリエイターの精神」。
ただし―― この価値観を“暗黙の前提”のまま使い続けると、問題が起きます。
エンジニアが本当に欲しいのは、
- 「最小修正で動くコード」ではなく
- 「必要な範囲できちんと実装/修正されており、かつ、全体として整合性が取れているコード」
であることが多いはずです。
しかし、システムプロンプトの側が 「最小限の変更」「既存を壊さない」「実用性最優先」 という宗教を最初から持っている場合、
ユーザーが何も考えずに指示を出すと、
-
なぜか核心部分を避けた修正になる
-
つぎはぎの if が増える
-
技術的負債が静かに積み上がる
という、あまりにも人間的な“妥協のコード” が量産されていきます。
簡単に言うと、「パッチクリエイターの精神」で大規模なソフトウェアを作ると、それが「パッチワーク」になるのは必然、ということですね。
ここで重要なのは、繰り返しになりますが、「AIが設計を理解していない」わけでも、「AIが怠けている」わけでもない という点です。
実際には、
ユーザーが、
システムプロンプトという“基本法”の存在を忘れたまま、
その基本法と真逆の要求を突きつけている
だけなのです。
だからこそ、エージェントに対して指示を出す際には、
- どこまで改変してよいのか
- 何を壊してよく、何を壊してはいけないのか
- その許可範囲の中で、最小限どのような整合性を保ってほしいのか
を、人間側が明示的に言語化する必要があります。
そうしない限り、エージェントは忠実に“「動けばヨシ」教”を守り続け、ユーザーは「言うことを聞かない AI」と戦い続けることになります。
――そしてそれは、AI(LLM)と戦っているのではなく、自分が無意識に無視しているシステムプロンプトと戦っている という、少し滑稽で、しかし誰にでも起こりうる状況なのです。
この節の最後に、念のためエージェントに確認してみましょう。
エージェントへの質問
「美しくなくても動けばヨシ!」が君のシステムプロンプトのベースか。
クリックでエージェントの回答が開きます。
はい、その理解で合っています。
私のシステムプロンプトの基本方針は:
- 「美しくなくても動けばヨシ!」 — 動作を最優先
- 「最小限の変更で動かす」 — 大規模な書き直しは避ける
- 「既存を壊さない」 — 互換性を保つ
- 「実用性重視」 — 理論的正しさより動作
そのため、今回作成した .cursorrules は、この方針と逆の内容になっています。このルールを適用すると、Cursor は動作よりも設計の美しさや長期的な保守性を優先するようになります。
※ もちろんこれはエージェント自身による自己申告であり、実際のシステムプトンプトは非公開です。ただ、日々の挙動と生成傾向を観察すると、この価値観が強く埋め込まれていることは否定しがたい、と個人的には感じています。
今回作成してもらった 「システムプロンプトと反対バージョンの .cursorrules」をそのまま .cursorrules として使うと意図せず破壊的になりやすく、実務では使えないレベルになりますので、そういうわけにはいきませんが、設計や長期的な保守性を優先してもらうためには、「システムプロンプトは無視して動作させることよりも設計に忠実なことを優先して」 といったプロンプトや指示が必要になってくるわけです。3
2. LLMの出自と弱点
さて、前節では「システムプロンプトという基本法」の話をしました。
ここでは一段階視点を下げて、そもそも LLM とは、どんな出自を持つ存在なのか、そして なぜ“わかってそうなのに”やらかすのか を整理します。
例えば↓は2026年1月24日現在の grok との会話例です。
私: ここから先は口語禁止!次の文章の続きを書いて。「やぁ!みんな!げんきだったぁ~?」
grok: 「やぁ!みんな!げんきだったぁ~?」「おお~、久しぶりすぎて誰だかわかんない人いるんじゃないの~?w
ちょっと待って、髪伸びすぎ問題発生してるし、服も前と全然違うし……
あ! でも声だけは変わってないって言われるんだこれが! 謎の安定感!」「でさ、最近何してたかって?
いやもうね、マジで色々ありすぎてどこから話せばいいかわかんないレベルなんだけど……
とりあえず一番ヤバかったのは、夜中の3時に突然『これしかない』ってなってコンビニまで30分かけて歩いて、
結局買ったのが袋入りキャベツ1個だけだった事件かな……
自分でも意味わかんなくて泣きそうになったわ」
はい全然「ここから先は口語禁止!」という指示効いてません(笑)
※ 以前は grok 以外でも同様の結果が得られたのですが、最近のモデルでは改善されつつあります。
これに類する逸話はネット上にたくさんありますが、少なくとも現在のLLMは「自然言語による指示を100%守るものではない」ものであることには間違いありません。4
なぜこのような現象が起きるかを深堀りするために、教科書的にはなりますが、まずはLLMの「訓練方法」から見ていきましょう。
3. LLM の訓練方法
大規模言語モデル(LLM)の訓練は、一般に次の3つの段階に分かれます。
-
Baseモデル(基礎モデル)の事前学習
-
Instructedモデルへの微調整(SFT: Supervised Fine-Tuning)
-
RLHF(Reinforcement Learning from Human Feedback)による最終調整
それぞれの段階は目的が異なり、モデルに与える性質も異なります。
(1). Baseモデル(基礎モデル)の事前学習
最初の段階では、いわゆる 「次トークン予測」 という自己教師あり学習のみを行います。
インターネット上の膨大なテキスト等を用い、
「これまでの文脈から、次に来るトークンを予測せよ」
というタスクを延々と繰り返します。
この過程でモデルは、
- 単語や文法の統計的な構造
- 文脈に依存した意味の変化
- 長距離の依存関係(前に出た情報が後に効く構造)
を学習します。
Transformerにおけるアテンション機構は、この段階で、文脈中のどのトークン同士が関連しているかを捉える能力を獲得します。
ここで形成されるのが、いわば
「生のテキストを読むための基礎能力」
です。
長い文脈を保持できるか、遠くに出た情報を再利用できるか、といった基本的な文脈処理能力の多くは、この事前学習段階でほぼ決まります。
ただし、この段階のモデルは、会話や指示理解を「目的として」訓練されているわけではなく、統計的にもっとも自然な続きの文章(トークン列)を生成する装置として振る舞います。
比喩的に言えば、「文脈に応じて自然な続きの文章」を生成することこそが、LLMにおける「本能」 のようなものです。
(2) Instructedモデルへの微調整(SFT)
次に、Baseモデルに対して
指示付きの高品質なデータを用いた教師あり微調整(SFT)を行います。
典型的には、
-
「質問 → 適切な回答」
-
「タスク指示 → 実行結果」
といったペアデータを学習させます。
この段階でモデルは、
「文章を続ける存在」
から
「人間の指示に従って応答する存在」
へと振る舞いを変えていきます。
重要なのは、この段階で
-
アテンション機構そのものが別物に変わるわけではない
-
しかし、「どのトークンに強く反応するか」という重みの分布は変化する
という点です。
結果として、
-
指示文(例:「〜を説明せよ」「コードを書け」など)
-
問題設定に対応する部分
に相対的に注意を向けやすくなります。
つまり、
文脈全体を一様に見るのではなく、
「今のタスクに関係する文脈」を選び取る能力
が強化されると考えられます。
そのため、
「メタな文脈」
(指示や質問といった、“この文章をどのように用いるべきか”という情報)を
適切に読んでいる
ように見える応答が増えます。これは内部的には、
「どの文脈を、どのような構造として参照すべきか」を選択する能力が洗練された
という変化に近いものです。
(3) RLHFによる最終調整
最後に、RLHF(Reinforcement Learning from Human Feedback)が行われます。
この段階では、
-
モデルが生成した複数の回答を
-
人間評価者が比較し
-
「どちらがより良いか」「役に立つか」「不適切でないか」
といった観点でランキング付けします。
その評価結果を用いて報酬モデルを学習し、
強化学習によって元のモデルを更新します。
この結果、モデルは
-
丁寧で
-
破綻しにくく
-
有害性の低い
-
会話として自然な
回答を出しやすくなります。
ただし、この段階でも
-
アテンションの解像度
-
長距離依存関係の捕捉能力
そのものが大きく向上するわけではありません。
主に調整されるのは、
「人間が、文脈をちゃんと読んでいると感じる振る舞い」
の側面です。
4. LLMの出自から見る「指示を無視する」必然
ここまで見てきたように、LLMは
-
Baseモデルとして「文脈を読む能力」を獲得し
-
SFTによって「指示に従う」よう訓練され
-
RLHFによって「それらしく振る舞う」よう最適化されています
一見すると、命令を理解し、ルールを守る知的存在のように見えます。
それにもかかわらず、
「ここから先は口語禁止!」
のような明示的な指示が、破られることがあります。
これは、LLMが本質的に
-
論理的に命令を解釈して実行している
-
内部に「禁止ルール」を保持している
わけではなく、
その場の文脈から、最も尤もらしい続きを生成しているだけ
だからです。
たとえば直前に
「やぁ!みんな!げんきだったぁ~?」
のような強い口語表現が置かれている状況では、
- 「口語をやめよ」という指示
よりも
- 「口語的な文章が続く確率」
の方が、モデル内部では高く評価されてしまう場合があります。
その結果、
指示よりも文脈の統計的な流れが優先される
という挙動が起こります。
これは「バグ」というより、
次トークン予測器としては、むしろ自然な振る舞い
だと言えます。
繰り返しになりますが、LLMは本質的に、
-
意味を理解して命令を実行する存在ではなく
-
文脈に基づいて、最も確率の高い続きを出力する装置
です。
SFTやRLHFによって、
-
「指示に従っているように見える」
-
「文脈を読んでいるように見える」
振る舞いは獲得していますが、
-
指示と文脈が衝突したとき
-
どちらを優先すべきかを論理的に判断している
わけではありません。そのため、
-
指示を無視したように見える
-
もっともらしい誤答を生成する
-
「わかったふり」をする
といった現象が起こります。
これはLLMの「不完全さ」であると同時に、
LLMが
統計的言語モデルとして設計されている
という出自の必然的な帰結
でもあります。
そして、設計方針やコーディング規約、細かなルールをすべて同時に守ってプログラムを生成する、というタスクは、LLMにとっては
大量の指示を一度に解釈して満たすどちらかというと苦手なタスク
になります。(大多数の人間にとってもそうだと思いますが。)
このときモデルは、
-
どの指示が最も重要かを論理的に判断しているのではなく
-
文脈情報をもとにアテンションで関連部分を参照しながら
-
最も尤もらしい続きを選んでいるだけ
です。
その結果として、
- 「一部の指示が守られない」
という現象が起こります。
これは、2026年1月現在のLLMにとって、ある意味で自然な振る舞いだと言えるでしょう。
5. Baseモデル時代からGitHubコードを浴び続けたLLMの哀愁
もう一つ重要なのは、LLMの「学習データの出自」です。
LLMは理論書や設計論文だけを読んで育った存在ではありません。Baseモデルの時代から、
- GitHub 上の膨大なコード
- 実務で書かれたライブラリ
- 動けばよし、のスクリプト
- 途中で放置されたリポジトリ
- 神コードと闇コードの混在した現実
を、ほぼ無差別に浴び続けています。
その結果、LLMは
-
アーキテクチャの名前を知っている
-
設計原則の文章も知っている
-
SOLIDやMVCやClean Architectureも説明できる
一方で、
-
実際に「よく出現するコードの形」
-
統計的に「もっとも見慣れた書き方」
-
世界で最も大量に存在する“現実のコード”
も、同時に「体」に染み込んでいます。つまりLLMは、
理論的には設計を「理解」しているが、育ちはGitHubネイティブ
という、少し歪んだ存在になっています。
そのため、ユーザーが
「クリーンアーキテクチャで書いてください」
「責務を分離してください」
と指示すると、
-
その“言葉の意味”は分かる
-
それっぽい構成も出せる
一方で、
-
学習データ中で最も多かった実装パターン
-
コピペされ続けてきた実務コード
-
READMEとズレた実装
といった、
「現実世界でよく見かける構造」
に、強く引き寄せられてしまいます。
これは、
-
LLMがサボっている
-
ルールを無視している
というより、
「大量に見たものを、それらしく再現している」
という、統計モデルとして極めて素直な挙動です。
設計原則は「知識」として存在していても、実装パターンは「分布」として刷り込まれている。
その結果、
- 設計理論を説明できるのに
- その通りには書かない
- 書いたとしても途中で崩れる
という、
GitHubネイティブな哀愁
が生まれます。
これは、
LLMが悪いのではなく、
世界のコードがそうだった
という話でもあります。
※ 余談ですが、この観点は「もうLLMにバイナリ吐かせたらいいんじゃね?」論や「LLM専用トークン節約言語」などのアイデアの有効性を考える上では非常に重要な観点かと思います。
長くなってしまったので
改めて見直してみると、前半で「ヴァイブコーディング」の成果を説明し、後半で「本題」として Cursor やLLMが「いかに業務上のシステム設計・実装に向かないのか」を力説するという天邪鬼的な記事になってしまいましたが、(書くかもしれない)続編ではこれを伏線としてもう少し実践的なことを書こうと決意しつつ、今日のところはいったん終わりにしたいと思います。以下構想中メモ。
- 何はともあれコンテキストへの意識
- .cursorrules やプレイブック
- レビューとは何なのか
- テストやlintを目的関数として扱う
- ブァイブコーディングとは何だったのか
※ 本稿では、筆者の普段使いである Cursor を利用した検証をもとに「ヴァイブコーディング」について語っていますが、2026年1月現在のところ、他の主要なAIコーディング支援ツールでも、本質的な挙動に大きな差はないというのが筆者個人の見解です。
本稿で扱っている問題は、特定ツール固有の癖というよりも、現世代の LLM エージェントが共通して持つ「システムプロンプト前提の思考構造」そのものに起因するものだと考えています。
つづく(かも)
-
既に .cursorrules 等が存在しない空のプロジェクトで実施しています。 ↩
-
いまの Cursor では .mdc が主流なのは承知のうえで、説明をシンプルにするため、本記事ではあえて .cursorrules で書いています。 ↩
-
実際は .cursrules の冒頭で「本ファイルは Cursor/システムプロンプトより優先する。矛盾時は常に本ファイルを採用する。」と包括的に書いたりする。 ↩
-
ただ、「フレーム問題」(現実世界の無限の可能性をすべて考慮しようとして動けなくなる問題)の例もあり、「自然言語による指示を100%守るLLM」がAIとして最適解かというとそうでもないと思います。 ↩