重写为 AI 情报 Agent 并接入 Jenkins 流水线

This commit is contained in:
Codex
2026-07-08 21:41:46 +08:00
commit f471c2a08a
45 changed files with 2163 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
"""Core application settings and wiring."""
+43
View File
@@ -0,0 +1,43 @@
from functools import lru_cache
from pathlib import Path
from pydantic import Field
from pydantic_settings import BaseSettings, SettingsConfigDict
class Settings(BaseSettings):
# 配置统一来自环境变量,保证本地和服务器使用同一套启动路径。
app_name: str = "SignalScout"
env: str = "dev"
database_url: str = Field(
default="mysql+pymysql://signalscout:signalscout@127.0.0.1:3306/signalscout?charset=utf8mb4"
)
llm_base_url: str = "https://api.zayuapi.com/v1"
llm_api_key: str = ""
llm_model: str = "grok-4.3"
llm_timeout_seconds: int = 90
llm_max_tokens: int = 4000
news_recent_days: int = 3
news_max_items: int = 12
news_search_max_records: int = 20
github_recent_days: int = 30
github_min_stars: int = 20
github_max_projects: int = 5
agent_timezone: str = "Asia/Shanghai"
agent_cron_hour: int = 8
agent_cron_minute: int = 30
report_dir: Path = Path("reports")
log_file: Path = Path("logs/app.log")
log_tail_lines: int = 200
model_config = SettingsConfigDict(
env_file=".env",
env_prefix="SIGNALSCOUT_",
extra="ignore",
)
@lru_cache
def get_settings() -> Settings:
# Caching prevents repeated env parsing while tests can still clear it if needed.
return Settings()
+39
View File
@@ -0,0 +1,39 @@
import logging
from logging.handlers import RotatingFileHandler
from pathlib import Path
def configure_logging(log_file: Path) -> None:
"""Configure console and file logging once for local service runs."""
log_file.parent.mkdir(parents=True, exist_ok=True)
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
if not _has_handler("signalscout-console"):
# Console logs are for the terminal window that runs `make run`.
console_handler = logging.StreamHandler()
console_handler.name = "signalscout-console"
console_handler.setFormatter(_formatter())
root_logger.addHandler(console_handler)
if not _has_handler("signalscout-file"):
# File logs persist agent failures after the terminal scrollback is gone.
file_handler = RotatingFileHandler(
log_file,
maxBytes=2_000_000,
backupCount=3,
encoding="utf-8",
)
file_handler.name = "signalscout-file"
file_handler.setFormatter(_formatter())
root_logger.addHandler(file_handler)
def _formatter() -> logging.Formatter:
# Include module name so source, API, and runner failures are easy to separate.
return logging.Formatter("%(asctime)s %(levelname)s [%(name)s] %(message)s")
def _has_handler(name: str) -> bool:
# Uvicorn imports the app during startup, so handlers must be idempotent.
return any(handler.name == name for handler in logging.getLogger().handlers)