---
title: AgentBase 项目设计文档
source:
author: shenwei
published:
created:
description:
tags: []
---

# AgentBase 项目设计文档

> 设计日期：2026-04-05
> 状态：已确认，等待实施
> 项目位置：`~/Workspace/agentbase/`

---

## 1. 项目概述

**项目名**：agentbase
**项目类型**：Django Web 应用 + 数据归档系统
**核心目标**：遍历 OpenClaw Agent 的 session JSONL 文件，解析并保存到 MariaDB，通过 Django Admin 提供 Web 查询界面。

**使用场景**：
- 查找某个 Agent 在某天的所有对话记录
- 查找某个 Agent 在处理任务时调用了哪些工具（特别是 `exec` 命令的具体内容）
- 查看 Agent 的思考过程（thinking block）
- 增量解析，只解析未处理过的文件

---

## 2. 数据库设计

### 2.1 服务器与 Agent 映射

| 服务器 | 标识 | Session 根目录 |
|--------|------|----------------|
| Mac Mini | `macmini` | `/Users/weishen/.openclaw/agents/` |
| Ubuntu1 | `ubuntu1` | `/home/shenwei/.openclaw/agents/` |
| Ubuntu2 | `ubuntu2` | `/home/shenwei/.openclaw/agents/` |

**纳入的 Agent**：main、xingyao、xinghui、xingjiang、opencode、sisyphus

### 2.2 Session 文件类型

所有 `.jsonl` 文件均需解析，包括：
- `*.jsonl` — 正常 session
- `*.jsonl.reset.*` — reset 快照
- `*.jsonl.deleted.*` — 删除快照
- `*-topic-*.jsonl` — 线程 session

**session_type 判定**：
- 文件名含 `.reset.` → `reset`
- 文件名含 `.deleted.` → `deleted`
- 文件名含 `-topic-` → `topic`
- 默认 → `normal`

**忽略文件**：`sessions.json`（索引文件，不解析）

### 2.3 数据库 Schema

#### 表：`parsed_files`（增量解析控制）

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | INT AUTO_INCREMENT | 主键 |
| `server` | VARCHAR(32) | 服务器标识 |
| `agent_id` | VARCHAR(64) | Agent ID |
| `file_path` | VARCHAR(512) | 文件绝对路径 |
| `file_mtime` | BIGINT | 文件最后修改时间（Unix timestamp） |
| `file_size` | BIGINT | 文件大小（字节） |
| `status` | VARCHAR(16) | `pending` / `success` / `failed` |
| `parsed_at` | DATETIME | 本次解析时间 |
| `error_message` | TEXT | 失败时的错误信息（可空） |

**UNIQUE 约束**：`UNIQUE(server, agent_id, file_path)`

**增量解析逻辑**：
```sql
-- 插入前检查：同一文件 + 同一修改时间是否已解析
WHERE NOT EXISTS (
  SELECT 1 FROM parsed_files
  WHERE server=? AND agent_id=? AND file_path=?
    AND file_mtime=? AND file_size=? AND status='success'
)
```

---

#### 表：`sessions`（Session 根记录）

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | INT AUTO_INCREMENT | 主键 |
| `server` | VARCHAR(32) | 服务器标识 |
| `agent_id` | VARCHAR(64) | Agent ID |
| `session_uuid` | VARCHAR(64) | Session UUID（文件名去掉.jsonl等后缀） |
| `file_path` | VARCHAR(512) | 来源文件路径 |
| `session_type` | VARCHAR(32) | `normal` / `topic` / `reset` / `deleted` |
| `cwd` | VARCHAR(512) | Session 工作目录 |
| `started_at` | DATETIME(3) | Session 开始时间 |
| `first_message_at` | DATETIME(3) | 第一条 message 的时间 |
| `last_message_at` | DATETIME(3) | 最后一条 message 的时间 |
| `message_count` | INT | 该 session 的消息总数 |
| `created_at` | DATETIME | 记录创建时间 |

**UNIQUE 约束**：`UNIQUE(session_uuid, server, agent_id)`

---

#### 表：`messages`（消息内容）

| 字段 | 类型 | 说明 |
|------|------|------|
| `id` | INT AUTO_INCREMENT | 主键 |
| `session_id` | INT | 外键 → `sessions.id` |
| `server` | VARCHAR(32) | 服务器标识 |
| `agent_id` | VARCHAR(64) | Agent ID |
| `session_uuid` | VARCHAR(64) | Session UUID（冗余存储，方便查询） |
| `message_uuid` | VARCHAR(64) | 消息 UUID |
| `parent_message_uuid` | VARCHAR(64) | 父消息 UUID（可空） |
| `role` | VARCHAR(32) | `user` / `assistant` / `toolResult` |
| `content_blocks` | JSON | **原文**：原始 content 数组 |
| `text_preview` | VARCHAR(512) | 纯文本摘要（前512字符） |
| `first_tool_call` | VARCHAR(128) | 首个 toolCall name |
| `tool_call_count` | INT | toolCall 总数 |
| `tool_calls_json` | JSON | **拆出**：所有 toolCall block |
| `thinking_text` | TEXT | **拆出**：首个 thinking block |
| `has_thinking` | TINYINT | 是否有 thinking（0/1） |
| `has_tool_calls` | TINYINT | 是否有 toolCall（0/1） |
| `is_error` | TINYINT | 整条 isError 标记 |
| `provider` | VARCHAR(64) | AI Provider（如 `minimax`） |
| `model` | VARCHAR(128) | 模型 ID（如 `MiniMax-M2.7`） |
| `api` | VARCHAR(64) | API 类型（如 `anthropic-messages`） |
| `stop_reason` | VARCHAR(64) | stopReason 字段值 |
| `input_tokens` | INT | 输入 token 数 |
| `output_tokens` | INT | 输出 token 数 |
| `cache_read_tokens` | BIGINT | Cache Read token 数 |
| `cache_write_tokens` | BIGINT | Cache Write token 数 |
| `total_tokens` | INT | 总 token 数 |
| `cost_usd` | DECIMAL(12,8) | 该消息美元成本 |
| `timestamp` | DATETIME(3) | 消息时间 |
| `created_at` | DATETIME | 记录创建时间 |

**Indexes**：
- `idx_agent_timestamp` ON `(agent_id, timestamp)`
- `idx_first_tool_call` ON `(first_tool_call)`
- `idx_session_id` ON `(session_id)`
- `idx_role` ON `(role)`

### 2.4 Content Block 结构

JSONL 中每条 `type:message` 的 `content` 数组，成员类型如下：

| content block type | 关键提取字段 |
|---|---|
| `text` | `text`（纯文本） |
| `thinking` | `thinking`、`thinkingSignature` |
| `toolCall` | `id`、`name`、`arguments`（JSON 对象） |
| `toolResult` | `toolCallId`、`toolName`、`content`、`isError`、`details` |

**`text_preview` 提取规则**（按优先级）：
1. 找第一个 `type=="text"` 的 `text` 字段
2. 找不到则找 `type=="thinking"` 的 `thinking` 字段
3. 再找不到则找 `type=="toolResult"` 里第一个子 `type=="text"` 的内容
4. 截取前512字符，strip HTML

---

## 3. Django 项目结构

```
~/Workspace/agentbase/           # Git 仓库根目录
├── manage.py
├── agentbase/                    # Django 项目
│   ├── __init__.py
│   ├── settings.py              # 数据库配置在此
│   ├── urls.py
│   └── wsgi.py
├── messages/                     # Django App
│   ├── __init__.py
│   ├── models.py                # ParsedFile / Session / Message
│   ├── admin.py                 # Django Admin 配置
│   ├── views.py                 # Web 查询视图
│   ├── urls.py
│   ├── management/
│   │   └── commands/
│   │       └── parse_sessions.py    # Django command
│   └── templates/
│       └── messages/
│           └── message_list.html
├── scripts/
│   └── parse_and_import.py      # OpenClaw cron 调用的入口脚本
├── tests/
├── requirements.txt
└── README.md
```

### 3.1 配置方式

数据库连接信息直接写在 `settings.py` 中（不使用 config.yaml）。

### 3.2 Django Admin 预期界面

**Messages（核心）**：
- 列表页支持按 `agent_id` + `timestamp` 范围 + `role` + `first_tool_call` 过滤
- 详情页展示 `content_blocks` 原始 JSON、`thinking_text`、`tool_calls_json`
- 适合查找"某 Agent 某天所有对话和思考记录"

**Sessions**：列出所有 session，按 server/agent_id 过滤。

**ParsedFiles**：记录已解析文件列表，支持按 server/agent_id/file_path 搜索。

---

## 4. 解析脚本设计

### 4.1 入口脚本

`scripts/parse_and_import.py`

- OpenClaw cron 任务调用此脚本
- 也可单独运行：`python parse_and_import.py --server macmini --agent xingyao`
- 内部加载 Django settings，调用 Django ORM 写入数据库

### 4.2 解析流程

1. **遍历服务器和 Agent**：macmini/ubuntu1/ubuntu2 → 各 Agent 目录
2. **扫描 session 文件**：列出所有 `.jsonl` 文件，跳过 `sessions.json`
3. **增量检查**：查 `parsed_files` 表，文件 mtime+size 未变且 status=success → 跳过
4. **解析 JSONL**：逐行读取，提取 `type:session` 和 `type:message` 记录
5. **写入数据库**：先插入 `sessions`，再批量插入 `messages`
6. **更新 `parsed_files`**：status=success 或 status=failed + error_message

### 4.3 OpenClaw Cron 任务

每日 00:05 执行（00:00 照片整理完成后错开）：

```bash
python ~/Workspace/agentbase/scripts/parse_and_import.py
```

- 在 Mac Mini 上执行
- 通过 SSH 访问 ubuntu1/2 的 session 目录
- 所有 Agent 的当日新 session 均被解析

---

## 5. 典型查询示例

### 5.1 查某 Agent 某天的所有消息

```sql
SELECT id, message_uuid, role, text_preview, has_thinking, first_tool_call, timestamp
FROM messages
WHERE agent_id = 'xingyao'
  AND timestamp BETWEEN '2026-04-05 00:00:00' AND '2026-04-05 23:59:59'
ORDER BY timestamp;
```

### 5.2 查某 Agent 某天调用过的所有 exec 命令

```sql
SELECT m.id, m.timestamp, m.text_preview, m.session_uuid
FROM messages m
WHERE m.agent_id = 'xingyao'
  AND m.timestamp BETWEEN '2026-04-05 00:00:00' AND '2026-04-05 23:59:59'
  AND m.first_tool_call = 'exec';
```

### 5.3 查包含 thinking 的消息

```sql
SELECT id, message_uuid, role, thinking_text, timestamp
FROM messages
WHERE agent_id = 'xingyao'
  AND has_thinking = 1
  AND timestamp BETWEEN '2026-04-05 00:00:00' AND '2026-04-05 23:59:59';
```

---

## 6. 待确认事项

- [ ] 数据库类型：MariaDB？SQLite（开发测试用）？
- [ ] 数据库名称
- [ ] MariaDB 部署在哪里？（Mac Mini 本地？NAS？独立服务器？）

---

## 7. 后续步骤

1. 用户确认以上设计
2. 创建 Git 仓库 `~/Workspace/agentbase/`
3. 初始化 Django 项目
4. 编写 `settings.py`（含数据库配置）
5. 编写 `messages/models.py`（三张表）
6. 编写 `messages/admin.py`（Admin 配置）
7. 编写 `scripts/parse_and_import.py`（解析入口）
8. 编写 `messages/management/commands/parse_sessions.py`（Django command）
9. 配置 Django Admin 模板（message_list.html）
10. 创建 OpenClaw cron 任务
