database-tool.md 30 KB

数据库工具与交互文档

本文档描述 Search Agent 系统与 MySQL 数据库的交互机制、表结构设计、环境配置和使用指南。


1. 数据库交互架构

1.1 核心组件

┌─────────────────────────────────────────────────────────┐
│                   Pipeline 执行层                        │
│  ┌──────────────┐  ┌──────────────┐  ┌──────────────┐  │
│  │ DemandAnalysis│  │ContentSearch │  │QualityFilter │  │
│  └──────┬───────┘  └──────┬───────┘  └──────┬───────┘  │
│         │                  │                  │          │
│         └──────────────────┼──────────────────┘          │
│                            │                             │
│                    ┌───────▼────────┐                    │
│                    │ DatabaseHook   │                    │
│                    │ (旁路观测)      │                    │
│                    └───────┬────────┘                    │
└────────────────────────────┼──────────────────────────────┘
                             │
                    ┌────────▼─────────┐
                    │ AsyncMySQLPool   │
                    │ (连接池管理)      │
                    └────────┬─────────┘
                             │
                    ┌────────▼─────────┐
                    │  MySQL Database  │
                    │  (better_me)     │
                    └──────────────────┘

1.2 设计原则

  1. 旁路特性:数据库持久化失败不阻断主流程,只记录警告日志
  2. 异步非阻塞:使用 aiomysql 连接池,避免阻塞 Agent 执行
  3. 幂等性保证:通过 idempotency_key 防止重复任务创建
  4. 可追溯性:每个任务关联唯一 task_idtrace_id,全链路可追踪

2. 环境配置

2.1 必需的环境变量

.env 文件中配置以下变量:

# MySQL 数据库连接配置
SEARCH_AGENT_DB_HOST=localhost
SEARCH_AGENT_DB_PORT=3306
SEARCH_AGENT_DB_USER=your_username
SEARCH_AGENT_DB_PASSWORD=your_password
SEARCH_AGENT_DB_DB=better_me

# 可选:连接池配置
SEARCH_AGENT_DB_MINSIZE=5
SEARCH_AGENT_DB_MAXSIZE=20
SEARCH_AGENT_DB_CHARSET=utf8mb4

2.2 配置类说明

配置通过 SearchAgentMySQLConfig 类管理(位于 src/config/database/mysql_config.py):

字段 默认值 说明
host localhost MySQL 服务器地址
port 3306 MySQL 端口
user "" 数据库用户名(必填)
password "" 数据库密码(必填)
db "" 数据库名称(必填)
charset utf8mb4 字符集
minsize 5 连接池最小连接数
maxsize 20 连接池最大连接数

3. 数据库表结构

3.1 表关系图

supply_demand_snapshot (需求快照)
         │
         │ 1:N
         ▼
supply_task (任务主表) ──────┐
         │                   │
         │ 1:N               │ 1:N
         ▼                   ▼
supply_task_stage      supply_task_event
(阶段执行明细)          (事件日志)
         │
         │ 1:N
         ▼
supply_candidate_content (候选内容池)
         │
         │ 1:1
         ├──────────────────┬──────────────────┐
         ▼                  ▼                  ▼
supply_content_feature  supply_content_score  supply_account_profile
(内容特征)              (评分结果)            (账号画像)

3.2 核心表说明

3.2.1 supply_demand_snapshot (需求快照表)

用途:冻结上游需求上下文,便于回溯和审计

核心字段 类型 说明
demand_id BIGINT 上游需求 ID(业务侧主键)
query VARCHAR(255) 核心搜索词
query_expansion JSON 扩展词列表(同义词/场景词)
expected_count INT 期望返回条数
audience_profile JSON 目标人群画像

3.2.2 supply_task (任务主表)

用途:状态机核心,记录任务全生命周期

核心字段 类型 说明
task_id VARCHAR(64) 任务唯一 ID(UUID)
trace_id VARCHAR(64) Agent trace_id,串联整个执行链路
status VARCHAR(32) 任务状态(见下方状态机)
current_stage VARCHAR(32) 当前执行阶段
idempotency_key VARCHAR(128) 幂等键(防重复)
error_code / error_message VARCHAR / TEXT 错误信息

任务状态流转

PENDING → RUNNING → RECALL_DONE → ENRICHED → RANKED → PLAN_CREATED → COMPLETED
                                                                    ↘
                                                                    FAILED

3.2.3 supply_task_stage (阶段执行明细表)

用途:记录每个 Pipeline 阶段的执行情况

核心字段 类型 说明
stage_name VARCHAR(32) 阶段名(RECALL/RANK/CREATE_PLAN 等)
stage_status VARCHAR(32) 阶段状态(PENDING/RUNNING/SUCCESS/FAILED)
attempt_no INT 第几次执行(支持重试)
duration_ms BIGINT 阶段耗时(毫秒)
input_payload / output_payload JSON 输入输出快照

3.2.4 supply_candidate_content (候选内容池)

用途:存储搜索召回的原始候选文章

核心字段 类型 说明
source_keyword VARCHAR(255) 触发召回的关键词
recall_round INT 第几轮召回
title / url VARCHAR 文章标题和链接
account_id / account_name VARCHAR 公众号信息
view_count / like_count BIGINT 阅读量/点赞量
dedup_hash CHAR(64) 去重哈希(title+url 的 SHA256)
quality_flag VARCHAR(32) 初步质量标记(UNKNOWN/PASS/REJECT)

3.2.5 supply_content_score (评分与筛选结果表)

用途:存储最终排序与入选结果

核心字段 类型 说明
relevance_score DECIMAL(5,2) 相关性分(0-100)
quality_score DECIMAL(5,2) 内容质量分(0-100)
total_score DECIMAL(6,2) 综合分
is_selected TINYINT 是否入选最终结果
rank_no INT 最终排序名次
score_detail JSON 评分明细(权重、中间分)

3.2.6 supply_account_profile (账号画像表)

用途:账号维度的补全与打分输入

核心字段 类型 说明
account_id VARCHAR(128) 公众号 ID(wx_gh)
credibility_score DECIMAL(5,2) 可信度评分(0-100)
risk_tags JSON 风险标签(标题党/营销号)
median_view_count BIGINT 历史阅读量中位数

3.2.7 supply_task_event (事件日志表)

用途:完整可观测性与审计

核心字段 类型 说明
event_type VARCHAR(64) 事件类型(STATE_CHANGE/TOOL_CALL/ERROR)
event_level VARCHAR(16) 日志级别(DEBUG/INFO/WARN/ERROR)
tool_name VARCHAR(64) 工具名(如 weixin_search)
event_payload JSON 事件详情(入参/出参摘要)

4. 数据库初始化

4.1 创建数据库

CREATE DATABASE IF NOT EXISTS better_me
  DEFAULT CHARACTER SET utf8mb4
  DEFAULT COLLATE utf8mb4_unicode_ci;

USE better_me;

4.2 执行建表 SQL

完整的建表 SQL 请参考本文档末尾的「附录 A:完整建表 SQL」。

执行方式:

# 方式 1:通过 MySQL 客户端
mysql -u your_username -p better_me < docs/database-schema.sql

# 方式 2:通过命令行直接执行
mysql -u your_username -p -e "source /path/to/database-schema.sql"

5. Hook 工作机制

5.1 DatabasePersistHook 生命周期

DatabasePersistHook 实现了 PipelineHook 接口,在 Pipeline 各个关键节点被调用:

class DatabasePersistHook(PipelineHook):
    async def on_pipeline_start(ctx):
        # 1. 写入需求快照
        # 2. 创建任务主记录(status=RUNNING)
        # 3. 写入 INIT 事件

    async def on_stage_complete(stage_name, ctx):
        # 1. 写入阶段执行记录
        # 2. 根据阶段名写入业务表:
        #    - content_search → supply_candidate_content
        #    - quality_filter → supply_content_score
        #    - account_precipitate → supply_account_profile
        # 3. 更新任务状态

    async def on_gate_check(gate_name, result, ctx):
        # 写入门禁检查事件(通过/拦截/回退)

    async def on_error(stage_name, error, ctx):
        # 1. 更新任务状态为 FAILED
        # 2. 写入错误事件

    async def on_pipeline_complete(ctx):
        # 写入 FINALIZE 事件

5.2 错误处理策略

所有数据库操作均采用 try-except + warning 日志 模式:

try:
    await self.pool.async_save(sql, params)
except Exception as exc:
    logger.warning("persist task skipped: %s", exc)
    # 不抛出异常,不阻断主流程

这确保了即使数据库不可用,Pipeline 仍能正常执行。


6. 使用指南

6.1 启用数据库持久化

src/pipeline/runner.pyrun_search_agent.py 中注册 Hook:

from src.pipeline.hooks import DatabasePersistHook

# 创建 Pipeline
pipeline = PipelineOrchestrator(...)

# 注册数据库 Hook
pipeline.add_hook(DatabasePersistHook())

# 运行 Pipeline
await pipeline.run(ctx)

6.2 查询任务执行记录

-- 查询最近 10 个任务
SELECT task_id, status, current_stage, started_at, duration_ms
FROM supply_task
ORDER BY created_at DESC
LIMIT 10;

-- 查询某个任务的所有阶段执行记录
SELECT stage_name, stage_status, attempt_no, duration_ms
FROM supply_task_stage
WHERE task_id = 'your-task-id'
ORDER BY id;

-- 查询某个任务的候选文章
SELECT title, url, view_count, quality_flag
FROM supply_candidate_content
WHERE task_id = 'your-task-id'
ORDER BY view_count DESC;

-- 查询某个任务的最终入选文章
SELECT c.title, c.url, s.total_score, s.rank_no
FROM supply_candidate_content c
JOIN supply_content_score s ON c.id = s.candidate_id
WHERE s.task_id = 'your-task-id' AND s.is_selected = 1
ORDER BY s.rank_no;

6.3 监控与告警

-- 统计最近 24 小时的任务成功率
SELECT
    COUNT(*) as total,
    SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) as success,
    ROUND(SUM(CASE WHEN status = 'COMPLETED' THEN 1 ELSE 0 END) * 100.0 / COUNT(*), 2) as success_rate
FROM supply_task
WHERE created_at >= DATE_SUB(NOW(), INTERVAL 24 HOUR);

-- 查询失败任务的错误分布
SELECT error_code, COUNT(*) as count
FROM supply_task
WHERE status = 'FAILED' AND created_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY error_code
ORDER BY count DESC;

7. 故障排查

7.1 常见错误

错误 1:Table doesn't exist

WARNING - persist stage skipped: (1146, "Table 'better_me.supply_task_stage' doesn't exist")

原因:数据库表未创建 解决:执行本文档末尾的完整建表 SQL

错误 2:Connection refused

WARNING - DatabasePersistHook init skipped: (2003, "Can't connect to MySQL server on 'localhost'")

原因:MySQL 服务未启动或连接配置错误 解决

  1. 检查 MySQL 服务状态:systemctl status mysqlbrew services list
  2. 验证 .env 中的连接配置
  3. 测试连接:mysql -h localhost -u your_user -p

错误 3:Access denied

WARNING - DatabasePersistHook init skipped: (1045, "Access denied for user 'xxx'@'localhost'")

原因:用户名或密码错误,或用户无权限 解决

-- 创建用户并授权
CREATE USER 'your_user'@'localhost' IDENTIFIED BY 'your_password';
GRANT ALL PRIVILEGES ON better_me.* TO 'your_user'@'localhost';
FLUSH PRIVILEGES;

7.2 调试模式

启用详细日志:

import logging
logging.getLogger('src.pipeline.hooks.db_hook').setLevel(logging.DEBUG)
logging.getLogger('src.infra.database').setLevel(logging.DEBUG)

8. 性能优化建议

  1. 批量插入:候选文章和账号画像使用 executemany 批量插入
  2. 索引优化:已为常用查询字段创建索引(task_id, status, created_at 等)
  3. 分区表:若 supply_task_event 数据量大,建议按月分区
  4. 连接池调优:根据并发量调整 minsizemaxsize

附录 A:完整建表 SQL

将以下 SQL 保存为 docs/database-schema.sql 并执行:

SET NAMES utf8mb4;
SET FOREIGN_KEY_CHECKS = 0;

-- =========================================================
-- 1) 上游需求快照表:冻结需求上下文,便于回溯
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_demand_snapshot (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  demand_id BIGINT NOT NULL COMMENT '上游需求ID(业务侧主键)',
  demand_code VARCHAR(64) DEFAULT NULL COMMENT '上游需求编码(可选)',
  query VARCHAR(255) NOT NULL COMMENT '核心搜索词',
  query_expansion JSON DEFAULT NULL COMMENT '扩展词列表(同义词/场景词/禁用词等)',
  platform VARCHAR(32) NOT NULL DEFAULT 'weixin' COMMENT '目标平台,当前为 weixin',
  expected_count INT NOT NULL DEFAULT 10 COMMENT '期望返回条数',
  audience_profile JSON DEFAULT NULL COMMENT '目标人群画像(如50+)',
  quality_constraints JSON DEFAULT NULL COMMENT '质量约束(最低阅读量、黑名单词等)',
  source_payload JSON DEFAULT NULL COMMENT '上游原始请求快照',
  version INT NOT NULL DEFAULT 1 COMMENT '快照版本号(同一 demand_id 可多版本)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  KEY idx_demand_id (demand_id),
  KEY idx_platform (platform),
  KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='上游需求快照表';


-- =========================================================
-- 2) 任务主表:一个 demand 可触发多次任务(重跑/补跑)
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_task (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '任务唯一ID(建议UUID/雪花ID)',
  demand_snapshot_id BIGINT UNSIGNED NOT NULL COMMENT '关联 supply_demand_snapshot.id',
  demand_id BIGINT NOT NULL COMMENT '冗余字段:上游需求ID',
  trace_id VARCHAR(64) DEFAULT NULL COMMENT 'Agent trace_id,便于追踪模型执行',
  status VARCHAR(32) NOT NULL COMMENT '任务状态:PENDING/RUNNING/RECALL_DONE/ENRICHED/RANKED/PLAN_CREATED/COMPLETED/FAILED/CANCELLED',
  current_stage VARCHAR(32) DEFAULT NULL COMMENT '当前阶段:INIT/RECALL/ACCOUNT_ENRICH/ACCOUNT_PROFILE/CONTENT_ENRICH/RANK/CREATE_PLAN/FINALIZE',
  priority TINYINT NOT NULL DEFAULT 5 COMMENT '优先级(1最高,9最低)',
  retry_count INT NOT NULL DEFAULT 0 COMMENT '已重试次数',
  max_retry INT NOT NULL DEFAULT 3 COMMENT '最大重试次数',
  is_idempotent TINYINT NOT NULL DEFAULT 1 COMMENT '是否启用幂等(1是0否)',
  idempotency_key VARCHAR(128) DEFAULT NULL COMMENT '幂等键(可由 demand_id+query_hash 组成)',
  started_at DATETIME DEFAULT NULL COMMENT '任务启动时间',
  finished_at DATETIME DEFAULT NULL COMMENT '任务完成时间',
  duration_ms BIGINT DEFAULT NULL COMMENT '总耗时(毫秒)',
  error_code VARCHAR(64) DEFAULT NULL COMMENT '错误码(网络超时/参数错误/平台限流等)',
  error_message TEXT COMMENT '错误详情',
  operator VARCHAR(64) DEFAULT 'agent' COMMENT '执行方(agent/manual/cron)',
  ext JSON DEFAULT NULL COMMENT '扩展字段(灰度标记、AB实验信息等)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_task_id (task_id),
  UNIQUE KEY uk_idempotency_key (idempotency_key),
  KEY idx_demand_id (demand_id),
  KEY idx_status_priority (status, priority),
  KEY idx_current_stage (current_stage),
  KEY idx_created_at (created_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='供给任务主表(状态机核心)';


-- =========================================================
-- 3) 阶段执行表:记录每个阶段的执行情况
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_task_stage (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  stage_name VARCHAR(32) NOT NULL COMMENT '阶段名:RECALL/ACCOUNT_ENRICH/ACCOUNT_PROFILE/CONTENT_ENRICH/RANK/CREATE_PLAN',
  stage_status VARCHAR(32) NOT NULL COMMENT '阶段状态:PENDING/RUNNING/SUCCESS/FAILED/SKIPPED',
  attempt_no INT NOT NULL DEFAULT 1 COMMENT '该阶段第几次执行',
  input_payload JSON DEFAULT NULL COMMENT '阶段输入快照',
  output_payload JSON DEFAULT NULL COMMENT '阶段输出快照(摘要)',
  started_at DATETIME DEFAULT NULL COMMENT '阶段开始时间',
  finished_at DATETIME DEFAULT NULL COMMENT '阶段结束时间',
  duration_ms BIGINT DEFAULT NULL COMMENT '阶段耗时(毫秒)',
  error_code VARCHAR(64) DEFAULT NULL COMMENT '阶段错误码',
  error_message TEXT COMMENT '阶段错误信息',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  KEY idx_task_stage (task_id, stage_name),
  KEY idx_stage_status (stage_status),
  KEY idx_started_at (started_at)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务阶段执行明细表';


-- =========================================================
-- 4) 候选内容池:搜索召回到的原始候选
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_candidate_content (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  source_keyword VARCHAR(255) DEFAULT NULL COMMENT '触发召回的关键词',
  recall_round INT NOT NULL DEFAULT 1 COMMENT '第几轮召回(核心词/扩展词/场景词)',
  recall_page INT DEFAULT NULL COMMENT '召回分页页码/游标序号',
  platform VARCHAR(32) NOT NULL DEFAULT 'weixin' COMMENT '来源平台',
  content_id VARCHAR(128) DEFAULT NULL COMMENT '平台内容ID(若可提取)',
  title VARCHAR(512) NOT NULL COMMENT '文章标题',
  url VARCHAR(1024) NOT NULL COMMENT '文章链接(业务唯一候选)',
  digest VARCHAR(1000) DEFAULT NULL COMMENT '文章摘要',
  cover_url VARCHAR(1024) DEFAULT NULL COMMENT '封面图链接',
  publish_time DATETIME DEFAULT NULL COMMENT '发布时间',
  account_id VARCHAR(128) DEFAULT NULL COMMENT '公众号ID(wx_gh)',
  account_name VARCHAR(255) DEFAULT NULL COMMENT '公众号名称',
  biz_info VARCHAR(128) DEFAULT NULL COMMENT '公众号 biz 标识',
  view_count BIGINT DEFAULT NULL COMMENT '阅读量(若有)',
  like_count BIGINT DEFAULT NULL COMMENT '点赞量(若有)',
  comment_count BIGINT DEFAULT NULL COMMENT '评论量(若有)',
  favorite_count BIGINT DEFAULT NULL COMMENT '收藏量(若有)',
  share_count BIGINT DEFAULT NULL COMMENT '分享量(若有)',
  raw_statistics JSON DEFAULT NULL COMMENT '原始统计字段',
  raw_payload JSON DEFAULT NULL COMMENT '原始API返回(单条)',
  dedup_hash CHAR(64) DEFAULT NULL COMMENT '去重哈希(建议 title+url 归一化后sha256)',
  quality_flag VARCHAR(32) DEFAULT 'UNKNOWN' COMMENT '初步质量标记:UNKNOWN/PASS/REJECT',
  reject_reason VARCHAR(255) DEFAULT NULL COMMENT '初筛淘汰原因',
  is_deleted TINYINT NOT NULL DEFAULT 0 COMMENT '软删除标记(1删除)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_task_url (task_id, url(255)),
  KEY idx_task_round (task_id, recall_round),
  KEY idx_task_account (task_id, account_id),
  KEY idx_publish_time (publish_time),
  KEY idx_quality_flag (quality_flag),
  KEY idx_dedup_hash (dedup_hash)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='候选内容池(召回原始结果)';


-- =========================================================
-- 5) 账号画像表:账号维度的补全与打分输入
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_account_profile (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  account_id VARCHAR(128) NOT NULL COMMENT '公众号ID(wx_gh)',
  account_name VARCHAR(255) DEFAULT NULL COMMENT '账号名称',
  biz_info VARCHAR(128) DEFAULT NULL COMMENT '账号biz标识',
  intro TEXT COMMENT '账号简介',
  verify_status TINYINT DEFAULT NULL COMMENT '认证状态(0未认证1认证,按平台定义)',
  category VARCHAR(64) DEFAULT NULL COMMENT '账号分类(健康/资讯/情感等)',
  update_frequency VARCHAR(32) DEFAULT NULL COMMENT '更新频率(daily/weekly/irregular)',
  recent_article_count INT DEFAULT 0 COMMENT '近周期发文数',
  median_view_count BIGINT DEFAULT NULL COMMENT '历史阅读量中位数',
  median_like_count BIGINT DEFAULT NULL COMMENT '历史点赞量中位数',
  vertical_score DECIMAL(5,2) DEFAULT 0 COMMENT '垂直度评分(0-100)',
  stability_score DECIMAL(5,2) DEFAULT 0 COMMENT '稳定更新评分(0-100)',
  credibility_score DECIMAL(5,2) DEFAULT 0 COMMENT '可信度评分(0-100)',
  risk_tags JSON DEFAULT NULL COMMENT '风险标签(标题党/营销号/违规高风险)',
  raw_profile_payload JSON DEFAULT NULL COMMENT '账号详情原始返回',
  raw_history_payload JSON DEFAULT NULL COMMENT '历史文章原始返回(摘要)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_task_account (task_id, account_id),
  KEY idx_task_credibility (task_id, credibility_score),
  KEY idx_category (category)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='账号画像表(账号维度特征)';


-- =========================================================
-- 6) 内容特征表:文章详情解析后的质量特征
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_content_feature (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  candidate_id BIGINT UNSIGNED NOT NULL COMMENT '关联候选内容ID',
  content_length INT DEFAULT NULL COMMENT '正文长度(字符数)',
  image_count INT DEFAULT NULL COMMENT '图片数量',
  video_count INT DEFAULT NULL COMMENT '视频数量',
  has_source_reference TINYINT DEFAULT 0 COMMENT '是否包含来源引用(0否1是)',
  readability_score DECIMAL(5,2) DEFAULT 0 COMMENT '可读性评分(0-100)',
  information_density_score DECIMAL(5,2) DEFAULT 0 COMMENT '信息密度评分(0-100)',
  elder_friendly_score DECIMAL(5,2) DEFAULT 0 COMMENT '老年友好评分(0-100)',
  sentiment_score DECIMAL(5,2) DEFAULT NULL COMMENT '情绪倾向分(可选)',
  risk_score DECIMAL(5,2) DEFAULT 0 COMMENT '风险分(越高风险越大)',
  risk_reasons JSON DEFAULT NULL COMMENT '风险原因明细(夸张词/伪科学词/诱导词)',
  keyword_coverage JSON DEFAULT NULL COMMENT 'query关键词覆盖明细',
  summary TEXT COMMENT '文章摘要(抽取)',
  raw_detail_payload JSON DEFAULT NULL COMMENT '文章详情原始返回',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_task_candidate_feature (task_id, candidate_id),
  KEY idx_task_quality (task_id, readability_score, information_density_score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='内容特征表(文章维度特征)';


-- =========================================================
-- 7) 评分与筛选结果表:最终排序与入选
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_content_score (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  candidate_id BIGINT UNSIGNED NOT NULL COMMENT '关联候选内容ID',
  relevance_score DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '相关性分(0-100)',
  popularity_score DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '热度分(0-100)',
  quality_score DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '内容质量分(0-100)',
  account_score DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '账号可信分(0-100)',
  elder_fit_score DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '老年适配分(0-100)',
  diversity_penalty DECIMAL(5,2) NOT NULL DEFAULT 0 COMMENT '多样性惩罚分(防止同账号过多)',
  total_score DECIMAL(6,2) NOT NULL DEFAULT 0 COMMENT '综合分',
  filter_status VARCHAR(32) NOT NULL DEFAULT 'PENDING' COMMENT '过滤状态:PENDING/PASS/REJECT',
  filter_reason VARCHAR(255) DEFAULT NULL COMMENT '过滤原因',
  is_selected TINYINT NOT NULL DEFAULT 0 COMMENT '是否入选最终结果(1是0否)',
  rank_no INT DEFAULT NULL COMMENT '最终排序名次(从1开始)',
  score_version VARCHAR(32) DEFAULT 'v1' COMMENT '评分模型版本',
  score_detail JSON DEFAULT NULL COMMENT '评分明细(权重、中间分)',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  UNIQUE KEY uk_task_candidate_score (task_id, candidate_id),
  KEY idx_task_selected_rank (task_id, is_selected, rank_no),
  KEY idx_task_total_score (task_id, total_score)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='内容评分与筛选结果表';


-- =========================================================
-- 8) 抓取计划映射表:与AIGC平台交互记录
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_crawler_plan (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  platform VARCHAR(32) NOT NULL DEFAULT 'weixin' COMMENT '平台',
  plan_type VARCHAR(32) NOT NULL COMMENT '建计划方式:BY_CONTENT/BY_ACCOUNT',
  input_count INT NOT NULL DEFAULT 0 COMMENT '输入内容/账号数量',
  crawler_plan_id VARCHAR(128) DEFAULT NULL COMMENT 'AIGC爬取计划ID',
  crawler_plan_name VARCHAR(255) DEFAULT NULL COMMENT 'AIGC爬取计划名称',
  produce_plan_id VARCHAR(128) DEFAULT NULL COMMENT '绑定的生成计划ID(可空)',
  produce_bind_status VARCHAR(32) DEFAULT NULL COMMENT '绑定状态:NOT_REQUIRED/SUCCESS/FAILED',
  request_payload JSON NOT NULL COMMENT '创建计划请求体',
  response_payload JSON DEFAULT NULL COMMENT '创建计划响应体',
  plan_status VARCHAR(32) NOT NULL COMMENT '计划状态:CREATED/FAILED/BIND_SUCCESS/BIND_FAILED',
  error_code VARCHAR(64) DEFAULT NULL COMMENT '失败错误码',
  error_message TEXT COMMENT '失败错误信息',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  updated_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
  PRIMARY KEY (id),
  KEY idx_task_id (task_id),
  KEY idx_crawler_plan_id (crawler_plan_id),
  KEY idx_plan_status (plan_status)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='AIGC抓取计划映射表';


-- =========================================================
-- 9) 事件日志表:完整可观测性与审计
-- =========================================================
CREATE TABLE IF NOT EXISTS supply_task_event (
  id BIGINT UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '自增主键',
  task_id VARCHAR(64) NOT NULL COMMENT '关联任务ID',
  stage_name VARCHAR(32) DEFAULT NULL COMMENT '所属阶段',
  event_type VARCHAR(64) NOT NULL COMMENT '事件类型:STATE_CHANGE/TOOL_CALL/TOOL_RESULT/RETRY/ERROR/MANUAL_INTERVENTION',
  event_level VARCHAR(16) NOT NULL DEFAULT 'INFO' COMMENT '日志级别:DEBUG/INFO/WARN/ERROR',
  tool_name VARCHAR(64) DEFAULT NULL COMMENT '工具名(如 weixin_search)',
  request_id VARCHAR(64) DEFAULT NULL COMMENT '外部请求ID(可用于链路追踪)',
  event_payload JSON DEFAULT NULL COMMENT '事件详情(入参/出参摘要)',
  message VARCHAR(1000) DEFAULT NULL COMMENT '事件描述',
  created_at DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
  PRIMARY KEY (id),
  KEY idx_task_created (task_id, created_at),
  KEY idx_stage_type (stage_name, event_type),
  KEY idx_tool_name (tool_name),
  KEY idx_event_level (event_level)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COMMENT='任务事件日志表';

SET FOREIGN_KEY_CHECKS = 1;

附录 B:快速启动检查清单

  • 已安装 MySQL 5.7+ 或 MariaDB 10.3+
  • 已创建数据库 better_me
  • 已执行完整建表 SQL
  • 已在 .env 中配置数据库连接信息
  • 已验证数据库连接:mysql -h localhost -u your_user -p better_me
  • 已在 Pipeline 中注册 DatabasePersistHook
  • 运行测试任务,检查表中是否有数据写入