显示页面 讨论 修订记录 反向链接 本页面只读。您可以查看源文件,但不能更改它。如果您觉得这是系统错误,请联系管理员。 # README ## 项目简介 本项目实现了一个 **无人值守的电子硬件资讯采集与发布系统**,基于 GPT/Agent 自动完成: - **采集**:从 hackster.io、Kickstarter、Seeed Blog 等源站抓取最新文章; - **分析**:调用 GPT 自动提取标题、摘要、参数表、标签; - **翻译**:中英文双语输出; - **去重与合规**:SimHash 检查、引用标注、版权检测; - **发布**:每日自动向 [eetree.cn](https://www.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) ```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) ```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, final_url, status } - `extract_content(html)` → { title, text, images[], published_at, author } - `dedupe_check(fingerprint)` → { is_duplicate, matched_ids[] } - `llm_summarize_translate(payload)` → { summary_zh, summary_en, specs, tags, confidence } - `copyright_guard(payload)` → { risk_flags, 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.example`** 与 **`init/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。