Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
110 changes: 78 additions & 32 deletions graph_net/agent/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,16 +6,7 @@

### 基础依赖
```bash
# 已包含在 GraphNet 主依赖中
pip install torch torchvision
```

### Agent 可选依赖
```bash
# 安装 Agent 相关依赖(包括 huggingface_hub)
pip install -e ".[agent]"

# 或单独安装
pip install torch transformers accelerate
pip install huggingface_hub>=0.20.0
```

Expand All @@ -26,48 +17,103 @@ pip install huggingface_hub>=0.20.0
export GRAPH_NET_EXTRACT_WORKSPACE=/path/to/your/workspace
```

或在代码中指定
未设置时默认使用 `~/graphnet_workspace`。也可在代码中显式指定
```python
from graph_net.agent import GraphNetAgent
agent = GraphNetAgent(workspace="/path/to/workspace")
```

### HuggingFace Token(访问私有或受限模型)
```bash
export HF_TOKEN=hf_xxx
```

## 使用示例

### 单模型抽取
```python
from graph_net.agent import GraphNetAgent

# 初始化 Agent
agent = GraphNetAgent(
workspace="./agent_workspace",
hf_token=None # 可选,用于访问私有模型
hf_token=None, # 可选,私有模型需要
llm_retry=True, # 失败时自动调用 ducc/claude 修复脚本并重试
)

# 运行提取
success = agent.extract_sample("bert-base-uncased")
```

### 并行批量抽取
```bash
# 从文件读取模型列表(每行一个 model_id,# 开头为注释)
python graph_net/agent/parallel_extract.py --model-list models.txt

# 从 HuggingFace Hub 按下载量抓取模型
python graph_net/agent/parallel_extract.py --count 200 --task text-classification

if success:
print("✅ Sample extracted successfully")
else:
print("❌ Extraction failed")
# 指定 GPU 和 workspace
python graph_net/agent/parallel_extract.py \
--model-list models.txt \
--gpus 0,1,2,3 \
--workspace /data/graphnet_workspace \
--hf-token YOUR_TOKEN

# 结果保存为 JSON(默认自动生成带时间戳的文件名)
python graph_net/agent/parallel_extract.py --model-list models.txt --output result.json
```

`--gpus` 默认自动检测全部可用 GPU(读取 `CUDA_VISIBLE_DEVICES` 或 `nvidia-smi`)。

## parallel_extract.py 详解

`parallel_extract.py` 是面向批量场景的并行抽取脚本,适合一次性处理数百到数千个模型。

### 工作原理

- 所有待抽取的模型 ID 放入一个共享任务队列
- 每张 GPU 启动一个独立的 worker 子进程(`multiprocessing spawn` 模式,CUDA 安全)
- worker 空闲时主动从队列取任务,天然实现动态负载均衡
- 每个 worker 内部使用独立的 `GraphNetAgent`,彼此隔离,互不影响

### 命令行参数

| 参数 | 默认值 | 说明 |
|---|---|---|
| `--model-list` | — | 模型列表文件路径,每行一个 model_id,`#` 开头为注释 |
| `--count` | 100 | 未指定 `--model-list` 时,从 HuggingFace Hub 按下载量抓取的模型数量(需安装 `huggingface_hub`) |
| `--task` | — | HuggingFace 任务类型过滤,如 `text-classification`、`image-classification`(与 `--count` 配合使用) |
| `--gpus` | 自动检测 | 使用的 GPU 编号,逗号分隔,如 `0,1,2,3` |
| `--workspace` | `$GRAPH_NET_EXTRACT_WORKSPACE` 或 `~/graphnet_workspace` | 工作目录根路径 |
| `--hf-token` | — | HuggingFace API Token,私有或受限模型需要 |
| `--output` | 自动生成 | 结果 JSON 文件路径,默认为 `parallel_extract_<时间戳>.json` |

### 模型列表文件格式

```
# 文本模型
bert-base-uncased
google/flan-t5-base

# 视觉模型
openai/clip-vit-base-patch32
```

## 工作流程

1. **Fetch**: 从 HuggingFace 下载模型
2. **Analyze**: 解析 config.json 提取元数据
3. **CodeGen**: 生成 run_model.py 脚本
4. **Extract**: 执行脚本提取计算图
5. **Deduplicate**: 检查是否与已有样本重复
6. **Verify**: 验证样本完整性
7. **Archive**: 保存 run_model.py 到样本目录
1. **Fetch**: 从 HuggingFace 下载模型到本地缓存
2. **Analyze**: 解析 `config.json` 提取输入形状、dtype、模型类型等元数据
3. **CodeGen**: 根据元数据生成 `run_model.py` 抽取脚本
4. **Extract**: 在子进程中执行脚本抽取计算图
5. **LLM Retry**(可选):若抽取失败,调用 `ducc`/`claude -p` 修复脚本并最多重试 2 次
6. **Deduplicate**: 基于 SHA-256 图哈希检查是否与已有样本重复
7. **Verify**: 使用 ForwardVerifier 验证样本可 forward

## 测试
## LLM Retry

```bash
# 运行所有测试
pytest graph_net/agent/tests/ -v
当模板生成的脚本执行失败时,若系统中存在 `ducc` 或 `claude` CLI,Agent 会自动将失败脚本、报错信息和模型 config 发给 LLM 进行修复,最多重试 2 次。

# 运行实际模型测试(需要设置环境变量)
TEST_REAL_RUN=1 pytest graph_net/agent/tests/test_real_run.py -v
```python
# 禁用 LLM retry
agent = GraphNetAgent(llm_retry=False)
```

LLM retry 需要 `ducc` 或 `claude` 在 `PATH` 中可用。
184 changes: 184 additions & 0 deletions graph_net/agent/agent_usage.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,184 @@
# GraphNet Agent 使用指南

自动从 HuggingFace 模型抽取计算图的 Agent 工具。

---

## 环境准备

# 目录
在GraphNet目录下运行即可,不需要安装

```
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

L12多出来的


---

## 快速开始

```python
from graph_net.agent import GraphNetAgent

agent = GraphNetAgent()
ok = agent.extract_sample("prajjwal1/bert-tiny")
print("成功" if ok else "失败")
```

默认 workspace 为 `/work/graphnet_workspace`,输出目录按 `组织_模型名` 命名,例如:
`/work/graphnet_workspace/prajjwal1_bert-tiny/`
Comment on lines +26 to +27

---

## 初始化参数

```python
GraphNetAgent(
workspace = "/work/graphnet_workspace", # 工作目录根路径
hf_token = None, # HuggingFace Token(私有模型需要)
llm_retry = True, # 失败时调用 LLM 兜底修复
)
```

| 参数 | 默认值 | 说明 |
|------|--------|------|
| `workspace` | `/work/graphnet_workspace` | 工作目录,自动创建子目录结构 |
| `hf_token` | `None` | HF access token,公开模型无需填写 |
| `llm_retry` | `True` | 模板脚本失败后,调用 `ducc -p` 让 LLM 修复并重试一次 |

---

## 批量抽取

```python
from graph_net.agent import GraphNetAgent

agent = GraphNetAgent()

models = [
"prajjwal1/bert-tiny",
"distilbert/distilgpt2",
"hf-internal-testing/tiny-random-ViTModel",
"hf-internal-testing/tiny-random-T5Model",
"openai/clip-vit-base-patch32",
]

results = {}
for model_id in models:
results[model_id] = agent.extract_sample(model_id)

# 打印汇总
for mid, ok in results.items():
print(f"{'OK ' if ok else 'FAIL'} {mid}")
```

---

## 工作目录结构

```
/work/graphnet_workspace/
├── models/ # HuggingFace 下载缓存(仅 config,不含权重)
│ └── models--org--model-name/
├── generated/ # 自动生成的抽取脚本
│ └── org_model-name/
│ ├── run_model.py # 模板生成脚本
│ └── run_model_llm.py # LLM 修复脚本(首次失败时生成)
├── org_model-name/ # 计算图输出(以 组织_模型名 命名)
│ ├── model.py # 计算图结构
│ ├── graph_net.json # 图结构 JSON
│ ├── input_meta.py # 输入元信息
│ ├── input_tensor_constraints.py # 输入约束
│ ├── weight_meta.py # 权重元信息
│ ├── graph_hash.txt # 图结构哈希(用于去重)
│ └── run_model.py # 归档的抽取脚本
Comment on lines +91 to +92
├── samples/ # 去重比对参考库
└── logs/ # 运行日志
└── agent_YYYYMMDD_HHMMSS.log
```

---

## 抽取流程

```
HuggingFace model_id
① 下载配置文件 仅下载 config.json 等配置,跳过权重文件(*.bin / *.safetensors
│ / *.tflite / *.mlmodel / *.onnx 等),模型参数随机初始化
② 解析配置元数据 读取 config.json,推断 model_type / vocab_size / input_shapes
③ 生成抽取脚本 模板生成 run_model.py,含随机输入构造 + graph_net.torch.extract 调用
④ 子进程执行脚本 独立 Python 进程运行,注入 GRAPH_NET_EXTRACT_WORKSPACE 环境变量
├─ 成功 ──────────────────────────────────────────────────┐
│ │
└─ 失败 → ⑤ LLM 兜底(llm_retry=True 且 ducc 可用) │
│ │
▼ │
ducc -p "<prompt>" │
生成 run_model_llm.py │
│ │
▼ │
子进程重试执行 │
│ │
────────────┘ │
⑥ 生成 graph_hash.txt + 去重检查 + 验证输出文件完整性 + 归档脚本
```

---

## LLM 兜底机制

当模板脚本执行失败时,若满足以下条件则触发 LLM 兜底:

- `llm_retry=True`(默认开启)
- `ducc` 命令可用(在 PATH 中)

LLM 收到的信息包括:失败脚本原文、报错信息、`config.json` 内容。
LLM 必须遵守以下约束(写在 system prompt 里):

1. 必须调用 `graph_net.torch.extract(name="...")(model).eval()`
2. 只能使用 `torch`、`transformers`、`graph_net` 三个包
3. 输入张量随机构造,无需真实数据
4. 设备必须用 `torch.device("cuda" if torch.cuda.is_available() else "cpu")`
5. 不下载模型权重

每个模型**最多触发两次** LLM 兜底。第二次会把第一次修复后的脚本及其新的报错一并送给 LLM,方便它在上一轮基础上进一步修正。

---

## 常见问题

**Q:为什么某些模型下载很慢?**

HuggingFace 上部分模型除 PyTorch 权重外还包含 CoreML (`.mlmodel`)、TFLite (`.tflite`)、ONNX (`.onnx`) 等格式的文件,这些文件体积大(数百 MB)。Agent 已在 `ignore_patterns` 中跳过这些格式,若遇到新格式可在 [huggingface_fetcher.py](../graph_net/agent/model_fetcher/huggingface_fetcher.py) 里补充。

**Q:抽取结果目录名规则是什么?**

以 `组织_模型名` 命名(`/` 替换为 `_`),例如:
- `prajjwal1/bert-tiny` → `prajjwal1_bert-tiny/`
- `hf-internal-testing/tiny-random-ViTModel` → `hf-internal-testing_tiny-random-ViTModel/`

**Q:关闭 LLM 兜底怎么做?**

```python
agent = GraphNetAgent(llm_retry=False)
```

**Q:如何使用私有模型?**

```python
import os
agent = GraphNetAgent(hf_token=os.environ["HF_TOKEN"])
```

**Q:如何检查某次抽取是否成功?**

`extract_sample()` 返回 `True` 表示成功,同时可以检查输出目录是否存在 7 个文件:
`model.py`、`graph_net.json`、`input_meta.py`、`input_tensor_constraints.py`、
`weight_meta.py`、`graph_hash.txt`、`run_model.py`。
3 changes: 2 additions & 1 deletion graph_net/agent/code_generator/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@

from graph_net.agent.code_generator.base import BaseCodeGenerator
from graph_net.agent.code_generator.template_generator import TemplateCodeGenerator
from graph_net.agent.code_generator.llm_code_fixer import LLMCodeFixer

__all__ = ["BaseCodeGenerator", "TemplateCodeGenerator"]
__all__ = ["BaseCodeGenerator", "TemplateCodeGenerator", "LLMCodeFixer"]
Loading
Loading