InvesResearch DocsAgent架构 · 模块图 + 数据流
Architecture · docs/architecture.md

四条取舍, 决定了 MVP 的样子

Phase 1 Research Agent 的架构由四条互相强化的取舍决定:关键词规则分类、SQLite 本地落盘、CLI + FastAPI 双轨、Enum 显式本体。显式不做的: LLM、共指消解、向量检索、网页抓取、动态市场模型、自动 PPT, 全部留给 Phase 2 / 3。

§2 模块图

三层架构 · 职责严格分离

接口层薄, 领域层是规则与本体, 持久化层是 SQLite + seed。每个模块单一职责, 改 ontology 只动一个文件。

L1 Interfaceargparse · FastAPI
cli.py
argparse · 9 个子命令 · stdin 支持 · `satagent` 入口
api.py
FastAPI · 8 个业务端点 + /health · Swagger UI
L2 Domain纯函数, 无状态
classifier.py
纯函数 classify(text) → dict · 含负向反义 + RISK fallback + dedupe · 无 DB 依赖
ontology.py
唯一真实来源 · 4 主线 + 6 场景 + 6 维度 Enum + 关键词词典 · 改分类规则只动这里
report.py
时间窗聚合 · markdown + JSON 双格式渲染 · 周报 pipeline
regress.py
回归评测 · 整体 + per-thread P/R/F1 · 不入 ingest 流程
L3 PersistenceSQLite · 无 ORM
db.py
schema 4 表 + 连接管理 · schema 变动集中于此
repository.py
CRUD + `enrich_with_company_threads` 反哺
seed.py
市场模型 84 条 + 6 家代表公司
§3.1 Ingest pipeline

事件录入 · 5 步链路

从原文到入库的完整管线。Classifier 是纯函数, 公司反哺是后处理(classifier 不需要懂"公司"概念)。

01
原文输入
标题 + 正文 (text)
02
classify(text)
threads · scenarios · dimensions · thesis_impact · confidence · evidence
03
match_companies
通过 aliases 反向匹配文本中提到的公司
04
enrich_with_company_threads
把命中公司的所属 thread 并入 event.threads(去重)· evidence.company_threads 标记来源
05
insert_event
写入 events 表(JSON array 字段序列化)
§3.2 Weekly report pipeline

周报生成 · 聚合 + 风险 + 跟踪

从事件表到 markdown 周报的聚合链路。Thread 桶聚合 + 风险提取 + 下周指标三路并发, 最后 render。

01
filter window
按 occurred_at 时间窗过滤 → 候选事件
02a
thread 桶聚合
strengthen / weaken / neutral 计数 + 置信度加权净分
02b
风险预警
提取 Risk dim 或 weaken 事件 → 风险列表
03
指标聚合
收集所有 next_indicators 去重保序
04
render
render_markdown / JSON 双格式输出
§4 关键决策

四个架构取舍

每条决策都带"代价说明", 让未来要扩展的人能判断边界。

1

本体用 Enum, 不用自由 tag

自由 tag 会带来 聚合困难 / 回归无标尺 / 关键词词典挂载难。Enum 是周报分桶的天然 key, 让 expected 可序列化, 让词典挂在 THREAD_KEYWORDS[Thread.CHIP] 上。
代价增加新主线 / 场景需要改代码而不仅是配置。MVP 阶段四主线已定死, 不是问题。
2

分类器是 纯函数

classify(text) -> dict 不依赖 DB、不依赖任何全局状态。测试简单 · 回归可批量(30 条 < 1ms) · 公司反哺是后处理(在 ingest pipeline 单独做)。
代价无显式代价 — 这是一条单边受益的设计。
3

_NEGATION_PAIRS 反义对

当负向短语含正向子串(如 "未中标" 含 "中标")时, kw in text 会同时命中 pos 和 neg, 变成假中性。_NEGATION_PAIRS 是一组 (负向, 正向) 关系对, 出现负向短语时扣减对应正向 hit。目前覆盖 8 对。
代价扩展正向词典时, 如果新词常以 "未/被/暂停 + X" 出现, 需要同步加反义对。
4

SQLite 不用 ORM

SQLAlchemy 在 MVP 阶段是过度设计:schema 只有 4 表 · 查询都是简单 filter + sort · 行级 row_factory 直接给 dict · 测试用 tmp_path 一行搞定
代价迁 Postgres 时要重写 SQL, 但 SQL 都集中在 db.py + repository.py, 工作量可控。
§5 数据模型 · events

events 表 schema (含 Phase 2 待扩字段)

当前 14 个字段全部落地。橙色行是 Phase 2 待补字段, 直接关系到 Phase 3 决策层能否启动。

events · 事件主表

indices: occurred_at · thesis_impact
字段类型说明
idINT PK自增主键
title / contentTEXT事件标题 / 正文
source / urlTEXT来源 / 链接(可空)
occurred_atTEXT (ISO)事件发生时间 ISO8601
created_atTEXT (ISO)入库时间
threads / scenarios / dimensionsTEXT (JSON array)分类结果, 多标签
thesis_impactTEXT增强 / 削弱 / 中性
confidenceREAL0.0–1.0
next_indicatorsTEXT (JSON array)下周跟踪项
companiesTEXT (JSON array)命中公司名
numeric_fields Phase 2TEXT (JSON)订单金额 / capex / opex 等数字解析, 如 {"order_amount_cny_yi": 5.2, "client": "某军种"}
time_window_start/end Phase 2TEXT事件涉及时间窗(订单交付期 · 政策生效期)
quality_flag Phase 2TEXT数据质量标记(原文 / 二手 / 推算)
§5 数据模型 · companies

companies 表 schema

当前 8 个字段。橙色行是 Phase 2 待补字段, 支持 Phase 3 仓位信号与公司相对位势。

companies · 公司卡片

unique: name
字段类型说明
nameTEXT UNIQUE公司名(主键)
threadTEXT所属主线(用于公司反哺)
products / customers / aliasesJSON array产品 · 客户 · 别名
moat / risk / revenue_mappingTEXT护城河 / 风险 / 收入映射
scoreREAL评分
orders Phase 2JSON array已披露订单清单 [{"client":..., "amount_cny_yi":..., "signed_at":...}]
revenue_actual Phase 2JSON object财报口径收入按主线 / 季度拆分
peer_rank Phase 2JSON object同主线 peer 排名 {"by_orders_4w": 2, "by_thesis_4w": 3}
position_signal Phase 3TEXT当前仓位信号 加 / 减 / 持