# 数据库工具与交互文档 本文档描述 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_id` 和 `trace_id`,全链路可追踪 --- ## 2. 环境配置 ### 2.1 必需的环境变量 在 `.env` 文件中配置以下变量: ```bash # 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 创建数据库 ```sql 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」。 执行方式: ```bash # 方式 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 各个关键节点被调用: ```python 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 日志** 模式: ```python 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.py` 或 `run_search_agent.py` 中注册 Hook: ```python from src.pipeline.hooks import DatabasePersistHook # 创建 Pipeline pipeline = PipelineOrchestrator(...) # 注册数据库 Hook pipeline.add_hook(DatabasePersistHook()) # 运行 Pipeline await pipeline.run(ctx) ``` ### 6.2 查询任务执行记录 ```sql -- 查询最近 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 监控与告警 ```sql -- 统计最近 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 mysql` 或 `brew 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'") ``` **原因**:用户名或密码错误,或用户无权限 **解决**: ```sql -- 创建用户并授权 CREATE USER 'your_user'@'localhost' IDENTIFIED BY 'your_password'; GRANT ALL PRIVILEGES ON better_me.* TO 'your_user'@'localhost'; FLUSH PRIVILEGES; ``` ### 7.2 调试模式 启用详细日志: ```python 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. **连接池调优**:根据并发量调整 `minsize` 和 `maxsize` --- ## 附录 A:完整建表 SQL 将以下 SQL 保存为 `docs/database-schema.sql` 并执行: ```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` - [ ] 运行测试任务,检查表中是否有数据写入