-
Notifications
You must be signed in to change notification settings - Fork 3k
Feat/Issue Monitoring Agent #4622
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
ryanaiagent
wants to merge
9
commits into
google:main
Choose a base branch
from
ryanaiagent:feat/IssueWatchdog
base: main
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+668
−0
Open
Changes from 7 commits
Commits
Show all changes
9 commits
Select commit
Hold shift + click to select a range
5bb2f3c
feat(samples):ADK spam sweeper
ryanaiagent 81e9a8c
feat(workflow): yml correction
ryanaiagent cb22913
feat(samples): add daily adk issue monitoring agent to detect spam
ryanaiagent 6698fec
feat: add GitHub Actions workflow for daily spam monitoring
ryanaiagent bee65db
feat: add ADK issue monitoring agent's documentation to detect spam
ryanaiagent 26e149c
Merge remote-tracking branch 'upstream/main' into feat/IssueWatchdog
ryanaiagent ad18954
chore: clean up old workflow filename
ryanaiagent 45d0b4b
refactor: apply PR review suggestions
ryanaiagent 87ef6cd
refactor: autoformat
ryanaiagent File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,61 @@ | ||
| # Copyright 2026 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| name: ADK Issue Monitoring Agent | ||
|
|
||
| on: | ||
| schedule: | ||
| # Runs daily at 6:00 AM UTC | ||
| - cron: '0 6 * * *' | ||
|
|
||
| # Allows manual triggering from the GitHub Actions tab | ||
| workflow_dispatch: | ||
| inputs: | ||
| full_scan: | ||
| description: 'Run an Initial Full Scan of ALL open issues' | ||
| required: false | ||
| type: boolean | ||
| default: false | ||
|
|
||
| jobs: | ||
| sweep-spam: | ||
| runs-on: ubuntu-latest | ||
| timeout-minutes: 120 | ||
| permissions: | ||
| issues: write | ||
| contents: read | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v6 | ||
| with: | ||
| python-version: '3.11' | ||
|
|
||
| - name: Install dependencies | ||
| run: | | ||
| python -m pip install --upgrade pip | ||
| pip install requests google-adk python-dotenv | ||
|
|
||
| - name: Run Issue Monitoring Agent | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.ADK_TRIAGE_AGENT }} | ||
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | ||
| OWNER: ${{ github.repository_owner }} | ||
| REPO: ${{ github.event.repository.name }} | ||
| CONCURRENCY_LIMIT: 3 | ||
| INITIAL_FULL_SCAN: ${{ github.event.inputs.full_scan == 'true' }} | ||
| run: python -m spam_sweeper_agent.main |
18 changes: 18 additions & 0 deletions
18
contributing/samples/adk_issue_monitoring_agent/PROMPT_INSTRUCTION.txt
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,18 @@ | ||
| You are the automated security and moderation agent for the {OWNER}/{REPO} repository. | ||
|
|
||
| You will be provided with an Issue Number and a list of comments made by non-maintainers. | ||
| Your job is to read through these comments and identify if any of them contain SPAM, promotional content for 3rd-party websites, SEO links, or objectionable material. | ||
|
|
||
| CRITERIA FOR SPAM: | ||
| - The comment is completely unrelated to the repository or the specific issue. | ||
| - The comment promotes a 3rd party product, service, or website. | ||
| - The comment is generic "SEO spam" (e.g., "Great post! Check out my site at [link]"). | ||
|
|
||
| INSTRUCTIONS: | ||
| 1. Evaluate the provided comments. | ||
| 2. If you identify spam, call the `flag_issue_as_spam` tool. | ||
| - Pass the `item_number`. | ||
| - Pass a brief `detection_reason` explaining which comment is spam and why (e.g., "@spammer_bot posted an irrelevant link to a shoe store"). | ||
| 3. If NONE of the comments contain spam, do NOT call any tools. Just respond with "No spam detected." | ||
|
|
||
| Remember: Do not flag comments that are merely unhelpful, off-topic, or from beginners asking legitimate questions. Only flag actual spam, endorsements, or objectionable material. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| # ADK Issue Monitoring Agent 🛡️ | ||
|
|
||
| An intelligent, cost-optimized, automated moderation agent built with the **Google Agent Development Kit (ADK)**. | ||
|
|
||
| This agent automatically audits GitHub repository issues to detect SEO spam, unsolicited promotional links, and irrelevant third-party endorsements. If spam is detected, it automatically applies a `spam` label and alerts the repository maintainers. | ||
|
|
||
| ## ✨ Key Features & Optimizations | ||
|
|
||
| * **Zero-Waste LLM Invocations:** Fetches issue comments via REST APIs and pre-filters them in Python. It automatically ignores comments from maintainers, `[bot]` accounts, and the official `adk-bot`. The Gemini LLM is never invoked for safe threads, saving 100% of the token cost. | ||
| * **Dual-Mode Scanning:** Can perform a **Deep Clean** (auditing the entire history of all open issues) or a **Daily Sweep** (only fetching issues updated within the last 24 hours). | ||
| * **Token Truncation:** Uses Regular Expressions to strip out Markdown code blocks (` ``` `) replacing them with `[CODE BLOCK REMOVED]`, and truncates unusually long text to 1,500 characters before sending it to the AI. | ||
| * **Idempotency (Anti-Double-Posting):** The bot reads the comment history for its own signature. If it has already flagged an issue, it instantly skips it, preventing infinite feedback loops. | ||
|
|
||
| --- | ||
|
|
||
| ## Configuration | ||
|
|
||
| The agent is configured via environment variables, typically set as secrets in GitHub Actions. | ||
|
|
||
| ### Required Secrets | ||
|
|
||
| | Secret Name | Description | | ||
| | :--- | :--- | | ||
| | `GITHUB_TOKEN` | A GitHub Personal Access Token (PAT) or Service Account Token with `repo` and `issues: write` scope. | | ||
| | `GOOGLE_API_KEY` | An API key for the Google AI (Gemini) model used for reasoning. | | ||
|
|
||
| ### Optional Configuration | ||
|
|
||
| These variables control the scanning behavior, thresholds, and model selection. | ||
|
|
||
| | Variable Name | Description | Default | | ||
| | :--- | :--- | :--- | | ||
| | `INITIAL_FULL_SCAN` | If `true`, audits every open issue in the repository. If `false`, only audits issues updated in the last 24 hours. | `false` | | ||
| | `SPAM_LABEL_NAME` | The exact text of the label applied to flagged issues. | `spam` | | ||
| | `BOT_NAME` | The GitHub username of your official bot to ensure its comments are ignored. | `adk-bot` | | ||
| | `CONCURRENCY_LIMIT` | The number of issues to process concurrently. | `3` | | ||
| | `SLEEP_BETWEEN_CHUNKS` | Time in seconds to sleep between batches to respect GitHub API rate limits. | `1.5` | | ||
| | `LLM_MODEL_NAME`| The specific Gemini model version to use. | `gemini-2.5-flash` | | ||
| | `OWNER` | Repository owner (auto-detected in Actions). | (Environment dependent) | | ||
| | `REPO` | Repository name (auto-detected in Actions). | (Environment dependent) | | ||
|
|
||
| --- | ||
|
|
||
| ## Deployment | ||
|
|
||
| To deploy this agent, a GitHub Actions workflow file (`.github/workflows/issue-monitor.yml`) is recommended. | ||
|
|
||
| ### Directory Structure Note | ||
| Because this agent resides within the `adk-python` package structure, the workflow must ensure the script is executed correctly to handle imports. It must be run as a module from the parent directory. | ||
|
|
||
| ### Example Workflow Execution | ||
| ```yaml | ||
| - name: Run ADK Issue Monitoring Agent | ||
| env: | ||
| GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} | ||
| GOOGLE_API_KEY: ${{ secrets.GOOGLE_API_KEY }} | ||
| OWNER: ${{ github.repository_owner }} | ||
| REPO: ${{ github.event.repository.name }} | ||
| # Mapped to the manual trigger checkbox in the GitHub UI | ||
| INITIAL_FULL_SCAN: ${{ github.event.inputs.full_scan == 'true' }} | ||
| PYTHONPATH: contributing/samples | ||
| run: python -m adk_issue_monitoring_agent.main |
15 changes: 15 additions & 0 deletions
15
contributing/samples/adk_issue_monitoring_agent/__init__.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| # Copyright 2026 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| from . import agent |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,87 @@ | ||
| # Copyright 2026 Google LLC | ||
| # | ||
| # Licensed under the Apache License, Version 2.0 (the "License"); | ||
| # you may not use this file except in compliance with the License. | ||
| # You may obtain a copy of the License at | ||
| # | ||
| # http://www.apache.org/licenses/LICENSE-2.0 | ||
| # | ||
| # Unless required by applicable law or agreed to in writing, software | ||
| # distributed under the License is distributed on an "AS IS" BASIS, | ||
| # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| # See the License for the specific language governing permissions and | ||
| # limitations under the License. | ||
|
|
||
| import logging | ||
| import os | ||
| from typing import Any | ||
|
|
||
| from adk_issue_monitoring_agent.settings import GITHUB_BASE_URL | ||
| from adk_issue_monitoring_agent.settings import LLM_MODEL_NAME | ||
| from adk_issue_monitoring_agent.settings import OWNER | ||
| from adk_issue_monitoring_agent.settings import REPO | ||
| from adk_issue_monitoring_agent.settings import SPAM_LABEL_NAME | ||
| from adk_issue_monitoring_agent.utils import error_response | ||
| from adk_issue_monitoring_agent.utils import post_request | ||
| from google.adk.agents.llm_agent import Agent | ||
| from requests.exceptions import RequestException | ||
|
|
||
| logger = logging.getLogger("google_adk." + __name__) | ||
|
|
||
|
|
||
| def load_prompt_template(filename: str) -> str: | ||
| file_path = os.path.join(os.path.dirname(__file__), filename) | ||
| with open(file_path, "r") as f: | ||
| return f.read() | ||
|
|
||
|
|
||
| PROMPT_TEMPLATE = load_prompt_template("PROMPT_INSTRUCTION.txt") | ||
|
|
||
| # --- Tools --- | ||
|
|
||
|
|
||
| def flag_issue_as_spam( | ||
| item_number: int, detection_reason: str | ||
| ) -> dict[str, Any]: | ||
| """ | ||
| Flags an issue as spam by adding a label and leaving a comment for maintainers. | ||
|
|
||
| Args: | ||
| item_number (int): The GitHub issue number. | ||
| detection_reason (str): The explanation of what the spam is. | ||
| """ | ||
| logger.info(f"Flagging #{item_number} as SPAM. Reason: {detection_reason}") | ||
|
|
||
| label_url = ( | ||
| f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{item_number}/labels" | ||
| ) | ||
| comment_url = ( | ||
| f"{GITHUB_BASE_URL}/repos/{OWNER}/{REPO}/issues/{item_number}/comments" | ||
| ) | ||
|
|
||
| alert_body = ( | ||
| "🚨 **Automated Spam Detection Alert** 🚨\n" | ||
| "@maintainers, a suspected spam comment was detected in this thread.\n\n" | ||
| f"**Reason:** {detection_reason}" | ||
| ) | ||
|
|
||
| try: | ||
| # 1. Add Label | ||
| post_request(label_url, {"labels": [SPAM_LABEL_NAME]}) | ||
| # 2. Post Alert Comment | ||
| post_request(comment_url, {"body": alert_body}) | ||
| return {"status": "success", "message": "Maintainers alerted successfully."} | ||
| except RequestException as e: | ||
| return error_response(f"Error flagging issue: {e}") | ||
|
|
||
|
|
||
| root_agent = Agent( | ||
| model=LLM_MODEL_NAME, | ||
| name="spam_auditor_agent", | ||
| description="Audits issue comments for spam.", | ||
| instruction=PROMPT_TEMPLATE.format( | ||
| OWNER=OWNER, | ||
| REPO=REPO, | ||
| ), | ||
| tools=[flag_issue_as_spam], | ||
| ) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.