はじめに
前回までで、friend はかなり多層的な存在になってきた。
- 短期記憶
- 中期記憶
- foundation
- project memory
ここまで揃うと、単に「覚えている」だけでなく、
どう振る舞うべきかの基準 まで持ち始める。
すると自然に、次の問いが出てくる。
では、現在の発話やアイデアが、その基準とズレたとき、
friendはどう振る舞うべきか。
これはなかなか微妙な問題だった。
強く拒絶するのは違う。
だが、何でも黙って受け入れるのも違う。
ユーザーの勘違いや一時的な勢い、あるいは冗談に対して、
少しだけ「それ、いつもの方針とズレていませんか?」と返せる方が、
むしろ相棒らしい。
こうして生まれたのが、consistency check である。
本稿では、friend が foundation / project memory / summary を背景に、
やんわりとツッコミを入れられる存在 になるまでを書いていく。
そして、この機能が単なる警告装置ではなく、
“良い塩梅” の設計問題だったことを振り返りたい。
1. なぜツッコミが必要だったのか
foundation や project memory を導入した時点で、
friend はすでに「長く効く前提」を持つようになっていた。
しかし、その段階ではまだ、それらは 読むだけ の存在だった。
- こういう原則がある
- こういう project memory がある
- だから、たぶん今後の対話に効くだろう
もちろん、これだけでも十分に価値はある。
だが実際に使っていると、少し違う場面が見えてくる。
たとえば、
- 今回のアイデアは、いつもの設計原則と少し違う
- project memory にある前提と、今の提案が噛み合っていない
- ただし完全に間違いというわけでもない
- もしかすると、一時的な例外なのかもしれない
こういう場面で、friend が何も言わずにそのまま進むと、少し物足りない。
逆に「それはダメです」と強く拒絶すると、窮屈すぎる。
つまり必要だったのは、
ズレが見えたときに、軽く言及するが、進行は止めない
という中間的な振る舞いだった。
ここに consistency check の役割がある。
2. 拒絶ではなく、確認である
この機能を考えるとき、最初に決めておくべきことがあった。
それは、
consistency check は拒絶機能ではない
ということだ。
もしこれを「ルール違反検出器」として作ってしまうと、
- foundation に反する
- project memory に反する
- だから却下
という、非常に窮屈な存在になってしまう。
しかし、friend が相手にしているのは、
固定された仕様だけではない。
人間のアイデア、試行錯誤、勘違い、冗談、探索、思いつきも含まれる。
そうしたものに対して、いちいち「違反です」と返すのは、相棒というより監視装置である。
そこで consistency check は、最初からこう整理された。
やること
- foundation / project memory / summary と照らす
- 明らかなズレがあるときだけ軽く言及する
- 一時的な例外かもしれない、と扱う余地を残す
- 必要なら「foundation 見直し候補かもしれません」と示唆する
やらないこと
- その場で却下する
- 強い断定をする
- 毎回うるさく口を挟む
この整理がかなり重要だった。
この時点で friend は、ルールチェッカーではなく、
穏やかな自己点検相手 として位置づけられたのである。
3. 基準は一つではない
さらに重要だったのは、ツッコミの基準を foundation だけにしなかったことだ。
もし foundation だけを見てズレを検出すると、
かなり大雑把な判定になる。
たとえば、
- foundation 的には問題ない
- でもこの project の memory には少し合わない
ということは普通に起こりうる。
あるいは、
- foundation 的にはズレて見える
- でも今回の summary の流れの中では自然
ということもある。
だから consistency check の基準は、最終的にこうなった。
- foundation
- project memory
- session summary
つまり、最上位原則だけでなく、
その project の長期前提 と 最近の流れ も見るのである。
この三層を背景に置いたことで、friend のツッコミはかなり自然になった。
- これは普遍的原則とのズレなのか
- それとも project 特有前提とのズレなのか
- あるいは最近の流れから見て違和感があるだけなのか
こうした違いを、少なくとも応答のトーンに反映できるようになった。
4. いきなり複雑な判定装置を作らなかった
ここでも、最初から複雑な矛盾判定装置を作らなかったのは良かったと思う。
やろうと思えば、
- 各層を解析して
- 現在入力と照合して
- どのルールに何点で違反していて
- どういう重要度で……
という仕組みも考えられた。
だが、それをいきなりやるのは重すぎるし、何より friend の自然さを損ないやすい。
そこで最初の実装は、もっと柔らかいものになった。
つまり、現在入力に加えて、
consistency check 用の応答方針文を一緒に入れる のである。
概念的には、次のようなものだ。
(defun appsdk-friend--consistency-guidance-text ()
"Return consistency-check policy text for Friend."
(when appsdk-friend-use-consistency-check
(concat
"# Consistency Check Policy\n\n"
"- Compare the current user message with Foundation, Project Memory, and Session Summary.\n"
"- If there is a clear and meaningful tension, briefly point it out first.\n"
"- Keep the note gentle, concise, and non-blocking.\n"
"- Do not overreact to jokes, brainstorming, tentative ideas, or obvious exploration.\n"
"- If the tension looks like a temporary exception, say so and continue.\n"
"- If the tension looks stable or fundamental, mention that Foundation or Project Memory may need review.\n"
"- If there is no clear tension, do not mention this policy.\n\n")))
この方式の良いところは、実装が軽いことだけではない。
“どうツッコむべきか” 自体を friend に読ませている ことにある。
ここで初めて、friend は
原則と現在入力を照らし合わせたうえで、
「軽く違和感を言う」という振る舞いを持ち始める。
5. “良い塩梅” は何か
この機能を作るとき、一番難しかったのは技術そのものではなく、
むしろ どの程度の強さで口を出すか だった。
これが “良い塩梅” の問題である。
強すぎると
- 窮屈
- いちいち説教される感じがする
- 冗談や探索まで止めてしまう
弱すぎると
- 何のためにあるのか分からない
- 気づき支援にならない
- ただ記憶を読むだけで終わる
つまり必要だったのは、
- ちゃんと気づかせる
- でも邪魔しない
- 例外や探索の余地を残す
という、非常に中途半端で、しかし実用的な強さだった。
ここで設計上かなり大きかったのは、
明らかなズレがあるときだけ言う
という条件を入れたことだ。
何でもかんでも「少しズレています」と言い出したら、
それはただのうるさい存在になってしまう。
だから
- clear and meaningful tension
- no overreaction to jokes or brainstorming
という条件は、実はかなり本質的だった。
6. 冗談や探索に過剰反応しない
ここは想像以上に重要だった。
friend を使っていると、
真面目な設計相談ばかりしているわけではない。
思いつき、極論、たとえ話、冗談、探索的な発言は普通に出てくる。
たとえば、
- 「まあ冗談ですが、全部 magical AI でやりたいですね」
- 「極論すると、state も summary もいらない世界が理想かも」
- 「もちろん本気ではなく、対極として言っています」
こういう発話は、人間同士ならまず文脈で処理する。
もし friend がここに対して
- foundation に反するので不可です
- project memory に合致しません
などと言い始めたら、たちまち息苦しくなる。
だから consistency check は、
探索や冗談を「ルール違反」と誤認しないこと を強く要求された。
この点は、技術的には単なる prompt policy かもしれない。
しかし UX 的には極めて重要である。
friend が「賢い相棒」に見えるか、「融通の利かない装置」に見えるかの分かれ目だからだ。
7. では、どういう時にツッコむのか
逆に、どのような時にツッコミが有効か。
たとえば次のような場面である。
foundation とのズレ
- 大改造より段階的実装を好むはずなのに、全面作り直しを軽く提案している
- manual patch を好むのに、曖昧な大規模 rewrite に飛びそうになっている
project memory とのズレ
- AppSDK project root を唯一の基準にするはずなのに、friend 独自の root 解決を増やそうとしている
-
.friend/に寄せる前提があるのに、AI artifacts を散らそうとしている
summary とのズレ
- 直近で合意した方針と逆方向へ急に進みそうになっている
- いま解決すべき論点を飛ばして別方向へ行こうとしている
このときの理想的な応答は、
- 「いつもの方針とは少しズレていますね」
- 「これは一時的な例外ならそのまま進められます」
- 「恒常的なら foundation / memory 見直し候補かもしれません」
のようなものだった。
ここで大事なのは、
間違いだと断定しないこと である。
ずれているかもしれない。
しかしそのズレが、誤解なのか、探索なのか、ルール変更の兆候なのかは、まだその時点では分からない。
だからこそ friend は、あくまで「気づき」として返す。
8. 例外と見直し候補を区別する
この機能が面白くなったのは、
ズレを単に「ある / ない」で終わらせなかったところだと思う。
friend は、ズレを見つけたときに、少なくとも感覚的には二種類に分け始める。
一時的な例外
- 今だけ
- 実験的
- 探索的
- あえて普段と違う方向を試している
原則見直し候補
- 繰り返し現れる
- 一時的というより持続的
- 今後もその方向が続きそう
- foundation や project memory そのものを更新した方が良いかもしれない
この違いは大きい。
前者なら「今回は例外として進める」でよい。
後者なら「原則層を見直す契機かもしれない」となる。
ここで consistency check は、単なる警告ではなく、
原則を育てるための観測点 にもなっている。
これはかなり面白い構造だ。
9. toggle があることの意味
この機能は、当然ながら万能ではない。
ときには少しうるさいかもしれないし、
探索モードの時には邪魔になるかもしれない。
だから、ON/OFF を切り替えられるようにしてみた。
(defun appsdk-friend-toggle-consistency-check ()
"Toggle Friend consistency check."
(interactive)
(setq appsdk-friend-use-consistency-check
(not appsdk-friend-use-consistency-check))
(message "[AppSDK] friend consistency-check: %s"
(if appsdk-friend-use-consistency-check "ON" "OFF")))
この手の機能では、
「絶対正しいから常時有効にすべきだ」という発想に行きがちだ。
だが実際には、使い手のモードによって適切な強さは変わる。
- 真面目に設計を固めたい時
- いろいろブレストしたい時
- ただ軽く試したい時
では、求める friend の口の出し方も違う。
だから consistency check は、
friend の人格の一部ではあるが、
環境に応じて少し声量を変えられる方が良い。
この柔軟さも、結果として “良い塩梅” を支えていた。
10. friend は初めて「自分の原則で自分を点検する」ようになった
ここまでくると、friend はかなり面白い存在になってくる。
- 記憶を持つ
- 原則を持つ
- それを背景に現在入力を読む
- 必要なら軽く違和感を言う
つまり friend は、単に人間の入力に応じて返答するだけでなく、
自分が持つ原則を使って、自分の返答の方向性を少し点検する ようになる。
これは、設計上かなり大きな変化だったと思う。
もちろん、ここで「自律的判断」などと大げさに言うつもりはない。
だが少なくとも、friend はこの時点で
単なる記憶付きチャット窓ではなく、
記憶と原則を背景にした相棒らしさ を持ち始めていた。
この変化は、実装量以上に体感で効く。
たまに「それ、いつもの方針と少しズレていませんか」と言われるだけで、
こちらも少し立ち止まって考えるようになるからだ。
おわりに
第8回では、foundation / project memory / summary を背景に、
friend がやんわりとツッコミを入れられるようになるまでを書いた。
ここで重要だったのは、
- consistency check を拒絶機能にしなかったこと
- foundation だけでなく memory / summary も基準にしたこと
- 冗談や探索には過剰反応しないようにしたこと
- 例外と見直し候補を区別したこと
だった。
この段階で friend は、単に「覚えている」だけでなく、
覚えていることを背景に、現在の発話へ軽く異議を差し挟める
存在になった。
次回は、そこからさらに一歩進んで、
「最近こんなことよく言っていませんか?」と project memory 候補をやんわり示唆する
memory hint の話に進みたい。
friend が、ただツッコむだけでなく、原則の成長まで支援し始める段階である。
次回予告
第9回
「最近こんなことよく言ってない?」:memory hint の遊び心と実用性
- project memory 更新問題
- 自動昇格ではなく、自動気づき
- recurring theme をどう扱うか
-
strait-aiがfunny-guyに軽く言う感じ
付録:本稿で扱った consistency check 関連コード断片
consistency policy
(defun appsdk-friend--consistency-guidance-text ()
"Return consistency-check policy text for Friend."
(when appsdk-friend-use-consistency-check
(concat
"# Consistency Check Policy\n\n"
"- Compare the current user message with Foundation, Project Memory, and Session Summary.\n"
"- If there is a clear and meaningful tension, briefly point it out first.\n"
"- Keep the note gentle, concise, and non-blocking.\n"
"- Do not overreact to jokes, brainstorming, tentative ideas, or obvious exploration.\n"
"- If the tension looks like a temporary exception, say so and continue.\n"
"- If the tension looks stable or fundamental, mention that Foundation or Project Memory may need review.\n"
"- If there is no clear tension, do not mention this policy.\n\n")))
input builder への注入イメージ
(defun appsdk-friend--build-input-text (prompt)
"Build final input text for API request.
Inject foundation, project memory, session summary, and consistency policy when enabled."
(let ((foundation (and appsdk-friend-use-foundation
(appsdk-friend-foundation-read)))
(memory (and appsdk-friend-use-memory
(appsdk-friend-memory-read)))
(summary (and appsdk-friend-use-summary
(appsdk-friend-summary-read)))
(consistency (appsdk-friend--consistency-guidance-text)))
(concat
(when (and foundation (not (string-empty-p foundation)))
(concat "# Foundation\n\n" foundation "\n\n"))
(when (and memory (not (string-empty-p memory)))
(concat "# Project Memory\n\n" memory "\n\n"))
(when (and summary (not (string-empty-p summary)))
(concat "# Session Summary\n\n" summary "\n\n"))
(when (and consistency (not (string-empty-p consistency)))
consistency)
"# Current User Message\n\n"
prompt)))
ON/OFF toggle
(defun appsdk-friend-toggle-consistency-check ()
"Toggle Friend consistency check."
(interactive)
(setq appsdk-friend-use-consistency-check
(not appsdk-friend-use-consistency-check))
(message "[AppSDK] friend consistency-check: %s"
(if appsdk-friend-use-consistency-check "ON" "OFF")))