Metadata-Version: 2.4
Name: astro-agent
Version: 0.2.3
Summary: A verifiable LLM agent for hierarchical molecular-cloud catalog analysis
Requires-Python: >=3.11
Description-Content-Type: text/markdown
Requires-Dist: fastapi<1,>=0.115
Requires-Dist: uvicorn[standard]<1,>=0.30
Requires-Dist: pandas<3,>=2.2
Requires-Dist: numpy<3,>=2.0
Requires-Dist: scipy<2,>=1.13
Requires-Dist: odrpack<0.7,>=0.6.1
Requires-Dist: matplotlib<4,>=3.9
Requires-Dist: openai<3,>=1.68
Requires-Dist: pydantic-settings<3,>=2.5
Requires-Dist: httpx<1,>=0.27
Provides-Extra: dev
Requires-Dist: pytest<9,>=8.3; extra == "dev"
Requires-Dist: build<2,>=1.2; extra == "dev"
Requires-Dist: twine<7,>=6; extra == "dev"
Requires-Dist: commitizen<5,>=4; extra == "dev"

# AstroAgent

AstroAgent 是一个面向分子云层级结构目录的科学数据分析 Agent。DeepSeek API 负责理解问题和选择工具；数据筛选、统计拟合与绘图由本地 Pandas、SciPy、odrpack 和 Matplotlib 完成。项目使用 FastAPI 提供接口，并通过自研 Agent harness 管理工具校验、执行、重试、预算与终止。

## 系统架构

```text
用户问题
   ↓
FastAPI /chat
   ↓
DeepSeek API（生成下一步动作）
   ↓
Agent harness（状态、校验、执行、观察、重试、终止）
   ↓
受控工具层（schema、query、fit、compare、plot）
   ↓
Pandas + SciPy + Matplotlib（本地数值计算）
   ↓
回答、工具轨迹、终止原因、token 用量与数值结果
```

工程特性：

- FastAPI 服务与自动生成的 OpenAPI 文档
- DeepSeek tool calling 与本地工具执行隔离
- 工具参数白名单、错误预算、步数预算、时间预算和重复动作熔断
- 完整工具调用轨迹和 token 用量记录
- Windows 与 macOS 初始化脚本
- 不依赖外部 API 的单元测试
- Agent 工具选择和 API 延迟、并发、token 用量评测脚本

科学分析能力：

- 分子云目录字段和数据来源检查
- 条件筛选与结构预览
- 对数空间幂律标度关系拟合
- bootstrap 斜率置信区间
- 数值阈值分组后的标度关系比较
- 对数坐标散点图和拟合线输出

## 环境要求

- Python 3.11 或 3.12
- DeepSeek API Key
- Windows 11 或 macOS

API Key 只应保存在本机 `.env` 中，不要提交到 Git。

## Windows 安装与启动

在 PowerShell 中进入项目目录：

```powershell
cd C:\你的路径\astro-agent
```

如果尚未安装 Python 3.12：

```powershell
winget install Python.Python.3.12
```

重新打开 PowerShell，然后运行初始化脚本：

```powershell
Set-ExecutionPolicy -Scope Process Bypass
.\scripts\setup_windows.ps1
```

该脚本会创建 `.venv`、安装依赖、生成示例目录、运行离线测试，并在 `.env` 不存在时根据模板创建配置文件。

编辑配置：

```powershell
notepad .env
```

启动服务：

```powershell
.\.venv\Scripts\python.exe -m uvicorn app.api:app --reload
```

## macOS 安装与启动

安装 Python 并初始化项目：

```bash
brew install python@3.12
cd /你的路径/astro-agent
bash scripts/setup_mac.sh
```

编辑配置后启动服务：

```bash
source .venv/bin/activate
uvicorn app.api:app --reload
```

## 配置

`.env` 的基本配置如下：

```text
DEEPSEEK_API_KEY=sk-你的密钥
DEEPSEEK_BASE_URL=https://api.deepseek.com
DEEPSEEK_MODEL=deepseek-flash
DEEPSEEK_THINKING=false
ASTRO_DATA_PATH=data/sample_catalog.csv
ASTRO_RESULTS_DIR=results
```

工具路由通常不需要长思考，因此默认关闭 thinking，以降低延迟与 API 费用。模型名称与价格可能变化，运行前请查看 [DeepSeek 官方文档](https://api-docs.deepseek.com/)。

## API 使用

服务启动后打开：

```text
http://127.0.0.1:8000/docs
```

先调用 `GET /health`，确认数据路径、行数和 API Key 配置状态。该接口不会调用 DeepSeek API。

再调用 `POST /chat`：

```json
{
  "question": "拟合半径与速度弥散的标度关系，报告斜率、样本量和95%置信区间。"
}
```

Linux/macOS 也可以使用 curl：

```bash
curl -X POST http://127.0.0.1:8000/chat \
  -H 'Content-Type: application/json' \
  -d '{"question":"拟合半径与速度弥散的标度关系，报告斜率和95%置信区间。"}'
```

响应中的主要字段：

- `answer`：基于工具结果生成的回答
- `trace`：工具名称、参数、结果、状态和耗时
- `steps`：Agent 决策步数
- `termination_reason`：正常回答或预算终止原因
- `api_usage`：API 返回的 token 用量

## 工具

当前工具层包括：

| 工具 | 功能 |
|---|---|
| `get_catalog_schema` | 返回行数、字段、类型、物理含义和数据转换记录 |
| `query_structures` | 使用 `eq`、`ne`、`gt`、`ge`、`lt`、`le`、`in` 筛选目录 |
| `fit_scaling_relation` | 在 log10 空间使用 OLS 或 ODR 拟合两个物理量的幂律关系 |
| `compare_scaling_relations` | 按数值阈值拆分样本并比较两组斜率 |
| `make_plot` | 生成对数坐标散点图和拟合线 PNG |
| `plot_grouped_scaling_relation` | 按类别分别拟合，并将各组散点和拟合线绘制在同一张 PNG 中 |
| `run_scaling_relation_skill` | 固定执行字段检查、单关系拟合、bootstrap 和可选绘图流程 |
| `run_grouped_scaling_skill` | 固定执行字段检查、分组拟合和统一绘图流程 |

模型只能调用注册工具，不能执行任意 Python、Shell 或 SQL。

## Skills 与 Tools

Tools 是单个原子动作，例如查询字段、执行一次拟合或生成一张图；Skills 是由项目控制的完整科学工作流。对于标准标度关系问题，模型优先调用
`run_scaling_relation_skill`，由 Skill 固定执行：

```text
get_catalog_schema → fit_scaling_relation → 可选 make_plot
```

对于旋臂或其他类别分组问题，模型优先调用 `run_grouped_scaling_skill`，由 Skill 固定执行字段检查、分组拟合和统一绘图。每个 Skill 都返回名称、版本、步骤、结果、artifact 和警告，便于复核和测试。低层 Tools 仍保留，用于数据探索和调试；未知任务则回退到通用工具调用流程。

Skill 的版本化结果示例：

```json
{
  "skill": {
    "name": "scaling_relation_analysis",
    "version": "1.0.0"
  },
  "status": "completed",
  "steps": [],
  "result": {},
  "artifacts": [],
  "warnings": []
}
```

## 数据格式

### 标准格式

标准 CSV 至少需要以下字段：

```text
structure_id,cloud_id,radius_pc,velocity_dispersion_kms
```

可以同时包含：

```text
parent_id,mass_msun,column_density_cm2,virial_parameter,hierarchy_level
```

其中半径单位为 pc，速度弥散单位为 km/s，质量单位为太阳质量。

将文件放入 `data/`，并修改 `.env`：

```text
ASTRO_DATA_PATH=data/molecular_cloud_catalog.csv
```

### newtrunks 格式

项目会自动识别包含以下字段的目录：

```text
_idx,cloudidx,parent,radius,v_rms,mass,vp,level,Nstru,Dist,arms,touch
```

加载时保留原始字段，并生成统一分析字段：

| 原始字段 | 统一字段 | 处理方式 |
|---|---|---|
| `_idx`、`cloudidx` | `structure_id` | 组合为 `cloudidx:_idx`，避免不同云内编号重复 |
| `parent`、`cloudidx` | `parent_id` | 组合为 `cloudidx:parent` |
| `radius` | `radius_pc` | 单位保持为 pc |
| `v_rms` | `velocity_dispersion_kms` | 从 m/s 除以 1000 转换为 km/s |
| `mass` | `mass_msun` | 单位保持为太阳质量 |
| `vp` | `virial_parameter` | 数值转换 |
| `level` | `hierarchy_level` | 整数转换 |
| `Nstru` | `child_structure_count` | 整数转换 |
| `Dist` | `distance_kpc` | 单位保持为 kpc |
| `arms` | `spiral_arm` | 字符串转换 |
| `touch` | `touches_datacube_edge` | `0/1` 转换为布尔值 |

例如将数据保存为 `data/newtrunks.csv`，并设置：

```text
ASTRO_DATA_PATH=data/newtrunks.csv
```

`get_catalog_schema` 返回的 `data_provenance` 会记录转换规则和缺失父节点引用数量。程序不会自动补造输入表中不存在的父节点。`data/*.csv` 默认不会被 Git 跟踪。

### 按旋臂分组拟合并绘图

当需要把不同旋臂的数据画在同一张对数坐标图中时，Agent 会调用
`plot_grouped_scaling_relation`。该工具对 `spiral_arm` 的每个分组分别拟合，
并返回每组的样本量、斜率、95% bootstrap 置信区间和 PNG 路径。例如：

```json
{
  "question": "使用不与datacube相接的结构，分旋臂拟合线宽尺度关系，报告每个旋臂的样本量、斜率和95%置信区间，并将所有旋臂画在同一张图里。"
}
```

工具参数中的 `filters` 可用于筛选 `touches_datacube_edge=false`，
`group_column` 默认是 `spiral_arm`。输出中的 `groups` 保存各组数值结果，
`plot_path` 指向生成的图像；有效正值样本少于 8 个的分组会标记为 `skipped`。

## 科学计算方法

标度关系统一在 log10–log10 空间中拟合：

```text
log10(y) = intercept + slope × log10(x)
y = 10^intercept × x^slope
```

计算流程：

1. 应用用户指定的目录筛选条件。
2. 删除拟合字段中的空值。
3. 删除 `x <= 0` 或 `y <= 0` 的数据，因为其对数无定义。
4. 要求至少 8 个有效样本。
5. 默认使用普通最小二乘法（OLS），通过 `scipy.stats.linregress` 计算斜率、截距、相关系数、p 值和标准误。
6. 用户指定 `fit_method=odr` 时，使用 `odrpack` 执行正交距离回归；如果提供误差列，则在 log 空间中使用对应的一倍标准差进行加权。
7. 使用固定随机种子的重复有放回抽样，计算斜率的 2.5% 和 97.5% 分位数作为 bootstrap 95% 置信区间。

选择拟合方法的请求示例：

```json
{
  "question": "使用ODR正交回归拟合半径与速度弥散的标度关系。"
}
```

如果目录包含 `radius_error_pc` 和 `velocity_error_kms` 两个误差列，可以显式指定：

```json
{
  "x": "radius_pc",
  "y": "velocity_dispersion_kms",
  "fit_method": "odr",
  "x_error_column": "radius_error_pc",
  "y_error_column": "velocity_error_kms",
  "bootstrap": 1000
}
```

误差列应是原始线性单位的一倍标准差。程序会近似转换为
`sigma_log10(x) = sigma_x / (x * ln(10))`。没有误差列时仍可运行无权重 ODR，
但结果会明确标记为没有使用测量误差。ODR 的结果不提供与 OLS 相同含义的斜率
p 值，建议主要比较斜率和 bootstrap 置信区间。

默认执行 300 次 bootstrap，工具参数允许的范围为 0–2000 次。固定随机种子用于保证同一数据和参数下结果可复现。

科学解释时需要注意：

- 对数线性拟合描述相关关系，不直接证明因果关系。
- OLS 假设 x 方向误差可以忽略；ODR 适用于 x、y 都存在误差的情形。
- 无误差列的 ODR 是无权重正交拟合，不等于已经建模了测量误差。
- bootstrap 区间不替代完整的系统误差、选择效应或层级相关性分析。
- 同一分子云内部的父子结构可能不满足独立同分布假设。
- 比较两组斜率的数值差异不等价于差异具有统计显著性。
- datacube 边缘截断、距离选择效应和目录完备性可能影响拟合结果。

## 测试

运行全部离线测试：

```powershell
.\.venv\Scripts\python.exe -m pytest -q
```

或在已激活的虚拟环境中运行：

```bash
python -m pytest -q
```

单元测试使用模拟模型和临时目录，不调用 DeepSeek API，因此不会产生 API 费用。

## 性能与 Agent 评测

运行 Agent 工具选择评测：

```bash
python scripts/benchmark_agent.py
```

该脚本会调用真实 API，结果写入：

```text
results/agent_benchmark.csv
```

运行 DeepSeek API 性能评测：

```bash
python scripts/benchmark_api.py
```

评测记录总响应时间、prompt/completion/total tokens、近似输出速度，以及 API 返回的 cache hit/miss tokens。结果写入：

```text
results/deepseek_api_benchmark.csv
```

评测脚本不硬编码费用；应结合结果中的 token 数与运行时官方价格计算成本。

## 安全与边界

- `.env` 已加入 `.gitignore`，API Key 只从环境变量读取。
- 用户问题、系统提示词和返回给模型的工具结果会发送给 DeepSeek API；不要直接处理不能发送到第三方服务的保密数据。
- 数值计算在本地进行，但模型仍会接收经过截断的工具观察结果。
- 当前系统针对目录级数据，不直接处理完整 datacube 的体素级计算。
- 当前 FastAPI 服务没有身份验证，不应直接暴露到公网。
- 工具调用轨迹有助于复核执行过程，但不等同于科学结果的同行评议。
