mcpField
B
building-loops

building-loops

github.com/posthogエージェントワークフロー7647 回インストール1 日前に更新
リポジトリ

building-loops について

Loops を作成するためのスキルです。Loops はトリガーから AI タスクを実行する形式が定められたワークフローで、通知も任意で追加できます。設定、テスト、スケジュール登録、有効化に加え、トリガーごとのフィルターや制御方法を扱います。

主な機能

  1. 01トリガー起点の AI タスクループ作成
  2. 02スケジュール、GitHub、Slack、PostHog の設定
  3. 03任意の Slack・メール通知
  4. 04ワークフローの段階的なテスト
  5. 05トリガーフィルターと実行抑制

活用シーン

  1. 01定期的な AI タスクの実行
  2. 02指定した GitHub イベントでのタスク開始
  3. 03特定の Slack メッセージや PostHog イベントへの対応
  4. 04Slack またはメールでのタスク結果通知

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/posthog/skills --skill building-loops

インストール後、Claude Code で/building-loopsを入力して読み込まれたか確認します。

詳細

ループの構築

ループは、origin_product: "loops" というタグが付いたワークフローで、トリガー、「Create AI task」ステップ、任意の通知ステップ、終了という1つの構成を持ちます。デスクトップのループ画面では、この構成が読み込まれます。それ以外の構成を持つワークフローも実行されますが、デスクトップでは「ワークフローエディターで変更済み」と表示され、読み取り専用になります。

次の順序で構築します。まず workflows-create でドラフトを作成し、workflows-test-run でステップごとにテストします。スケジュールループの場合は workflows-schedule-create を使い、ユーザーの承認後に workflows-enable で有効化します。ここに記載されていない操作(ドラフトのパッチ適用、公開済みワークフローの公開、ログの確認)には、building-workflows スキルを使ってください。

グラフ

山括弧内の値だけを置き換えてください。ユーザーが通知を希望しない場合は、notify ステップとそのエッジを省略します。

{
  "name": "<short name>",
  "description": "",
  "status": "draft",
  "origin_product": "loops",
  "exit_condition": "exit_only_at_end",
  "variables": [{ "key": "task_final_message", "type": "string", "default": "" }],
  "actions": [
    { "id": "trigger", "name": "Trigger", "type": "trigger", "config": <trigger config> },
    {
      "id": "create_task",
      "name": "Create AI task",
      "type": "function",
      "config": {
        "template_id": "template-posthog-create-task",
        "inputs": {
          "prompt": { "value": "<task prompt>" },
          "repository": { "value": "<owner/name>" },
          "connectors": { "value": ["<mcp connection id>"] },
          "skills": { "value": ["<skill name>"] },
          "posthog_mcp_scopes": { "value": "read_only" },
          "non_failure_status_codes": { "value": [409] }
        }
      },
      "output_variable": [{ "key": "task_final_message", "result_path": "final_message" }]
    },
    { "id": "notify", "name": "Notify", "type": "function", "config": <notify config> },
    { "id": "exit", "name": "Exit", "type": "exit", "config": { "reason": "Task finished" } }
  ],
  "edges": [
    { "from": "trigger", "to": "create_task", "type": "continue" },
    { "from": "create_task", "to": "notify", "type": "continue" },
    { "from": "notify", "to": "exit", "type": "continue" }
  ]
}

タスクステップ

prompt は必須です。タスクは無人で実行されるため、何をするか、「完了」の状態、報告する内容を含む、完全な指示を記述してください。

イベントのテキストを {event.properties.<name>} テンプレートでプロンプトに入れないでください。実行ごとに、トリガーとなったイベント全体が、データとしてラベル付けされ、山括弧がエスケープされた別の <triggering_event> ブロックとして渡されるため、その中の内容がタグを偽装することはありません。プロパティ名を指定し、実行時にその値を読み取らせてください。

トリガーとなったイベントの text プロパティからメッセージを読み取り、次に…

テンプレートを使うと、エスケープされない生の値が、そのブロックより前にあるプロンプトの指示部分に展開されます。その値を書くのは、Slack の投稿者、イシューの作成者、または顧客のエンドユーザーです。そのため、細工された値は、リポジトリの認証情報を持っている可能性のあるエージェントへの指示として読み取られてしまいます。

ループで必要な場合に限り、ほかの入力を含めてください。

  • repository:コードを扱うタスクの場合は、ユーザーが指定した owner/name を使います。記憶から選んではいけません。まずアクセス可能であることを確認します。GitHub では integrations-list を実行し、続けてその正確な名前を指定して integrations-github-repos-retrieve を実行してください。
  • connectors:mcp-connections-list から取得した ID を指定します。プロジェクト内の全員と共有されている接続のみ受け付けられます。
  • skills:skill-list にある正確な名前を指定します。最大10個です。
  • posthog_mcp_scopes:デフォルトは read_only です。ユーザーが PostHog の変更をタスクに求めた場合に限り、full を使用します。
  • channel:ループが属するスペースを <space id>|<space name> の形式で指定します。アプリからループの作成先スペースが示された場合に限り設定してください。ユーザーが入力した名前から設定してはいけません。各実行はそのスペースのフィードに表示され、ループもそのスペースの下に一覧表示されます。
  • reply_in_slack_thread:結果をスレッドに返す Slack トリガーのループでは、true(テンプレート文字列ではなく、JSON のブール値)を指定します。これで通知も行われるため、notify ステップは省略してください。

すべてのループで、グラフに記載されている non_failure_status_codes をそのまま指定してください。実行がタスク上限に達すると、API は 409 を返します。この入力がない場合、ステップは一般的なフェッチエラーで失敗し、ユーザーは上限に関するメッセージを読めません。

タスクが完了するまで、ワークフローはこのステップで待機します。次のステップでは、ステップの結果に含まれる final_message、pr_urls、status を参照できます。

トリガー設定

ユーザーが指定したソースのトリガーを選びます。スケジュールと GitHub は、Desktop のループフォームで編集できるトリガーです。

スケジュール。下記のスケジュール行で頻度を指定します。

{ "type": "schedule" }

GitHub イベント。リポジトリを 1 つ、イベントタイプを 1 つ指定します。

{
  "type": "internal-event",
  "filters": {
    "source": "internal-events",
    "events": [{ "id": "$github_event_received", "type": "events" }],
    "properties": [
      { "key": "repository", "value": ["<owner/name>"], "operator": "exact", "type": "event" },
      {
        "key": "event_type",
        "value": ["<issues | issue_comment | pull_request | push>"],
        "operator": "exact",
        "type": "event"
      },
      { "key": "actor_access", "value": ["write"], "operator": "exact", "type": "event" }
    ]
  }
}

actor_access フィルターは残してください。これにより、リポジトリへの書き込み権限がないユーザーがタスクを開始できなくなります。ユーザーから特定のアクション(「イシューが開かれたとき」など)の指定があった場合は、プロパティフィルターを 1 つ追加してアクションを絞り込みます:{ "key": "action", "value": ["<action>"], "operator": "exact", "type": "event" }。action フィルターのない pull_request ループは、開いているすべてのプルリクエストへのプッシュごとに起動します。

イベントアクション
issuesopened、reopened、closed、edited、deleted、labeled、unlabeled、assigned、unassigned、pinned、unpinned、transferred
pull_requestopened、reopened、closed、synchronize、edited、ready_for_review、converted_to_draft、review_requested、review_request_removed、labeled、unlabeled、assigned、unassigned
issue_commentcreated、edited、deleted
pushなし。push にアクションはありません

Slack メッセージ。1 つ以上のチャンネルを指定します。channel は必須で、チャンネル ID を指定します。プロジェクトの Slack 統合(integrations-list、種類 slack)で integrations-channels-retrieve を使って名前から ID を解決してください。ID を推測してはいけません。

{
  "type": "internal-event",
  "filters": {
    "source": "internal-events",
    "events": [{ "id": "$slack_message_received", "type": "events" }],
    "properties": [
      { "key": "channel", "value": ["<channel id>"], "operator": "exact", "type": "event" },
      { "key": "bot_id", "value": "is_not_set", "operator": "is_not_set", "type": "event" },
      { "key": "thread_ts", "value": "is_not_set", "operator": "is_not_set", "type": "event" }
    ]
  }
}

bot_id と thread_ts のフィルターは残してください。これにより、ワークフローエディターの Slack トリガーが作成する、人が入力したトップレベルメッセージにループを限定できます。これらを指定しないと、別のアプリが投稿するアラートや、スレッド内のすべての返信でループがタスクを開始し、そのたびに 1 日あたりのタスク予算を消費します。ユーザーから指定があった場合にのみ範囲を広げてください。アプリとボットだけを対象にする場合は bot_id に is_set を指定し、投稿者を指定する場合は user または app_id に exact フィルターを設定します。返信も対象にする場合は thread_ts フィルターを削除します。

PostHog イベント。一致するたびに実行します。

{
  "type": "event",
  "filters": {
    "events": [{ "id": "<event>", "name": "<event>", "type": "events", "order": 0, "properties": [] }],
    "properties": [],
    "filter_test_accounts": false
  }
}

PostHog イベントトリガーは、一致するたびにタスクを作成します。この経路では、ワークフローエディターのボリューム警告は実行されません。ループを作成する前に上限を設けてください。プロパティフィルターでイベントを絞り込み、発生頻度の高いイベントには、actions と edges の横にあるトップレベルフィールド trigger_masking を使ってスロットルを設定します。

"trigger_masking": { "hash": "{person.id}", "ttl": 3600, "threshold": null }

hash は重複排除キーの HogQL テンプレートであるため、{person.id} を指定すると、1 人につき 1 回だけ起動します。ttl は、そのハッシュの繰り返しを抑制する秒数で、60 秒から約 3 年まで指定できます。代わりに threshold を指定すると、同じハッシュに一致する N 回ごとに 1 回起動します。通常の重複排除では省略してください。bytecode は送信しないでください。サーバーが hash からコンパイルします。ワークフローが 1 日に作成できるタスクは 100 件、プロジェクト全体ではすべてのワークフローを合わせて 500 件です。そのため、頻度の高いイベントで上限のないループを実行するとプロジェクトの予算を消費し、次に起動するワークフローは拒否されます。Desktop でループを編集すると、trigger_masking は維持されます。

手動。[トリガーを手動で実行]ボタンから実行します。

{
  "type": "manual",
  "template_id": "template-source-webhook",
  "inputs": { "event": { "value": "$workflow_triggered" }, "distinct_id": { "value": "{request.body.user_id}" } }
}

スケジュール行

ワークフローの作成後に、ワークフロー ID を workflow_id として指定し、workflows-schedule-create で作成します。rrule は次のいずれかをそのまま指定します。BYHOUR や BYMINUTE は使用しません。

頻度rrule
毎時FREQ=HOURLY;INTERVAL=1
毎日FREQ=DAILY;INTERVAL=1
平日FREQ=WEEKLY;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR
週に 1 日FREQ=WEEKLY;INTERVAL=1;BYDAY=<MO..SU>
1 回FREQ=DAILY;COUNT=1

starts_at は最初の実行日時で、UTC オフセット付きの ISO 8601 形式にします。実行時刻はこの値から決まるため、ユーザーが希望する時刻で次に実行される日時を選んでください。timezone はユーザーの IANA タイムゾーンです。毎時のスケジュールは正時に開始します。

通知ステップ

タスクの結果は、タスクステップの output_variable を通じて通知ステップに渡されます。{variables.task_final_message} はエージェントの締めくくりのメッセージです。プルリクエストへのリンクをメッセージに含める場合は、リストに { "key": "task_pr_urls", "result_path": "pr_urls" } を追加し、対応する variables エントリも追加します。

Slack の場合、template_id は template-slack です。slack_workspace には、integrations-list から取得した Slack インテグレーション ID を指定します。integrations-channels-retrieve で channel を解決し、要約する前に、そのチャンネルの is_member を確認してください。値が false の場合、PostHog Slack アプリはそのチャンネルに参加していません。最初の実行では投稿に失敗し、結果は誰にも届きません。そのため、ループを作成するのではなく、アプリを招待するようユーザーに依頼してください。

{
  "template_id": "template-slack",
  "inputs": {
    "slack_workspace": { "value": <integration id> },
    "channel": { "value": "<channel id>" },
    "text": { "value": "<loop name> finished: {variables.task_final_message}" }
  }
}

メールの場合、ステップの type を function ではなく function_email に設定します。from は integrations-list から取得した送信者 ID、to はユーザーのメールアドレスを格納したオブジェクトです。html は空文字列にします。ランタイムではこのキーが必須で、空にするとテキストのみのメールが送信されます。email 入力では Liquid を使用できるため、ここでは {{ variables.task_final_message }} を使用します。

{
  "template_id": "template-email",
  "inputs": {
    "email": {
      "value": {
        "from": { "integrationId": <sender id> },
        "to": { "email": "<user email>", "name": "" },
        "subject": "<loop name> finished",
        "text": "{{ variables.task_final_message }}",
        "html": ""
      }
    }
  }
}

構造化出力

通知ステップまたはユーザーが、判定や件数など、タスクから特定のフィールドを必要とする場合は、文章を解析するのではなく、タスクにそのフィールドを返すよう依頼します。各フィールドに result_path が output.<name> の出力変数を追加し、対応する variables エントリには型(string、number、boolean)を指定します。タスクには返すフィールドが伝えられ、その後 {variables.<name>} に値が格納されます。final_message で十分な場合は、この設定は不要です。

テスト実行

スケジュールを設定または有効化する前に、ドラフトをテストしてください。workflows-test-run はステップを 1 つずつ実行し、すべての外部呼び出しをモックするため、実際のデータは作成されません。これはタスクだけでなく通知ステップにも適用されるため、テスト実行に成功しても、グラフが正しく接続されていることを示すだけで、Slack やメールが実際に配信されることを示すものではありません。

  1. スケジュールループの場合は、current_action_id を指定せず、globals を { "event": { "event": "$scheduled", "properties": {} } } として実行します。GitHub または Slack のループの場合は、トリガーのイベント名とフィルターに一致するプロパティを指定し、人物は指定しません。PostHog イベントループの場合は、そのイベントと人物を指定します。
  2. nextActionId が create_task になることを確認します。current_action_id: "create_task" を指定して再実行します。
  3. タスク呼び出しがモックされ、nextActionId が notify(または exit)になることを確認します。終了ステップまで続けます。

ステップ 1 で status=skipped になった場合、サンプルイベントがトリガーのフィルターに一致していません。トリガーではなく、サンプルを修正してください。

Loops で利用できない機能

アプリ内通知、プッシュ通知、およびその他すべてのステップタイプは利用できません。ユーザーから依頼された場合は、Loops ではまだサポートされていないことを伝え、要望に最も近いループを提案してください。回避策としてアクション、エッジ、入力を追加しないでください。

コメント