nickusevich/ai-engineering-assignment
GitHub: nickusevich/ai-engineering-assignment
基于混合检索与 LLM 重排序的足球新闻新颖性检测 pipeline,自动判断新文章是发布、跳过还是送人工审查。
Stars: 0 | Forks: 0
# 足球新闻 Pipeline
该 Pipeline 会读取一篇新的足球文章,并判断与数据库中已有的文章相比,它是提供了新的信息,还是仅仅报道了同一事件。
它分三个步骤进行:
1. 混合搜索通过语义(vector embeddings)和关键词(Postgres 全文搜索)查找相似文章,然后使用 Reciprocal Rank Fusion (RRF) 将两个排名合并。
2. LLM 将这些候选文章筛选为确实与同一事件相关的文章。
3. 第二次 LLM 调用会检查新文章是否带来了新的事实、状态更新或财务细节。如果模型不确定,文章将被发送给人工编辑,而不是被默默地分类。
## 架构
```
┌──────────────┐
news.csv ──▶│ Ingestion │──▶ embeddings ──▶ Postgres (pgvector + tsvector)
└──────────────┘ │
▼
┌────────────────────────────────────────┐
incoming ─── Task 1 ───▶│ Hybrid retrieval │
article │ semantic (pgvector) + keyword (tsv) │
│ ────── RRF fusion ────── │
└────────────────────────────────────────┘
│
┌─────────────────▼──────────────────────┐
Task 2 ──▶│ LLM rerank (parallel, bounded) │
│ → relevant matches above threshold │
└─────────────────┬──────────────────────┘
▼
┌────────────────────────────────────────┐
│ LLM novelty assessment │
│ → PUBLISH / SKIP / REVIEW │
│ → persist to `decisions` table │
└─────────────────┬──────────────────────┘
▼
Task 3 ──▶ Observability: REVIEW queue + audit + drift stats
```
## 快速开始
**要求:** Docker、Docker Compose 和 OpenRouter API key。
复制示例 env 文件,粘贴您的 API key,然后启动 stack。Docker Compose 会使用 pgvector 启动 Postgres,加载 `data/news.csv`,运行所有三个任务,并将结果写入 `outputs/`。
```
cp .env.example .env # then edit and set OPENROUTER_API_KEY
docker compose up
```
## 任务
### 任务 1:相似文章搜索
给定一个查询,返回最相似的前 K 篇文章。两个搜索并行运行:用于语义的 pgvector 余弦相似度和用于关键词重叠的 Postgres tsvector。这两个排名列表通过 Reciprocal Rank Fusion (RRF) 合并。RRF 不需要分数归一化,这非常有用,因为这两个评分系统产生的量级截然不同。
```
docker compose run --rm app uv run python main.py --task 1 \
--query "Tottenham Bergvall injury" --top-k 5
```
### 任务 2:发布、跳过或审查
对于 `data/incoming_news.json` 中的每一篇文章,Pipeline 会找到最相似的现有文章,然后要求 LLM 只保留那些确实关于同一事件的文章(这些调用并行运行,受 semaphore 限制)。第二次 LLM 调用随后判断新文章是否添加了现有报道中没有的新事实、状态变化或财务细节。
如果置信度低于 `NOVELTY_CONFIDENCE_THRESHOLD`,决策将被标记为 REVIEW,以便人工编辑进行查看。每个决策都会保存到 `decisions` 表中,以标准化文本的 hash 为键,因此再次运行 Pipeline 会原地更新行,而不是插入重复项。
```
docker compose run --rm app uv run python main.py --task 2
```
输出到 `outputs/task2_decisions.json`。示例运行的输出已提交在 `outputs/sample_task2_decisions.json` 中。
### 任务 3:编辑决策的可观测性
独立做出编辑决策的 Pipeline 不应在没有监控的情况下运行。任务 3 展示了系统一直在做什么,以便编辑和 ML 工程师能够尽早发现问题:每个决策的审计跟踪(输入文本、决策、置信度、推理、匹配文章、时间戳),PUBLISH、SKIP 和 REVIEW 的聚合计数,以及带有完整上下文的当前 REVIEW 队列。
在生产环境中,这将成为编辑工作清单和尽早发现问题的方法。平均置信度的突然下降通常意味着出错了:embedding 模型损坏、糟糕的 prompt 更改或输入文章类型发生了转变。
```
docker compose run --rm app uv run python main.py --task 3
```
输出:`outputs/task3_analysis.json`。
## 评估
位于 `data/ground_truth.json` 的一个小型基准数据文件(目前包含两篇标记文章)由 `scripts/evaluate.py` 使用,用于针对最新的任务 2 运行结果测量决策准确度。
```
docker compose run --rm app uv run python scripts/evaluate.py
```
## 配置
所有设置都位于 `.env` 中。有关完整列表,请参见 `.env.example`。
重要配置项:
* `LLM_MODEL`:任何 OpenRouter chat 模型。
* `EMBEDDING_MODEL` 和 `EMBEDDING_DIMENSION`:必须与模型输出大小匹配。
* `TOP_K` 和 `RRF_K`:检索设置。
* `RERANKER_RELEVANCE_THRESHOLD` 和 `RERANKER_MAX_CONCURRENT`:rerank 阶段。
* `NOVELTY_CONFIDENCE_THRESHOLD`:REVIEW 门控阈值。
## 开发
如果要在不重新构建 app container 的情况下进行本地工作,请在 Docker 中运行 Postgres,并在主机上运行 app:
```
cp .env.example .env # add OPENROUTER_API_KEY
docker compose up -d db # Postgres with pgvector only
uv venv && uv sync # Python 3.12+ via uv
uv run python main.py --task 1 # default query
```
## 工程笔记
文章和决策通过标准化文本的 SHA256 hash 进行去重:Unicode NFC,去除脚注和 markdown,折叠空白字符。微小的格式差异不会改变 hash,因此同一篇文章不会被插入两次。
每篇文章还存储了生成其 vector 的 embedding 模型。如果 `EMBEDDING_MODEL` 在运行之间发生了变化,app 将拒绝启动。否则,来自两个不同 embedding 空间的 vector 会悄无声息地破坏相似度排名。
reranker 使用 `asyncio.gather` 并行运行 LLM 调用,并受 `asyncio.Semaphore(RERANKER_MAX_CONCURRENT)` 限制。如果某个候选文章的调用失败,fallback 会使用 RRF 分数并再次检查阈值。
## 测试
```
make test # 44 isolated unit tests, runs in under 3 seconds
make check # black + ruff + mypy + tests
```
测试涵盖 RRF 融合、异步 reranker(并行调度、阈值、fallback、并发限制)、novelty 检测器分支、LLM 客户端(JSON 解析和 schema 验证)、embedding 服务(整个 batch 的维度检查、batching)以及 hash 和标准化辅助函数。所有内容均被 mock,因此测试套件不需要数据库和网络。CI 会在每次 push 和 PR 时运行相同的套件(`.github/workflows/ci.yml`)。
## 许可证
MIT
标签:PostgreSQL, 人工智能, 向量数据库, 数据检索, 文本处理, 新闻去重, 测试用例, 版权保护, 用户模式Hook绕过, 计算机取证, 请求拦截, 逆向工具