nanoClaw/docs/Design.md

11 KiB
Raw Blame History

NanoClaw 后端设计文档

架构概览

graph TB
    subgraph Frontend[前端]
        UI[Vue 3 UI]
    end

    subgraph Backend[后端]
        API[Flask Routes]
        SVC[Services]
        TOOLS[Tool System]
        DB[(Database)]
    end

    subgraph External[外部服务]
        GLM[GLM API]
        WEB[Web Resources]
    end

    UI -->|REST/SSE| API
    API --> SVC
    API --> TOOLS
    SVC --> GLM
    TOOLS --> WEB
    SVC --> DB
    TOOLS --> DB

项目结构

backend/
├── __init__.py          # 应用工厂,数据库初始化
├── models.py            # SQLAlchemy 模型
├── run.py               # 入口文件
├── config.py            # 配置加载器
│
├── routes/              # API 路由
│   ├── __init__.py
│   ├── conversations.py # 会话 CRUD
│   ├── messages.py      # 消息 CRUD + 聊天
│   ├── models.py        # 模型列表
│   ├── stats.py         # Token 统计
│   └── tools.py         # 工具列表
│
├── services/            # 业务逻辑
│   ├── __init__.py
│   ├── chat.py          # 聊天补全服务
│   └── glm_client.py    # GLM API 客户端
│
├── tools/               # 工具系统
│   ├── __init__.py
│   ├── core.py          # 核心类
│   ├── factory.py       # 工具装饰器
│   ├── executor.py      # 工具执行器
│   ├── services.py      # 辅助服务
│   └── builtin/         # 内置工具
│       ├── crawler.py   # 网页搜索、抓取
│       ├── data.py      # 计算器、文本、JSON
│       ├── weather.py   # 天气查询
│       ├── file_ops.py  # 文件操作
│       └── code.py      # 代码执行
│
└── utils/               # 辅助函数
    ├── __init__.py
    └── helpers.py       # 通用函数

类图

核心数据模型

classDiagram
    direction TB

    class User {
        +Integer id
        +String username
        +String password
        +String phone
        +relationship conversations
    }

    class Conversation {
        +String id
        +Integer user_id
        +String title
        +String model
        +String system_prompt
        +Float temperature
        +Integer max_tokens
        +Boolean thinking_enabled
        +DateTime created_at
        +DateTime updated_at
        +relationship messages
    }

    class Message {
        +String id
        +String conversation_id
        +String role
        +LongText content
        +Integer token_count
        +DateTime created_at
    }

    class TokenUsage {
        +Integer id
        +Integer user_id
        +Date date
        +String model
        +Integer prompt_tokens
        +Integer completion_tokens
        +Integer total_tokens
        +DateTime created_at
    }

    User "1" --> "*" Conversation : 拥有
    Conversation "1" --> "*" Message : 包含
    User "1" --> "*" TokenUsage : 消耗

Message Content JSON 结构

content 字段统一使用 JSON 格式存储:

User 消息:

{
  "text": "用户输入的文本内容",
  "attachments": [
    {"name": "utils.py", "extension": "py", "content": "def hello()..."}
  ]
}

Assistant 消息:

{
  "text": "AI 回复的文本内容",
  "thinking": "思考过程(可选)",
  "tool_calls": [
    {
      "id": "call_xxx",
      "name": "read_file",
      "arguments": "{\"path\": \"...\"}",
      "result": "{\"content\": \"...\"}",
      "success": true,
      "skipped": false,
      "execution_time": 0.5
    }
  ]
}

服务层

classDiagram
    direction TB

    class ChatService {
        -GLMClient glm_client
        -ToolExecutor executor
        +Integer MAX_ITERATIONS
        +sync_response(conv, tools_enabled) Response
        +stream_response(conv, tools_enabled) Response
        -_build_tool_calls_json(calls, results) list
        -_message_to_dict(msg) dict
        -_process_tool_calls_delta(delta, list) list
    }

    class GLMClient {
        -str api_url
        -str api_key
        +call(model, messages, kwargs) Response
    }

    class ToolExecutor {
        -ToolRegistry registry
        -dict _cache
        -list _call_history
        +process_tool_calls(calls, context) list
        +build_request(messages, model, tools) dict
        +clear_history() void
    }

    ChatService --> GLMClient : 使用
    ChatService --> ToolExecutor : 使用

工具系统

classDiagram
    direction TB

    class ToolDefinition {
        <<dataclass>>
        +str name
        +str description
        +dict parameters
        +Callable handler
        +str category
        +to_openai_format() dict
    }

    class ToolRegistry {
        -dict _tools
        +register(ToolDefinition) void
        +get(str name) ToolDefinition?
        +list_all() list~dict~
        +list_by_category(str) list~dict~
        +execute(str name, dict args) dict
        +remove(str name) bool
        +has(str name) bool
    }

    class ToolExecutor {
        -ToolRegistry registry
        -dict _cache
        -list _call_history
        +process_tool_calls(list, dict) list
        +clear_history() void
    }

    class ToolResult {
        <<dataclass>>
        +bool success
        +Any data
        +str? error
        +to_dict() dict
        +ok(Any)$ ToolResult
        +fail(str)$ ToolResult
    }

    ToolRegistry "1" --> "*" ToolDefinition : 管理
    ToolExecutor "1" --> "1" ToolRegistry : 使用
    ToolDefinition ..> ToolResult : 返回

API 总览

会话管理

方法 路径 说明
POST /api/conversations 创建会话
GET /api/conversations 获取会话列表(游标分页)
GET /api/conversations/:id 获取会话详情
PATCH /api/conversations/:id 更新会话
DELETE /api/conversations/:id 删除会话

消息管理

方法 路径 说明
GET /api/conversations/:id/messages 获取消息列表(游标分页)
POST /api/conversations/:id/messages 发送消息(支持 SSE 流式)
DELETE /api/conversations/:id/messages/:mid 删除消息

其他

方法 路径 说明
GET /api/models 获取模型列表
GET /api/tools 获取工具列表
GET /api/stats/tokens Token 使用统计

SSE 事件

事件 说明
thinking_start 新一轮思考开始,前端应清空之前的思考缓冲
thinking 思维链增量内容(启用时)
message 回复内容的增量片段
tool_calls 工具调用信息
tool_result 工具执行结果
process_step 处理步骤按顺序thinking/tool_call/tool_result支持交替显示
error 错误信息
done 回复结束,携带 message_id 和 token_count

思考与工具调用交替流程

iteration 1:
  thinking_start  -> 前端清空 streamThinking
  thinking (增量)  -> 前端累加到 streamThinking
  process_step(thinking, "思考内容A")
  tool_calls -> 批量通知(兼容)
  process_step(tool_call, "file_read")   -> 调用工具
  process_step(tool_result, {...})       -> 立即返回结果
  process_step(tool_call, "file_list")   -> 下一个工具
  process_step(tool_result, {...})       -> 立即返回结果

iteration 2:
  thinking_start  -> 前端清空 streamThinking
  thinking (增量)  -> 前端累加到 streamThinking
  process_step(thinking, "思考内容B")
  done

process_step 事件格式

// 思考过程
{"index": 0, "type": "thinking", "content": "完整思考内容..."}

// 工具调用
{"index": 1, "type": "tool_call", "id": "call_abc123", "name": "web_search", "arguments": "{\"query\": \"...\"}"}

// 工具返回
{"index": 2, "type": "tool_result", "id": "call_abc123", "name": "web_search", "content": "{\"success\": true, ...}", "skipped": false}

字段说明:

  • index: 步骤序号,确保按正确顺序显示
  • type: 步骤类型thinking/tool_call/tool_result
  • id: 工具调用唯一标识,用于匹配工具调用和返回结果
  • name: 工具名称
  • content: 内容或结果
  • skipped: 工具是否被跳过(失败后跳过)

数据模型

User用户

字段 类型 说明
id Integer 自增主键
username String(50) 用户名(唯一)
password String(255) 密码(可为空,支持第三方登录)
phone String(20) 手机号

Conversation会话

字段 类型 默认值 说明
id String(64) UUID 主键
user_id Integer - 外键关联 User
title String(255) "" 会话标题
model String(64) "glm-5" 模型名称
system_prompt Text "" 系统提示词
temperature Float 1.0 采样温度
max_tokens Integer 65536 最大输出 token
thinking_enabled Boolean False 是否启用思维链
created_at DateTime now 创建时间
updated_at DateTime now 更新时间

Message消息

字段 类型 说明
id String(64) UUID 主键
conversation_id String(64) 外键关联 Conversation
role String(16) user/assistant/system/tool
content LongText JSON 格式内容(见上方结构说明)
token_count Integer Token 数量
created_at DateTime 创建时间

TokenUsageToken 使用统计)

字段 类型 说明
id Integer 自增主键
user_id Integer 外键关联 User
date Date 统计日期
model String(64) 模型名称
prompt_tokens Integer 输入 token
completion_tokens Integer 输出 token
total_tokens Integer 总 token
created_at DateTime 创建时间

分页机制

所有列表接口使用游标分页

GET /api/conversations?limit=20&cursor=conv_abc123

响应:

{
  "code": 0,
  "data": {
    "items": [...],
    "next_cursor": "conv_def456",
    "has_more": true
  }
}
  • limit:每页数量(会话默认 20消息默认 50最大 100
  • cursor:上一页最后一条的 ID

错误码

Code 说明
0 成功
400 请求参数错误
404 资源不存在
500 服务器错误

错误响应:

{
  "code": 404,
  "message": "conversation not found"
}

配置文件

配置文件:config.yml

# 服务端口
backend_port: 3000
frontend_port: 4000

# GLM API
api_key: your-api-key
api_url: https://open.bigmodel.cn/api/paas/v4/chat/completions

# 数据库
db_type: mysql  # mysql, sqlite, postgresql
db_host: localhost
db_port: 3306
db_user: root
db_password: ""
db_name: nano_claw
db_sqlite_file: app.db  # SQLite 时使用