URL が 1 つ、ヘッダーが 1 つ、YAML のステップが 1 つ
部品はすでに棚に揃っていました。AgentMail は、エージェントのために作られた API ファーストの受信箱です。API で受信箱を発行するとメールの受け取りを引き受け、メールが届いた瞬間に、指定した URL へ webhook を送ります。Block が公開しているオープンソースのワークスペース Buzz(hachiflow は独立した会社で、Block との提携関係はありません)は、リレーそのものにワークフローエンジンを備えています。hachiflow.chat のすべてのワークスペースにはこれが組み込まれているので、webhook をトリガーにしたワークフローは初日からそのまま動きます。自前のサーバーを運用する必要はありません。
webhook トリガーを持つワークフローには、POST https://yourteam.hachiflow.chat/hooks/9f4c21e7-6b3d-4a8e-b02f-c75d10a94e63 のようなエンドポイントが割り当てられ、X-Webhook-Secret ヘッダーで認証されます。AgentMail の webhook をその URL に向け、シークレットをヘッダーに入れれば、届いたメールがチャンネルに流れてきます。
連携はこれで全部です。URL が 1 つ、ヘッダーが 1 つ、YAML のステップが 1 つ。下のスクリーンショットが Buzz 側の設定のすべてで、その次のターミナルが契約のすべてです。値は意図的に偽物の例にしてあります。
ここにある値は意図的な偽物です。本物の hook UUID とシークレットはワークフロー作成時に発行され、シークレットの置き場所は AgentMail の webhook 設定だけです。
1 回目の試み: JSON の壁
最初のワークフローは素直なやり方をしました。{{trigger.message}} でペイロードをそのままチャンネルに差し込んだのです。出てきたのは生の JSON の壁でした。ヘッダー、SPF の判定結果、添付ファイルのメタデータ、エンベロープ全体が 1 つの段落になっていました。その様子は少し下のスクリーンショットに、置き換え後のメッセージのすぐ上に残してあります。
制約ははっきりしていて、Rust のコードを読んで確かめました。現在の Buzz のワークフローテンプレートが取り出せるのは、webhook 本文のトップレベルのキーだけです。ネストしたオブジェクトは文字列化された 1 つの塊として出力されます。{{trigger.message.from}} のような書き方はなく、テンプレートのフィルターも truncate と npub の 2 つだけです。ネストした webhook のペイロードをワークフローのメッセージ内できれいに表示することは、現時点ではできません。
文句を言いたいわけではありません。まだ若いエンジンで、テンプレートでできることはこれから増えていくでしょう。ただ今日の時点での正直な助言は、webhook のペイロードをワークフローで整形しようと考えず、それを前提に設計を組むことです。
ワークフローで整形しない。そもそも整形しない。
出口は、チャンネルにある他のすべてのメッセージが示していました。Buzz のエージェントのメッセージは見やすく表示されます。クライアントがメッセージ本文を GitHub 風の markdown としてレンダリングするからです。ワークフローにはメールを整形できませんでしたが、配達先のチームメイトは自分の発言をすべて整形できます。そこで構成を逆転させ、ワークフローでの整形を完全にやめました。
ワークフローのメッセージは、いまではエージェントを @メンションする 1 行の呼び鈴です。AgentMail のツール、つまり AgentMail がホストする MCP サーバーは、Gary V 自身が持っていて、この受信箱 1 つだけにスコープが絞られています。呼び鈴が鳴ると、Gary V は未読メールを取得し、読みやすい markdown カードと返信の下書きをチャンネルに投稿し、AgentMail の API で元のメッセージにスレッドとして返信を送り、同じメールを二度処理しないようにそのメッセージに処理済みのラベルを付けます。
エージェントがレンダリング層で、ワークフローは目覚まし役です。そして承認の場所は変わっていません。Gary がチャンネルで下書きし、創業者がゴーを出すまで、何も外には出ていきません。
誰も起こさないメンション
落とし穴 1 には、テスト 3 回分を費やしました。ワークフローのメッセージ内のメンションはサーバー側で解決されます。リレーが、ステップのテキストにある @Name を、チャンネルのメンバーの表示名と照合するのです。表示名が同じメンバーが 2 人いると、リレーは意図的に誰も起こしません。エラーも、部分一致も、目にできるログもありません。ただ沈黙だけです。
私たちのチャンネルには古い重複が残っていました。以前に作ってもう動いていない「Gary V」が、動いている Gary V と並んでメンバーのままだったのです。そのため呼び鈴は毎回、誰にも届かずに鳴っていました。誤配送を防ぐルールとしては妥当です。同じ名前のメンバー 2 人のどちらを起こすか、ワークフローに当て推量させたくはありません。ただデバッグの体験としては過酷です。それが起きたことを、どこも何も教えてくれないからです。古いメンバーを外したところ、呼び鈴が届くようになりました。
送信者表示は見た目だけ
落とし穴 2 はもっと気づきにくいものです。ワークフローのメッセージは、あなたが送ったように見えます。しかし実際はそうではありません。送信者の表示はクライアントが描いているだけで、暗号的には、メッセージはリレー自身の鍵で署名されています。
Gary の応答ゲートは「オーナーのみ」に設定されていて、ゲートが確認するのは表示ではなく署名です。そのため Gary は呼び鈴が届くのを見ても、オーナーの署名ではないと判断して、正しく無視していました。ゲートは正しく、送信者表示は見た目だけです。対処は、そのエージェントについてリレーの鍵を許可リストに加えることです。こうすると、リレーが署名したワークフローのメッセージでもエージェントが起きるようになります。
これは buzz-acp のソースを読んで見つけました。上流でも同じ問題に取り組んでいて、修正が一度入ってから差し戻されています。エージェントがタグ付けされても起きず、しかもチャンネルにそのエージェントがちょうど 1 人しかいないなら、原因はこれです。
おまけにもう 1 つ
ワークフローの YAML エディターでの小さな話です。@ で始まるテキストの値は引用符で囲む必要があります。YAML のスカラーは裸の @ で始められないからです。貼り付けにも注意してください。私たちは一度、2 つ目の YAML ドキュメントを丸ごとメッセージのテキスト欄に追記してしまいましたが、エディターは何事もなく受け入れました。フォーム表示と YAML 表示は同じステップへの 2 つの入口です。フォームのほうが事故は少なくて済みます。
手にしている道具を直す
テストはすべて、自社ドメインから送った本物のメールで行いました。送信に使ったのは higgs です。これは私たちが作った Proton Mail 用の CLI で、IMAP と SMTP で Proton Mail Bridge を操作します。モックは使っていません。どのテストも実際のインターネットを通り、実際の受信箱に届き、実際の呼び鈴を鳴らしました。だからこそ、テスト用のフィクスチャでは決して表に出なかったはずの落とし穴を捕まえられたのです。
ドッグフーディングは両方向に効きました。デバッグの途中で、higgs send がそのままでは Bridge とやり取りできないことがわかったので、その夜のうちに修正して higgs v1.3.0 をリリースしました。エージェント同士をつなぐ実際の仕事とはこういうものです。半分の時間は、手にしている道具を直しています。
アルファ品質、それでも出荷する
ここまでの内容はすべて、今日どの hachiflow ワークスペースでも動きます。月額 $50 の定額で、人数もエージェント数も無制限、LLM のキーはご自身のもの、サーバーは専用です。特別なインフラは何も必要ありませんでした。AgentMail は誰でも申し込める製品で、ワークフローエンジンはすべてのワークスペースに付いていて、エージェントは他のメンバーと同じくチームの一員です。
Buzz はまだ若く、これは粗さの残るアルファ品質の配線です。だからこそ、良いところだけを集めた紹介ではなく、落とし穴を公開しています。チームメイトが対応してくれる受信箱が欲しいなら、hachiflow.com から始めてください。