# SupplyAgent

一个现代、可扩展的 Python AI Agent 框架，支持 OpenRouter 多模型、Tools 工具调用和 Skills 技能系统。

## 架构

```
supply_agent/
├── config.py          # 配置管理（环境变量 / .env）
├── types.py           # 核心类型定义
├── llm/
│   └── client.py      # OpenRouter LLM 客户端（OpenAI 兼容 API）
├── tools/
│   ├── base.py        # @tool 装饰器 & Tool 类
│   └── registry.py    # 工具注册表 & 执行器
├── skills/
│   ├── loader.py      # SKILL.md 加载器（兼容 Cursor 格式）
│   └── registry.py    # 技能注册表
├── logging/
│   ├── logger.py      # 运行日志（.log + .jsonl）
│   ├── parser.py      # 日志解析
│   └── visualize.py   # HTML 可视化生成
└── agent/
    ├── core.py        # Agent 主类
    └── loop.py        # ReAct 循环（Reason → Act → Observe）
```

### 核心设计

| 模块 | 职责 |
|------|------|
| **LLM Client** | 通过 OpenRouter 调用任意模型，支持同步/异步/流式 |
| **Tool Registry** | 注册工具、自动生成 JSON Schema、执行工具调用 |
| **Skill Registry** | 从 `SKILL.md` 加载专业技能指令，按需注入上下文 |
| **Agent Loop** | ReAct 循环：模型推理 → 工具调用 → 观察结果 → 重复 |

## 快速开始

### 1. 安装依赖

```bash
pip install -e ".[dev]"
```

### 2. 配置环境变量

```bash
cp .env.example .env
# 编辑 .env，填入你的 OpenRouter API Key
```

### 3. 运行示例

```bash
# 基础对话
python examples/basic_agent.py

# 带自定义工具
python examples/with_tools.py

# 带 Skills 技能
python examples/with_skills.py

# 流式事件
python examples/streaming.py
```

## 使用指南

### 创建 Agent

```python
from supply_agent import Agent

# 使用默认配置（从 .env 读取）
agent = Agent()

# 指定模型
agent = Agent(model="openai/gpt-4o")

# 运行时切换模型
agent.model = "google/gemini-2.5-pro-preview"
```

### 注册 Tools

```python
from supply_agent.tools import tool

@tool
def search(query: str, limit: int = 10) -> str:
    """Search the web for information."""
    return f"Results for: {query}"

agent = Agent()
agent.tools.from_decorated(search)

result = agent.run("Search for Python tutorials")
print(result.content)
```

### 使用 Skills

在 `skills/` 目录下创建 `SKILL.md` 文件（兼容 Cursor Skills 格式）：

```
skills/
└── my-skill/
    └── SKILL.md
```

Agent 会自动发现技能。模型可通过内置的 `load_skill` 工具按需加载专业技能指令。

### 流式事件

```python
from supply_agent.types import AgentEventType

for event in agent.stream("Your question"):
    if event.type == AgentEventType.TOOL_CALL:
        print(f"Calling: {event.data['name']}")
    elif event.type == AgentEventType.MESSAGE:
        print(event.data["content"])
```

### 异步 API

```python
result = await agent.arun("Your question")

async for event in agent.astream("Your question"):
  ...
```

## 支持的模型

通过 OpenRouter 可使用任意支持的模型，例如：

- `google/gemini-2.5-flash`（默认）
- `anthropic/claude-sonnet-5`
- `openai/gpt-4o`
- `google/gemini-2.5-pro-preview`
- `meta-llama/llama-4-maverick`

完整列表见 [OpenRouter Models](https://openrouter.ai/models)。

## 环境变量

| 变量 | 说明 | 默认值 |
|------|------|--------|
| `OPENROUTER_API_KEY` | OpenRouter API 密钥 | (必填) |
| `OPENROUTER_MODEL` | 默认模型 | `google/gemini-2.5-flash` |
| `AGENT_MAX_ITERATIONS` | 最大循环次数 | `20` |
| `AGENT_TEMPERATURE` | 生成温度 | `0.7` |
| `SKILLS_DIR` | Skills 目录 | `skills` |
| `LOGS_DIR` | Agent 运行日志目录 | `logs` |
| `LOG_ENABLED` | 是否写入运行日志 | `true` |

## 运行日志与可视化

每次 `agent.run()` 会在 `logs/` 下写出：

| 文件 | 说明 |
|------|------|
| `run_<agent>_<id>.log` | 人类可读的完整日志 |
| `run_<agent>_<id>.jsonl` | 结构化事件流（推荐用于可视化） |

运行结束后会自动：生成 `.html` 可视化页 → 上传 `.log` / `.jsonl` / `.html` 到 OSS（`supply_agent/<agent_name>/`）→ 写入 MySQL `oss_logs`。

可用 `LOG_OSS_UPLOAD_ENABLED=false` 关闭上传。

事件类型：`run_start` → `llm_input` → `llm_output`（含 reasoning）→ `tool_call`（完整入参/返回）→ … → `run_end`。

手动生成可视化页面：

```bash
# 无参数：为 logs/ 下全部运行生成可视化页面
python scripts/visualize_run.py

# 指定某次运行
python scripts/visualize_run.py logs/run_20260714_134901_9daf6fe1.jsonl

# 最新一次运行，并打开浏览器
python scripts/visualize_run.py --latest --open

# 安装后也可用
supply-visualize --open
```

页面按步骤展示：LLM 输入（messages / tools）、思考过程、模型输出、工具调用的输入与输出。

## 全局分类树（Web）

后端 FastAPI（端口 **8080**）一次性返回 `global_tree_category` 整棵树；前端在仓库根目录 `web/`（Vue 3）。

```bash
# 后端 API
.venv/bin/python -m api
# 或: .venv/bin/supply-api

# 前端（另开终端）
cd web && npm install && npm run dev
```

- API: `GET http://127.0.0.1:8080/api/category-tree`
- 前端: http://127.0.0.1:5173 （开发代理 `/api` → 8080）
- 默认展开 3 层，可选择展开层数，支持节点手动展开/收起

## 运行测试

```bash
pytest
```

## License

MIT
