# Reson Agent

可扩展的 Agent 框架。支持多步工具调用、计划管理、子 Agent 协作、回溯重跑和上下文压缩。

## Quick Start

```bash
pip install -r requirements.txt

# 配置 LLM API Key
cp .env.example .env  # 编辑填入 API Key
```

### 最小示例

```python
import asyncio
from agent import AgentRunner, RunConfig
from agent.trace import FileSystemTraceStore
from agent.llm import create_openrouter_llm_call

runner = AgentRunner(
    trace_store=FileSystemTraceStore(base_path=".trace"),
    llm_call=create_openrouter_llm_call(model="anthropic/claude-sonnet-4.5"),
)

async def main():
    async for item in runner.run(
        messages=[{"role": "user", "content": "列出当前目录的文件"}],
        config=RunConfig(model="anthropic/claude-sonnet-4.5"),
    ):
        print(item)

asyncio.run(main())
```

## 自定义工具

用 `@tool` 装饰器注册。`RunConfig(tools=None)`（默认）时所有已注册工具自动对 LLM 可用，无需额外配置。

```python
from agent import tool, ToolResult

@tool(description="查询产品库存")
async def check_inventory(product_id: str, warehouse: str = "default") -> ToolResult:
    """查询指定仓库的产品库存

    Args:
        product_id: 产品唯一标识符
        warehouse: 仓库编码，默认为主仓库
    """
    stock = await query_db(product_id, warehouse)
    return ToolResult(output=f"库存: {stock}")

# 确保此模块在 runner.run() 之前被 import
```

**注意**: `@tool` 通过副作用注册到全局 registry，必须确保定义工具的模块在调用 `runner.run()` 前被 import。

### 参数 Schema 生成

框架从函数签名和 docstring 自动生成 OpenAI Tool Schema，无需手写 JSON：

- **参数类型**：从类型注解推断（`str`/`int`/`float`/`bool`/`list`/`dict`，支持 `Optional`、`Literal`、`List[T]`）
- **参数描述**：从 Google 风格 docstring 的 `Args:` 段提取
- **必填/可选**：有默认值的参数为可选，否则为必填
- **工具描述**：优先使用 `@tool(description=...)` 参数，其次取 docstring 首行
- `uid` 和 `context` 参数由框架自动注入，不会出现在 Schema 中

上面的 `check_inventory` 会生成：

```json
{
  "type": "function",
  "function": {
    "name": "check_inventory",
    "description": "查询产品库存",
    "parameters": {
      "type": "object",
      "properties": {
        "product_id": { "type": "string", "description": "产品唯一标识符" },
        "warehouse": {
          "type": "string",
          "description": "仓库编码，默认为主仓库",
          "default": "default"
        }
      },
      "required": ["product_id"]
    }
  }
}
```

### 限制工具范围

```python
# 只启用指定工具（在内置工具基础上追加）
config = RunConfig(tools=["check_inventory", "another_tool"])
```

## 自定义 Skills

Skills 是 Markdown 文件，提供领域知识，注入到 system prompt。

```
my_project/
└── skills/
    └── my_domain.md
```

```markdown
---
name: my-domain-skill
description: 领域专属知识
---

## Guidelines

- 规则 1
- 规则 2
```

```python
runner = AgentRunner(
    llm_call=...,
    trace_store=...,
    skills_dir="./skills",  # 指向你的 skills 目录
)
```

内置 skills（`agent/skill/skills/`）始终自动加载，`skills_dir` 的内容额外追加。

## 知识管理系统（Knowledge Management）

知识管理系统通过**提取、存储、注入**三个环节，让 Agent 积累和复用结构化知识。

### 核心流程

**1. 提取（Extract）**

- **触发时机**：
  - 压缩时提取：消息量超阈值触发压缩时，在 Level 1 过滤前用完整 history 反思
  - 完成时提取：Agent 运行完成后（不代表任务完成，可能中途退出等待人工评估）
- **提取方式**：调用 LLM 对执行过程进行反思，提取可复用的知识
- **自定义 Prompt**：可通过配置自定义反思 prompt，空则使用默认（见 `agent/core/prompts/knowledge.py`）

**2. 存储（Store）**

- **存储位置**：KnowHub 服务（默认 `http://localhost:8765`）
- **知识结构**：
  - `title`: 知识标题
  - `content`: 知识内容
  - `type`: 知识类型（strategy/tool/pattern/pitfall 等）
  - `tags`: 标签（键值对，用于分类和检索）
  - `scopes`: 作用域（如 `org:cybertogether`）
  - `owner`: 所有者（默认从 git config user.email 获取）
  - `resource_ids`: 关联资源 ID 列表（代码片段、凭证、cookies 等）
- **资源管理**：
  - 知识可关联多个资源（通过 `resource_ids` 字段）
  - 资源包含 `body`（公开内容）和 `secure_body`（加密内容）
  - 支持代码片段、API 凭证、cookies 等多种资源类型

**3. 注入（Inject）**

- **触发时机**：Agent 切换当前工作的 Goal 时自动触发
- **检索策略**：基于 Goal 描述和上下文，从知识库检索相关知识
- **注入方式**：将检索到的知识注入到 Agent 的上下文中

### 配置

知识管理配置通过 `RunConfig.knowledge` 传递：

```python
from agent.core.runner import KnowledgeConfig, RunConfig

run_config = RunConfig(
    model="claude-sonnet-4.5",
    temperature=0.3,
    max_iterations=1000,

    knowledge=KnowledgeConfig(
        # 压缩时提取（消息量超阈值触发压缩时，用完整 history 反思）
        enable_extraction=True,
        reflect_prompt="",  # 空则使用默认，见 agent/core/prompts/knowledge.py:REFLECT_PROMPT

        # agent运行完成后提取
        enable_completion_extraction=True,
        completion_reflect_prompt="",  # 空则使用默认

        # 知识注入（agent切换当前工作的goal时，自动注入相关知识）
        enable_injection=True,

        # 默认字段（保存/搜索时自动注入）
        owner="",  # 空则从 git config user.email 获取（隐藏参数，LLM 不可见）
        default_tags={"project": "my_project"},  # 与 LLM 传递的 tags 合并
        default_scopes=["org:cybertogether"],  # 与 LLM 传递的 scopes 合并
        default_search_types=["strategy", "tool"],
        default_search_owner=""  # 空则不过滤
    )
)
```

**参数注入规则**（通过框架 `inject_params` 机制实现，详见 `agent/docs/tools.md`）：

- `owner`：隐藏参数，LLM 不可见，框架自动注入（`mode: default`）
- `tags`：LLM 可追加新 key，框架默认 key 不可被覆盖（`mode: merge`）
- `scopes`：LLM 可追加，与框架默认值合并去重（`mode: merge`）

### 知识工具

框架提供以下内置工具用于知识管理：

- `knowledge_save`: 保存知识到知识库
- `knowledge_search`: 搜索知识库
- `knowledge_get`: 获取指定知识详情
- `resource_save`: 保存资源（代码、凭证等）
- `resource_get`: 获取资源内容

这些工具会自动注入配置的默认字段（owner, tags, scopes 等）。

## AgentRunner 参数

```python
AgentRunner(
    llm_call,                # 必需：LLM 调用函数
    trace_store=None,        # Trace 持久化（推荐 FileSystemTraceStore）
    tool_registry=None,      # 工具注册表（默认：全局 registry）
    skills_dir=None,         # 自定义 skills 目录
    utility_llm_call=None,   # 轻量 LLM（生成任务标题等）
    debug=False,             # 调试模式
)
```

## RunConfig 参数

```python
RunConfig(
    model="gpt-4o",          # 模型标识
    temperature=0.3,
    max_iterations=200,       # Agent loop 最大轮数
    tools=None,               # None=全部已注册工具，List[str]=内置+指定工具
    system_prompt=None,       # None=从 skills 自动构建
    agent_type="default",     # 预设类型：default / explore / analyst
    trace_id=None,            # 续跑/回溯时传入已有 trace ID
    after_sequence=None,      # 从哪条消息后续跑（message sequence）
    goal_compression="on_overflow",  # Goal 压缩模式：none / on_complete / on_overflow
    knowledge=KnowledgeConfig(),  # 知识管理配置
)
```

    system_prompt=None,       # None=从 skills 自动构建
    agent_type="default",     # 预设类型：default / explore / analyst
    trace_id=None,            # 续跑/回溯时传入已有 trace ID
    after_sequence=None,      # 从哪条消息后续跑（message sequence）

)

````

## LLM Providers

框架内置两个 provider：

```python
from agent.llm import create_openrouter_llm_call, create_gemini_llm_call

# OpenRouter（支持多种模型）
llm = create_openrouter_llm_call(model="anthropic/claude-sonnet-4.5")

# Google Gemini
llm = create_gemini_llm_call(model="gemini-2.5-flash")
````

自定义 provider 只需实现签名：

```python
async def my_llm_call(messages, model, tools, temperature, **kwargs) -> dict:
    # 调用你的 LLM
    return {
        "content": "...",
        "tool_calls": [...] or None,
        "prompt_tokens": 100,
        "completion_tokens": 50,
        "cost": 0.001,
        "finish_reason": "stop",
    }
```

## API Server

```bash
python api_server.py
```

| 方法 | 路径                        | 说明        |
| ---- | --------------------------- | ----------- |
| GET  | `/api/traces`               | 列出 Traces |
| GET  | `/api/traces/{id}`          | Trace 详情  |
| GET  | `/api/traces/{id}/messages` | 消息列表    |
| POST | `/api/traces`               | 新建并执行  |
| POST | `/api/traces/{id}/run`      | 续跑/回溯   |
| POST | `/api/traces/{id}/stop`     | 停止        |
| WS   | `/api/traces/{id}/watch`    | 实时事件    |

需在 `api_server.py` 中配置 Runner 才能启用 POST 端点。

## 项目结构

```
agent/
├── core/           # AgentRunner + 预设
├── tools/          # 工具系统（registry + 内置工具）
├── trace/          # 执行追踪 + 计划（GoalTree）+ API
├── skill/          # Skills（技能系统）
└── llm/            # LLM Provider 适配
```

详细架构文档：[docs/README.md](./docs/README.md)

## Agent 预设（Presets）

预设定义不同类型 Agent 的配置模板（工具权限、system prompt、skills 等）。

**项目级预设**：在项目目录创建 `presets.json`，支持从 `.prompt` 文件加载自定义 system prompt：

```json
{
  "tool_research": {
    "system_prompt_file": "tool_research.prompt",
    "max_iterations": 50,
    "skills": ["planning", "research", "browser"],
    "description": "工具调研 Agent"
  }
}
```

**加载预设**：
```python
from agent.core.presets import load_presets_from_json

load_presets_from_json("path/to/presets.json")
```

**使用预设**：LLM 调用 `agent` 工具时指定 `agent_type`：
```python
agent(task="调研视频生成工具", agent_type="tool_research")
```

详见 `examples/production/` 示例和 `agent/docs/architecture.md`。

## 交互式 CLI（Interactive CLI）

框架提供交互式控制器，支持实时监控、手动干预和经验总结。

### 使用方式

```python
from agent.cli import InteractiveController

# 创建交互控制器
interactive = InteractiveController(
    runner=runner,
    store=store,
    enable_stdin_check=True  # 启用标准输入检查
)

# 在执行循环中检查用户输入
async for item in runner.run(messages=messages, config=config):
    cmd = interactive.check_stdin()
    if cmd == 'pause':
        await runner.stop(trace_id)
        menu_result = await interactive.show_menu(trace_id, current_sequence)
        # 处理菜单结果...
    elif cmd == 'quit':
        await runner.stop(trace_id)
        break
```

### 交互控制

在执行过程中，可以通过命令行实时控制：

| 按键          | 动作         | 说明                              |
| ------------- | ------------ | --------------------------------- |
| `p` / `pause` | **暂停执行** | 立即挂起 Agent 循环，进入交互菜单 |
| `q` / `quit`  | **停止执行** | 安全停止并保存当前的执行状态      |

### 交互菜单功能

进入暂停模式后，系统提供以下操作：

1. **插入干预消息**：直接向 Agent 下达新指令
2. **触发经验总结 (Reflect)**：强制 Agent 对当前过程进行反思
3. **查看 GoalTree**：可视化当前任务的拆解结构和完成进度
4. **上下文压缩 (Compact)**：手动精简对话历史

### 项目配置示例

完整的项目配置示例见 `examples/research/config.py`：

```python
from agent.core.runner import KnowledgeConfig, RunConfig
from agent.utils import setup_logging

# Agent 运行配置
RUN_CONFIG = RunConfig(
    model="claude-sonnet-4.5",
    temperature=0.3,
    max_iterations=1000,
    name="Research Agent",

    knowledge=KnowledgeConfig(
        enable_extraction=True,
        enable_completion_extraction=True,
        enable_injection=True,
        owner="",  # 空则从 git config 获取
        default_tags={"project": "research"},
        default_scopes=["org:cybertogether"],
        default_search_types=["strategy", "tool"],
    )
)

# 基础设施配置
SKILLS_DIR = "./skills"
TRACE_STORE_PATH = ".trace"
DEBUG = True
LOG_LEVEL = "INFO"
LOG_FILE = None  # 可设置为文件路径

# 在 run.py 中使用
setup_logging(level=LOG_LEVEL, file=LOG_FILE)

runner = AgentRunner(
    trace_store=FileSystemTraceStore(base_path=TRACE_STORE_PATH),
    llm_call=create_openrouter_llm_call(model=f"anthropic/{RUN_CONFIG.model}"),
    skills_dir=SKILLS_DIR,
    debug=DEBUG
)

async for item in runner.run(messages=messages, config=RUN_CONFIG):
    # 处理执行结果
    pass
```

**配置说明**：

- 直接使用框架的 `RunConfig` 和 `KnowledgeConfig`，不需要自定义配置类
- 基础设施配置（skills_dir, trace_store_path 等）用简单变量定义
- 使用 `agent.utils.setup_logging()` 配置日志

## 任务可视化与调试

框架在运行期间会生成唯一的 `trace_id`。

- **本地日志**：所有的执行细节、工具调用和 Goal 状态均持久化在 `.trace/` 目录下。
- **Web 可视化**：

1. 启动服务器：`python api_server.py`
2. 启动前端:

```
  cd frontend/react-template
  yarn
  yarn dev
```

3. 访问控制台：`http://localhost:3000`
4. 在前端界面中切换任务，即直观追踪 Agent 的思考链路。
5. 因为该可视化读取的是根目录下的.trace文件，建议运行项目时，可以在根目录下用命令行运行`python examples/[project_name]/run.py`，使运行得到的trace存放在根目录

### 提示：目前前端可视化只供观看本地运行过的trace结果，新任务运行等功能正在开发中，运行可在命令行中执行

### 绿色节点为整体的goal(目标)，蓝色节点为子goal(目标)，灰色节点为基础信息节点。点击蓝色边/绿色边会折叠节点，点击节点会在右侧显示详情。

---

## 示例项目结构

可以参考其他文件夹中的结构：

```text
examples/[your_example]/
├── input/             # (可选)输入数据
├── output_1/          # (可选)输出目录
├── skills/            # (可选)领域专属 Skill (.md)
├── tool/              # (可选)自定义工具
├── presets.json       # (可选)预定义的子 Agent 配置
├── config.py          # (推荐)项目配置
├── [task].prompt      # (必须)任务 System Prompt 和 User Prompt
└── run.py             # (必须)交互式运行入口
```

---

## 环境兼容性

针对 Clash Verge / TUN 模式等网络环境，本项目已内置代理自动避让逻辑：

- **代理优化**：通过 `no_proxy` 配置防止 `httpx` 错误引导流量。
- **Browser 模式**：支持 `cloud` (远程) 和 `local` (本地) 模式切换。

## 运行结果存储

运行过程中，会自动存储以下内容：

- **运行轨迹**：根目录下 `.trace/` 文件夹下的实际运行路径结果
- **知识库**：KnowHub 服务中保存的知识条目（通过 API 访问）
