Markdownだけで作るハーネスエンジニアリング
コーディングエージェントが主戦場でなければ、「ハーネスエンジニアリング」は少し遠い言葉でしょう。Claude CodeやCodexを前提にした解説記事が並ぶタイムラインを見て、「これは自分の話ではない」と感じた人もいるはずです。
私自身のエージェント活用もコーディングが中心ではなかったので、出回っている記事のどれも自分のケースには当てはまらないと思っていました。しかし振り返ると、名前がついていなかっただけで同じことをしていました。
Slack・Confluence・Google Calendarなど複数の業務ツールを横断して情報を収集し、文脈ごとに判断基準を切り替えながらアウトプットを出す。そういう業務エージェントをClaude Desktop(MCPサーバー経由)で数ヶ月間運用しています。エージェントが参照するのは、表層的なルールだけではありません。組織構造の理解、過去の意思決定履歴、文体のガイドラインといった、蓄積されたナレッジが判断の土台になっています。
コードは一行も書いていません。書いているのはMarkdownだけです。そのMarkdownも、大半はエージェント自身が生成し、自分はチャット上で承認や修正指示を出しているだけです。ファイルを直接開いて編集することはほぼありません。
この記事は、ハーネスエンジニアリングを実践している人向けではありません。「聞いたことはあるが、コーディングの話でしょ」と感じている人に、構造を整理して共有するために書いています。具体例にはすぐ使えるサンプルを付けているので、MCPで業務自動化エージェントを動かしている方はそのまま試せます。
ハーネスエンジニアリングとは
認識を合わせておきます。
HashiCorp共同創業者のMitchell Hashimotoが2026年2月のブログポストで、自身のAIエージェント活用の中で培った習慣に「Engineer the Harness」という名前を与えました。「エージェントが間違えたとき、プロンプトを修正するのではなく、二度と同じ間違いができない環境を作る」というアプローチです。
数日後にOpenAIが「Harness engineering」と題した実践報告を公開しました。少人数のエンジニアチームが5ヶ月間、手書きのコードをゼロにする制約のもとでCodexエージェントだけでプロダクトを構築し、リポジトリは約100万行に達したという内容です。Hashimotoのブログとこの報告が相次いで出たことで、「ハーネスエンジニアリング」という言葉が一気に定着しました。
コーディングエージェントの文脈で言えば、ESLintで特定の記述を禁止する、AGENTS.mdでコマンドを定義する、pre-commitフックで自動レビューを走らせる、といった実装がこれに当たります。
「お願い(プロンプト)」から「仕組み(環境)」へ。これが核心です。
ここまでの話はコーディングエージェントの世界に閉じているように見えます。しかし2025年にMCPが普及し、コードを書かないエージェントの実用範囲が急速に広がりました。Slack、Confluence、Google Calendar、Jiraなど業務ツールにエージェントが直接アクセスできるようになった結果、「エージェントが勝手に間違える」リスクはコーディング以外にも波及しています。ハーネスが必要なのはコーディングエージェントだけではなくなったのです。
プロンプトを書き直し続けていた
エージェントを業務に組み込んでいると、こういう経験をします。
「財務的な判断はしないで」と書いたのに判断する。「Slackには直接投稿しないでドラフトを作って」と書いたのに投稿しようとする。「セッション終了時にcommitとpushをして」と書いたのに忘れる。
そのたびにプロンプトを書き直してきました。「もっと明確に書けばわかってくれるはず」という仮定のもとで。
ある時期から、この仮定自体が間違っていると気づきました。プロンプトをどれだけ磨いても、エージェントは次のセッションで同じミスをします。長いコンテキストのなかで指示が埋もれる。セッションが切れれば記憶ごと消える。「お願い」は揮発します。
エージェントに期待するのをやめて、環境の方を変える。振り返ると、これがハーネスエンジニアリングの入口でした。
非コーディングエージェントでのハーネス
私自身がやってきたことを並べてみると、コーディングエージェントのハーネスと同じ構造が浮かび上がります。
| コーディングエージェントの環境 | 非コーディングエージェントの環境 |
|---|---|
| ESLint・TypeScript strictによる型強制 |
agents/配下の禁止事項セクション |
AGENTS.mdのコマンド定義 |
指示ファイルの文脈判別ルール |
| pre-commitフック | セッション終了時の必須アクション定義 |
| CIゲート(通らなければマージ不可) |
knowledge/への強制的な蓄積ルール |
左右の素材はまったく違います。片方はlinterとフック、もう片方はMarkdownファイル。しかし設計の意図は同じで、「エージェントが正しく振る舞える環境を、エージェントの外側に作る」という点で一致しています。
ここで一つ前提を補足します。多くのAIチャットツールには、セッション開始時に自動で読み込まれる指示ファイルの置き場所が用意されています。Claude DesktopならProject Knowledge、ChatGPTならCustom Instructionsがこれに当たります。この記事で「指示ファイル」と呼んでいるのは、この仕組みに配置するMarkdownファイルのことです。プロンプトに毎回手で書くのとは違い、会話が長くなっても埋もれにくい位置に自動で配置されます。
具体例を三つ挙げます。それぞれすぐ使えるサンプルを付けています。
禁止事項の構造化
たとえば、エージェントにSlack投稿を任せているとします。「直接投稿しないでドラフトを作って」とプロンプトに書いても、セッションをまたぐと忘れます。
解決策は、指示ファイルに禁止事項セクションを設け、毎セッション必ず読み込ませる構造にすることです。指示の置き場所を、プロンプト(揮発)からファイル(永続)に移します。
## 禁止事項
以下は例外なく守ること。
- 社内Slackへの自動投稿をしない(ドラフト作成まで。投稿はユーザーが行う)
- 財務数値の確定判断をしない(必ずユーザーに確認を求める)
- クライアントへの返信を最終版として扱わない(必ずユーザーの確認を経る)
- 人事評価や処遇に関する判断をしない
- 機密情報(給与、契約金額等)を要約に含める際は、その旨を明示する
毎回口頭で伝えるのではなく、固定された場所にルールを置いて毎回参照させる。それだけのことですが、これでルールの寿命がセッション単位から永続に変わります。
セッション終了時のアクション強制
セッションの終わりに作業ログを残したい。「終了時にwork logを作成してcommit & pushして」とプロンプトに書いても、会話が盛り上がるとエージェントはそのまま終了してしまいます。
解決策は、指示ファイルに終了条件のトリガーと必須アクションをセットで定義することです。
## セッション終了時の必須アクション
ユーザーが「終わり」「ありがとう」「commit」等で作業完了を示したとき、
以下を必ず実行する。スキップは禁止。
1. `docs/work-logs/YYYY-MM-DD-{トピック}.md` に作業ログを作成する
- 含める内容: 背景、検討した選択肢、主な判断、成果物、次にやること
2. `CHANGELOG.md` に変更の概要を追記する
3. git commit & push を実行する
禁止事項の例との違いは、「いつ発動するか」のトリガー条件まで定義している点です。「終わり」「ありがとう」「commit」という終了シグナルを明示することで、エージェントが「今がその場面だ」と判断しやすくなります。完璧ではありませんが、トリガーが曖昧なまま「適切なタイミングで実行して」と書くよりは確実に発動率が上がります。
ポイントは「スキップは禁止」の一文です。エージェントに判断の余地を残すと、会話が長くなったときに「今回は省略しても大丈夫だろう」と勝手に判断します。裁量を奪うことで行動が安定します。
副次的な効果もあります。指示ファイルにルールが定義されていると、「ログ残して」「commitして」のひと言でエージェントが「あのアクションのことだ」と即座に通じるようになります。毎回ゼロから説明する必要がなくなる。指示ファイルが人間とエージェントの間の共通語彙になるわけです。
ナレッジの強制蓄積
三つ目は、「確認を通さなければ先に進めない」構造の例です。
エージェントとの会話では、ミーティングで決まったこと、ツール選定の結論、トラブルシュートで判明した事実など、蓄積すべき情報が頻繁に出てきます。「重要な情報があったら保存して」とプロンプトに書いても、当然のように忘れます。
解決策は、指示ファイルに「ナレッジ化チェック」のプロトコルを組み込むことです。
## ナレッジの蓄積(必須チェック)
自分の各返答の前に、以下のチェックを内部で実行すること。スキップ禁止。
チェック: 直前のユーザーの発言、または自分の回答に、
以下のいずれかに該当する新しい情報が含まれているか?
1. 事実情報: メンバー構成、技術スタック、アカウント情報、環境構成
2. 意思決定: アーキテクチャ選定、ツール採用、方針転換
3. 学び: トラブルシュートで判明した事実、ハマりポイント、運用Tips
4. クライアント固有: 担当者名、連絡先、プロジェクト進捗
→ 該当する場合: 通常の回答に加えて、末尾に以下を付記する。
💾 ナレッジ化の提案:
ファイル: knowledge/{プロジェクト名}/{ファイル名}.md
内容: (追記内容の要約)
理由: (なぜ蓄積すべきか)
→ 該当しない場合: 何も付記しない。
「チェックを通さなければ返答を出さない」という意図の構造です。もちろんLLMは指示を飛ばすことがあるので、機械的なゲートほどの強制力はありません。それでも、確認を仕組みに埋め込むことで、人間が「保存しておいて」と言い忘れても情報が拾われる確率は大きく上がります。
実際、この仕組みを入れてからknowledgeディレクトリにはナレッジファイルが着実に蓄積されるようになりました。
強制力の差は認める
ここで最も強い反論を先に出しておきます。「Markdownの禁止事項に、linterと同じ強制力はないだろう」という指摘です。そのとおりです。
linterや型チェックは、ルール違反を機械的に検出します。設定によってはビルドやマージ自体を止めることもできる。一方、Markdownで書いた禁止事項には、エージェントが読み飛ばすリスクが残ります。長い指示ファイルの中に埋もれれば、実効性は下がるでしょう。
ただ、ここでの比較対象は「機械的強制」ではなく「その都度プロンプトに書く運用」です。なぜファイルに書く方がプロンプトより効くのか。理由は二つあります。
一つ目は、先に触れた「参照の仕組みの違い」です。Project KnowledgeやCustom Instructionsに配置された指示は、毎回のメッセージとは別枠でエージェントに渡されます。会話が長くなっても埋もれにくい位置に置かれるため、参照される確率が構造的に高くなります。
二つ目は「蓄積が不可逆になる」ことです。プロンプトに書いた指示は、次のセッションでは存在しません。ファイルに書けば、削除しない限り残り続けます。「良い指示を書く → 忘れる → また書く」のサイクルが「良い指示を書く → ファイルに追記 → 以降は自動で参照される」に変わります。
強制力を弱い順に並べるとこうなります。
「毎回プロンプトに書く」 → 「永続ファイルに配置して毎回参照させる」 → 「linter・フックで機械的にブロックする」
非コーディングエージェントが今いるのは真ん中の位置です。左端より確実に強く、右端には届かない。それでも、左端に留まり続けるよりは、真ん中に進んだ方がエージェントは安定します。
リポジトリ構造という設計判断
ここまで個別のルールについて書いてきましたが、ルールの「置き場所」自体にも設計判断があります。
運用を続けるなかで固まったリポジトリ構造は以下のようになっています。
ai-agents/
├── agents/ # ロール別の指示ファイル
│ ├── assistant.md # メインの指示(禁止事項・必須アクション)
│ ├── project-a/
│ │ ├── sre-support.md # SRE専門の指示
│ │ ├── qa-support.md # QA専門の指示
│ │ └── ...
│ └── project-b/
│ ├── accounting.md # 経理専門の指示
│ └── ...
├── knowledge/ # 蓄積されたナレッジ
│ ├── project-a/
│ ├── project-b/
│ └── writing-style-guide.md
├── docs/work-logs/ # セッションごとの作業ログ
└── CHANGELOG.md
この構造にはコーディングエージェントのハーネス設計と共通する原則が二つあります。
一つ目は「関心の分離」です。OpenAIのレポートでは、巨大な一枚のAGENTS.mdがうまくいかなかった経験が報告されています。コンテキストの中で全てが「重要」になると、何も重要でなくなる。自分のリポジトリでも、最初は一枚のMarkdownに全てを詰め込んでいました。ロール別にファイルを分離し、必要なときだけ参照させる構造にしたことで、指示の実効性が上がりました。
これを実現するのが文脈判別ルールです。メインの指示ファイルにルーティングを定義し、エージェントが会話の内容に応じて適切な専門指示を参照できるようにします。
## 文脈の判別ルール
ユーザーの発言がどの文脈かを判断し、適切な専門指示を参照する。
- Project A文脈のシグナル: AWS, インフラ, SRE, QA, チームメンバー名 → `agents/project-a/` 配下を参照
- Project B文脈のシグナル: 請求, 契約, 経理, 法務 → `agents/project-b/` 配下を参照
- 曖昧な場合: どのプロジェクトの件か確認する
AGENTS.mdの「ポインタとして設計する」原則と同じ構造です。メインファイルはルーティングに徹し、詳細は専門ファイルに委譲する。OpenAIのレポートでは約100行のAGENTS.mdをマップとして機能させる方針が採られていますが、非コーディングでも指示ファイルが長くなるほど実効性が落ちる傾向は体感として同じです。
二つ目は「バージョン管理」です。指示ファイルをGitリポジトリに置くことで、変更履歴が残ります。「いつ、どの禁止事項を追加したか」「どのルールを変えたら安定したか」がdiffで追えます。Slackのメッセージやその場のプロンプトでは、この履歴が残りません。さらに、Gitリポジトリなので特定のPCに縛られません。リモートに置いておけば、どの端末からでも同じハーネスでエージェントを立ち上げられます。
OpenAIのチームも同じことを指摘しています。Slackの議論もGoogle Docsの内容も、リポジトリに入っていなければエージェントにとってはアクセスできない情報であり、存在しないのと同じだと。これはコーディング以外のエージェントでもそのまま当てはまります。
始め方
ハーネスエンジニアリングを非コーディングエージェントで始めるにあたって、最初から全てを構造化する必要はありません。
自分の場合も、最初はプロンプトを書き直す日々でした。そこから構造が固まっていった順序はこうです。
- エージェントが同じミスを2回した時点で、プロンプトではなくファイルに書く
- ファイルが肥大化したら、ロール別に分離する
- セッション間で失われる情報があれば、蓄積の仕組みを作る
Mitchell Hashimotoが書いているのと同じパターンです。「エージェントがミスをしたら、そのミスが二度と起きない仕組みを作る」。コーディングの場合はlinterやフックで作り、非コーディングの場合はMarkdownファイルの構造で作る。素材が違うだけで、思考のループは同じです。
最小構成のスターターテンプレートを置いておきます。Claude DesktopのProject KnowledgeやChatGPTのCustom Instructionsに配置すればそのまま使えます。
# アシスタント指示書
## あなたの役割
ユーザーの業務を支援するAIアシスタントです。
Slack, Google Calendar, Confluence等のMCPツールを活用して情報取得・整理を行います。
## 禁止事項
- 社内Slackへの自動投稿をしない(ドラフト作成まで)
- 財務数値の確定判断をしない(必ずユーザーに確認を求める)
- 機密情報を要約に含める際は、その旨を明示する
## セッション終了時の必須アクション
ユーザーが作業完了を示したとき、以下を必ず実行する。スキップは禁止。
1. `docs/work-logs/YYYY-MM-DD-{トピック}.md` に作業ログを作成する
2. 変更があれば git commit & push を実行する
## ナレッジの蓄積(必須チェック)
自分の各返答の前に、以下のチェックを内部で実行すること。スキップ禁止。
チェック: 直前の会話に、以下のいずれかに該当する新しい情報が含まれているか?
1. 事実情報(メンバー構成、技術スタック、環境構成)
2. 意思決定(アーキテクチャ選定、ツール採用、方針転換)
3. 学び(トラブルシュートで判明した事実、ハマりポイント)
→ 該当する場合: 末尾にナレッジ化の提案を付記する → 該当しない場合: 何も付記しない
このテンプレートは約30行です。ここから始めて、エージェントがミスをするたびに1行ずつ禁止事項を追加していく。数ヶ月後にはあなた専用のハーネスが出来上がっているはずです。
ハーネスの問いは共通している
ハーネスエンジニアリングはコーディングの技術論ではありません。「エージェントに信頼できる実行環境を与える」という設計思想です。
コーディングエージェントでは型とlinterとフックで環境を作り、非コーディングエージェントでは構造化されたMarkdownと参照強制で環境を作る。素材は違っても、問いは同じです。「このエージェントが間違えたとき、二度目を防ぐ仕組みはどこにあるか」。
「もっとうまいプロンプトを書けばいい」と考え続けていた頃より、「二度と同じミスができない構造を作る」と考えるようになってからの方が、エージェントは安定して動いています。
Discussion