Ver código fonte

上传文件至 ''

wxcz_admin 2 semanas atrás
pai
commit
08fa6969b9
5 arquivos alterados com 719 adições e 199 exclusões
  1. 2 0
      .gitignore
  2. 197 199
      README.md
  3. 51 0
      app.md
  4. 452 0
      app.py
  5. 17 0
      pyproject.toml

+ 2 - 0
.gitignore

@@ -0,0 +1,2 @@
+/database
+/venv

+ 197 - 199
README.md

@@ -1,199 +1,197 @@
-# AI智能瞭望与智能问数系统
-
-# AI智能瞭望与智能问数系统
-
-## 1. 项目概述
-本项目是一个基于 B/S 架构的 Web 应用程序,采用 Tornado MVC 组织代码,目标是构建“AI智能瞭望与智能问数系统”。当前已具备较完整的后台管理能力(RBAC 权限、模型引擎、瞭望采集与数据仓库等),并提供若干独立业务页面用于采集与测试。
-
-## 2. 技术栈
-- **后端语言**: Python 3.11
-- **Web 框架**: Tornado 6.5.5
-- **数据库**: SQLite3
-- **前端技术**: HTML5 + CSS3 + JavaScript
-- **前端UI库与组件** (已本地化部署于 `app/static/dist/` 目录):
-  - Layui 2.13.6
-  - Bootstrap 5.3.8
-  - FontAwesome 5.15.4
-  - ECharts(`app/static/dist/echarts/echarts.min.js`)
-
-## 3. 架构设计 (MVC 架构)
-本项目采用了经典的 MVC(Model-View-Controller)设计模式:
-- **Model (模型层)**: 负责数据访问与业务逻辑。使用 `sqlite3` 与数据库交互,通过实体类(如 `UserRepository`)封装数据操作。
-- **View (视图层)**: 负责用户界面的呈现。使用 Tornado 原生的模板引擎,结合 HTML/CSS/JS 渲染页面(如 `base.html`, `login.html`, `index.html`)。
-- **Controller (控制层)**: 负责接收 HTTP 请求、处理业务流程,并协调 Model 和 View。在 Tornado 中,以 `RequestHandler` 的形式存在(如 `LoginHandler`, `IndexHandler`)。
-
-## 4. 目录结构说明
-```text
-cnAgentOS/
-├── app.md                  # 原有目录结构说明文档
-├── app.py                  # 项目主入口,Tornado Web 容器及路由配置
-├── test.py                 # 单元测试与临时测试脚本,包含数据库初始化和用户测试
-├── app/                    # 核心应用程序包
-│   ├── __init__.py         # 标识 app 为 Python 包
-│   ├── controllers/        # 控制层 (Controller)
-│   │   ├── __init__.py     # 标识 controllers 为 Python 包
-│   │   ├── auth.py         # 认证相关路由 (登录、退出)
-│   │   ├── base.py         # 基础 RequestHandler,提供公共方法(如 get_current_user)
-│   │   ├── home.py         # 后台首页/控制台(动态菜单渲染)
-│   │   ├── portal.py       # 用户侧(登录/注册/问数/历史对话/流式对话)
-│   │   ├── user.py         # 用户管理(CRUD/分页/角色绑定)
-│   │   ├── role.py         # 角色管理(CRUD/超管保护)
-│   │   ├── menu.py         # 功能菜单管理(树形CRUD)
-│   │   ├── model_engine.py # 模型引擎(CRUD/默认模型/Token统计/测试对话SSE)
-│   │   ├── api_manage.py   # 接口管理(CRUD/分页/对内代理服务/QPS限制)
-│   │   ├── digital_employee.py # 数字员工(CRUD/分页/@别名解析/SSE对话/普通类接口调用/记忆管理/技能路由)
-│   │   ├── skill_manage.py # 技能管理(全局技能库 CRUD/zip 导入/与数字员工联动)
-│   │   ├── chat_admin.py # 会话/对话管理(后台查看/搜索/删除)
-│   │   ├── spy_source.py   # 瞭望采集源管理(CRUD/启用列表)
-│   │   ├── spy.py          # 瞭望采集(SSE日志+候选数据推送+手动入库)
-│   │   └── data_warehouse.py # 数据仓库(列表/删除/批删/AI深度采集/详情查看)
-│   ├── models/             # 模型层 (Model)
-│   │   ├── __init__.py     # 标识 models 为 Python 包
-│   │   ├── db.py           # 数据库连接池与初始化建表脚本
-│   │   ├── user.py         # 用户与用户-角色映射
-│   │   ├── role.py         # 角色与角色-菜单映射
-│   │   ├── menu.py         # 菜单树构建与按角色过滤
-│   │   ├── model_engine.py # 模型服务与Token统计
-│   │   ├── api_manage.py   # 接口管理数据访问(api_endpoints)
-│   │   ├── digital_employee.py # 数字员工数据访问(digital_employees + 全局 skills/links + 记忆管理)
-│   │   ├── skill_manage.py # 技能管理数据访问(全局技能库 + 导入解析)
-│   │   ├── chat.py # 会话/对话管理(CRUD/搜索/导出/后台管理)
-│   │   ├── spy_source.py   # 采集源管理
-│   │   └── data_warehouse.py # 数据仓库(含 upsert_batch / 深采详情查询 / 会话存储)
-│   ├── static/             # 静态资源 (View 辅助)
-│   │   ├── css/base.css    # 基础样式表
-│   │   └── js/base.js      # 基础脚本
-│   └── templates/          # 模板文件 (View)
-│       ├── base.html       # 基础母版页面
-│       ├── index.html      # 后台首页模板
-│       ├── login.html      # 登录页面模板
-│       ├── dashboard.html  # 控制台(ECharts 报表 + 懒加载 + 历史回放)
-│       ├── user/list.html  # 用户管理页
-│       ├── role/list.html  # 角色管理页
-│       ├── menu/list.html  # 功能菜单管理页
-│       ├── model_engine/list.html # 模型引擎页
-│       ├── api_manage/list.html   # 接口管理页
-│       ├── digital_employee/list.html # 数字员工管理页
-│       ├── skill_manage/list.html # 技能管理页(全局技能库 + zip 导入)
-│       ├── chat_admin/sessions.html # 会话管理页(后台查看/搜索/删除)
-│       ├── chat_admin/messages.html # 对话管理页(消息查看/检索)
-│       ├── spy/source_list.html   # 瞭望数据源管理页
-│       ├── spy/spy.html           # 瞭望采集页(独立搜索风格)
-│       ├── data_warehouse/list.html # 数据仓库列表页
-│       └── portal/               # 用户侧页面
-│           ├── login.html        # 用户登录
-│           ├── register.html     # 用户注册
-│           └── chat.html         # 智能问数(历史会话 + 流式对话 + 模型切换 + @数字员工)
-├── database/               # 数据库文件存放目录
-│   └── app.db              # SQLite 数据库文件 (由程序自动生成)
-└── venv/                   # Python 虚拟环境目录
-```
-
-## 5. 已实现功能
-当前系统已完成以下核心功能:
-1. **服务启动与配置**: 通过 `app.py` 启动 Tornado 服务,配置模板/静态资源路径、安全 Cookie(`cookie_secret`)以及 CSRF 防护(`xsrf_cookies`)。
-2. **数据库初始化与轻量迁移**: 启动时调用 `init_db()` 自动建表,并对历史库做兼容(例如为 `spy_data` 补列、建立唯一索引)。
-3. **登录态与认证**:
-   - 安全 Cookie 会话管理(`set_secure_cookie`)。
-   - `BaseHandler.get_current_user()` 提供统一登录态获取;`@tornado.web.authenticated` 保护页面与接口。
-   - 密码存储采用 PBKDF2-HMAC-SHA256 加盐哈希。
-4. **RBAC 权限体系(管理侧)**:
-   - 用户/角色/菜单功能管理(CRUD + 分页/树形)。
-   - 角色与菜单授权(二级联动映射)。
-   - 根据用户角色动态过滤并渲染后台左侧菜单树(超级管理员默认全量菜单)。
-5. **模型引擎(管理侧)**:
-   - 模型服务配置 CRUD(OpenAI 范式:`model_name/api_key/base_url`)。
-   - 设置默认模型、Token 统计展示。
-   - 测试对话支持 SSE 流式响应。
-6. **智能瞭望与数据仓库(管理侧 + 独立采集页)**:
-   - 瞭望数据源管理(动态配置 URL 模板与请求 headers,启用/停用)。
-   - 采集过程 SSE 日志输出;采集结果以橱窗列表展示(不自动入库)。
-   - 支持批量勾选后手动入库到数据仓库(upsert:存在则更新,不存在则新增)。
-   - 数据仓库列表分页、单删/批删。
-   - AI 深度采集:对数据仓库条目进行深度抓取与 AI 解析,入库到详情表并标注深采状态;支持单条/多条,SSE 日志与统计,支持详情查看。
-7. **接口管理(管理侧)**:
-   - 接口 URL 管理(新增/修改/删除/分页)。
-   - 对系统其他模块提供统一代理服务接口:`/api/interface_proxy?id=<endpoint_id>`。
-   - QPS 默认限制(可配置):窗口期内超过上限返回 429;携带 Token 可绕过限制。
-8. **数字员工(管理侧 + 对话接口)**:
-   - 数字员工管理(新增/修改/删除/分页),支持 AI/普通 两类。
-   - AI 类:默认模型 + Prompt,通过 SSE 流式输出对话内容与 Token 统计。
-   - 普通类:绑定接口管理 API,支持将用户输入作为指定参数名透传到外部接口。
-   - 增强能力:
-     - 记忆:按用户隔离(employee_id + user_id 唯一),后台支持查看/清空;支持 LLM 自动摘要/压缩。
-     - 技能:AI 员工可绑定多个全局技能(SKILL.md 规范),用户侧自动识别并渲染卡片(天气/音乐等)。
-     - 多模型:AI 员工支持绑定多个模型,按任务/模态/推理需求选择最优模型。
-9. **技能管理(独立模块)**:
-   - 全局技能库 CRUD(skill_key 唯一约束,支持绑定接口服务)。
-   - 支持导入 zip 技能包,自动扫描并识别 `SKILL.md`(YAML frontmatter)入库。
-   - 与数字员工技能联动(同一套 `skills` 表 + `digital_employee_skill_links` 绑定关系表)。
-   - 数字员工支持从技能库选择并绑定技能;解绑不删除全局技能。
-10. **会话/对话管理(管理侧)**:
-   - 会话管理:列表分页/按用户筛选/按标题搜索/删除会话。
-   - 对话管理:查看会话内所有消息/按角色筛选/关键词检索/删除单条消息。
-11. **控制台(管理侧)**:
-   - ECharts 本地化加载(`/static/dist/echarts/echarts.min.js`)。
-   - 系统运行状态、模块概览、数据概览与图形报表。
-   - 懒加载:图表进入可视区域后初始化(IntersectionObserver + fallback)。
-   - 历史回放:指标快照存储 + 时间点回放。
-
-12. **用户侧卡片与对话增强**:
-   - 天气卡片:横向布局(max-width 520px),动态呼吸特效图标,展示温度/天气/湿度/风力/AQI/气压/能见度,无预报数据时自动隐藏预报容器。
-   - 音乐卡片:后端自动跟踪音乐 URL 跳转获取真实 MP3 地址;新增 `/api/audio_proxy` 音频代理接口(Base64 编码 URL),解决跨域和 Referer 限制,点击播放按钮可正常播放。
-   - Markdown 表格渲染:增加表格检测与解析(识别 `| 列1 | 列2 |` 格式),生成 `<table>/<thead>/<tbody>` 结构,样式支持圆角边框/表头高亮/行悬停/横向滚动。
-   - 意图识别优化:增强模板(详细表结构说明、分类规则、SQL 示例),上下文窗口从 12 条增加到 20 条,System Prompt 动态调整(根据意图类型调整语气),LRU 缓存(200 条,5 分钟过期)。
-   - 记忆压缩:使用 LLM 结构化摘要(用户偏好/重要事实/最近交互),按主题聚类保留关键信息。
-
-## 6. 主要路由
-- 用户侧:`/`、`/login`、`/register`、`/chat`、`/logout`
-- 用户侧 API:`/api/models`、`/api/employees`、`/api/sessions`、`/api/messages`、`/api/chat/stream`、`/api/audio_proxy`
-- 管理侧:`/admin`(后台框架)、`/admin/login`、`/admin/logout`、`/admin/dashboard`
-- 管理侧控制台 API:`/api/dashboard`(current/history_list/history_get)
-- 管理侧兼容:`/auth/login`、`/auth/logout`、`/dashboard`
-- 用户/角色/菜单:`/user/list`、`/role/list`、`/menu/list`(对应 API:`/api/user`、`/api/role`、`/api/menu`)
-- 模型引擎:`/model_engine/list`(API:`/api/model_engine`、`/api/model_engine/test`)
-- 接口管理:`/api_manage/list`(API:`/api/api_manage`、`/api/interface_proxy`)
-- 数字员工:`/digital_employee/list`(API:`/api/digital_employee`、`/api/digital_employee/chat`)
-- 技能管理:`/skill_manage/list`(API:`/api/skill_manage`)
-- 会话管理:`/chat_admin/sessions`(API:`/api/chat_admin/sessions`)
-- 对话管理:`/chat_admin/messages`(API:`/api/chat_admin/messages`)
-- 瞭望管理:`/spy_source/list`(API:`/api/spy_source`)、`/spy`(API:`/api/spy/run`、`/api/spy/commit`)
-- 数据仓库:`/data_warehouse/list`(API:`/api/data_warehouse`)
-
-## 7. 数据库设计(SQLite)
-核心表(由 `app/models/db.py:init_db()` 创建/维护):
-- `users`:用户基础信息
-- `roles`:角色
-- `menus`:菜单/功能
-- `user_roles`:用户-角色映射
-- `role_menus`:角色-菜单映射
-- `ai_models`:模型服务配置(含 `is_default`、`total_tokens`)
-- `api_endpoints`:接口管理(URL/Method/限流参数/绕过 Token/示例请求等)
-- `digital_employees`:数字员工(alias 唯一;AI Prompt 或绑定 api_endpoint_id)
-- `digital_employee_memories`:数字员工记忆(employee_id + user_id 唯一,存储结构化摘要)
-- `skills`:全局技能库(skill_key 唯一;spec_markdown/YAML frontmatter;绑定接口服务)
-- `digital_employee_skill_links`:数字员工与技能的绑定关系(employee_id + skill_id 联合主键)
-- `spy_sources`:瞭望采集源
-- `spy_data`:数据仓库(包含 `update_at`;并对 `(source_id,url)` 建立唯一索引用于去重与 upsert;包含深采状态字段)
-- `spy_data_details`:深采详情(与 `spy_data.id` 关联,保存正文/摘要/关键词/要点/统计等)
-- `chat_sessions`:用户侧历史会话
-- `chat_messages`:用户侧历史消息(流式结果最终落库)
-- `dashboard_snapshots`:控制台指标快照(用于历史回放)
-
-## 8. 后续开发指南
-在后续开发“AI智能瞭望与智能问数系统”时,请遵循以下规范:
-1. **新增业务路由**: 
-   - 在 `app/controllers/` 目录下创建新的 `.py` 文件,继承 `app.controllers.base.BaseHandler`。
-   - 如果接口需要登录权限,请在请求方法(如 `get`, `post`)上添加 `@tornado.web.authenticated` 装饰器。
-   - 编写完成后,务必在根目录的 `app.py` 中的 `make_app()` 路由列表里注册新路由。
-2. **新增数据模型**:
-   - 在 `app/models/` 目录下创建新的业务模型文件。
-   - 涉及建表操作的,请在 `app/models/db.py` 的 `init_db()` 方法中追加相应的 `CREATE TABLE IF NOT EXISTS` 语句。
-   - 尽量将数据库的 CRUD 操作封装在 `XxxRepository` 静态方法中。
-3. **前端页面开发**:
-   - HTML 文件放在 `app/templates/` 下,建议继承 `base.html` 保持页面结构统一。
-   - 表单提交时,务必在 `<form>` 标签内包含 `{% module xsrf_form_html() %}` 以通过 CSRF 校验。
-   - 静态资源(CSS, JS, 图片等)存放在 `app/static/` 目录下,在模板中通过 `{{ static_url('...') }}` 引用。
-   - 页面自定义样式请放在 `{% block style %}` 中(由 `base.html` 在 head 内输出),避免样式丢失。
-4. **测试规范**:
-   - 可以继续在 `test.py` 中编写临时的功能测试代码。
+# AI智能瞭望与智能问数系统
+
+## 1. 项目概述
+本项目是一个基于 B/S 架构的 Web 应用程序,采用 Tornado MVC 组织代码,目标是构建“AI智能瞭望与智能问数系统”。当前已具备较完整的后台管理能力(RBAC 权限、模型引擎、瞭望采集与数据仓库等),并提供若干独立业务页面用于采集与测试。
+
+## 2. 技术栈
+- **后端语言**: Python 3.11
+- **Web 框架**: Tornado 6.5.5
+- **数据库**: SQLite3
+- **前端技术**: HTML5 + CSS3 + JavaScript
+- **前端UI库与组件** (已本地化部署于 `app/static/dist/` 目录):
+  - Layui 2.13.6
+  - Bootstrap 5.3.8
+  - FontAwesome 5.15.4
+  - ECharts(`app/static/dist/echarts/echarts.min.js`)
+
+## 3. 架构设计 (MVC 架构)
+本项目采用了经典的 MVC(Model-View-Controller)设计模式:
+- **Model (模型层)**: 负责数据访问与业务逻辑。使用 `sqlite3` 与数据库交互,通过实体类(如 `UserRepository`)封装数据操作。
+- **View (视图层)**: 负责用户界面的呈现。使用 Tornado 原生的模板引擎,结合 HTML/CSS/JS 渲染页面(如 `base.html`, `login.html`, `index.html`)。
+- **Controller (控制层)**: 负责接收 HTTP 请求、处理业务流程,并协调 Model 和 View。在 Tornado 中,以 `RequestHandler` 的形式存在(如 `LoginHandler`, `IndexHandler`)。
+
+## 4. 目录结构说明
+```text
+cnAgentOS/
+├── app.md                  # 原有目录结构说明文档
+├── app.py                  # 项目主入口,Tornado Web 容器及路由配置
+├── test.py                 # 单元测试与临时测试脚本,包含数据库初始化和用户测试
+├── app/                    # 核心应用程序包
+│   ├── __init__.py         # 标识 app 为 Python 包
+│   ├── controllers/        # 控制层 (Controller)
+│   │   ├── __init__.py     # 标识 controllers 为 Python 包
+│   │   ├── auth.py         # 认证相关路由 (登录、退出)
+│   │   ├── base.py         # 基础 RequestHandler,提供公共方法(如 get_current_user)
+│   │   ├── home.py         # 后台首页/控制台(动态菜单渲染)
+│   │   ├── portal.py       # 用户侧(登录/注册/问数/历史对话/流式对话)
+│   │   ├── user.py         # 用户管理(CRUD/分页/角色绑定)
+│   │   ├── role.py         # 角色管理(CRUD/超管保护)
+│   │   ├── menu.py         # 功能菜单管理(树形CRUD)
+│   │   ├── model_engine.py # 模型引擎(CRUD/默认模型/Token统计/测试对话SSE)
+│   │   ├── api_manage.py   # 接口管理(CRUD/分页/对内代理服务/QPS限制)
+│   │   ├── digital_employee.py # 数字员工(CRUD/分页/@别名解析/SSE对话/普通类接口调用/记忆管理/技能路由)
+│   │   ├── skill_manage.py # 技能管理(全局技能库 CRUD/zip 导入/与数字员工联动)
+│   │   ├── chat_admin.py # 会话/对话管理(后台查看/搜索/删除)
+│   │   ├── spy_source.py   # 瞭望采集源管理(CRUD/启用列表)
+│   │   ├── spy.py          # 瞭望采集(SSE日志+候选数据推送+手动入库)
+│   │   └── data_warehouse.py # 数据仓库(列表/删除/批删/AI深度采集/详情查看)
+│   ├── models/             # 模型层 (Model)
+│   │   ├── __init__.py     # 标识 models 为 Python 包
+│   │   ├── db.py           # 数据库连接池与初始化建表脚本
+│   │   ├── user.py         # 用户与用户-角色映射
+│   │   ├── role.py         # 角色与角色-菜单映射
+│   │   ├── menu.py         # 菜单树构建与按角色过滤
+│   │   ├── model_engine.py # 模型服务与Token统计
+│   │   ├── api_manage.py   # 接口管理数据访问(api_endpoints)
+│   │   ├── digital_employee.py # 数字员工数据访问(digital_employees + 全局 skills/links + 记忆管理)
+│   │   ├── skill_manage.py # 技能管理数据访问(全局技能库 + 导入解析)
+│   │   ├── chat.py # 会话/对话管理(CRUD/搜索/导出/后台管理)
+│   │   ├── spy_source.py   # 采集源管理
+│   │   └── data_warehouse.py # 数据仓库(含 upsert_batch / 深采详情查询 / 会话存储)
+│   ├── static/             # 静态资源 (View 辅助)
+│   │   ├── css/base.css    # 基础样式表
+│   │   └── js/base.js      # 基础脚本
+│   └── templates/          # 模板文件 (View)
+│       ├── base.html       # 基础母版页面
+│       ├── index.html      # 后台首页模板
+│       ├── login.html      # 登录页面模板
+│       ├── dashboard.html  # 控制台(ECharts 报表 + 懒加载 + 历史回放)
+│       ├── user/list.html  # 用户管理页
+│       ├── role/list.html  # 角色管理页
+│       ├── menu/list.html  # 功能菜单管理页
+│       ├── model_engine/list.html # 模型引擎页
+│       ├── api_manage/list.html   # 接口管理页
+│       ├── digital_employee/list.html # 数字员工管理页
+│       ├── skill_manage/list.html # 技能管理页(全局技能库 + zip 导入)
+│       ├── chat_admin/sessions.html # 会话管理页(后台查看/搜索/删除)
+│       ├── chat_admin/messages.html # 对话管理页(消息查看/检索)
+│       ├── spy/source_list.html   # 瞭望数据源管理页
+│       ├── spy/spy.html           # 瞭望采集页(独立搜索风格)
+│       ├── data_warehouse/list.html # 数据仓库列表页
+│       └── portal/               # 用户侧页面
+│           ├── login.html        # 用户登录
+│           ├── register.html     # 用户注册
+│           └── chat.html         # 智能问数(历史会话 + 流式对话 + 模型切换 + @数字员工)
+├── database/               # 数据库文件存放目录
+│   └── app.db              # SQLite 数据库文件 (由程序自动生成)
+└── venv/                   # Python 虚拟环境目录
+```
+
+## 5. 已实现功能
+当前系统已完成以下核心功能:
+1. **服务启动与配置**: 通过 `app.py` 启动 Tornado 服务,配置模板/静态资源路径、安全 Cookie(`cookie_secret`)以及 CSRF 防护(`xsrf_cookies`)。
+2. **数据库初始化与轻量迁移**: 启动时调用 `init_db()` 自动建表,并对历史库做兼容(例如为 `spy_data` 补列、建立唯一索引)。
+3. **登录态与认证**:
+   - 安全 Cookie 会话管理(`set_secure_cookie`)。
+   - `BaseHandler.get_current_user()` 提供统一登录态获取;`@tornado.web.authenticated` 保护页面与接口。
+   - 密码存储采用 PBKDF2-HMAC-SHA256 加盐哈希。
+4. **RBAC 权限体系(管理侧)**:
+   - 用户/角色/菜单功能管理(CRUD + 分页/树形)。
+   - 角色与菜单授权(二级联动映射)。
+   - 根据用户角色动态过滤并渲染后台左侧菜单树(超级管理员默认全量菜单)。
+5. **模型引擎(管理侧)**:
+   - 模型服务配置 CRUD(OpenAI 范式:`model_name/api_key/base_url`)。
+   - 设置默认模型、Token 统计展示。
+   - 测试对话支持 SSE 流式响应。
+6. **智能瞭望与数据仓库(管理侧 + 独立采集页)**:
+   - 瞭望数据源管理(动态配置 URL 模板与请求 headers,启用/停用)。
+   - 采集过程 SSE 日志输出;采集结果以橱窗列表展示(不自动入库)。
+   - 支持批量勾选后手动入库到数据仓库(upsert:存在则更新,不存在则新增)。
+   - 数据仓库列表分页、单删/批删。
+   - AI 深度采集:对数据仓库条目进行深度抓取与 AI 解析,入库到详情表并标注深采状态;支持单条/多条,SSE 日志与统计,支持详情查看。
+7. **接口管理(管理侧)**:
+   - 接口 URL 管理(新增/修改/删除/分页)。
+   - 对系统其他模块提供统一代理服务接口:`/api/interface_proxy?id=<endpoint_id>`。
+   - QPS 默认限制(可配置):窗口期内超过上限返回 429;携带 Token 可绕过限制。
+8. **数字员工(管理侧 + 对话接口)**:
+   - 数字员工管理(新增/修改/删除/分页),支持 AI/普通 两类。
+   - AI 类:默认模型 + Prompt,通过 SSE 流式输出对话内容与 Token 统计。
+   - 普通类:绑定接口管理 API,支持将用户输入作为指定参数名透传到外部接口。
+   - 增强能力:
+     - 记忆:按用户隔离(employee_id + user_id 唯一),后台支持查看/清空;支持 LLM 自动摘要/压缩。
+     - 技能:AI 员工可绑定多个全局技能(SKILL.md 规范),用户侧自动识别并渲染卡片(天气/音乐等)。
+     - 多模型:AI 员工支持绑定多个模型,按任务/模态/推理需求选择最优模型。
+9. **技能管理(独立模块)**:
+   - 全局技能库 CRUD(skill_key 唯一约束,支持绑定接口服务)。
+   - 支持导入 zip 技能包,自动扫描并识别 `SKILL.md`(YAML frontmatter)入库。
+   - 与数字员工技能联动(同一套 `skills` 表 + `digital_employee_skill_links` 绑定关系表)。
+   - 数字员工支持从技能库选择并绑定技能;解绑不删除全局技能。
+10. **会话/对话管理(管理侧)**:
+   - 会话管理:列表分页/按用户筛选/按标题搜索/删除会话。
+   - 对话管理:查看会话内所有消息/按角色筛选/关键词检索/删除单条消息。
+11. **控制台(管理侧)**:
+   - ECharts 本地化加载(`/static/dist/echarts/echarts.min.js`)。
+   - 系统运行状态、模块概览、数据概览与图形报表。
+   - 懒加载:图表进入可视区域后初始化(IntersectionObserver + fallback)。
+   - 历史回放:指标快照存储 + 时间点回放。
+
+12. **用户侧卡片与对话增强**:
+   - 天气卡片:横向布局(max-width 520px),动态呼吸特效图标,展示温度/天气/湿度/风力/AQI/气压/能见度,无预报数据时自动隐藏预报容器。
+   - 音乐卡片:后端自动跟踪音乐 URL 跳转获取真实 MP3 地址;新增 `/api/audio_proxy` 音频代理接口(Base64 编码 URL),解决跨域和 Referer 限制,点击播放按钮可正常播放。
+   - Markdown 表格渲染:增加表格检测与解析(识别 `| 列1 | 列2 |` 格式),生成 `<table>/<thead>/<tbody>` 结构,样式支持圆角边框/表头高亮/行悬停/横向滚动。
+   - 意图识别优化:增强模板(详细表结构说明、分类规则、SQL 示例),上下文窗口从 12 条增加到 20 条,System Prompt 动态调整(根据意图类型调整语气),LRU 缓存(200 条,5 分钟过期)。
+   - 记忆压缩:使用 LLM 结构化摘要(用户偏好/重要事实/最近交互),按主题聚类保留关键信息。
+
+## 6. 主要路由
+- 用户侧:`/`、`/login`、`/register`、`/chat`、`/logout`
+- 用户侧 API:`/api/models`、`/api/employees`、`/api/sessions`、`/api/messages`、`/api/chat/stream`、`/api/audio_proxy`
+- 管理侧:`/admin`(后台框架)、`/admin/login`、`/admin/logout`、`/admin/dashboard`
+- 管理侧控制台 API:`/api/dashboard`(current/history_list/history_get)
+- 管理侧兼容:`/auth/login`、`/auth/logout`、`/dashboard`
+- 用户/角色/菜单:`/user/list`、`/role/list`、`/menu/list`(对应 API:`/api/user`、`/api/role`、`/api/menu`)
+- 模型引擎:`/model_engine/list`(API:`/api/model_engine`、`/api/model_engine/test`)
+- 接口管理:`/api_manage/list`(API:`/api/api_manage`、`/api/interface_proxy`)
+- 数字员工:`/digital_employee/list`(API:`/api/digital_employee`、`/api/digital_employee/chat`)
+- 技能管理:`/skill_manage/list`(API:`/api/skill_manage`)
+- 会话管理:`/chat_admin/sessions`(API:`/api/chat_admin/sessions`)
+- 对话管理:`/chat_admin/messages`(API:`/api/chat_admin/messages`)
+- 瞭望管理:`/spy_source/list`(API:`/api/spy_source`)、`/spy`(API:`/api/spy/run`、`/api/spy/commit`)
+- 数据仓库:`/data_warehouse/list`(API:`/api/data_warehouse`)
+
+## 7. 数据库设计(SQLite)
+核心表(由 `app/models/db.py:init_db()` 创建/维护):
+- `users`:用户基础信息
+- `roles`:角色
+- `menus`:菜单/功能
+- `user_roles`:用户-角色映射
+- `role_menus`:角色-菜单映射
+- `ai_models`:模型服务配置(含 `is_default`、`total_tokens`)
+- `api_endpoints`:接口管理(URL/Method/限流参数/绕过 Token/示例请求等)
+- `digital_employees`:数字员工(alias 唯一;AI Prompt 或绑定 api_endpoint_id)
+- `digital_employee_memories`:数字员工记忆(employee_id + user_id 唯一,存储结构化摘要)
+- `skills`:全局技能库(skill_key 唯一;spec_markdown/YAML frontmatter;绑定接口服务)
+- `digital_employee_skill_links`:数字员工与技能的绑定关系(employee_id + skill_id 联合主键)
+- `spy_sources`:瞭望采集源
+- `spy_data`:数据仓库(包含 `update_at`;并对 `(source_id,url)` 建立唯一索引用于去重与 upsert;包含深采状态字段)
+- `spy_data_details`:深采详情(与 `spy_data.id` 关联,保存正文/摘要/关键词/要点/统计等)
+- `chat_sessions`:用户侧历史会话
+- `chat_messages`:用户侧历史消息(流式结果最终落库)
+- `dashboard_snapshots`:控制台指标快照(用于历史回放)
+
+## 8. 后续开发指南
+在后续开发“AI智能瞭望与智能问数系统”时,请遵循以下规范:
+1. **新增业务路由**: 
+   - 在 `app/controllers/` 目录下创建新的 `.py` 文件,继承 `app.controllers.base.BaseHandler`。
+   - 如果接口需要登录权限,请在请求方法(如 `get`, `post`)上添加 `@tornado.web.authenticated` 装饰器。
+   - 编写完成后,务必在根目录的 `app.py` 中的 `make_app()` 路由列表里注册新路由。
+2. **新增数据模型**:
+   - 在 `app/models/` 目录下创建新的业务模型文件。
+   - 涉及建表操作的,请在 `app/models/db.py` 的 `init_db()` 方法中追加相应的 `CREATE TABLE IF NOT EXISTS` 语句。
+   - 尽量将数据库的 CRUD 操作封装在 `XxxRepository` 静态方法中。
+3. **前端页面开发**:
+   - HTML 文件放在 `app/templates/` 下,建议继承 `base.html` 保持页面结构统一。
+   - 表单提交时,务必在 `<form>` 标签内包含 `{% module xsrf_form_html() %}` 以通过 CSRF 校验。
+   - 静态资源(CSS, JS, 图片等)存放在 `app/static/` 目录下,在模板中通过 `{{ static_url('...') }}` 引用。
+   - 页面自定义样式请放在 `{% block style %}` 中(由 `base.html` 在 head 内输出),避免样式丢失。
+4. **测试规范**:
+   - 可以继续在 `test.py` 中编写临时的功能测试代码。

+ 51 - 0
app.md

@@ -0,0 +1,51 @@
+│  app.md (本文件,说明整个项目的目录结构及文件归属,是指导AI完成开发的重要框架性帮助文件)
+│  app.py (整个程序的主入口,采用tornado框架构建实现,MVC三层经典架构)
+│  test.py (程序单元测试用脚本文件,主要用于模块/包/方法的测试,可以写入一些临时性的测试用例)
+│  
+├─app (整个项目的主包)
+│  │  **init**.py
+│  │  
+│  ├─controllers (MVC中的控制层模块)
+│  │  │  auth.py (鉴权有关的控制层方法,涉及登录、注册、退出)
+│  │  │  base.py (控制层 公共基类,提供纺一的登录态获取逻辑,供其他Handler继承使用)
+│  │  │  home.py (后台首页控制类)
+│  │  │  **init**.py
+│  │  │  
+│  │  └─\_\_pycache\_\_
+│  │          auth.cpython-311.pyc
+│  │          base.cpython-311.pyc
+│  │          home.cpython-311.pyc
+│  │          **init**.cpython-311.pyc
+│  │  
+│  ├─models (业务与数据模型层)
+│  │  │  db.py (sqlite数据库访问层【model】,后续可在此拓展兼容mysql/pgsql等数据库访问逻辑)
+│  │  │  user.py (对应用户相关的model)
+│  │  │  **init**.py 
+│  │  │  
+│  │  └─\_\_pycache\_\_
+│  │          db.cpython-311.pyc
+│  │          user.cpython-311.pyc
+│  │          **init**.cpython-311.pyc
+│  │  
+│  ├─static (view中的静态资源)
+│  │  ├─css (样式)
+│  │  │      base.css (基础公共样式)
+│  │  │  
+│  │  └─js  (JS脚本)
+│  │          base.js (基础公共脚本)
+│  │  
+│  ├─templates (view-视图)
+│  │      base.html  (基础模板)
+│  │      index.html  (后台首页模板)
+│  │      login.html (登录页模板)
+│  │      register.html (注册页模板)
+│  │  
+│  └─\_\_pycache\_\_
+│          **init**.cpython-311.pyc
+│  
+├─database (sqlite数据库目录,用于存放sqlite文件或sql脚本文件)
+│      app.db (当前自动创建的sqLite数据库,通过init\_db() 在启动时检查创建)
+│  
+└─venv (python3.11下创建venv空间,语法:python -m venv venv ,后续开发、运行启动需要在此空间中完成)
+
+

+ 452 - 0
app.py

@@ -0,0 +1,452 @@
+# 程序的主入口
+# 承担服务器容器+程序作用
+# 服务器容器:提供http容器服务,程序放置于该容器中运行
+# 程序:本体-智能瞭望与智能问数系统 B/s架构
+import os
+import sys
+import tornado.ioloop
+import tornado.web
+from tornado.httpserver import HTTPServer
+
+# from app.controllers.base import BaseHandler
+# 引入auth - controller层
+from app.controllers.auth import LoginHandler,LogoutHandler
+from app.controllers.home import IndexHandler, DashboardHandler, DashboardApiHandler
+from app.controllers.user import UserPageHandler, UserApiHandler
+from app.controllers.menu import MenuPageHandler, MenuApiHandler
+from app.controllers.role import RolePageHandler, RoleApiHandler
+from app.controllers.model_engine import ModelEnginePageHandler, ModelEngineApiHandler, ModelTestApiHandler
+from app.controllers.spy_source import SpySourcePageHandler, SpySourceApiHandler
+from app.controllers.data_warehouse import DataWarehousePageHandler, DataWarehouseApiHandler, DataWarehouseDeepCrawlSseHandler
+from app.controllers.spy import SpyPageHandler, SpyRunApiHandler, SpyCommitApiHandler
+from app.controllers.api_manage import ApiManagePageHandler, ApiManageApiHandler, ApiInterfaceProxyHandler
+from app.controllers.digital_employee import DigitalEmployeePageHandler, DigitalEmployeeApiHandler, DigitalEmployeeChatApiHandler
+from app.controllers.chat_admin import ChatSessionsAdminPageHandler, ChatSessionsAdminApiHandler, ChatMessagesAdminPageHandler, ChatMessagesAdminApiHandler
+from app.controllers.skill_manage import SkillManagePageHandler, SkillManageApiHandler
+from app.controllers.portal import PortalRootHandler, PortalLoginHandler, PortalRegisterHandler, PortalChatPageHandler, PortalLogoutHandler, PortalModelsApiHandler, PortalEmployeesApiHandler, PortalSessionsApiHandler, PortalMessagesApiHandler, PortalChatStreamApiHandler, PortalAudioProxyHandler
+# 引入db - model层
+from app.models.db import init_db, get_connection
+from app.models.user import UserRepository
+from app.models.role import RoleRepository
+from app.models.menu import MenuRepository
+
+try:
+	if hasattr(sys.stdout, "reconfigure"):
+		sys.stdout.reconfigure(encoding="utf-8", errors="replace")
+	if hasattr(sys.stderr, "reconfigure"):
+		sys.stderr.reconfigure(encoding="utf-8", errors="replace")
+except Exception:
+	pass
+
+# class HealthHandler(tornado.web.RequestHandler):
+# 	def get(self):
+# 		self.write({"status":"ok"})
+
+# class LoginHandler(tornado.web.RequestHandler):
+# 	def get(self):
+# 		self.write(f"""<h3>模拟登录验证测试BaesHandler</h3>
+
+# 			<form method="post">
+
+			
+# 			<button type="submit">登录admin</button>
+# 			"""
+# 			+  self.xsrf_form_html() +
+# 			"""
+# 			</form>
+# 			""")
+
+# 	def post(self):
+# 		next_url = self.get_argument("next","/private")
+# 		self.set_secure_cookie("username","admin")
+# 		# 写完安全的cookie以后,跳转到目标地址
+# 		self.redirect(next_url)
+
+
+# class PrivateHandler(BaseHandler):
+# 	@tornado.web.authenticated
+# 	def get(self):
+# 		self.write(self.current_user)
+
+
+def make_app():
+	# return tornado.web.Application([
+	# 	("/abc",HealthHandler),
+	# 	("/login.jsp",HealthHandler),
+	# 	("/",HealthHandler),
+	# 	("/login.php",HealthHandler)
+	# ],debug=True)
+	# return tornado.web.Application([
+	# 		(r"/",LoginHandler),
+	# 		(r"/login",LoginHandler),
+	# 		(r"/abc",HealthHandler),
+	# 		(r"/private",PrivateHandler)
+	# 	],
+	# 	cookie_secret="demo-cookie-secret-change-me",
+	# 	login_url="/",
+	# 	xsrf_cookies=True,
+	# 	debug=True
+	# )
+	base_url = os.path.dirname(os.path.abspath(__file__))
+	settings = dict(
+		# 预留view 层的内容配置
+		template_path=os.path.join(base_url,"app","templates"),
+		static_path=os.path.join(base_url,"app","static"),
+		cookie_secret="demo-cookie-secret-change-me",
+		login_url="/admin/login",
+		xsrf_cookies=True,
+		debug=True,
+		autoreload=True
+	)
+	return tornado.web.Application([
+			(r"/", PortalRootHandler),
+			(r"/login", PortalLoginHandler),
+			(r"/register", PortalRegisterHandler),
+			(r"/chat", PortalChatPageHandler),
+			(r"/logout", PortalLogoutHandler),
+			(r"/api/models", PortalModelsApiHandler),
+			(r"/api/employees", PortalEmployeesApiHandler),
+			(r"/api/sessions", PortalSessionsApiHandler),
+			(r"/api/messages", PortalMessagesApiHandler),
+			(r"/api/chat/stream", PortalChatStreamApiHandler),
+			(r"/api/audio_proxy", PortalAudioProxyHandler),
+			(r"/u/?", tornado.web.RedirectHandler, {"url": "/"}),
+			(r"/u/login", tornado.web.RedirectHandler, {"url": "/login"}),
+			(r"/u/register", tornado.web.RedirectHandler, {"url": "/register"}),
+			(r"/u/chat", tornado.web.RedirectHandler, {"url": "/chat"}),
+			(r"/u/logout", tornado.web.RedirectHandler, {"url": "/logout"}),
+			(r"/api/u/models", tornado.web.RedirectHandler, {"url": "/api/models"}),
+			(r"/api/u/employees", tornado.web.RedirectHandler, {"url": "/api/employees"}),
+			(r"/api/u/sessions", tornado.web.RedirectHandler, {"url": "/api/sessions"}),
+			(r"/api/u/messages", tornado.web.RedirectHandler, {"url": "/api/messages"}),
+			(r"/api/u/chat/stream", tornado.web.RedirectHandler, {"url": "/api/chat/stream"}),
+
+			(r"/admin/?", IndexHandler),
+			(r"/admin/login", LoginHandler),
+			(r"/admin/logout", LogoutHandler),
+			(r"/admin/dashboard", DashboardHandler),
+			(r"/dashboard", DashboardHandler),
+			(r"/api/dashboard", DashboardApiHandler),
+			(r"/auth/login", LoginHandler),
+			(r"/auth/logout", LogoutHandler),
+			(r"/user/list", UserPageHandler),
+			(r"/api/user", UserApiHandler),
+			(r"/menu/list", MenuPageHandler),
+			(r"/api/menu", MenuApiHandler),
+			(r"/role/list", RolePageHandler),
+			(r"/api/role", RoleApiHandler),
+			(r"/model_engine/list", ModelEnginePageHandler),
+			(r"/api/model_engine", ModelEngineApiHandler),
+			(r"/api/model_engine/test", ModelTestApiHandler),
+			# 瞭望数据源管理路由
+			(r"/spy_source/list", SpySourcePageHandler),
+			(r"/api/spy_source", SpySourceApiHandler),
+			# 数据仓库路由
+			(r"/data_warehouse/list", DataWarehousePageHandler),
+			(r"/api/data_warehouse", DataWarehouseApiHandler),
+			(r"/api/data_warehouse/deep_sse", DataWarehouseDeepCrawlSseHandler),
+			# 独立瞭望采集界面路由
+			(r"/spy", SpyPageHandler),
+			(r"/api/spy/run", SpyRunApiHandler),
+			(r"/api/spy/commit", SpyCommitApiHandler),
+			# 接口管理
+			(r"/api_manage/list", ApiManagePageHandler),
+			(r"/api/api_manage", ApiManageApiHandler),
+			(r"/api/interface_proxy", ApiInterfaceProxyHandler),
+			# 数字员工
+			(r"/digital_employee/list", DigitalEmployeePageHandler),
+			(r"/api/digital_employee", DigitalEmployeeApiHandler),
+			(r"/api/digital_employee/chat", DigitalEmployeeChatApiHandler),
+			# 技能管理
+			(r"/skill_manage/list", SkillManagePageHandler),
+			(r"/api/skill_manage", SkillManageApiHandler),
+			# 用户侧会话/对话管理(后台)
+			(r"/chat_admin/sessions", ChatSessionsAdminPageHandler),
+			(r"/api/chat_admin/sessions", ChatSessionsAdminApiHandler),
+			(r"/chat_admin/messages", ChatMessagesAdminPageHandler),
+			(r"/api/chat_admin/messages", ChatMessagesAdminApiHandler),
+		],
+		**settings
+	)
+
+if __name__ == "__main__":
+	# 启动服务之前,检查并初始化数据库表
+	init_db()
+	
+	# 注入默认超管角色
+	roles = RoleRepository.get_all_roles()
+	super_role_id = None
+	for r in roles:
+		if r['code'] == 'super_admin':
+			super_role_id = r['id']
+			break
+	if not super_role_id:
+		super_role_id = RoleRepository.create_role("超级管理员", "super_admin", is_system=1)
+
+	user_role = RoleRepository.get_by_code("user")
+	user_role_id = user_role["id"] if user_role else RoleRepository.create_role("普通用户", "user", is_system=1)
+	member_role = RoleRepository.get_by_code("member")
+	if not member_role:
+		RoleRepository.create_role("会员用户", "member", is_system=1)
+		
+	# 注入默认菜单
+	menus = MenuRepository.get_all_menus()
+	if not menus:
+		sys_id = MenuRepository.create_menu("系统管理", "", "layui-icon-set", 0, 1)
+		MenuRepository.create_menu("用户管理", "/user/list", "layui-icon-user", sys_id, 1)
+		MenuRepository.create_menu("角色管理", "/role/list", "layui-icon-group", sys_id, 2)
+		MenuRepository.create_menu("功能管理", "/menu/list", "layui-icon-app", sys_id, 3)
+		# 给超管赋权
+		all_menus = MenuRepository.get_all_menus()
+		RoleRepository.assign_role_menus(super_role_id, [m['id'] for m in all_menus])
+
+	# 注入默认管理员
+	admin_user = UserRepository.get_user_by_username("admin")
+	if not admin_user:
+		UserRepository.create_user("admin", "admin888", super_role_id)
+	else:
+		# 确保已存在的 admin 绑定了超管角色
+		with get_connection() as conn:
+			conn.execute("INSERT OR IGNORE INTO user_roles (user_id, role_id) VALUES (?, ?)", (admin_user['id'], super_role_id))
+		
+	with get_connection() as conn:
+		# 插入默认菜单
+		conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (5, 0, '模型引擎', 'layui-icon-engine', '/model_engine/list', 5))
+		conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (6, 0, '瞭望管理', 'layui-icon-find-fill', '', 6))
+		conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (7, 6, '数据源管理', '', '/spy_source/list', 1))
+		conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (8, 6, '瞭望采集', '', '/spy', 2))
+		conn.execute("INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)", (9, 0, '数据仓库', 'layui-icon-table', '/data_warehouse/list', 7))
+		api_parent_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("接口服务",)).fetchone()
+		if not api_parent_row:
+			api_parent_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("系统管理",)).fetchone()
+		api_parent_id = api_parent_row["id"] if api_parent_row else 0
+		api_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/api_manage/list",)).fetchone()
+		if not api_row:
+			conn.execute(
+				"INSERT OR IGNORE INTO menus (id, parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?, ?)",
+				(10, api_parent_id, '接口管理', 'layui-icon-link', '/api_manage/list', 8)
+			)
+			api_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/api_manage/list",)).fetchone()
+		if not api_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(api_parent_id, '接口管理', 'layui-icon-link', '/api_manage/list', 8)
+			)
+			api_menu_id = cursor.lastrowid
+		else:
+			api_menu_id = api_row["id"]
+			api_current = conn.execute("SELECT name, icon FROM menus WHERE id=?", (api_menu_id,)).fetchone()
+			if api_current:
+				if not (api_current["name"] or "").strip():
+					conn.execute("UPDATE menus SET name=? WHERE id=?", ('接口管理', api_menu_id))
+				if not (api_current["icon"] or "").strip():
+					conn.execute("UPDATE menus SET icon=? WHERE id=?", ('layui-icon-link', api_menu_id))
+		
+		# 确保超级管理员拥有上述菜单权限
+		for mid in [5, 6, 7, 8, 9, api_menu_id]:
+			conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, mid))
+
+		conn.execute(
+			"""
+			INSERT OR IGNORE INTO api_endpoints
+				(name, url, method, response_format, sample_request, qps_window, qps_max, bypass_token, remark, is_active, update_at)
+			VALUES
+				(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)
+			""",
+			(
+				"网易云随机音乐",
+				"https://api.52vmy.cn/api/music/wy/rand",
+				"GET",
+				"JSON",
+				"https://api.52vmy.cn/api/music/wy/rand",
+				2,
+				4,
+				"",
+				"",
+				1,
+			),
+		)
+		conn.execute(
+			"""
+			INSERT OR IGNORE INTO api_endpoints
+				(name, url, method, response_format, sample_request, qps_window, qps_max, bypass_token, remark, is_active, update_at)
+			VALUES
+				(?, ?, ?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)
+			""",
+			(
+				"三日天气查询",
+				"https://api.52vmy.cn/api/query/tian",
+				"GET",
+				"JSON",
+				"https://api.52vmy.cn/api/query/tian?city=北京市",
+				2,
+				4,
+				"",
+				"点击前往三日天气API",
+				1,
+			),
+		)
+
+		service_row = conn.execute("SELECT id FROM menus WHERE parent_id=0 AND name=?", ("智能服务",)).fetchone()
+		if not service_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(0, "智能服务", "layui-icon-component", "", 4)
+			)
+			service_menu_id = cursor.lastrowid
+		else:
+			service_menu_id = service_row["id"]
+
+		de_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/digital_employee/list",)).fetchone()
+		if not de_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(service_menu_id, "数字员工", "layui-icon-username", "/digital_employee/list", 1)
+			)
+			de_menu_id = cursor.lastrowid
+		else:
+			de_menu_id = de_row["id"]
+			conn.execute(
+				"UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?",
+				("数字员工", "layui-icon-username", service_menu_id, 1, de_menu_id)
+			)
+
+		sess_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/chat_admin/sessions",)).fetchone()
+		if not sess_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(service_menu_id, "会话管理", "layui-icon-dialogue", "/chat_admin/sessions", 2)
+			)
+			sess_menu_id = cursor.lastrowid
+		else:
+			sess_menu_id = sess_row["id"]
+			conn.execute(
+				"UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?",
+				("会话管理", "layui-icon-dialogue", service_menu_id, 2, sess_menu_id)
+			)
+
+		msg_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/chat_admin/messages",)).fetchone()
+		if not msg_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(service_menu_id, "对话管理", "layui-icon-chat", "/chat_admin/messages", 3)
+			)
+			msg_menu_id = cursor.lastrowid
+		else:
+			msg_menu_id = msg_row["id"]
+			conn.execute(
+				"UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?",
+				("对话管理", "layui-icon-chat", service_menu_id, 3, msg_menu_id)
+			)
+
+		skill_row = conn.execute("SELECT id FROM menus WHERE url=?", ("/skill_manage/list",)).fetchone()
+		if not skill_row:
+			cursor = conn.execute(
+				"INSERT INTO menus (parent_id, name, icon, url, order_num) VALUES (?, ?, ?, ?, ?)",
+				(service_menu_id, "技能管理", "layui-icon-template-1", "/skill_manage/list", 4)
+			)
+			skill_menu_id = cursor.lastrowid
+		else:
+			skill_menu_id = skill_row["id"]
+			conn.execute(
+				"UPDATE menus SET name=?, icon=?, parent_id=?, order_num=? WHERE id=?",
+				("技能管理", "layui-icon-template-1", service_menu_id, 4, skill_menu_id)
+			)
+
+		conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, service_menu_id))
+		conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, de_menu_id))
+		conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, sess_menu_id))
+		conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, msg_menu_id))
+		conn.execute("INSERT OR IGNORE INTO role_menus (role_id, menu_id) VALUES (?, ?)", (super_role_id, skill_menu_id))
+
+		music_api = conn.execute("SELECT id FROM api_endpoints WHERE url=? AND method='GET'", ("https://api.52vmy.cn/api/music/wy/rand",)).fetchone()
+		weather_api = conn.execute("SELECT id FROM api_endpoints WHERE url=? AND method='GET'", ("https://api.52vmy.cn/api/query/tian",)).fetchone()
+
+		conn.execute(
+			"""
+			INSERT OR IGNORE INTO digital_employees
+				(name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at)
+			VALUES
+				(?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)
+			""",
+			(
+				"川小农",
+				"川小农",
+				"AI",
+				"基于默认模型与 Prompt 的智能对话数字员工",
+				"你是数字员工“川小农”。你以专业、克制、可执行的方式回答用户问题。你必须用中文输出。你会先给出结论,再给出清晰步骤与注意事项。遇到信息不足时,先提出最少的澄清点,然后给出可行的默认方案。",
+				None,
+				"",
+				1,
+			),
+		)
+		if weather_api:
+			conn.execute(
+				"""
+				INSERT OR IGNORE INTO digital_employees
+					(name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at)
+				VALUES
+					(?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)
+				""",
+				(
+					"天气",
+					"天气",
+					"NORMAL",
+					"通过接口服务查询天气:@天气 北京市",
+					"",
+					int(weather_api["id"]),
+					"city",
+					1,
+				),
+			)
+		if music_api:
+			conn.execute(
+				"""
+				INSERT OR IGNORE INTO digital_employees
+					(name, alias, category, description, ai_prompt, api_endpoint_id, api_param_name, is_active, update_at)
+				VALUES
+					(?, ?, ?, ?, ?, ?, ?, ?, current_timestamp)
+				""",
+				(
+					"音乐",
+					"音乐",
+					"NORMAL",
+					"随机音乐卡片:@音乐",
+					"",
+					int(music_api["id"]),
+					"",
+					1,
+				),
+			)
+
+		# 注入默认的百度新闻采集源
+		baidu_source = conn.execute("SELECT id FROM spy_sources WHERE name='百度新闻'").fetchone()
+		if not baidu_source:
+			headers_str = """Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
+Accept-Encoding: gzip, deflate, br, zstd
+Accept-Language: zh-CN,zh;q=0.9
+Cache-Control: no-cache
+Connection: keep-alive
+Cookie: BIDUPSID=5CFCC8D701BF7571598CE9F66EE8E6B5; PSTM=1775407070; BD_UPN=1a314753; BAIDUID=5CFCC8D701BF75717E6B8B51D00D380F:SL=0:NR=10:FG=1;
+Host: www.baidu.com
+Pragma: no-cache
+Referer: https://news.baidu.com/
+Upgrade-Insecure-Requests: 1
+User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/138.0.0.0 Safari/537.36 QQBrowser/21.1.8743.400"""
+			
+			conn.execute(
+				"INSERT INTO spy_sources (name, entry_url, request_headers, is_active) VALUES (?, ?, ?, ?)",
+				("百度新闻", "https://www.baidu.com/s?rtt=1&bsst=1&cl=2&tn=news&rsv_dl=ns_pc&word={关键字}&pn={分页步进}", headers_str, 1)
+			)
+			
+	app = make_app()
+	server = HTTPServer(app)
+	server.bind(10088)
+	# 自动CPU核心数
+	server.start()
+
+	try:
+		print("====== Server 启动成功 ======== 端口:10088 ======", flush=True)
+	except OSError:
+		pass
+	tornado.ioloop.IOLoop.current().start()

+ 17 - 0
pyproject.toml

@@ -0,0 +1,17 @@
+[build-system]
+requires = ["setuptools>=65.5.0"]
+build-backend = "setuptools.build_meta"
+
+[project]
+name = "cnagentos"
+version = "0.1.0"
+description = "AI智能瞭望与智能问数系统(Tornado MVC + SQLite3)"
+requires-python = ">=3.11,<3.12"
+dependencies = [
+  "tornado==6.5.5",
+  "requests==2.34.2",
+  "beautifulsoup4==4.14.3",
+  "openai==2.38.0",
+  "urllib3==2.7.0",
+  "crawl4ai==0.8.6",
+]