PubMed Monitor #61
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
| name: PubMed Monitor | |
| on: | |
| schedule: | |
| # 每天早上 8:17 UTC 运行一次(避开整点) | |
| - cron: '17 8 * * *' | |
| workflow_dispatch: | |
| inputs: | |
| query: | |
| description: 'PubMed 检索词' | |
| required: false | |
| default: '"Speciation"[Mesh] OR "Hybridization, Genetic"[Mesh] OR "Genetic Introgression"[Mesh] OR "Reproductive Isolation"[Mesh] OR "Gene Flow"[Mesh] OR (speciation[Title/Abstract] AND species[Title/Abstract]) OR ("lineage sorting"[Title/Abstract]) OR ("adaptive radiation"[Title/Abstract]) OR ("incipient species"[Title/Abstract])' | |
| type: string | |
| days: | |
| description: '追溯天数' | |
| required: false | |
| default: '7' | |
| type: string | |
| max_papers: | |
| description: '最大文献数(分析前)' | |
| required: false | |
| default: '20' | |
| type: string | |
| concurrency: | |
| group: pubmed-monitor | |
| cancel-in-progress: true | |
| permissions: | |
| issues: write | |
| contents: read | |
| env: | |
| QUERY: ${{ github.event.inputs.query || '"Speciation"[Mesh] OR "Hybridization, Genetic"[Mesh] OR "Genetic Introgression"[Mesh] OR "Reproductive Isolation"[Mesh] OR "Gene Flow"[Mesh] OR (speciation[Title/Abstract] AND species[Title/Abstract]) OR ("lineage sorting"[Title/Abstract]) OR ("adaptive radiation"[Title/Abstract]) OR ("incipient species"[Title/Abstract])' }} | |
| DAYS: ${{ github.event.inputs.days || '7' }} | |
| MAX_PAPERS: ${{ github.event.inputs.max_papers || '20' }} | |
| GH_TOKEN: ${{ secrets.PAT_TOKEN }} | |
| LOG_LEVEL: DEBUG | |
| MCP_LOG_DETAIL: "1" | |
| PROMPT_LOG: "1" | |
| PUBMED_EMAIL: ${{ secrets.PUBMED_EMAIL }} | |
| # Anthropic API 配置 | |
| ANTHROPIC_AUTH_TOKEN: ${{ secrets.ANTHROPIC_AUTH_TOKEN }} | |
| ANTHROPIC_BASE_URL: ${{ secrets.ANTHROPIC_BASE_URL || 'https://api.minimaxi.com/anthropic' }} | |
| ANTHROPIC_MODEL: ${{ secrets.ANTHROPIC_MODEL || 'MiniMax-M2.1' }} | |
| CLAUDE_AGENT_SDK_SKIP_VERSION_CHECK: "true" | |
| jobs: | |
| monitor: | |
| if: github.repository == 'gqy20/IssueLab' | |
| runs-on: ubuntu-latest | |
| steps: | |
| - uses: actions/checkout@v4 | |
| - uses: astral-sh/setup-uv@v7 | |
| with: | |
| python-version: '3.13' | |
| enable-cache: true | |
| - run: uv sync | |
| - name: Run PubMed Monitor | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts/observability | |
| echo "🔍 扫描 PubMed 新文献..." | |
| echo "📊 检索词: ${{ env.QUERY }}" | |
| echo "📅 追溯天数: ${{ env.DAYS }}" | |
| echo "" | |
| uv run python scripts/monitor_pubmed.py \ | |
| --token "${{ env.GH_TOKEN }}" \ | |
| --repo "${{ github.repository }}" \ | |
| --query '${{ env.QUERY }}' \ | |
| --days ${{ env.DAYS }} \ | |
| --max-papers ${{ env.MAX_PAPERS }} \ | |
| --email "${{ env.PUBMED_EMAIL }}" \ | |
| --output artifacts/pubmed_candidates.json \ | |
| --metrics-output artifacts/observability/pubmed_metrics.json \ | |
| 2>&1 | tee artifacts/observability/pubmed_monitor.log | |
| - name: Emit observability summary | |
| if: always() | |
| env: | |
| WF_NAME: ${{ github.workflow }} | |
| JOB_NAME: monitor | |
| RUN_ID: ${{ github.run_id }} | |
| REPO_NAME: ${{ github.repository }} | |
| SHA: ${{ github.sha }} | |
| run: | | |
| set -euo pipefail | |
| mkdir -p artifacts/observability | |
| python - << 'PY' | |
| import json | |
| import os | |
| from datetime import UTC, datetime | |
| from pathlib import Path | |
| metrics_path = Path("artifacts/observability/pubmed_metrics.json") | |
| m = {} | |
| if metrics_path.exists(): | |
| try: | |
| m = json.loads(metrics_path.read_text(encoding="utf-8")) | |
| except Exception: | |
| m = {} | |
| input_count = int(m.get("fetched_count", 0) or 0) | |
| success_count = int(m.get("created_issues", 0) or 0) | |
| failed_count = 0 | |
| skipped_count = 0 | |
| status_raw = str(m.get("status", "")) | |
| failures = [] | |
| if status_raw in {"no_recommendation", "insufficient_candidates", "no_new_after_dedupe", "no_new_papers"}: | |
| skipped_count = 1 | |
| if status_raw == "started": | |
| failures.append({"code": "METRICS_MISSING", "count": 1, "samples": []}) | |
| status = "success" | |
| if failures: | |
| status = "partial" | |
| elif skipped_count > 0 and success_count == 0: | |
| status = "skipped" | |
| payload = { | |
| "schema_version": "v1", | |
| "workflow": os.getenv("WF_NAME"), | |
| "job": os.getenv("JOB_NAME"), | |
| "run_id": os.getenv("RUN_ID"), | |
| "repo": os.getenv("REPO_NAME"), | |
| "sha": os.getenv("SHA"), | |
| "finished_at": datetime.now(UTC).isoformat().replace("+00:00", "Z"), | |
| "metrics": { | |
| "input_count": input_count, | |
| "success_count": success_count, | |
| "failed_count": failed_count, | |
| "skipped_count": skipped_count, | |
| "new_papers_count": int(m.get("new_papers_count", 0) or 0), | |
| "recommended_count": int(m.get("recommended_count", 0) or 0), | |
| "created_issues": int(m.get("created_issues", 0) or 0), | |
| }, | |
| "failures_topn": failures[:5], | |
| "status": status, | |
| } | |
| Path("artifacts/observability/pubmed_monitor__monitor.json").write_text( | |
| json.dumps(payload, ensure_ascii=False, indent=2), encoding="utf-8" | |
| ) | |
| summary = [ | |
| "## Observability (pubmed monitor)", | |
| "", | |
| "| metric | value |", | |
| "|---|---:|", | |
| f"| input_count | {payload['metrics']['input_count']} |", | |
| f"| new_papers_count | {payload['metrics']['new_papers_count']} |", | |
| f"| recommended_count | {payload['metrics']['recommended_count']} |", | |
| f"| created_issues | {payload['metrics']['created_issues']} |", | |
| f"| status | {payload['status']} |", | |
| ] | |
| with open(os.environ["GITHUB_STEP_SUMMARY"], "a", encoding="utf-8") as fh: | |
| fh.write("\n".join(summary) + "\n") | |
| PY | |
| - name: Upload observability artifact | |
| if: always() | |
| uses: actions/upload-artifact@v4 | |
| with: | |
| name: pubmed-monitor-observability-${{ github.run_id }} | |
| path: artifacts/observability/ | |
| retention-days: 14 |