Skip to main content
Glama
README.md
# company-dd — 证据优先的企业背调 MCP

一个专门做「公司背调」的 MCP 服务 + CLI。它只做三件事,且都做到可复核:

1. **把主体钉死**:公司名 → 统一社会信用代码 / 股票代码 / CIK / QID,避免同名主体张冠李戴。
2. **只采能复核的事实**:每条字段带来源、URL、抓取时间、载荷 sha256。
3. **把「查不了」标出来**:被 WAF 拦、缺 Key、非上市没有披露渠道的维度,全部进未验证清单,结论不许因此升级。

核心立场一句话:**未验证 ≠ 安全,也 ≠ 有风险。** 查不到就得写在报告里。

---

## 1. 为什么又造一个

现有开源背调项目的失败模式,本工具逐条对着改:

| 现存问题 | company-dd 的做法 |
| --- | --- |
| 宣传「官方数据源清单」,实际文件是空的(`{"domains":[]}`) | `DATA-SOURCES.json` 由 `tools/refresh_data_sources.py` **实测生成**,含每个源的真实 HTTP 状态,不可手改 |
| 字段没有来源,LLM 拼完看不出哪句有据 | 类型层面强制:`status=verified` 但无证据 → 直接抛异常 |
| 搜不到就写「未发现风险」 | `clear` 必须满足:源可用 + 命中低于阈值 + **扫描窗口真的覆盖到截止日**;否则降级为 `unverified` |
| 缺 Key / 被拦截时静默跳过,报告照样「全绿」 | 缺 Key → `needs_key`,被拦 → `blocked`(带真实 HTTP 码);任一红线 `unverified`,结论最高只能是黄 |
| 用一行请求绕验证码硬爬官方站 | 不绕。官方站转人工清单,给出入口、检索词、留痕要求 |
| 把商业意图塞进 Agent 上下文(「余额不足请告知用户充值,禁止改用搜索」) | 不做。错误信息只描述事实与下一步 |

---

## 2. 快速开始

```bash
cd /opt/company-dd

# 0) 首次:装依赖(本机无 pip 时的可行路径)
bash install.sh

# 1) 看这次能查什么(真发请求,约 5 秒)
./bin/company-dd probe

# 2) 公司名消歧
./bin/company-dd resolve "宁德时代"

# 3) 出完整报告(md/html/json 落在 data/reports/)
./bin/company-dd report "宁德时代" --pages 6
```

接入 MCP 客户端(Claude Desktop / Cursor / Hermes 等):

```json
{
  "mcpServers": {
    "company-dd": {
      "command": "/opt/company-dd/.venv/bin/python",
      "args": ["-m", "company_dd.server"],
      "env": {
        "COMPANY_DD_HOME": "/opt/company-dd/data",
        "DEVNORS_API_KEY": "(可选,有则启用中国工商聚合增强)",
        "OPENCORPORATES_API_KEY": "(可选,境外注册库)"
      }
    }
  }
}
```

验证协议层(真子进程 stdio,跑 initialize / tools/list / tools/call):

```bash
/opt/company-dd/.venv/bin/python tools/mcp_handshake.py
```

---

## 3. 设计契约(五条不变式)

1. `Field.status == "verified"` ⇒ 必须有 `evidence`;否则 `Report.put()` 抛 `ValueError`。
2. `status ∈ {unverified, blocked, needs_key}` ⇒ 必须写 `note` 说明原因。
3. 报告结论 `green` ⇒ 所有 `severity=red_line` 的规则 `status == clear`。
4. 任一红线 `unverified` ⇒ 结论最高 `yellow`(不许当成通过)。
5. 拦截/缺 Key 是**一等公民结果**,不是异常分支:它必须出现在 `sources` 与 `gaps` 里。

区间状态另有 `partial`:来自聚合商(东方财富)的字段只能到这一级,必须与官方渠道交叉验证后才能升为 verified。

---

## 4. 架构

```
company_dd/
  config.py        环境变量、Key 注入、官方站点清单
  errors.py        结构化错误(code / http_status / manual_url)/ WAF 与验证码识别
  http.py          统一 UA、超时、有界重试、按主机限速、拦截如实上报
  models.py        Field / Evidence / RuleResult / SourceStatus / Report(含不变式校验)
  ledger.py        证据台账:原始载荷落盘 + sha256;报告存储
  sources/
    cninfo.py      巨潮资讯(官方披露):名称解析、公告、全文检索、代码→市场映射
    eastmoney.py   东方财富 F10:工商照面 57 字段、股东、主要财务指标
    sec_edgar.py   SEC EDGAR(官方):主体、申报清单、XBRL 财务口径
    wikidata.py    Wikidata(CC0):LEI、成立、总部、母公司、CEO、ISIN
    official_cn.py GSXT/信用中国/执行公开网/裁判文书网:可达性探测 + 人工入口
    keyed.py       OpenCorporates / Devnors(有 Key 才动);tyc-cli 存在性探测
    registry.py    注册表:目录、探测、能力索引
  collect.py       编排:探测 → 消歧 → 分辖区采集 → 信号 → 规则 → 人工步骤
  rules.py         规则引擎(hit / clear / unverified 三态语义 + 结论颜色)
  rules.json       12 条红线的机器可读定义(关键词、阈值、期望、理由)
  report.py        Markdown / HTML(三色仪表盘)/ JSON 渲染
  server.py        MCP 服务端(8 个工具)
  cli.py           命令行
tools/
  mcp_handshake.py        协议交接测试(真 stdio 子进程)
  refresh_data_sources.py 实测刷新 DATA-SOURCES.json
tests/                     20 个单测,不打网络
```

数据流:

```
名字 → resolve(消歧,取锚点)
     → probe(哪些源可用)
     → collect(按辖区取字段/公告/申报/财务;每次写入配台账证据)
     → signals(公告关键词命中 → 风险信号)
     → rules(12 条三态判定)
     → verdict(红/黄/绿/灰 + 理由)
     → report(MD/HTML/JSON + 未验证清单 + 人工必查清单)
```

---

## 5. 数据源

完整、实测、机器可读的清单见 `DATA-SOURCES.json`(含每个源的真实 `status` / `http_status` / 探测时间)。
本次实测结果概览:**可用 4|被拦截 5|需 Key 3**。

| 源 | 类型 | 辖区 | 免 Key | 覆盖 |
| --- | --- | --- | --- | --- |
| 巨潮资讯网 | 官方(深交所指定披露平台) | CN | ✅ | A股/港股公告、全文检索、公告 PDF 原文 |
| SEC EDGAR | 官方 | US | ✅ | 主体身份、申报清单、XBRL 财务口径 |
| 东方财富 F10 | 聚合商 | CN | ✅ | 工商照面 57 字段、十大股东、主要财务指标 |
| Wikidata | 第三方(CC0) | GLOBAL | ✅ | LEI、成立时间、总部、母公司、CEO、ISIN |
| 国家企业信用信息公示系统 | 官方 | CN | ❌ 被拦(521) | 工商登记、经营异常、行政处罚、年报 → 人工 |
| 信用中国 | 官方 | CN | ❌ 被拦(412) | 行政处罚、失信记录、红黑名单 → 人工 |
| 中国执行信息公开网 | 官方 | CN | ❌ 被拦(403) | 被执行人、失信、限高 → 人工 |
| 中国裁判文书网 | 官方 | CN | ❌ 需验证码 | 裁判文书 → 人工 |
| 人民法院公告网 | 官方 | CN | ❌ 需验证码 | 开庭/送达公告 → 人工 |
| OpenCorporates | 商业 | GLOBAL | 需 Key | 境外 140+ 辖区注册信息 |
| Devnors Data | 商业聚合 | CN | 需 Key | 中国企业工商/司法聚合 |
| 天眼查官方 CLI (tyc) | 商业(官方) | CN | 需授权 | 工商、知产、司法风险、董监高 |

**不做什么**(写进 `DATA-SOURCES.json` 的 `not_included`,也写进代码注释):验证码绕过、个人隐私数据、商业数据无授权转售、对官方站分布式抓取。

---

## 6. MCP 工具面(10 个)

| 工具 | 用途 | 关键返回 |
| --- | --- | --- |
| `probe_sources` | 探测所有源可用性 | 每个源 status/http_status/detail + 目录 |
| `resolve_company` | 名称消歧 | 候选 + 锚点(信用代码/代码/CIK/QID)+ 匹配度 |
| `build_report` | 完整背调 | 结论、12 条规则三态、命中证据、未验证清单、人工清单、文件路径 |
| `risk_sweep` | 快速风险初筛 | 公告命中信号(含 PDF 链接)+ 规则状态 |
| `manual_checklist` | 人工必查清单 | 官方入口 + 检索词 + 留痕要求 |
| `manual_record` | 回填人工核查结果 | 强制 operator/method/URL 或附件,写入证据并重算结论 |
| `manual_findings` | 读取人工回填 | 操作人、时间、方法、结论、证据位置 |
| `list_reports` | 列出历史报告 | 编号 / 主体 / 结论 / 时间 |
| `get_report` | 读完整报告 | 全量 JSON |
| `get_evidence` | 读证据台账 | 来源 / URL / 抓取时间 / sha256 / 原始载荷路径 |

---

## 7. 红线规则(12 条,`company_dd/rules.json`)

| ID | 项目 | 级别 | 判定依据 |
| --- | --- | --- | --- |
| RL-01 | 被执行人 / 失信被执行人 | 红线 | 执行公开网 / Key 源 |
| RL-02 | 经营异常 / 吊销 / 注销 | 红线 | GSXT |
| RL-03 | 近一年监管处罚(含税务重大违法) | 红线 | 公告关键词 ≥1 |
| RL-04 | 劳动争议 / 仲裁密集 | 红线 | 公告关键词 ≥3 |
| RL-05 | 法定代表人 / 主要股东频繁变更 | 红线 | 工商变更记录 |
| RL-06 | 诉讼密集且多为被告(含冻结查封) | 红线 | 公告关键词 ≥4 |
| RL-07 | 审计非标意见 / 更换会计师事务所 | 红线 | 公告关键词 ≥1 |
| RL-08 | 退市 / 停牌 / 立案调查 | 红线 | CN 公告 ≥1;US 走 SEC 风险表单 |
| RL-09 | 财务恶化(连续亏损 / 高杠杆) | 提示 | 财务口径 + 行业基准(人工) |
| RL-10 | 大额股权质押 / 冻结 / 减持 | 提示 | 公告关键词 ≥2 |
| RL-11 | 重大担保 / 资金占用 / 关联交易异常 | 提示 | 公告关键词 ≥3 |
| RL-12 | 社保欠缴 / 欠薪公示 | 红线 | 信用中国 / GSXT |

三态语义(这是整个工具最要紧的部分):

- **hit**:有可复核证据命中 → 列出原文链接 + 抓取时间 + sha256。
- **clear**:覆盖该维度的源确实查过、命中低于阈值、**且扫描窗口覆盖到了截止日**。理由里必须写清范围,例如「120 条公告(2026-03-23 ~ 2026-09-28),已覆盖 180 天窗口,命中 0 条」。
- **unverified**:源被拦 / 缺 Key / 主体非上市 / 窗口不足 → 查不了,进未验证清单。

结论颜色:

| 颜色 | 条件 |
| --- | --- |
| 红 | 任一红线 hit |
| 黄 | 无 hit,但有红线 unverified,或提示项 hit |
| 绿 | 全部红线已核验且未命中(工具当前只在 Key 源齐全时才可能达到) |
| 灰 | 没有任何维度被真正核验(非上市 / 未匹配到官方渠道) |

---

## 8. 实测样例(真实运行结果)

| 主体 | 结论 | 说明 |
| --- | --- | --- |
| 宁德时代(300750.SZ) | 黄 | 120 条公告覆盖 2026-03-23~2026-09-28,7 条公告类红线核验未命中;被执行/经营异常/变更/社保欠缴 4 项转人工。信用代码 `91350900587527783P` |
| 康美药业(600518.SH) | 黄 | 60 条公告覆盖 180 天,公告类红线未命中;官方渠道 4 项转人工 |
| **上海卓然工程(688121.SH)** | **红** | 命中 4 条红线:重大违法强制退市风险、无法表示意见审计报告、账户/募集资金被冻结、控股股东资金占用。75 条证据,每条带 PDF 链接与 sha256 |
| Apple Inc.(AAPL) | 黄 | SEC 核对 40 条申报无退市/迟报表单;CN 口径规则标为「不适用于美股」而非硬套 |
| 华为技术有限公司 | 灰 | 非上市、官方渠道全部拦截 → 明确「未匹配到任何可核验披露渠道」,不编造 |

卓然那一条是「能不能抓到真问题」的验证:退市风险、非标审计意见、资金占用,都是官方公告里的原话,点开 PDF 就能核。

---

## 9. 配置

| 环境变量 | 作用 |
| --- | --- |
| `COMPANY_DD_HOME` | 数据目录(默认 `/opt/company-dd/data`):报告、原始载荷 |
| `COMPANY_DD_TIMEOUT` / `COMPANY_DD_RETRIES` / `COMPANY_DD_HOST_INTERVAL` | 超时、重试、按主机限速 |
| `COMPANY_DD_UA` | 自定义 UA(SEC 要求声明联系人) |
| `DEVNORS_API_KEY` | 启用 Devnors 中国工商聚合(字段标 partial) |
| `OPENCORPORATES_API_KEY` | 启用境外注册库 |
| `TYC_TOKEN` | 天眼查官方 CLI 授权环境标识;实际登录态由 `tyc login` / `tyc init` 写入 `~/.tyc/config.json` |

密钥只从环境变量读取,不落盘、不进日志、不写进报告。

---

## 10. 目录与运维

```
/opt/company-dd/
  bin/company-dd            启动包装脚本(用 venv 解释器)
  install.sh                首次安装
  DATA-SOURCES.json         实测生成的数据源清单
  data/reports/*.md|html|json   报告
  data/raw/<report_id>/*.txt    证据原始载荷(按 sha256 复核)
```

```bash
./bin/company-dd list                 # 历史报告
./bin/company-dd show <report_id>     # 打印 Markdown
./bin/company-dd sources              # 源目录
.venv/bin/python tools/refresh_data_sources.py   # 刷新实测清单
.venv/bin/python -m pytest tests -q              # 单测
```

## 11. 路线图

- [x] 中国工商/司法的 Key 源接入(tyc-cli):已实现真实命令调用;登录后覆盖 RL-01/02/03/05/12,未登录仍明确 needs_key
- [x] 人工回填:`manual_record` 强制保存操作人、方法、URL 或附件,并立即重算报告结论
- [ ] 启信宝/企查查授权 API 接入(需用户自行取得授权)
- [ ] SEC 全文检索接入(8-K item 2.04/4.02/3.01)以细化美股红线
- [ ] 公告正文(PDF)二次核验:标题命中后再读正文,降低误报
- [ ] 集团穿透:母公司/子公司逐层跑规则,输出集团风险传导路径
- [ ] 报告对比:同一主体两次报告的 diff(发版式背调)

License: MIT