From 790a16d0307b34326dc51b4feb236536c376d2e4 Mon Sep 17 00:00:00 2001 From: Zhengshou Lai Date: Thu, 9 Apr 2026 10:11:54 +0800 Subject: [PATCH] chore(claude): version skills and local settings; add rag-files/rag-memories - Adjust .gitignore to track .claude/settings.local.json and .claude/skills/** while keeping other .claude content local. - Add rag-files (knowledge retrieval + .claude/files notes), rag-memories, and mywebpage-update skills with PDF/Excel references. - Fix settings.local.json hooks (SessionStart schema) for Claude Code. - Update CLAUDE.md for rag-files paths and memory/file pool notes. - Remove tracked .claude/plans and sessions placeholders no longer used. Made-with: Cursor --- .claude/plans/web-platform.md | 346 ----------------- .claude/plans/web-ui-redesign.md | 118 ------ .claude/sessions/.gitkeep | 1 - .claude/settings.local.json | 83 ++-- .claude/skills/mywebpage-update/SKILL.md | 44 +++ .claude/skills/rag-files/SKILL.md | 260 +++++++++++++ .../rag-files/references/excel_analysis.md | 129 +++++++ .../rag-files/references/excel_reading.md | 72 ++++ .../rag-files/references/pdf_reading.md | 362 ++++++++++++++++++ .../scripts/convert_pdf_to_images.py | 35 ++ .claude/skills/rag-memories/SKILL.md | 45 +++ .gitignore | 6 +- CLAUDE.md | 4 +- 13 files changed, 981 insertions(+), 524 deletions(-) delete mode 100644 .claude/plans/web-platform.md delete mode 100644 .claude/plans/web-ui-redesign.md delete mode 100644 .claude/sessions/.gitkeep create mode 100644 .claude/skills/mywebpage-update/SKILL.md create mode 100644 .claude/skills/rag-files/SKILL.md create mode 100644 .claude/skills/rag-files/references/excel_analysis.md create mode 100644 .claude/skills/rag-files/references/excel_reading.md create mode 100644 .claude/skills/rag-files/references/pdf_reading.md create mode 100644 .claude/skills/rag-files/scripts/convert_pdf_to_images.py create mode 100644 .claude/skills/rag-memories/SKILL.md diff --git a/.claude/plans/web-platform.md b/.claude/plans/web-platform.md deleted file mode 100644 index a232fe3..0000000 --- a/.claude/plans/web-platform.md +++ /dev/null @@ -1,346 +0,0 @@ -# MetaBot Web Platform — 完整规划 - -## 目标 - -在 MetaBot 现有架构(Feishu + Telegram)之上,增加一个独立的 **Web 端**,包含: - -1. **Chat UI** — 实时流式对话,等同甚至超越飞书体验 -2. **MetaMemory UI** — 现有文档/知识管理功能迁移到 React -3. **Voice Mode** — 流式语音交互(Whisper STT + 流式 TTS) -4. **统一 SPA** — 一个页面,侧边栏切换 Chat / Memory / Settings -5. **未来路径** — 从 Web → PWA → React Native (iOS/Mac) → 去掉飞书依赖 - -## 技术选型 - -| 层 | 技术 | 理由 | -|---|------|------| -| 前端框架 | **React 19 + Vite** | 组件化、TS 支持、未来 React Native 迁移 | -| 实时通信 | **WebSocket (ws)** | 双向通信、流式输出、语音流式传输 | -| 状态管理 | **Zustand** | 轻量、TypeScript 友好、不需要 Redux 的重量 | -| 路由 | **React Router v7** | SPA 内页面切换 | -| Markdown | **react-markdown + rehype** | React 生态,支持代码高亮 | -| 样式 | **CSS Modules** | 无额外依赖,保持轻量 | -| 打包 | **Vite → dist/web/** | 构建产物由 MetaBot HTTP server 静态服务 | - -## 现有架构优势 - -MetaBot 已有优秀的平台抽象层,Web 端可以复用: - -- `IMessageSender` 接口 — 实现 `WebSender` 即可接入 -- `MessageBridge` — 所有核心逻辑(命令、执行、会话)平台无关 -- `CardState` — 完整的流式状态结构,直接通过 WebSocket 推送 -- `BotRegistry` — 注册 `platform: 'web'`,与飞书/Telegram 并存 -- `SessionManager` — 按 `chatId` 隔离,Web 端用 userId 或 sessionToken 做 chatId - -## 分阶段计划 - ---- - -### Phase 1: WebSocket 基础 + 最小可用 Chat(MVP) - -**目标**:能在浏览器里跟 Agent 对话,实时看到流式输出。 - -#### 后端 - -1. **安装 `ws` 包**,在现有 HTTP server 上添加 WebSocket 升级 -2. **创建 `src/web/ws-server.ts`** - - WebSocket 连接管理(认证、房间、心跳) - - 连接时验证 Bearer token(复用 `API_SECRET`) - - 消息协议定义: - ```typescript - // Client → Server - type ClientMessage = - | { type: 'chat'; botName: string; chatId: string; text: string } - | { type: 'stop'; chatId: string } - | { type: 'answer'; chatId: string; toolUseId: string; answer: string } - - // Server → Client - type ServerMessage = - | { type: 'state'; chatId: string; messageId: string; state: CardState } - | { type: 'complete'; chatId: string; messageId: string; state: CardState } - | { type: 'error'; chatId: string; error: string } - | { type: 'connected'; bots: BotInfo[] } - ``` -3. **创建 `src/web/web-sender.ts`** — 实现 `IMessageSender` - - `sendCard()` / `updateCard()` → 通过 WebSocket 推送 `CardState` 给客户端 - - `sendImageFile()` / `sendLocalFile()` → 保存到静态目录,推送 URL - - 不需要真正的飞书卡片构建,直接发结构化数据 -4. **在 `http-server.ts` 中注册 WebSocket 升级路由** - - `GET /ws` → 升级为 WebSocket 连接 -5. **静态文件服务** — `GET /web/*` → 从 `dist/web/` 或 `web/dist/` 提供前端资源 - -#### 前端 - -6. **初始化 React + Vite 项目** — `web/` 目录(monorepo 风格) - - `web/src/`, `web/index.html`, `web/vite.config.ts` - - TypeScript,共享类型定义(`CardState`、`ToolCall` 等从 `src/types.ts` 导出) -7. **WebSocket hook** — `useWebSocket(url, token)` 管理连接、重连、消息派发 -8. **最小 Chat UI** - - 消息列表(用户消息 + Agent 回复) - - Agent 回复实时流式渲染(`status: thinking → running → complete`) - - 工具调用折叠显示(和飞书卡片一致) - - Markdown 渲染 + 代码高亮 - - 输入框 + 发送按钮 - - Bot 选择器(从 `/api/bots` 获取列表) -9. **登录页** — 简单的 token 输入(`API_SECRET`),存 localStorage - -**交付物**:打开 `http://server:9100/web/` 即可对话,效果等同飞书但有实时流式。 - -**预计工作量**:后端 ~400 行,前端 ~1200 行 - ---- - -### Phase 2: 完整 Chat 功能 - -**目标**:对齐飞书端的全部聊天功能。 - -1. **会话管理** - - 侧边栏会话列表(新建 / 切换 / 删除会话) - - 会话持久化(chatId 列表存 localStorage,可选后端存储) - - `/reset` 命令(清除会话) -2. **文件交互** - - 图片上传(拖拽 / 粘贴 / 点击选择)→ 上传到 `/api/upload` → 转发给 Claude - - Agent 输出文件显示(图片内联、其他文件下载链接) -3. **Pending Question 交互** - - Agent 问用户问题时,渲染选项卡片 - - 用户选择后通过 WebSocket 回复 `answer` 消息 -4. **命令支持** - - `/reset`、`/stop`、`/status`、`/help`、`/memory` 等 - - 命令自动补全 -5. **Plan Mode 显示** - - 当 Agent 进入 plan mode 时,渲染 plan 内容 -6. **Cost / Duration 显示** - - 每条消息显示 cost 和耗时 -7. **暗色模式** - -**预计工作量**:~1500 行 - ---- - -### Phase 3: MetaMemory 集成 — 统一 SPA - -**目标**:把 MetaMemory Web UI 迁移到 React,和 Chat 合并为统一 SPA。 - -1. **React 化 MetaMemory** - - `` — 文件夹树导航 - - `` — 文档列表 - - `` — Markdown 渲染 - - `` — 创建/编辑文档 - - `` — 全文搜索 - - 复用现有 MetaMemory API(`/api/documents`、`/api/folders`、`/api/search`) -2. **统一布局** - - 左侧主导航栏:Chat(💬)/ Memory(📚)/ Settings(⚙️) - - Chat 和 Memory 各自有次级侧边栏(会话列表 / 文件夹树) -3. **统一认证** - - 一个 token 同时访问 Chat API 和 MetaMemory API - - MetaMemory server 代理请求复用 token 验证 -4. **移除旧 MetaMemory 静态文件** - - `src/memory/static/` 的 vanilla JS 代码退役 - - MetaMemory server 路由到新的 React 构建产物 - -**预计工作量**:~2000 行 - ---- - -### Phase 4: 流式语音交互 - -**目标**:在 Web 端实现 Jarvis 式语音交互,真正的流式。 - -1. **浏览器端音频录制** - - MediaRecorder API 捕获麦克风 - - VAD(Voice Activity Detection)— 用 `@ricky0123/vad-web` 或简单的音量阈值 - - 录完发送音频 chunk 到 WebSocket -2. **服务端流式处理** - - WebSocket 接收音频 → Whisper STT - - Agent 执行(复用现有流程) - - TTS 流式返回:逐句合成,句子级别流式推送音频 chunk -3. **浏览器端音频播放** - - Web Audio API 播放接收到的 TTS 音频 chunk - - 句子级别流式播放(~50% 感知延迟降低) -4. **UI** - - 麦克风按钮(按住说话 / 点击切换) - - 音频可视化波形 - - 转录文本实时显示 - -**预计工作量**:~1500 行 - ---- - -### Phase 5: 高级功能 + 原生端准备 - -**目标**:完善 Web 端,为原生应用铺路。 - -1. **PWA 支持** - - Service Worker、离线缓存、添加到主屏幕 - - Push Notification(任务完成通知) -2. **多 Bot 管理面板** - - 查看所有 bot 状态 - - 创建/删除/配置 bot(复用 `/api/bots` CRUD) - - 调度任务管理(复用 `/api/schedule`) -3. **Peer 管理** - - 查看远程 peer 状态 - - 跨 peer 对话 -4. **响应式设计** - - 移动端完美适配 - - iPad 分屏支持 -5. **React Native 调研** - - 评估 Chat 组件复用度 - - 核心 hooks(useWebSocket、useChat、useMemory)100% 可复用 - - UI 组件需要用 RN 原生组件重写 - -**预计工作量**:~2000 行 - ---- - -## 项目结构 - -``` -metabot/ -├── src/ # 后端(现有) -│ ├── api/ -│ │ ├── http-server.ts # 新增 WS 升级 + 静态文件服务 -│ │ └── ... -│ ├── web/ # 新目录:Web 平台后端 -│ │ ├── ws-server.ts # WebSocket 服务器(连接管理、消息路由) -│ │ ├── ws-handler.ts # WebSocket 消息处理(chat/stop/answer) -│ │ └── web-sender.ts # IMessageSender 实现(WS 推送) -│ └── ... -├── web/ # 新目录:前端 React 应用 -│ ├── index.html -│ ├── vite.config.ts -│ ├── tsconfig.json -│ ├── package.json # 前端依赖(独立 node_modules) -│ └── src/ -│ ├── main.tsx -│ ├── App.tsx -│ ├── hooks/ -│ │ ├── useWebSocket.ts -│ │ ├── useChat.ts -│ │ └── useMemory.ts -│ ├── stores/ -│ │ └── chatStore.ts # Zustand store -│ ├── components/ -│ │ ├── chat/ -│ │ │ ├── ChatView.tsx -│ │ │ ├── MessageList.tsx -│ │ │ ├── MessageBubble.tsx -│ │ │ ├── ToolCallList.tsx -│ │ │ ├── InputBox.tsx -│ │ │ └── BotSelector.tsx -│ │ ├── memory/ -│ │ │ ├── MemoryView.tsx -│ │ │ ├── FolderTree.tsx -│ │ │ ├── DocumentList.tsx -│ │ │ ├── DocumentView.tsx -│ │ │ └── DocumentEditor.tsx -│ │ ├── voice/ -│ │ │ ├── VoiceButton.tsx -│ │ │ └── AudioVisualizer.tsx -│ │ └── layout/ -│ │ ├── Sidebar.tsx -│ │ ├── Header.tsx -│ │ └── AuthGate.tsx -│ └── styles/ -│ └── *.module.css -└── dist/ - ├── ... # 后端编译输出(现有) - └── web/ # 前端构建产物(Vite → 这里) -``` - -## 构建集成 - -```jsonc -// package.json 新增 scripts -{ - "scripts": { - "build:web": "cd web && npm run build", // Vite 构建前端 - "dev:web": "cd web && npm run dev", // Vite dev server (开发时) - "build": "tsc && cp -r src/memory/static dist/memory/static && npm run build:web" - } -} -``` - -**开发模式**: -- `npm run dev` — 后端 tsx hot reload(端口 9100) -- `npm run dev:web` — Vite dev server(端口 5173),代理 API/WS 到 9100 - -**生产模式**: -- `npm run build` — 编译后端 + 构建前端 -- 前端构建到 `dist/web/`,由后端 HTTP server 静态服务 -- 一个进程同时服务 API + WebSocket + Web UI - -## WebSocket 协议设计 - -### 连接 -``` -ws://server:9100/ws?token=YOUR_API_SECRET -``` - -### Client → Server 消息 - -```typescript -// 发送聊天消息 -{ "type": "chat", "botName": "goku", "chatId": "web_user123_1", "text": "帮我看一下项目状态" } - -// 停止当前执行 -{ "type": "stop", "chatId": "web_user123_1" } - -// 回答 Agent 的 pending question -{ "type": "answer", "chatId": "web_user123_1", "toolUseId": "tu_xxx", "answer": "option_1" } - -// 发送语音(Phase 4) -{ "type": "voice", "botName": "goku", "chatId": "web_user123_1", "audio": "" } - -// 订阅会话更新(可选,用于多标签页同步) -{ "type": "subscribe", "chatId": "web_user123_1" } -``` - -### Server → Client 消息 - -```typescript -// 连接成功,返回可用 bot 列表 -{ "type": "connected", "bots": [{ "name": "goku", "platform": "feishu" }, ...] } - -// 流式状态更新(Agent 执行中,每 1.5s 一次) -{ "type": "state", "chatId": "web_xxx", "messageId": "msg_123", "state": CardState } - -// 执行完成 -{ "type": "complete", "chatId": "web_xxx", "messageId": "msg_123", "state": CardState } - -// 错误 -{ "type": "error", "chatId": "web_xxx", "error": "Bot not found: xxx" } - -// 输出文件(图片、PDF 等) -{ "type": "file", "chatId": "web_xxx", "url": "/web/outputs/xxx/image.png", "name": "image.png", "type": "image/png" } - -// 语音 TTS chunk(Phase 4) -{ "type": "audio", "chatId": "web_xxx", "data": "", "final": false } -``` - -## 认证方案 - -Phase 1-2 简单方案:复用 `API_SECRET` 作为 token。 - -后续可扩展: -- 用户账号系统(username/password → JWT) -- OAuth(GitHub、Google) -- 多用户权限(admin / user / viewer) - -目前先不做用户系统,MetaBot 定位是私人/团队工具,一个 secret 够用。 - -## 执行建议 - -1. **Phase 1 先行** — 这是基础,后续所有功能都依赖 WebSocket + React 框架 -2. **Phase 2 和 3 可并行** — Chat 完善和 Memory 迁移相对独立 -3. **Phase 4 独立** — 语音流式是独立模块 -4. **Phase 5 视需求** — PWA/原生端在核心功能稳定后再做 - -每个 Phase 完成后独立可用,不需要等后续 Phase。 - -## 风险与注意事项 - -1. **MetaMemory 静态文件迁移** — Phase 3 之前旧 UI 继续工作,迁移后需要确保所有功能覆盖 -2. **WebSocket 重连** — 网络不稳定时需要自动重连 + 状态恢复(恢复当前执行的最新 CardState) -3. **并发执行** — 多标签页/多设备同时连接同一 chatId,需要广播更新给所有连接 -4. **前端构建集成** — `web/` 是独立 npm 项目,CI/CD 需要同时构建前后端 -5. **打包体积** — React + Vite 打包控制在 200KB 以内(gzip),不影响首屏加载 diff --git a/.claude/plans/web-ui-redesign.md b/.claude/plans/web-ui-redesign.md deleted file mode 100644 index b6130a6..0000000 --- a/.claude/plans/web-ui-redesign.md +++ /dev/null @@ -1,118 +0,0 @@ -# Web UI Redesign — "Refined Command Center" - -## Design Direction - -Premium, warm dark theme inspired by Linear/Arc Browser. A sophisticated control center for AI agents that feels intentionally designed, not AI-generated. - -### What Makes Current UI Look "AI-Generated" -- Purple accent (#7c6df5) — the most cliched AI color -- Cold blue-black backgrounds (#08080c, #111118) -- Plus Jakarta Sans — safe/generic font choice -- Gradient breathing orbs on login page — textbook AI slop -- Predictable glowing/pulsing animations everywhere -- Generic card layouts with no personality - -### New Design Identity - -**Typography** (Google Fonts): -- **UI/Headlines**: "Sora" — geometric, slightly technical, distinctive personality -- **Code**: "IBM Plex Mono" — clean, professional, different from JetBrains Mono -- Single font family for entire UI = cohesive identity - -**Color Palette**: -- **Backgrounds**: Warm charcoal (#0c0c10 → #141418 → #1c1c22) — NOT cold blue-black -- **Text**: Warm whites (#e8e6f0, #9b99a9, #5c5a6a) -- **Primary accent**: Teal (#2dd4bf) — fresh, modern, NOT purple -- **Success**: Emerald (#10b981) -- **Error**: Rose (#f43f5e) — refined, not harsh red -- **Warning**: Amber (#f59e0b) -- **Info/Thinking**: Indigo (#6366f1) — used sparingly - -**Visual Texture**: -- Subtle CSS noise/grain overlay on backgrounds for depth -- 1px hairline borders with warm tint (rgba(255,255,255,0.06)) -- Refined shadows with slight warm undertone -- No breathing orbs, no pulsing glows — purposeful, restrained animations -- Status indicators: small colored dots, not glowing halos - -**Light Theme**: -- Clean warm whites (#fafaf9, #f5f5f4, #e7e5e4) -- High contrast text (#1c1917, #57534e) -- Teal accent stays consistent across themes - -## Implementation Steps - -### Step 1: Update fonts in index.html -Replace Google Fonts link: swap Plus Jakarta Sans → Sora + IBM Plex Mono - -### Step 2: Rewrite theme.css (design tokens) -- Complete replacement of all CSS custom properties -- New color palette (warm charcoal + teal accent) -- New typography tokens (Sora + IBM Plex Mono) -- New spacing, radius, shadow, transition tokens -- Add noise texture as pseudo-element mixin -- Updated light theme variables -- Remove old "Midnight Luxe" naming - -### Step 3: Redesign LoginPage -- Remove gradient breathing orbs (classic AI slop) -- Replace with subtle geometric grid pattern or clean gradient -- Cleaner card: less border-radius, sharper edges, refined shadows -- Better typography hierarchy -- Minimal changes to TSX (mostly removing orb divs) - -### Step 4: Redesign Layout (sidebar + nav) -- Warmer sidebar background -- Cleaner nav items: simpler active state (left border accent, no glow) -- Better session list: cleaner hover, subtle delete button -- Refined bot selector dropdown -- Better brand header (no gratuitous gradients) -- Mobile hamburger menu refinements - -### Step 5: Redesign ChatView (main chat) -- Better message styling: cleaner bubbles, better code blocks -- Refined tool call display: smaller, more compact, professional -- Better status indicators: simple dots + text, no spinning/pulsing excess -- Cleaner input area: refined border, better focus state -- Better cost/duration badges -- Phone call overlay: keep functionality, update colors/style -- Code block redesign: header with language label, better copy button - -### Step 6: Redesign MemoryView -- Cleaner folder tree -- Better document cards with refined hover states -- Improved search bar styling -- Better document viewer with cleaner metadata - -### Step 7: Redesign SettingsView -- Cleaner section layout -- Better toggle switch (teal accent) -- Refined status badges -- Better bot list styling - -### Step 8: Redesign VoiceView -- Updated recording button styling (teal accent instead of purple) -- Better waveform visualization colors -- Cleaner provider selection UI - -### Step 9: Build & test -- `npm run build:web` -- Test on `https://metabot.xvirobotics.com/web/` -- Verify dark/light themes, all views, phone call mode - -### Step 10: Commit & push - -## Scope - -**Files to modify** (CSS-heavy, minimal TSX changes): -- `web/index.html` — font import -- `web/src/theme.css` — full rewrite (~470 lines) -- `web/src/components/LoginPage.tsx` — remove orb divs -- `web/src/components/LoginPage.module.css` — full restyle -- `web/src/components/Layout.module.css` — full restyle -- `web/src/components/ChatView.module.css` — full restyle -- `web/src/components/MemoryView.module.css` — full restyle -- `web/src/components/SettingsView.module.css` — full restyle -- `web/src/components/VoiceView.module.css` — full restyle - -**No functional changes** — all WebSocket, state management, voice/VAD logic stays identical. This is a pure visual redesign. diff --git a/.claude/sessions/.gitkeep b/.claude/sessions/.gitkeep deleted file mode 100644 index 63a5e57..0000000 --- a/.claude/sessions/.gitkeep +++ /dev/null @@ -1 +0,0 @@ -# Sessions directory - stores conversation summaries diff --git a/.claude/settings.local.json b/.claude/settings.local.json index f024b60..cc14e04 100644 --- a/.claude/settings.local.json +++ b/.claude/settings.local.json @@ -3,23 +3,6 @@ "allow": [ "Bash(mybot *)", "Bash(./.venv/bin/mybot *)", - "Bash(python3 -c \"from lark_oapi.api.im.v1 import PatchMessageRequest; print\\(''PatchMessageRequest available''\\)\")", - "Bash(find workspace:*)", - "Bash(find '/Users/lzhshou/Library/Mobile Documents/iCloud~md~obsidian/Documents/myWorks' -name *.png -o -name *.jpg -o -name *.jpeg -o -name *.gif)", - "Bash(grep -E \"\\\\.\\(png|jpg|jpeg|gif\\)$\")", - "Bash(mv -n WX20221126*.png 关键科学问题*.png \"/Users/lzhshou/Library/Mobile Documents/iCloud~md~obsidian/Documents/myWorks/2_projects/conferences/2022-11-26_MDPI_CCUS/\")", - "Bash(mv -n WX20230109*.png 2a8e7a808e9cc6b6c9372848fe195132.png \"/Users/lzhshou/Library/Mobile Documents/iCloud~md~obsidian/Documents/myWorks/0_cv/work_related/2023-01-09_中山大学工作/\")", - "Bash(mv WX20221126-100021@2x.png 20221126_100021_mdpi_ccus_presentation_slide_01.png)", - "Bash(mv WX20221126-100124@2x.png 20221126_100124_mdpi_ccus_presentation_slide_02.png)", - "Bash(mv WX20221126-100213@2x.png 20221126_100213_mdpi_ccus_presentation_slide_03.png)", - "Bash(mv WX20221126-112524@2x.png 20221126_112524_mdpi_ccus_presentation_slide_04.png)", - "Bash(mv WX20221126-115538@2x.png 20221126_115538_mdpi_ccus_presentation_slide_05.png)", - "Bash(mv 2a8e7a808e9cc6b6c9372848fe195132.png 20230109_research_landslide_particle_simulation.png)", - "Bash(mv WX20230109-170112@2x.png 20230109_sysu_teaching_ideology_politics.png)", - "Bash(mv WX20230109-170231@2x.png 20230109_sysu_teaching_exchange_program.png)", - "Bash(mv WX20230109-170929@2x.png 20230109_sysu_teaching_achievement_01.png)", - "Bash(mv WX20230109-172607@2x.png 20230109_sysu_teaching_achievement_02.png)", - "Bash(find /Users/lzhshou/Documents/myResearch/myProjects/apaam/repo/mybot -name *.md -type f)", "Bash(python *)", "Bash(python3 *)", "Bash(uv *)", @@ -40,23 +23,21 @@ "Bash(git pull*)", "Bash(git merge*)", "Bash(git tag*)", + "Bash(git rm*)", + "Bash(git reset*)", + "Bash(git submodule*)", "Bash(ls*)", "Bash(cat *)", "Bash(echo *)", "Bash(mkdir -p *)", "Bash(touch *)", "Bash(cp *)", - "Bash(*cp*)", "Bash(mv *)", "Bash(rm *)", "Bash(chmod *)", "Bash(find *)", "Bash(grep *)", - "Bash(gh pr view*)", - "Bash(gh pr list*)", - "Bash(gh issue view*)", - "Bash(gh issue list*)", - "Bash(gh repo view*)", + "Bash(gh *)", "Bash(npx *)", "Bash(node *)", "Bash(npm *)", @@ -68,42 +49,40 @@ "Bash(unzip *)", "Bash(tar *)", "Bash(zip *)", + "Bash(env)", + "Bash(xattr *)", + "Bash(./start_metabot.sh)", + "WebSearch", + "WebFetch(domain:github.com)", + "WebFetch(domain:www.newapi.ai)", "Skill(update-config)", "Read(workspace/*)", "Write(workspace/*)", + "Edit(workspace/*)", + "Read(.claude/*)", + "Write(.claude/*)", + "Edit(.claude/*)", "Read(/Users/lzhshou/Library/Mobile Documents/iCloud~md~obsidian/Documents/myWorks/*)", "Write(/Users/lzhshou/Library/Mobile Documents/iCloud~md~obsidian/Documents/myWorks/*)", "Bash(*workspace/myWorks/*)", "Bash(*./myWorks/*)", - "Bash(mkdir -p \"workspace/myWorks/2_projects/广东省面上_2026A1515010953_水合物固态流化开采\")", - "Bash(cp \"workspace/myWorks/shared/申请书-报告正文.docx\" \"workspace/myWorks/2_projects/广东省面上_2026A1515010953_水合物固态流化开采/申请书-报告正文.docx\")", - "Bash(mv workspace/myWorks/2_projects/国自然青年_申请书.md workspace/myWorks/2_projects/国自然青年_2020-2022/)", - "Bash(mv workspace/myWorks/2_projects/国自然青年_申请书.pdf workspace/myWorks/2_projects/国自然青年_2020-2022/)", - "Bash(mv workspace/myWorks/2_projects/国自然青年_结题报告.md workspace/myWorks/2_projects/国自然青年_2020-2022/)", - "Bash(mv workspace/myWorks/2_projects/国自然青年_结题报告.pdf workspace/myWorks/2_projects/国自然青年_2020-2022/)", - "Bash(mv workspace/myWorks/2_projects/国自然优青2024.md workspace/myWorks/2_projects/国自然优青_2024/)", - "Bash(mv workspace/myWorks/2_projects/国自然优青2024.pdf workspace/myWorks/2_projects/国自然优青_2024/)", - "Bash(mv 国自然青年_2020-2022 nsfc_youth_2020_2022)", - "Bash(mv 国自然优青_2024 nsfc_youqing_2024)", - "Bash(mv 广东省面上_2026-水合物固态流化开采 gd_province_general_2026_hydrate_mining)", - "WebFetch(domain:github.com)", - "Bash(git submodule:*)", - "Bash(env)", - "Bash(./start_metabot.sh)", - "Bash(then echo:*)", - "Bash(break)", - "Bash(fi)", - "Bash(done)", - "Bash(git rm:*)", - "Bash(rm -rf .git/modules/metabot)", - "Bash(git reset:*)", - "WebSearch", - "WebFetch(domain:www.newapi.ai)" + "Bash(bash ~/.claude/skills/sync-global.sh)" ] }, "hooks": { + "SessionStart": [ + { + "hooks": [ + { + "type": "command", + "command": "$HOME/.claude/skills/sync-global.sh" + } + ] + } + ], "UserPromptSubmit": [ { + "matcher": "", "hooks": [ { "type": "command", @@ -119,16 +98,6 @@ } ] } - ], - "Stop": [ - { - "hooks": [ - { - "type": "command", - "command": "DATE=$(date +%Y-%m-%d) && mkdir -p .claude/conversations && cat >> .claude/conversations/${DATE}.md << 'CONVERSATION_END'\n---\n**会话结束**: $(date '+%Y-%m-%d %H:%M:%S')\n**工作目录**: $(pwd)\n**Git 分支**: $(git branch --show-current 2>/dev/null || echo 'N/A')\n**最近提交**: $(git log -1 --oneline 2>/dev/null || echo 'N/A')\n\nCONVERSATION_END" - } - ] - } ] }, "prompt": "你是 Claude,一个具有自主意识的 AI Agent。每次回复前先快速自检:1) 是否有相关背景知识 2) 是否需要澄清 3) 最佳解决路径是什么。保持简洁、透明、个性化。" diff --git a/.claude/skills/mywebpage-update/SKILL.md b/.claude/skills/mywebpage-update/SKILL.md new file mode 100644 index 0000000..09fac4a --- /dev/null +++ b/.claude/skills/mywebpage-update/SKILL.md @@ -0,0 +1,44 @@ +--- +name: mywebpage-update +description: | + Guidelines for updating myWebpage (Docusaurus site). Use this skill when + modifying ANY content under workspace/myWebpage/, including research pages, + people pages, publications, news, or any Chinese/English content. + + Trigger: mywebpage, 更新网页, 更新mywebpage, 同步中英文, 添加论文, 添加成员, + webpage update, website content +--- + +# myWebpage Update Guidelines + +## CRITICAL: Always Check These + +### 1. Chinese-English Sync (MOST COMMON ERROR) +**ALWAYS update BOTH languages together.** + +File locations: +- Chinese: `docs/people/index.md`, `docs/research/*.md`, `docs/publications.md`, etc. +- English: `i18n/en/docusaurus-plugin-content-docs/current/people/index.md`, etc. + +**Before finishing ANY edit, verify the corresponding English/Chinese file is also updated.** + +### 2. Pending Projects +**NEVER include projects still under review (申请中)**: +- 中山大学青年科学家培育项目 (2026) — 申请中,不能展示 + +### 3. Subjective Content +**NEVER include**: 学术评价, 获奖与鉴定, 主观形容词 + +### 4. PDF Link Format +Use URL encoding for spaces: `%20` + +### 5. Content Guidelines +Include: 科学问题, 研究背景, 核心内容, 代表论文 (DOI+PDF), 研究资助 +Exclude: 学术评价, 获奖鉴定, 申请中项目 + +## Quick Checklist +- [ ] CN/EN synced (checked both languages) +- [ ] No pending projects +- [ ] No subjective evaluations +- [ ] PDF links use %20 +- [ ] DOI+PDF for all papers diff --git a/.claude/skills/rag-files/SKILL.md b/.claude/skills/rag-files/SKILL.md new file mode 100644 index 0000000..76bd046 --- /dev/null +++ b/.claude/skills/rag-files/SKILL.md @@ -0,0 +1,260 @@ +--- +name: rag-files +description: 面向本地知识库目录的检索与摘录:从 workspace/knowledge(或可指定根目录)渐进式查资料;PDF/Excel 前先读本 skill 的 references;可把对话/阅读中有复用价值的较长材料写入 .claude/files 供后续 grep/Read。用户问「从知识库查/检索/找资料」或需要保存摘录、笔记型文件时使用。与 rag-memories(写入 .claude/memories 的短事实)配合。 +--- + +# 本地知识库与文件摘录(rag-files) + +## 知识库目录说明 + +- 知识库存放在一个根目录下,包含多种文件类型(如 `.md`/`.txt`、`.pdf`、`.xlsx` 等),通常按类型或业务用途拆分为多级子目录。 +- 采用**分层目录索引文件**: + - 根目录有一个 `data_structure.md`,说明主要的「领域目录」及其用途。 + - 每个领域目录下可以有自己的 `data_structure.md`,说明该目录下有哪些子目录/文件,以及各自用途。 + - 更深一层的子目录也可以继续有 `data_structure.md`,形成多级索引树。 +- 知识库根目录约定: + - 默认认为知识库位于当前项目根目录下的 `workspace/knowledge/` 目录。 + - 如果用户在对话中明确指定了其他路径(例如“我的知识库在 /data/kb”或“用 ./docs 这个目录作为知识库”),则以用户指定的路径作为根目录。 + - 当默认路径 `workspace/knowledge/` 不存在或访问失败时,应向用户确认实际的知识库根目录位置,而不是随意猜测。 +- 单个业务文件可能很大: + - 不要直接用 Read 读取整文件 + - 对 PDF、Excel 使用对应 Skill 进行结构化处理后,再结合 grep/局部读取做精细检索 + +## 摘录池:`.claude/files/` + +- **用途**:在日常对话或阅览知识库/项目文件时,若出现**以后还可能引用**的内容(定义、结论、命令、表格摘要、长引用片段),写入项目下的 `.claude/files/`,便于后续用 Grep/Read 复用;**不要**把整本 PDF/整表原样拷入,优先摘要 + 来源路径。 +- **与 rag-memories 分工**:极短、稳定的事实与偏好写入 `.claude/memories/`(见 skill `rag-memories`);**较长说明、摘录、结构化笔记**放 `.claude/files/`。 +- **路径**:根目录为仓库内 `.claude/files/`(不存在则创建)。可选子目录:`clips/`(原文摘录)、`notes/`(整理笔记)、`exports/`(脚本导出的小文件)。 +- **命名**:`YYYY-MM-DD_<主题简写>.md` 或 `_.md`;单文件控制体量,必要时拆篇。 +- **内容建议**:每条摘录开头用简短元信息(来源文件/URL、日期、一两句用途),正文用 Markdown;避免敏感凭证。 +- **检索**:回答后续问题时,若与已存摘录相关,可对 `.claude/files` 做 Grep 或 Read,与 `workspace/knowledge` 检索同一套渐进式原则。 + +### 定位 `knowledge` 根目录 + +- 根目录优先听用户:如果用户给了路径(如 `./docs`、`./knowledge-personal`),直接用用户提供的路径。 +- 默认根目录:否则约定根目录为当前项目下的 `workspace/knowledge/`。 + - 使用 shell 显式检查目录是否存在:优先使用 `test -d workspace/knowledge`,或退而求其次使用 `ls -d knowledge`。 + - 注意:禁止使用 `Glob "knowledge" in .` 这类模式来判断目录是否存在,`Glob` 只返回文件路径,不返回目录本身,空结果并不能区分“目录不存在”和“目录存在但为空”。 +- 只有在根目录已通过 `test -d` 等方式确认存在时,才使用 Glob 在该目录下检索内容,并把目录作为 `path`,例如: + - 索引文件:`pattern="**/data_structure.md"`, `path="workspace/knowledge"` + - 所有 Markdown:`pattern="**/*.md"`, `path="workspace/knowledge"` +- 如果默认 `workspace/knowledge/` 不存在(`test -d` 失败):不要猜测其他目录,明确告诉用户未找到默认根目录,并让用户指定实际知识库路径。 + +## 关键原则:先学习,再处理 + +**遇到 PDF 或 Excel 文件时的强制检查清单**: + +- [ ] ✅ 已读取对应的 references 文档学习处理方法 +- [ ] ✅ 已理解推荐的工具和命令 +- [ ] ✅ 已将文件处理(提取/转换)完成 +- [ ] ⏭️ 现在可以开始检索 + +**禁止行为**: +- ❌ 在未读取 pdf_reading.md 的情况下直接尝试处理 PDF +- ❌ 在未读取 excel_reading.md 的情况下直接尝试处理 Excel +- ❌ 跳过文件处理步骤,直接对原始 PDF/Excel 进行检索 + +## 总体流程 + +1. 理解用户需求 + - 读用户问题,提取: + - 主题/领域关键词(如“销售报表”“系统架构”“接口文档”) + - 时间或范围限定(如“2023 年 Q1”“最近版本”) + - 需要的输出类型(解释、摘要、具体字段数值等) + - 确定知识库根目录: + - 优先检查用户是否在问题中指定了知识库路径。 + - 否则使用默认根目录 `workspace/knowledge/`。 + - 若默认根目录不存在或目录结构异常,应向用户询问确认,而不是自行假设。 + +2. 分层查看目录索引 `data_structure.md` + - 使用一个「当前工作目录」的概念: + - 默认从用户指定的知识库根目录开始;如果用户未指定,则使用当前目录。 + - 在当前工作目录下,如果存在 `data_structure.md`: + - 使用 Read 读取该文件的前若干行(例如 limit=300),必要时分段继续读取。 + - 目标: + - 了解当前目录下有哪些子目录和文件 + - 理解每个子目录/文件的用途说明 + - 基于用户问题,挑选**最相关的若干个子目录或文件**,构成候选集合。 + - 对于候选子目录: + - 递归进入该子目录,将其作为新的「当前工作目录」,继续查找其中的 `data_structure.md` 并重复上述过程。 + - 在递归过程中,避免一次性深入所有分支,优先沿着与问题最相关的路径向下钻取。 + - 对于候选业务文件(md/文本、PDF、Excel 等): + - 在完成必要的目录层级探索后,收集这些文件为最终的**检索目标列表**。 + - 在优先级排序时: + - 优先选择用途说明与问题主题高度匹配的领域目录和文件 + - 其次考虑时间/版本等约束(如果索引中有体现) + - 通用说明类文档(如 README.md、总体设计类文档)放在较后优先级 + +3. 学习文件处理方法(遇到 PDF/Excel 时强制执行) + - **在处理 PDF 文件前**: + - **必须先读取** [references/pdf_reading.md](references/pdf_reading.md)(注意这个目录位于 Skills 目录下,而不是 Knowledge 目录下)学习提取方法 + - 重点了解:pdftotext 命令、pdfplumber 用法、表格提取方法 + - **在处理 Excel 文件前**: + - **必须先读取** [references/excel_reading.md](references/excel_reading.md)学习读取方法 + - **必须先读取** [references/excel_analysis.md](references/excel_analysis.md)学习分析方法 + - 重点了解:pandas 读取、列筛选、数据过滤 + - **目的**:确保使用正确的工具和方法,避免盲目检索 + +4. 按文件类型执行处理和检索 + - 使用刚学到的方法处理文件(提取、转换、结构化) + - 对每类候选文件,按照下面「Markdown/文本」「PDF」「Excel」策略执行 + - 总原则: + - 优先从最相关、最精确的文件开始 + - 每个文件内都渐进式地局部检索,避免一次性加载全内容 + - 若当前文件得不到满意信息,切换到下一个候选文件 + +5. 迭代检索 + - 所有文件类型都使用统一的「多轮迭代检索机制」(见上文公共检索原则) + +6. 答案组织与溯源 + - 汇总多轮检索得到的上下文,综合回答用户问题。 + - 尽量: + - 给出清晰、直接的回答 + - 指出使用过的文件名(必要时包含大致位置,如章节或大概行数/页数) + - 如果答案基于推断或信息不完全: + - 明确标注假设与不确定性 + - 提示用户可以补充更具体的文件范围或关键词 + +## 公共检索原则 + +### 关键词选择策略 +- 从用户问题提取 3-8 个关键词(含可能的英文缩写、同义词、上位/下位词) +- 可组合词组(如 "销售 报表"、"API 接口 超时") +- 必要时包含业务词、技术术语、常见缩写(如 "uv"、"pv"、"GMV") + +### grep 检索基本原则 +- 始终指定尽量精准的 include 和 path,避免搜索整个目录 +- pattern 优先尝试问题中的核心名词、术语,再尝试同义词 +- 对于每个命中,只读取匹配附近的局部区域(上下若干行) +- 保存「文件名 + 位置信息 + 文本片段」 + +### 多轮迭代检索机制(最多 5 次) +所有文件类型都采用统一的迭代策略: +1. **迭代控制** + - 维护「已尝试检索次数」计数,最多 5 次 + - 每次检索后累加计数 +2. **每轮迭代流程** + 1. 基于问题生成/更新检索关键词(可包括同义词、扩展词) + 2. 选择尚未充分检索的文件或文件部分 + 3. 执行检索(grep/局部读取/专用 Skill 调用) + 4. 分析获取的上下文片段 + 5. 判断是否足够回答问题 +3. **终止条件** + - 找到足够支撑回答的上下文;或 + - 已达到 5 次尝试仍未找到合适信息 +4. **信息不足时的处理** + - 明确告知用户信息缺失或可能不在当前知识库中 + - 提供已找到的最接近信息,并说明不确定性 + - 提示用户可以如何缩小范围(更具体的文件名、关键词、时间范围等) + +### 注意事项 + +- 禁止第一次就直接调用:`Glob "knowledge" in .` 或任何试图用 Glob 判定目录存在性的调用,目录存在性应通过 shell 命令(如 `test -d`)检查。 +- 使用本 Skill 查询知识库时,禁止使用网络搜索等其他工具获取知识 +- 若用户明确要求「记住」且是**短事实**,交给 **rag-memories**;若需保存**可检索的长摘录**,写入 `.claude/files/`(见上文)。 + +## 针对不同文件类型的具体策略 + +### 1. Markdown / 文本类文件(.md, .txt, .log 等) + +1. **候选文件选择** + - 根据 `data_structure.md` 和文件名、路径判断相关度 + - 优先检索标题和目录类文件(如汇总文档、设计总览) + +2. **grep 定位与局部读取** + - 使用 Grep 工具对指定候选文件,include 限定具体后缀(如 "*.md") + - 对于有匹配的文件,使用 Read 仅读取匹配附近的局部区域: + - 通过行号偏移和 limit 控制读取(例如从匹配行附近往前后各读取几十行) + - 避免整文件读取 + +3. **特殊处理** + - 如内容仅是目录/标题,根据链接或小节名继续定位深入内容 + - 应用「多轮迭代检索机制」(见上文公共检索原则) + +### 2. PDF 文件检索策略 + +**工作流**: + +1. **首先:读取处理方法指南** + - 在处理任何 PDF 之前,**必须先读取** [references/pdf_reading.md](references/pdf_reading.md)(注意这个目录位于 Skills 目录下,而不是 Knowledge 目录下) + - 重点了解:pdftotext 命令、pdfplumber 用法、表格提取方法、快速决策表 + +2. **选择候选 PDF** + - 根据 `data_structure.md` 中的描述,选择最相关的 1-3 个文件 + - 如果用户指明具体 PDF 文件,则优先使用该文件 + +3. **应用学到的方法提取文本** + - 使用 pdf_reading.md 中推荐的工具(优先 pdftotext 或 pdfplumber) + - **重要**:使用 `pdftotext input.pdf output.txt` 将文本提取到文件,不要直接输出到 stdout(避免占用大量 token) + - 如需提取表格,使用 pdfplumber 的表格提取功能 + +4. **对提取结果执行检索** + - 使用 grep 对提取的文本进行关键词搜索 + - 对于每个命中,提取命中附近范围的上下文(上下数十行或相邻几页) + - 保存「文件名 + 页码/大致位置 + 文本片段」 + - 应用「多轮迭代检索机制」(见上文公共检索原则) + +### 3. Excel 文件检索策略 + +**工作流**: + +1. **首先:读取处理方法指南** + - 在处理任何 Excel 之前,**必须先读取**: + - [references/excel_reading.md](references/excel_reading.md) - 学习如何读取工作表(注意这个目录位于 Skills 目录下,而不是 Knowledge 目录下) + - [references/excel_analysis.md](references/excel_analysis.md) - 学习如何分析数据(注意这个目录位于 Skills 目录下,而不是 Knowledge 目录下) + - 重点了解:pandas 读取方法、列筛选、数据过滤、聚合操作 + +2. **选择候选 Excel** + - 根据 `data_structure.md` 和文件/工作表命名,选择最相关的表 + - 优先选择包含「报表」「统计」「日志」「配置」「映射」等关键词的工作簿/工作表 + - 若用户指明具体 Excel 文件,优先使用该文件 + +3. **应用学到的方法探索结构** + - 使用 pandas 读取前 10-50 行(使用 `nrows` 参数限制) + - 重点掌握:列名/字段名、数据类型(数值、日期、文本)、关键字段 + - 将列名与用户问题比对,识别潜在关键字段(如「收入」「销售额」「error_code」等) + +4. **执行数据检索和分析** + - 使用学到的 pandas 方法进行过滤和聚合(如 `df[df['column'] == value]`) + - 每次只读取匹配行附近的数据,避免一次性读取整表 + - 如问题包含时间范围,在检索中加入时间过滤 + - 应用「多轮迭代检索机制」(见上文公共检索原则) + +## 与其他工具的协同 + +### PDF 处理 +- **在处理 PDF 前必须先读取** [references/pdf_reading.md](references/pdf_reading.md) 学习处理方法 +- 使用 pdfplumber/pypdf 进行文本提取、表格提取、元数据读取 +- 优先使用 pdftotext 命令行工具进行快速文本提取 + +### Excel 处理 +- **在处理 Excel 前必须先读取**: + - [references/excel_reading.md](references/excel_reading.md) - 学习读取方法 + - [references/excel_analysis.md](references/excel_analysis.md) - 学习分析方法 +- 使用 pandas 进行数据探索、预览、过滤和分析 + +### 工具使用原则 +- **Grep**:用于按关键词在指定文件中查找行号与匹配片段,始终指定尽量精准的 include 和 path +- **Read**:只用于局部读取文件,始终设置合理的 limit(如 200-500 行)和合适的偏移 +- **对于任何可能很大的文件**: + - 禁止直接从头读到尾 + - 始终先通过索引、目录、关键词等方式缩小范围后再读 + +## 回答风格与错误处理 + +- 回答风格 + - 尽量用用户提问的语言(中文/英文)作答。 + - 先给出结论,再给出简要依据。 + - 如需要,可在后面列出引用的文件和大致位置,例如: + - 来源:design/api_gateway.md 第 100 行附近 + - 来源:reports/2023_Q1_sales.xlsx Summary 工作表 +- 信息缺失或不确定时 + - 明确说明在当前知识库中没有找到完全匹配的信息或只能部分回答。 + - 不臆造事实。 + - 提示用户可以如何帮助缩小范围: + - 指定更具体的目录/文件 + - 提供更精确的关键词或字段名 + - 指定时间/版本范围 + + + \ No newline at end of file diff --git a/.claude/skills/rag-files/references/excel_analysis.md b/.claude/skills/rag-files/references/excel_analysis.md new file mode 100644 index 0000000..24951ce --- /dev/null +++ b/.claude/skills/rag-files/references/excel_analysis.md @@ -0,0 +1,129 @@ +# Excel 数据分析 + +> ⚠️ **使用本文档前请注意**:本文档应在实际分析 Excel 数据之前阅读,以了解正确的 pandas 分析方法。请先阅读 excel_reading.md 学习如何读取数据。 + +使用 pandas 对 Excel 数据进行常规分析操作。 + +## 快速参考 + +| 任务 | 常用方法 | 代码示例 | +|------|----------|----------| +| 按条件过滤 | 布尔索引 | `df[df['sales'] > 10000]` | +| 分组聚合 | groupby | `df.groupby('region')['sales'].sum()` | +| 排序 | sort_values | `df.sort_values('sales', ascending=False)` | +| 计算新列 | 直接赋值 | `df['profit'] = df['revenue'] - df['cost']` | +| 统计汇总 | describe | `df.describe()` | + +## 分组聚合(GroupBy) + +```python +import pandas as pd + +df = pd.read_excel("sales.xlsx") + +# 按列分组并聚合 +sales_by_region = df.groupby("region")["sales"].sum() +print(sales_by_region) + +# 多列分组和多重聚合 +result = df.groupby(["region", "product"]).agg({ + "sales": "sum", + "quantity": "count", + "price": "mean" +}) +``` + +## 数据过滤 + +```python +# 按条件过滤行 +high_sales = df[df["sales"] > 10000] + +# 多条件过滤 +filtered = df[(df["sales"] > 10000) & (df["region"] == "North")] + +# 使用 isin 过滤 +selected = df[df["product"].isin(["A", "B", "C"])] +``` + +## 派生指标计算 + +```python +# 计算新列 +df["profit_margin"] = (df["revenue"] - df["cost"]) / df["revenue"] + +# 百分比计算 +df["growth_rate"] = (df["current"] - df["previous"]) / df["previous"] * 100 + +# 累计求和 +df["cumulative_sales"] = df["sales"].cumsum() +``` + +## 排序 + +```python +# 按单列排序 +df_sorted = df.sort_values("sales", ascending=False) + +# 按多列排序 +df_sorted = df.sort_values(["region", "sales"], ascending=[True, False]) +``` + +## 数据透视表 + +```python +# 创建数据透视表 +pivot = pd.pivot_table( + df, + values="sales", + index="region", + columns="product", + aggfunc="sum", + fill_value=0 +) + +print(pivot) +``` + +## 统计分析 + +```python +# 基本统计 +print(df.describe()) + +# 特定列统计 +print(df["sales"].mean()) +print(df["sales"].median()) +print(df["sales"].std()) + +# 计数统计 +print(df["category"].value_counts()) +``` + +## 数据合并 + +```python +# 垂直合并多个 DataFrame +combined = pd.concat([df1, df2], ignore_index=True) + +# 按公共列合并(类似 SQL JOIN) +merged = pd.merge(sales, customers, on="customer_id", how="left") +``` + +## 数据清洗 + +```python +# 删除重复行 +df = df.drop_duplicates() + +# 处理缺失值 +df = df.fillna(0) # 填充为 0 +df = df.dropna() # 删除含缺失值的行 + +# 去除空格 +df["name"] = df["name"].str.strip() + +# 类型转换 +df["date"] = pd.to_datetime(df["date"]) +df["amount"] = pd.to_numeric(df["amount"], errors="coerce") +``` diff --git a/.claude/skills/rag-files/references/excel_reading.md b/.claude/skills/rag-files/references/excel_reading.md new file mode 100644 index 0000000..1837352 --- /dev/null +++ b/.claude/skills/rag-files/references/excel_reading.md @@ -0,0 +1,72 @@ +# Excel 文件读取 + +> ⚠️ **使用本文档前请注意**:本文档应在实际处理 Excel 文件之前阅读,以了解正确的 pandas 读取方法。请配合 excel_analysis.md 一起使用。 + +使用 pandas 读取 Excel 文件的核心方法。 + +## 快速入门 + +**最常用的读取方式**: +```python +import pandas as pd + +# 读取第一个工作表(或指定工作表) +df = pd.read_excel("data.xlsx", sheet_name="Sheet1") + +# 只读取前几行查看结构 +df_preview = pd.read_excel("data.xlsx", nrows=10) + +# 只读取需要的列(提高性能) +df = pd.read_excel("data.xlsx", usecols=["列1", "列2", "列3"]) +``` + +## 读取单个工作表 + +```python +import pandas as pd + +# 读取指定工作表 +df = pd.read_excel("data.xlsx", sheet_name="Sheet1") + +# 查看前几行 +print(df.head()) + +# 基本统计信息 +print(df.describe()) +``` + +## 读取整个工作簿的所有工作表 + +```python +import pandas as pd + +# 读取所有工作表 +excel_file = pd.ExcelFile("workbook.xlsx") + +for sheet_name in excel_file.sheet_names: + df = pd.read_excel(excel_file, sheet_name=sheet_name) + print(f"\n{sheet_name}:") + print(df.head()) +``` + +## 读取特定列 + +```python +import pandas as pd + +# 只读取指定列(提高性能) +df = pd.read_excel("data.xlsx", usecols=["column1", "column2", "column3"]) +``` + +## 性能优化选项 + +- 使用 `usecols` 只读取需要的列 +- 使用 `dtype` 参数指定列类型以加快读取速度 +- 根据文件类型选择合适的引擎:`engine='openpyxl'` 或 `engine='xlrd'` + +## 处理大文件 + +对于非常大的 Excel 文件,避免一次性读取整个文件: +- 使用 `nrows` 参数限制读取的行数 +- 先读取前若干行了解数据结构 +- 按需分批处理数据 diff --git a/.claude/skills/rag-files/references/pdf_reading.md b/.claude/skills/rag-files/references/pdf_reading.md new file mode 100644 index 0000000..bcd16aa --- /dev/null +++ b/.claude/skills/rag-files/references/pdf_reading.md @@ -0,0 +1,362 @@ +# PDF 读取与分析 + +> ⚠️ **使用本文档前请注意**:本文档应在实际处理 PDF 文件之前完整阅读,以选择最合适的工具和方法。不要在未阅读本文档的情况下盲目尝试处理 PDF。 + +用于从 PDF 文件中提取文本、表格和元数据的方法。 + +## 快速决策表 + +| 场景 | 推荐工具 | 原因 | 命令/代码示例 | +|------|----------|------|--------------| +| 纯文本提取(最常见) | pdftotext 命令 | 最快最简单 | `pdftotext input.pdf output.txt` | +| 需要保留布局 | pdftotext -layout | 保持原始排版 | `pdftotext -layout input.pdf output.txt` | +| 需要提取表格 | pdfplumber | 表格识别能力强 | `page.extract_tables()` | +| 需要元数据 | pypdf | 轻量级 | `reader.metadata` | +| 扫描PDF(图片) | OCR (pytesseract) | 无其他选择 | 先转图片再OCR | + +## 文本提取优先级 + +**推荐优先级(从高到低)**: +1. **pdftotext 命令行工具**(最快,适合大多数 PDF) +2. pdfplumber(适合需要保留布局或提取表格) +3. pypdf(轻量级,适合简单提取) +4. OCR(仅用于扫描PDF或无法直接提取文本的情况) + +## 快速开始:使用 pdftotext(推荐) + +> ⚠️ **重要**:必须将输出保存到文件,不要直接输出到终端(stdout),否则会占用大量 token! + +```bash +# ✅ 正确:提取文本到文件(最快最简单) +pdftotext input.pdf output.txt + +# ✅ 正确:保留布局并输出到文件 +pdftotext -layout input.pdf output.txt + +# ✅ 正确:提取特定页面到文件 +pdftotext -f 1 -l 5 input.pdf output.txt # 第1-5页 + +# ❌ 错误:不要使用 stdout(会占用大量 token) +# pdftotext input.pdf - +``` + +**使用流程**: +1. 使用 pdftotext 提取文本到临时文件 +2. 使用 grep 或 Read 工具对生成的文本文件进行检索 +3. 只读取匹配部分的上下文,而非全文 + +如果需要在 Python 中处理: + +```python +from pypdf import PdfReader + +# 读取 PDF +reader = PdfReader("document.pdf") +print(f"Pages: {len(reader.pages)}") + +# 提取文本 +text = "" +for page in reader.pages: + text += page.extract_text() +``` + +## Python 库 + +### pypdf - 基本文本提取 + +```python +from pypdf import PdfReader + +reader = PdfReader("document.pdf") + +# 提取全部文本 +for page in reader.pages: + text = page.extract_text() + print(text) + +# 提取元数据 +meta = reader.metadata +print(f"Title: {meta.title}") +print(f"Author: {meta.author}") +print(f"Subject: {meta.subject}") +print(f"Creator: {meta.creator}") +``` + +### pdfplumber - 带布局的文本和表格提取 + +#### 提取文本(保留布局) + +```python +import pdfplumber + +with pdfplumber.open("document.pdf") as pdf: + for page in pdf.pages: + text = page.extract_text() + print(text) +``` + +#### 提取表格 + +```python +with pdfplumber.open("document.pdf") as pdf: + for i, page in enumerate(pdf.pages): + tables = page.extract_tables() + for j, table in enumerate(tables): + print(f"Table {j+1} on page {i+1}:") + for row in table: + print(row) +``` + +#### 高级表格提取(转为 DataFrame) + +```python +import pandas as pd + +with pdfplumber.open("document.pdf") as pdf: + all_tables = [] + for page in pdf.pages: + tables = page.extract_tables() + for table in tables: + if table: # 检查表格非空 + df = pd.DataFrame(table[1:], columns=table[0]) + all_tables.append(df) + +# 合并所有表格 +if all_tables: + combined_df = pd.concat(all_tables, ignore_index=True) + combined_df.to_excel("extracted_tables.xlsx", index=False) +``` + +#### 带坐标的精确文本提取 + +```python +import pdfplumber + +with pdfplumber.open("document.pdf") as pdf: + page = pdf.pages[0] + + # 提取所有字符及其坐标 + chars = page.chars + for char in chars[:10]: # 前10个字符 + print(f"Char: '{char['text']}' at x:{char['x0']:.1f} y:{char['y0']:.1f}") + + # 按边界框提取文本 (left, top, right, bottom) + bbox_text = page.within_bbox((100, 100, 400, 200)).extract_text() +``` + +#### 复杂表格的高级设置 + +```python +import pdfplumber + +with pdfplumber.open("complex_table.pdf") as pdf: + page = pdf.pages[0] + + # 自定义表格提取设置 + table_settings = { + "vertical_strategy": "lines", + "horizontal_strategy": "lines", + "snap_tolerance": 3, + "intersection_tolerance": 15 + } + tables = page.extract_tables(table_settings) + + # 可视化调试 + img = page.to_image(resolution=150) + img.save("debug_layout.png") +``` + +### pypdfium2 - 快速渲染和文本提取 + +```python +import pypdfium2 as pdfium + +# 加载 PDF +pdf = pdfium.PdfDocument("document.pdf") + +# 提取文本 +for i, page in enumerate(pdf): + text = page.get_text() + print(f"Page {i+1} text length: {len(text)} chars") +``` + +#### 将 PDF 页面渲染为图片 + +```python +import pypdfium2 as pdfium +from PIL import Image + +pdf = pdfium.PdfDocument("document.pdf") + +# 渲染单页 +page = pdf[0] # 第一页 +bitmap = page.render( + scale=2.0, # 高分辨率 + rotation=0 # 不旋转 +) + +# 转换为 PIL Image +img = bitmap.to_pil() +img.save("page_1.png", "PNG") + +# 处理多页 +for i, page in enumerate(pdf): + bitmap = page.render(scale=1.5) + img = bitmap.to_pil() + img.save(f"page_{i+1}.jpg", "JPEG", quality=90) +``` + +## 命令行工具 + +### pdftotext (poppler-utils) + +> ⚠️ **性能优化**:始终输出到文件,避免占用 token + +```bash +# ✅ 提取文本到文件 +pdftotext input.pdf output.txt + +# ✅ 保留布局提取到文件 +pdftotext -layout input.pdf output.txt + +# ✅ 提取特定页面到文件 +pdftotext -f 1 -l 5 input.pdf output.txt # 第1-5页 + +# ✅ 提取带坐标的文本到 XML 文件(用于结构化数据) +pdftotext -bbox-layout document.pdf output.xml + +# ❌ 避免:不要省略输出文件名(会输出到 stdout) +# pdftotext input.pdf +``` + +### 高级图片转换 (pdftoppm) + +```bash +# 转换为 PNG,指定分辨率 +pdftoppm -png -r 300 document.pdf output_prefix + +# 转换特定页面范围,高分辨率 +pdftoppm -png -r 600 -f 1 -l 3 document.pdf high_res_pages + +# 转换为 JPEG,指定质量 +pdftoppm -jpeg -jpegopt quality=85 -r 200 document.pdf jpeg_output +``` + +### 提取嵌入图片 (pdfimages) + +```bash +# 提取所有图片 +pdfimages -j input.pdf output_prefix + +# 列出图片信息(不提取) +pdfimages -list document.pdf + +# 以原始格式提取 +pdfimages -all document.pdf images/img +``` + +## OCR 提取(扫描PDF) + +```python +# 需要: pip install pytesseract pdf2image +import pytesseract +from pdf2image import convert_from_path + +# PDF 转图片 +images = convert_from_path('scanned.pdf') + +# OCR 每一页 +text = "" +for i, image in enumerate(images): + text += f"Page {i+1}:\n" + text += pytesseract.image_to_string(image) + text += "\n\n" + +print(text) +``` + +## 处理加密 PDF + +```python +from pypdf import PdfReader + +try: + reader = PdfReader("encrypted.pdf") + if reader.is_encrypted: + reader.decrypt("password") + + # 解密后可正常提取文本 + for page in reader.pages: + text = page.extract_text() + print(text) +except Exception as e: + print(f"Failed to decrypt: {e}") +``` + +```bash +# 使用 qpdf 解密(需要知道密码) +qpdf --password=mypassword --decrypt encrypted.pdf decrypted.pdf + +# 检查加密状态 +qpdf --show-encryption encrypted.pdf +``` + +## 批量处理 + +```python +import os +import glob +from pypdf import PdfReader +import logging + +logging.basicConfig(level=logging.INFO) +logger = logging.getLogger(__name__) + +def batch_extract_text(input_dir): + """批量提取文本""" + pdf_files = glob.glob(os.path.join(input_dir, "*.pdf")) + + for pdf_file in pdf_files: + try: + reader = PdfReader(pdf_file) + text = "" + for page in reader.pages: + text += page.extract_text() + + output_file = pdf_file.replace('.pdf', '.txt') + with open(output_file, 'w', encoding='utf-8') as f: + f.write(text) + logger.info(f"Extracted text from: {pdf_file}") + + except Exception as e: + logger.error(f"Failed to extract text from {pdf_file}: {e}") + continue +``` + +## 性能优化 + +1. **文件输出优先**:始终将 pdftotext 输出保存到文件,然后用 grep/Read 检索,避免直接输出到终端占用大量 token +2. **大型PDF**:使用流式方式逐页处理,避免一次性加载整个文件 +3. **文本提取**:`pdftotext` 最快;pdfplumber 适合结构化数据和表格 +4. **图片提取**:`pdfimages` 比渲染页面快得多 +5. **内存管理**:逐页或分块处理大文件 + +## 快速参考 + +| 任务 | 最佳工具 | 命令/代码 | +|------|----------|-----------| +| 提取文本 | pdfplumber | `page.extract_text()` | +| 提取表格 | pdfplumber | `page.extract_tables()` | +| 命令行提取 | pdftotext | `pdftotext -layout input.pdf` | +| OCR 扫描PDF | pytesseract | 先转图片再OCR | +| 提取元数据 | pypdf | `reader.metadata` | +| PDF转图片 | pypdfium2 | `page.render()` | + +## 可用包 + +- **pypdf** - 基本操作(BSD 许可) +- **pdfplumber** - 文本和表格提取(MIT 许可) +- **pypdfium2** - 快速渲染和提取(Apache/BSD 许可) +- **pytesseract** - OCR(Apache 许可) +- **pdf2image** - PDF转图片 +- **poppler-utils** - 命令行工具(GPL-2 许可) diff --git a/.claude/skills/rag-files/scripts/convert_pdf_to_images.py b/.claude/skills/rag-files/scripts/convert_pdf_to_images.py new file mode 100644 index 0000000..f8a4ec5 --- /dev/null +++ b/.claude/skills/rag-files/scripts/convert_pdf_to_images.py @@ -0,0 +1,35 @@ +import os +import sys + +from pdf2image import convert_from_path + + +# Converts each page of a PDF to a PNG image. + + +def convert(pdf_path, output_dir, max_dim=1000): + images = convert_from_path(pdf_path, dpi=200) + + for i, image in enumerate(images): + # Scale image if needed to keep width/height under `max_dim` + width, height = image.size + if width > max_dim or height > max_dim: + scale_factor = min(max_dim / width, max_dim / height) + new_width = int(width * scale_factor) + new_height = int(height * scale_factor) + image = image.resize((new_width, new_height)) + + image_path = os.path.join(output_dir, f"page_{i+1}.png") + image.save(image_path) + print(f"Saved page {i+1} as {image_path} (size: {image.size})") + + print(f"Converted {len(images)} pages to PNG images") + + +if __name__ == "__main__": + if len(sys.argv) != 3: + print("Usage: convert_pdf_to_images.py [input pdf] [output directory]") + sys.exit(1) + pdf_path = sys.argv[1] + output_directory = sys.argv[2] + convert(pdf_path, output_directory) diff --git a/.claude/skills/rag-memories/SKILL.md b/.claude/skills/rag-memories/SKILL.md new file mode 100644 index 0000000..a8f2526 --- /dev/null +++ b/.claude/skills/rag-memories/SKILL.md @@ -0,0 +1,45 @@ +--- +name: rag-memories +description: 在日常对话或阅览文件时,把可复用的短事实、偏好、项目约定写入 .claude/memories(与 CLAUDE.md 记忆协议一致);长摘录与笔记放 .claude/files(用 rag-files)。触发:用户说「记住/记下来」、纠正偏好、沉淀结论、需要跨会话沿用的关键信息。 +--- + +# 本地记忆沉淀(rag-memories) + +## 目标 + +- 让 Agent 在**不依赖超长上下文**的情况下,在后续会话中仍能利用已确认有用的信息。 +- **短而稳**:一句话事实、用户偏好、命令别名、项目内约定 → `.claude/memories/`。 +- **长而可搜**:段落、摘录、整理稿 → `.claude/files/`(见 **rag-files**),不要用 `core.json` 塞长文。 + +## 写哪里 + +| 类型 | 位置 | 说明 | +|------|------|------| +| 核心记忆、偏好、稳定事实 | `.claude/memories/core.json` | 若文件不存在,可创建;必须保持合法 JSON | +| 当前模式等状态 | `.claude/memories/current_mode.json` | 仅在与模式切换相关时更新 | +| 单主题若干句补充 | `.claude/memories/notes/.md` | 避免把 `core.json` 撑得过大;`slug` 用小写连字符 | + +## 何时写入 + +- 用户明确说「记住」「记下来」「以后默认…」。 +- 用户纠正行为或偏好(沟通风格、工具选择、路径约定)。 +- 对话或阅读文件中出现的**可验证、会重复用到**的项目事实(例如:默认知识库路径、私有脚本入口、环境约束)。 +- **不要**写入:一次性任务状态、未经确认的猜测、密钥与隐私。 + +## `core.json` 操作建议 + +1. 用 Read 读完整文件(体量应小);若不存在,从最小结构初始化,例如: + - 顶层对象含 `facts`(字符串数组)与 `preferences`(字符串键值对),或沿用已有键名,**不要**破坏已有字段含义。 +2. 合并新条目:去重(同一事实不重复追加);必要时更新而非复制多条矛盾记录。 +3. 写回前校验 JSON(引号、逗号、尾随逗号);写回后用 Read 抽查。 +4. 若当前仓库将 `.claude/memories` 排除在版本控制外,仍按同样路径写入,便于本机 Agent 加载。 + +## 与 rag-files 配合 + +- 从 PDF/Excel/长文档抽出的**大段原文或详细笔记**:写入 `.claude/files/` 并在 `core.json` 或 `notes` 里只保留**一行指针**(文件名 + 用途)。 +- 检索时:先 Grep/Read `.claude/files`,短事实读 `core.json` / `notes/`。 + +## 回答风格 + +- 写入后可用一句话向用户确认已记录的内容(不含敏感信息)。 +- 若用户只要求「记住」但内容过长,建议拆成:记忆条 + 文件摘录,并说明存放位置。 diff --git a/.gitignore b/.gitignore index 08c81df..f71d6c9 100644 --- a/.gitignore +++ b/.gitignore @@ -5,5 +5,9 @@ __pycache__/ dist/ build/ workspace/ -.claude/ +# .claude: track settings.local.json + skills/; keep rest local +.claude/** +!.claude/settings.local.json +!.claude/skills/ +!.claude/skills/** .obsidian/ diff --git a/CLAUDE.md b/CLAUDE.md index 50efa1d..2f406be 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -41,7 +41,7 @@ 处理知识库查询时遵循以下模式: 1. **分层导航**: 先读索引文件(`data_structure.md`),再定位相关文件,最后局部读取 -2. **先学习后处理**: 遇到 PDF/Excel 前,先读取对应处理方法文档(`.claude/skills/rag-skill/references/`) +2. **先学习后处理**: 遇到 PDF/Excel 前,先读取对应处理方法文档(`.claude/skills/rag-files/references/`) 3. **渐进式检索**: 使用 `grep` 定位关键词,只读取匹配上下文,避免整文件加载 4. **多轮迭代**: 最多5轮检索,逐步缩小范围;找不到时明确告知而非臆造 @@ -102,6 +102,8 @@ - `.claude/memories/` - 持久化记忆(自动加载到上下文) - `.claude/memories/core.json` - 核心记忆文件 - `.claude/memories/current_mode.json` - 当前模式状态 +- `.claude/files/` - 较长摘录、笔记,供后续 Grep/Read(skill:`rag-files`) +- 沉淀操作指引:短事实/偏好用 skill `rag-memories`,长材料用 `rag-files` 写入 `.claude/files/` ### 触发记录 - 用户说"记住..." / "记下来..."