# 接口参考 (CLI + REST)

> Satellite Agent Infra Analyst · v0.1.0

## 1. CLI (`satagent`)

```
satagent [--db PATH] <subcommand> [...args]
```

`--db` 可被环境变量 `SATAGENT_DB` 覆盖。默认 DB 在 `agent/data/agent.db`。

### 1.1 `init` — 初始化

```bash
satagent init
```
- 建 schema + 写入 84 条市场模型 + 6 家公司 seed
- 幂等(再跑一次只会覆盖 market_model,公司用 `INSERT OR IGNORE`)

### 1.2 `classify` — 只分类不入库

```bash
satagent classify --text "..."
echo "文本" | satagent classify        # 也支持 stdin
```

返回 JSON:
```json
{
  "threads": ["核心网"],
  "scenarios": [],
  "dimensions": ["技术", "产品"],
  "thesis_impact": "增强",
  "confidence": 0.75,
  "evidence": {
    "thread_keywords": {"核心网": ["5G NTN", "信关站"]},
    "positive_hits": 3,
    "negative_hits": 0
  }
}
```

### 1.3 `ingest` — 录入单条事件

```bash
satagent ingest \
  --title "..." \
  --text "..." \
  --source "公司公告" \
  --url "https://..." \
  --occurred-at "2026-05-29T18:30:00" \
  --indicators "跟踪订单确认" "跟踪交付节奏" \
  --companies "震有科技"        # 可选,缺省时按 aliases 自动匹配
```

返回 JSON:
```json
{
  "id": 42,
  "classification": {...},
  "companies": ["震有科技"]
}
```

### 1.3b `fetch` — Phase 2 抓取主入口 🆕

```bash
# JSONL 源(批量,等价于 ingest-file)
satagent fetch --source jsonl --path samples/events.jsonl

# 纯文本源(把一个文件当成一条公告,extract 自动跑)
satagent fetch --source text --path /tmp/announcement.txt \
    --source-name "公司公告" --occurred-at 2026-05-28T10:00:00
```

`fetch` 运行 `Source.fetch() → classify → extract → enrich_companies → insert_event`,
extract 自动从中文文本里抓订单金额(亿/万自动换算)、capex、opex、time_window。
返回 JSON 包含每条事件的 id / threads / thesis_impact / numeric。

支持的 source:`jsonl`(本地 JSONL,复用 events.jsonl 格式)· `text`(单文件文本)。
新增 source 只需在 `satellite_agent/sources/` 下 + 一个模块、注册到 `load_source`。

### 1.4 `ingest-file` — 批量灌入 JSONL

```bash
satagent ingest-file samples/events.jsonl
```

JSONL 每行:
```json
{"title": "...", "content": "...", "occurred_at": "ISO", "source": "...", "url": "...", "next_indicators": [], "companies": [], "numeric": {"order_amount_cny_yi": 5.2, "time_window": "2026-Q3"}}
```
`content` 字段是分类输入。`companies` / `next_indicators` / `numeric` 可选;`numeric` 字段如存在会**覆盖** `extract.py` 自动解析结果。

> `ingest-file` 现在是 `fetch --source jsonl` 的薄壳,保留为向后兼容入口。

### 1.5 `events` — 查询事件

```bash
satagent events --limit 50
satagent events --thread 核心网 --since 2026-05-01 --until 2026-06-01
```

返回事件 array,每条带反序列化好的 JSON array 字段。

### 1.6 `report` — 周报

```bash
satagent report                                       # 默认 7 天窗口,markdown
satagent report --window 14 --end 2026-06-01T00:00:00 --format md
satagent report --format json                         # 拿 JSON 给前端
```

输出包含:四主线评分表 / Thesis 变化 / 各主线关键事件 / 风险预警 / 本周点名公司 / 下周跟踪清单。

### 1.6b `decision` — Phase 3a 双视角决策周报 🆕

```bash
satagent decision                                     # 默认双视角 markdown
satagent decision --view ceo                          # 仅 CEO 视角
satagent decision --view investor                     # 仅投资视角
satagent decision --window 14 --baseline-weeks 4 --format json
satagent decision --end 2026-05-31T12:00:00 --view both
```

CEO 视角输出:主驱动变量 top-3(主线 × 维度强度排序)/ 主线评分(本周 vs 上周 + Δ + 数字流)/ 战略建议(进入 / 加速 / 等待 / 退出 / 观察)/ 风险预警 / 下周跟踪。

投资视角输出:主线相对热度(本周 vs N 周均值)/ 同主线公司位势矩阵(composite = log(1+订单)+log(1+营收)+0.5×点名+0.3×净 thesis)/ 仓位调节信号(主线级 加 / 减 / 持,含证据链)/ 异常告警。

### 1.7 `market-model` — 市场模型查询

```bash
satagent market-model                       # 全量(84 条)
satagent market-model --thread 芯片
satagent market-model --year 2027
satagent market-model --thread 终端 --year 2030
```

### 1.8 `companies` — 公司卡片

```bash
satagent companies
satagent companies --thread 核心网
```

### 1.9 `regress` — 分类器回归

```bash
satagent regress samples/labeled_regression.jsonl
satagent regress samples/labeled_regression.jsonl --format json
satagent regress samples/labeled_regression.jsonl --no-company-enrich    # 测试纯规则基线
```

输出:整体指标 + per-thread P/R/F1 + 失败 case 列表(带原因)。

## 2. REST API (FastAPI)

启动:
```bash
uvicorn satellite_agent.api:app --reload
# Swagger UI: http://127.0.0.1:8000/docs
```

### 2.1 `GET /health`

健康检查。

```json
{ "status": "ok", "version": "0.1.0" }
```

### 2.2 `POST /classify`

```json
// request
{ "text": "..." }
// response: 同 CLI classify
```

### 2.3 `POST /events/ingest`

```json
// request
{
  "title": "...",            // 可空,缺省取 text 首行
  "text": "...",             // 必填
  "source": "...",           // 可空
  "url": "...",              // 可空
  "occurred_at": "ISO",      // 可空,缺省取当前时间
  "next_indicators": [],     // 可空
  "companies": null          // null = 自动 alias 匹配;[] = 显式不匹配;["A","B"] = 手动指定
}
// response
{
  "id": 42,
  "classification": {...},
  "companies": [...]
}
```

### 2.4 `GET /events`

| Query | 类型 | 默认 |
|---|---|---|
| `thread` | str | — |
| `since` | str (ISO) | — |
| `until` | str (ISO) | — |
| `limit` | int (1-500) | 50 |

### 2.5 `GET /events/{event_id}`

单事件详情。`404` 表示不存在。

### 2.6 `GET /report/weekly`

| Query | 类型 | 默认 |
|---|---|---|
| `window` | int (1-90) | 7 |
| `end` | str (ISO) | 当前时间 |
| `format` | `json`\|`md` | `json` |

`format=md` 返回 `{ "markdown": "..." }`,前端直接渲染。

### 2.6b `GET /decision/weekly` 🆕

| Query | 类型 | 默认 | 说明 |
|---|---|---|---|
| `window` | int (1-90) | 7 | 当前窗口天数 |
| `baseline_weeks` | int (1-26) | 4 | 投资视角 4 周均值 baseline |
| `end` | str (ISO) | now | |
| `view` | `ceo`\|`investor`\|`both` | `both` | |
| `format` | `json`\|`md` | `json` | |

`format=json` 返回结构(以 view=both 为例):

```json
{
  "window": {"start": ..., "end": ..., "days": 7, "baseline_weeks": 4},
  "event_count": 7,
  "ceo": {
    "main_drivers": [{"thread": "终端", "dimension": "风险", "strength": -1.75, "direction": "下行", "evidence": [...]}],
    "thread_scores": {"核心网": {"current": 2.25, "previous": 1.00, "delta": 1.25, "event_count": 3, "money_cny_yi": 2.4}},
    "strategy_recommendations": [{"thread": "核心网", "action": "进入", "trigger": "...", "current_score": 2.25, "delta": 1.25}],
    "risks": [...],
    "next_tracking": [...]
  },
  "investor": {
    "thread_heat": [{"thread": "核心网", "current": 2.25, "avg_4w": 0.25, "delta_vs_avg": 2.00}],
    "company_matrix": {"核心网": [{"name": "震有科技", "peer_rank": 1, "composite": 2.50, ...}]},
    "position_signals": [{"thread": "核心网", "signal": "加", "evidence": [...]}],
    "alerts": [...]
  }
}
```

### 2.7 `GET /market-model`

| Query | 类型 |
|---|---|
| `thread` | str |
| `year` | int |

### 2.8 `GET /companies`

| Query | 类型 |
|---|---|
| `thread` | str |

返回的公司对象里,`products` / `customers` / `aliases` 已反序列化为数组。

## 3. 错误处理

MVP 阶段 FastAPI 用默认 422 / 404 / 500,无自定义错误码:

| 场景 | 状态码 |
|---|---|
| 请求体不符合 pydantic 模型 | 422 |
| event id 不存在 | 404 |
| 路径/查询参数类型错误 | 422 |
| 内部异常 | 500 |

CLI 错误从 stderr 输出,非零 exit code。`--text` 必填项缺失时退出码 2。

## 4. 典型工作流

### 4.1 本地日常跟踪

```bash
# 把今天的几条新闻塞进 JSONL
cat > today.jsonl <<EOF
{"title":"...","content":"...","occurred_at":"2026-06-01T10:00:00"}
{"title":"...","content":"...","occurred_at":"2026-06-01T15:30:00"}
EOF

satagent ingest-file today.jsonl
satagent report --window 7 --format md > weekly.md
```

### 4.2 前端对接

```js
// 1. 灌入事件
const r = await fetch('/events/ingest', {
  method: 'POST',
  headers: {'content-type': 'application/json'},
  body: JSON.stringify({title, text, occurred_at}),
});
const { id, classification } = await r.json();

// 2. 拉本周报告
const w = await fetch('/report/weekly?window=7&format=json').then(r => r.json());
// w.thread_scores / w.risks / w.next_tracking ...
```

### 4.3 CI 回归门

```yaml
# .github/workflows/regression.yml
- run: pip install -e ".[dev]"
- run: pytest -q                                 # 含 test_regression.py
- run: satagent regress samples/labeled_regression.jsonl
```
