**这是本文档旧的修订版!**

README

项目简介

本项目实现了一个 无人值守的电子硬件资讯采集与发布系统,基于 GPT/Agent 自动完成: - 采集:从 hackster.io、Kickstarter、Seeed Blog 等源站抓取最新文章; - 分析:调用 GPT 自动提取标题、摘要、参数表、标签; - 翻译:中英文双语输出; - 去重与合规:SimHash 检查、引用标注、版权检测; - 发布:每日自动向 eetree.cn 发布 10–20 篇文章。

系统完全无人值守,依赖 Agent/MCP 工具链完成全流程任务。支持自动回滚与黑名单机制,保障质量与合规。


目录结构

├── README.md               # 项目说明
├── openapi.yaml            # API 契约(抓取/分析/发布接口)
├── sources.yaml            # 源站配置文件
├── orchestrator.py         # 调度与流水线控制
├── workers/                # 各阶段 Worker 实现
│   ├── fetcher.py          # 抓取
│   ├── parser.py           # 解析
│   ├── deduper.py          # 去重
│   ├── nlp_agent.py        # GPT 调用
│   ├── guard.py            # 风控合规
│   └── publisher.py        # 发布
└── storage/                # 数据存储定义(DB schema / S3 等)

工作流

1. Source Poller:按 sources.yaml 配置轮询 RSS/API/HTML。 2. Fetcher:抓取 HTML/JSON,写入队列。 3. Parser:抽取正文、图片、作者、时间。 4. Deduper:SimHash 比对,重复则丢弃。 5. NLP Agent:调用 GPT,输出 JSON(标题、摘要、specs、标签、风险标记、置信度)。 6. Guard:合规检查,低置信或高风险任务丢回队列。 7. Publisher:写入 CMS API,附带引用与参考。 8. Monitor:监控成功率/成本/延迟,支持撤稿与黑名单。


OpenAPI (openapi.yaml)

snippet.yaml
openapi: 3.0.3
info:
  title: Eetree Auto Publisher API
  version: 1.0.0
servers:
  - url: https://api.eetree.cn
paths:
  /articles:
    post:
      summary: 发布文章
      requestBody:
        required: true
        content:
          application/json:
            schema:
              type: object
              properties:
                title:
                  type: string
                body:
                  type: string
                tags:
                  type: array
                  items: { type: string }
                source_url:
                  type: string
                lang:
                  type: string
                  enum: [zh, en]
                specs:
                  type: object
                attribution:
                  type: string
      responses:
        '200':
          description: 发布成功
          content:
            application/json:
              schema:
                type: object
                properties:
                  id: { type: string }
                  status: { type: string }
  /rollback/{id}:
    post:
      summary: 撤回已发布文章
      parameters:
        - in: path
          name: id
          schema: { type: string }
          required: true
      responses:
        '200': { description: 撤回成功 }

源站配置 (sources.yaml)

snippet.yaml
sources:
  - name: hackster
    type: rss
    url: https://www.hackster.io/feed
    rate_limit: 30/m
    strategy: rss-parse-summarize

  - name: kickstarter
    type: api
    url: https://api.kickstarter.com/v1/discover
    rate_limit: 10/m
    strategy: api-parse-summarize

  - name: seeed
    type: html
    url: https://www.seeedstudio.com/blog/
    rate_limit: 20/m
    strategy: html-parse-translate

  - name: sparkfun
    type: rss
    url: https://www.sparkfun.com/news.xml
    rate_limit: 15/m
    strategy: rss-parse-summarize

Agent/MCP 工具定义

- fetch_url(url) → { html, finalurl, status } - extract_content(html) → { title, text, images[], publishedat, author } - dedupe_check(fingerprint) → { isduplicate, matchedids[] } - llm_summarize_translate(payload) → { summaryzh, summaryen, specs, tags, confidence } - copyright_guard(payload) → { riskflags, attribution } - publish_to_cms(article) → { id, status } - rollback(id) → { status } — 提示词(NLP Agent) ``` 你是电子硬件资讯编辑机器人。输入为【原文】,输出 JSON: - title_zh, title_en - summary_zh, summary_en - bullets[3-6] - specs{k:v} - tags[3-6] - risk_flags[] - confidence [0-1] 要求:不杜撰,所有数据必须能在原文找到依据。 ``` --- 发布策略 - 每日发布 10–20 篇,按来源/专题配额分配。 - CRON 定时 + 随机抖动,避免集中发布。 - 自动在正文末尾生成“引用与参考”区块。 — 监控与合规 - 指标:抓取成功率、NLP 成功率、Token 成本、发布成功率。 - 风控:Robots 合规、版权检查、敏感词检测。 - 回滚:支持文章撤稿与黑名单机制。 --- 部署建议 - 抓取与处理:K8s CronJob + Worker Pod。 - 队列:Redis/RabbitMQ。 - 数据库:Postgres + pgvector。 - 对象存储:S3/OSS。 - 模型调用:OpenAI GPT-4o-mini 或私有化轻量模型。 — # Docker Compose 部署示例 下面提供 docker-compose.yml.env.exampleinit/pgvector.sql。把三段分别存成对应文件后,执行 docker compose up -d 即可起一套最小可用的无人值守流水线(含:Redis 队列、Postgres+pgvector、MinIO 对象存储、Orchestrator + 5 个 Workers)。 docker-compose.yml ```yaml version: "3.9" x-app-env: &app_env REDIS_URL: ${REDIS_URL} DATABASE_URL: ${DATABASE_URL} OPENAI_API_KEY: ${OPENAI_API_KEY} EETREE_API_URL: ${EETREE_API_URL} EETREE_API_TOKEN: ${EETREE_API_TOKEN} MINIO_ENDPOINT: ${MINIO_ENDPOINT} MINIO_ACCESS_KEY: ${MINIO_ACCESS_KEY} MINIO_SECRET_KEY: ${MINIO_SECRET_KEY} MINIO_BUCKET: ${MINIO_BUCKET} SOURCES_FILE: /app/config/sources.yaml PROMPT_FILE: /app/config/prompt.md services: # 队列 & 存储 redis: image: redis:7-alpine restart: unless-stopped ports: ["6379:6379"] command: ["redis-server", "--save", "60", "1000", "--loglevel", "warning"] postgres: image: postgres:16-alpine restart: unless-stopped environment: POSTGRES_DB: ${POSTGRES_DB} POSTGRES_USER: ${POSTGRES_USER} POSTGRES_PASSWORD: ${POSTGRES_PASSWORD} ports: ["5432:5432"] volumes: - pgdata:/var/lib/postgresql/data - ./init/pgvector.sql:/docker-entrypoint-initdb.d/pgvector.sql:ro minio: image: quay.io/minio/minio:RELEASE.2025-01-11T00-00-00Z restart: unless-stopped environment: MINIO_ROOT_USER: ${MINIO_ACCESS_KEY} MINIO_ROOT_PASSWORD: ${MINIO_SECRET_KEY} command: ["server", "/data", "--console-address", ":9001"] ports: ["9000:9000", "9001:9001"] volumes: - minio:/data createbuckets: image: quay.io/minio/mc:latest depends_on: - minio entrypoint: ["/bin/sh","-c"] command: > "mc alias set local http://minio:9000 ${MINIO_ACCESS_KEY} ${MINIO_SECRET_KEY} && mc mb -p local/${MINIO_BUCKET} || true && mc anonymous set download local/${MINIO_BUCKET} || true" # 代码镜像(可替换为你自己的 CI 构建镜像) app-base: image: python:3.11-slim working_dir: /app volumes: - ./:/app command: bash -lc "pip install -r requirements.txt && tail -f /dev/null" orchestrator: extends: { service: app-base } environment: *app_env depends_on: [redis, postgres, minio, createbuckets] command: bash -lc "pip install -r requirements.txt && python orchestrator.py" restart: unless-stopped worker-fetch: extends: { service: app-base } environment: *app_env depends_on: [redis] command: bash -lc "python workers/fetcher.py" restart: unless-stopped worker-parse: extends: { service: app-base } environment: *app_env depends_on: [redis] command: bash -lc "python workers/parser.py" restart: unless-stopped worker-dedupe: extends: { service: app-base } environment: *app_env depends_on: [redis, postgres] command: bash -lc "python workers/deduper.py" restart: unless-stopped worker-nlp: extends: { service: app-base } environment: *app_env depends_on: [redis] command: bash -lc "python workers/nlp_agent.py" restart: unless-stopped worker-guard-publish: extends: { service: app-base } environment: *app_env depends_on: [redis] command: bash -lc "python workers/guard.py && python workers/publisher.py" restart: unless-stopped volumes: pgdata: {} minio: {} ``` .env.example env # === 数据库 / 队列 === POSTGRES_DB=eetree POSTGRES_USER=eetree POSTGRES_PASSWORD=secret DATABASE_URL=postgresql+psycopg://eetree:secret@postgres:5432/eetree REDIS_URL=redis://redis:6379/0 # === MinIO 对象存储 === MINIO_ENDPOINT=http://minio:9000 MINIO_ACCESS_KEY=minioadmin MINIO_SECRET_KEY=minioadminsecret MINIO_BUCKET=eetree-media # === 外部服务 === OPENAI_API_KEY=sk-xxxx EETREE_API_URL=https://api.eetree.cn EETREE_API_TOKEN=your_eetree_token # === 配置文件路径(容器内) === SOURCES_FILE=/app/config/sources.yaml PROMPT_FILE=/app/config/prompt.md init/pgvector.sql ```sql -- 在首次初始化数据库时自动启用 pgvector 扩展 CREATE EXTENSION IF NOT EXISTS vector; -- 示例:为 articles 表添加向量列(若你使用 pgvector 存 embeddings) -- ALTER TABLE articles ADD COLUMN IF NOT EXISTS embedding vector(1536); ``` 目录与启动 bash # 1) 准备文件 mkdir -p init config # 将上面的 pgvector.sql 保存到 init/pgvector.sql # 将你的 sources.yaml、prompt.md 放到 config/ # 2) 本地测试 cp .env.example .env docker compose up -d # 3) 查看日志 docker compose logs -f orchestrator worker-nlp # 4) 停止与清理 # docker compose down -v ## 说明 - app-base 会挂载当前目录,默认安装 requirements.txt 后常驻;各 worker 继承它并执行相应脚本。 - 如你已将代码构建为镜像(例如 ghcr.io/eetree/auto-publisher:latest),可将 app-base 与各 worker 的 image 替换为你的镜像,并去掉 volumes/pip install。 - pgvector 用于存储文章向量,用来驱动“相关文章/AI 深解读”。若改用 Milvus/Weaviate,请在 Compose 中添加对应服务并调整 DATABASE_URL/索引层逻辑。 - MinIO 提供与 S3 兼容的对象存储,createbuckets 服务在首次启动时自动创建桶并设置匿名下载(可按需修改权限)。 - 生产环境建议: - 打开 Postgres 持久化备份、设置密码管理(Vault/Secrets Manager)。 - 使用 Traefik/Nginx 暴露 Publisher API 或内部管理面板。 - 将 OpenAI Key 等敏感信息改用 Docker Secrets 或 K8s Secret。