diff --git a/.obsidian/plugins/recent-files-obsidian/data.json b/.obsidian/plugins/recent-files-obsidian/data.json index e5e59f6..14aa7a1 100644 --- a/.obsidian/plugins/recent-files-obsidian/data.json +++ b/.obsidian/plugins/recent-files-obsidian/data.json @@ -1,5 +1,9 @@ { "recentFiles": [ + { + "basename": "Git团队协作指南", + "path": "博客/编程与工具/Git团队协作指南.md" + }, { "basename": "usage-guide", "path": "obsidian-halo/docs/usage-guide.md" diff --git a/.trae/documents/obsidian-halo功能增强对比计划.md b/.trae/documents/obsidian-halo功能增强对比计划.md deleted file mode 100644 index b44fd08..0000000 --- a/.trae/documents/obsidian-halo功能增强对比计划.md +++ /dev/null @@ -1,211 +0,0 @@ -# Obsidian Halo 插件功能增强计划 - -## 一、功能对比分析 - -### Halo CLI 已有功能 - -| 分类 | 功能 | CLI 支持 | -|------|------|---------| -| **文章管理** | 发布文章 | ✅ 完整 | -| | 更新文章 | ✅ 完整 | -| | 删除文章 | ✅ 完整 | -| | 列出文章列表 | ✅ 完整 | -| | 查看文章详情 | ✅ 完整 | -| | 批量操作 | ✅ PowerShell 脚本 | -| **导出/导入** | 导出为 JSON | ✅ 完整 | -| | 导出为 Markdown | ✅ 完整 | -| | 从 JSON 导入 | ✅ 完整 | -| | 从 Markdown 导入 | ✅ 完整 | -| **标签管理** | 列出标签 | ✅ 完整 | -| | 创建标签 | ✅ 完整 | -| | 更新标签 | ✅ 完整 | -| | 删除标签 | ✅ 完整 | -| | 为文章设置标签 | ✅ 完整 | -| **分类管理** | 列出分类 | ✅ 完整 | -| | 创建分类 | ✅ 完整 | -| | 更新分类 | ✅ 完整 | -| | 删除分类 | ✅ 完整 | -| | 设置优先级 | ✅ 完整 | -| **其他内容** | 单页管理 | ✅ 完整 | -| | 搜索内容 | ✅ 完整 | -| **系统管理** | 附件管理 | ✅ 完整 | -| | 备份管理 | ✅ 完整 | -| | 动态管理 | ✅ 完整 | -| | 评论管理 | ✅ 完整 | -| | 插件管理 | ✅ 完整 | -| | 主题管理 | ✅ 完整 | - ---- - -## 二、Obsidian 插件当前功能 - -| 分类 | 功能 | 状态 | -|------|------|------| -| **文章管理** | 发布文章 | ✅ 已实现 | -| | 更新文章(同步 Halo 到本地) | ✅ 已实现 | -| | 列出文章列表 | ❌ 缺失 | -| | 查看文章详情 | ❌ 缺失 | -| | 删除文章 | ❌ 缺失 | -| | 批量发布 | ❌ 缺失 | -| **图片上传** | 自动上传图片 | ✅ 已实现(新增) | -| **标签/分类** | 为文章设置标签 | ✅ 已实现 | -| | 为文章设置分类 | ✅ 已实现 | -| | 创建标签 | ❌ 缺失 | -| | 创建分类 | ❌ 缺失 | -| | 管理标签/分类 | ❌ 缺失 | -| **导出/导入** | 从 Markdown 导入 | ❌ 缺失 | -| | 导出为 Markdown | ❌ 缺失 | -| | 导出为 JSON | ❌ 缺失 | -| **其他** | 单页管理 | ❌ 缺失 | -| | 搜索 | ❌ 缺失 | - ---- - -## 三、功能优先级建议 - -### 高优先级(核心功能) - -1. **文章列表查看** - - 在命令面板中显示 Halo 文章列表 - - 支持分页、筛选(草稿/已发布) - - 快速预览文章信息 - -2. **导入功能增强** - - 支持从本地 Markdown 文件导入创建文章 - - 智能解析文件路径和文件名 - -3. **文章删除功能** - - 添加删除命令(需二次确认) - - 支持删除本地文件或仅删除 Halo 文章 - -### 中优先级(常用功能) - -4. **批量操作** - - 批量发布多篇文章 - - 批量同步已发布文章 - - 批量管理(删除、更新标签等) - -5. **标签/分类管理** - - 创建新标签/分类 - - 查看现有标签/分类列表 - - 更新/删除标签/分类 - -6. **导出功能** - - 导出文章为 Markdown - - 导出文章为 JSON(备份用) - -### 低优先级(增强功能) - -7. **搜索功能** - - 搜索 Halo 文章 - - 快速定位文章 - -8. **单页管理** - - 发布/管理独立页面 - - 与文章类似的工作流 - ---- - -## 四、实施步骤 - -### 第一阶段:核心补充(1-2 周) - -#### 任务 1:文章列表功能 -- [ ] 创建文章列表 Modal -- [ ] 实现分页、筛选功能 -- [ ] 添加快速操作按钮(查看、编辑、删除) -- [ ] 集成到命令面板 - -#### 任务 2:导入功能 -- [ ] 实现从文件导入创建文章 -- [ ] 智能解析 Markdown 文件 -- [ ] 支持选择文件对话框 - -#### 任务 3:删除功能 -- [ ] 添加删除命令 -- [ ] 二次确认机制 -- [ ] 删除选项(仅 Halo / 仅本地 / 全部) - -### 第二阶段:常用功能(2-4 周) - -#### 任务 4:批量操作 -- [ ] 批量选择界面 -- [ ] 批量发布队列 -- [ ] 进度显示和错误处理 - -#### 任务 5:标签/分类管理 -- [ ] 标签管理 Modal -- [ ] 分类管理 Modal -- [ ] 创建/编辑/删除功能 - -#### 任务 6:导出功能 -- [ ] 导出为 Markdown -- [ ] 导出为 JSON(备份) -- [ ] 选择保存路径 - -### 第三阶段:增强功能(4+ 周) - -#### 任务 7:搜索功能 -- [ ] 文章搜索 Modal -- [ ] 快速筛选和定位 - -#### 任务 8:单页管理 -- [ ] 单页发布命令 -- [ ] 单页列表查看 -- [ ] 单页导入/导出 - ---- - -## 五、技术实现建议 - -### API 端点 - -| 功能 | API 端点 | -|------|----------| -| 列出文章 | `GET /apis/uc.api.content.halo.run/v1alpha1/posts` | -| 获取文章 | `GET /apis/uc.api.content.halo.run/v1alpha1/posts/{name}` | -| 删除文章 | `DELETE /apis/uc.api.content.halo.run/v1alpha1/posts/{name}` | -| 列出标签 | `GET /apis/content.halo.run/v1alpha1/tags` | -| 创建标签 | `POST /apis/content.halo.run/v1alpha1/tags` | -| 列出分类 | `GET /apis/content.halo.run/v1alpha1/categories` | -| 创建分类 | `POST /apis/content.halo.run/v1alpha1/categories` | - -### 文件结构建议 - -``` -src/ -├── commands/ # 命令相关 -│ ├── publish.ts -│ ├── delete.ts -│ ├── list.ts -│ ├── import.ts -│ └── export.ts -├── modals/ # 弹窗相关 -│ ├── post-list-modal.ts -│ ├── tag-manager-modal.ts -│ ├── category-manager-modal.ts -│ └── search-modal.ts -├── service/ # 服务层 -│ ├── index.ts # 现有 -│ └── image-uploader.ts # 现有 -└── utils/ # 工具层 - ├── image.ts # 现有 - └── ... -``` - ---- - -## 六、建议实施顺序 - -1. **先实现文章列表**(最常用) -2. **再实现导入功能**(补全工作流) -3. **实现删除功能**(完整性) -4. **实现批量操作**(效率提升) -5. **实现标签/分类管理**(内容组织) -6. **实现导出功能**(备份需求) -7. **实现搜索功能**(快速定位) -8. **实现单页管理**(可选) - ---- - -请确认需要实现哪些功能,我将制定详细的技术方案。 \ No newline at end of file diff --git a/.trae/documents/obsidian-halo功能增强计划.md b/.trae/documents/obsidian-halo功能增强计划.md deleted file mode 100644 index 0291488..0000000 --- a/.trae/documents/obsidian-halo功能增强计划.md +++ /dev/null @@ -1,214 +0,0 @@ -# obsidian-halo 插件功能增强计划 - -## 当前功能分析 - -当前插件已实现: -- ✅ 发布文档到 Halo -- ✅ 从 Halo 拉取文档 -- ✅ 更新已发布文章内容 -- ✅ 多站点管理 -- ✅ Frontmatter 元数据同步(标题、摘要、分类、标签、封面) -- ✅ 图片上传功能 -- ✅ 文章列表增强(分页、筛选) -- ✅ Markdown 文件导入 -- ✅ 删除文章功能(支持选择性删除) -- ✅ 标签管理功能 -- ✅ 分类管理功能 -- ✅ 导出文章为 Markdown -- ✅ 导出文章为 JSON -- ✅ 搜索 Halo 文章 -- ✅ 同步状态面板 -- ✅ 同步历史记录 -- ✅ 使用指南文档 - ---- - -## 已完成功能(2024年4月实现) - -### 1. 图片/附件上传 ✅ -**优先级:高 | 难度:中** - -- [x] 自动检测并上传笔记中的图片到 Halo 媒体库 -- [x] 转换本地图片路径为 Halo URL - -### 2. 批量操作功能 ✅ -**优先级:高 | 难度:中** - -- [x] 文章列表查看(支持分页、筛选) -- [x] 从 Markdown 文件导入文章 - -### 3. 标签/分类管理 ✅ -**优先级:高 | 难度:中** - -- [x] 标签管理(创建、编辑、删除) -- [x] 分类管理(创建、编辑、删除) - -### 4. 导出功能 ✅ -**优先级:中 | 难度:中** - -- [x] 导出文章为 Markdown -- [x] 导出文章为 JSON(备份用) - -### 5. 搜索功能 ✅ -**优先级:中 | 难度:中** - -- [x] 搜索 Halo 文章(按标题、slug 搜索) -- [x] 按发布状态筛选 - -### 6. 删除功能 ✅ -**优先级:高 | 难度:中** - -- [x] 选择性删除(仅 Halo / 仅本地 / 全部) -- [x] 二次确认机制 - -### 7. 同步状态面板 ✅ (新增) -**优先级:高 | 难度:中** - -- [x] 侧边栏面板显示已发布文章列表 -- [x] 快速操作按钮(更新、拉取) -- [x] 同步历史记录 -- [x] 同步图标侧边栏入口 - -### 8. 使用指南 ✅ (新增) -**优先级:高 | 难度:中** - -- [x] 完整的命令列表说明 -- [x] 快速开始教程 -- [x] 常见问题解答 - ---- - -## 待实现功能 - -### 1. 双向同步增强 -**优先级:高 | 难度:高** - -- [ ] 差异检测:比较本地和 Halo 版本的修改时间 -- [ ] 选择性同步:仅同步本地/仅同步 Halo/完全合并 -- [ ] 冲突解决:处理两边同时修改的情况 -- [ ] 同步历史:记录每次同步的详情 ✅(已完成基础功能) - -### 2. 高级发布选项 -**优先级:中 | 难度:中** - -- [ ] 自定义 Slug 格式(支持模板变量:`{{title}}`, `{{date}}`) -- [ ] 发布时间调度(定时发布) -- [ ] SEO 元数据设置(meta description、keywords) -- [ ] 文章属性控制(评论开关、置顶、优先级、可见性) - -### 3. 内容预览 -**优先级:中 | 难度:中** - -- [ ] 发布前预览 Halo 渲染效果 -- [ ] 预览不同主题下的样式 -- [ ] 实时预览面板 - -### 4. 模板支持 -**优先级:低 | 难度:中** - -- [ ] 发布模板:预定义 frontmatter 结构 -- [ ] 快捷键自定义默认值 -- [ ] 模板变量支持 - -### 5. 命令面板增强 -**优先级:低 | 难度:低** - -- [ ] 键盘快捷键支持 -- [ ] 快速切换站点 -- [ ] 快捷操作菜单 - -### 6. 用户界面优化 -**优先级:低 | 难度:中** - -- [ ] 进度条显示 -- [ ] 更详细的错误提示 -- [ ] 通知中心整合 -- [ ] 右键菜单支持 - ---- - -## 推荐优先级(建议优先实现) - -1. **高级发布选项** - 增加灵活性 -2. **内容预览** - 提升用户体验 -3. **模板支持** - 提升工作效率 - ---- - -## 技术实现要点 - -### 图片上传实现思路 -```typescript -// 1. 解析 Markdown 中的图片引用 -// 2. 上传到 Halo 媒体库获取 URL -// 3. 替换本地路径为远程 URL - -// Halo 媒体上传 API 端点 -POST /apis/api.console.halo.run/v1alpha1/attachments/upload -``` - -### 批量操作实现思路 -```typescript -// 1. 创建批量选择界面 -// 2. 批量处理队列 -// 3. 进度显示和错误处理 -``` - -### 同步检测实现思路 -```typescript -// 1. 保存本地最后同步时间戳 -// 2. 对比 Halo 文章的更新时间 -// 3. 标记需要更新的文章 -``` - ---- - -## 开发建议 - -1. **模块化开发**:新增功能独立成模块 -2. **配置化**:新功能支持在设置中开关 -3. **国际化**:所有文案支持 i18n -4. **错误处理**:完善异常捕获和用户提示 -5. **测试**:添加单元测试和集成测试 - ---- - -## 文件结构 - -``` -src/ -├── commands/ # 命令相关 -│ ├── import-markdown.ts # ✅ 导入 Markdown 文件 -│ ├── delete-post.ts # ✅ 删除文章 -│ ├── manage-taxonomy.ts # ✅ 管理标签/分类 -│ ├── export-post.ts # ✅ 导出文章 -│ └── search-posts.ts # ✅ 搜索文章 -├── modals/ # 弹窗相关 -│ ├── file-preview-modal.ts # ✅ 文件预览 -│ ├── delete-confirm-modal.ts # ✅ 删除确认 -│ ├── tag-manager-modal.ts # ✅ 标签管理 -│ ├── category-manager-modal.ts # ✅ 分类管理 -│ ├── search-modal.ts # ✅ 搜索弹窗 -│ └── sync-history-modal.ts # ✅ 同步历史 -├── views/ # 视图相关 -│ └── sync-status-view.ts # ✅ 同步状态面板 -├── service/ # 服务层 -│ ├── index.ts # Halo API 服务 -│ └── image-uploader.ts # ✅ 图片上传 -├── utils/ # 工具层 -│ ├── image.ts # ✅ 图片处理 -│ └── ... -├── i18n/ # 国际化 -│ └── locales/ -│ ├── zh-cn.json # 中文 -│ ├── en.json # 英文 -│ └── zh-tw.json # 繁体中文 -└── main.ts # 主入口 - -docs/ -└── usage-guide.md # ✅ 使用指南 -``` - ---- - -最后更新:2024年4月26日 \ No newline at end of file diff --git a/.trae/skills/halo-blog-writer/SKILL.md b/.trae/skills/halo-blog-writer/SKILL.md deleted file mode 100644 index 1dd09ee..0000000 --- a/.trae/skills/halo-blog-writer/SKILL.md +++ /dev/null @@ -1,246 +0,0 @@ ---- -name: halo-blog-writer -description: | - Halo 博客写作和管理助手。当用户需要: - (1) 发布博客文章 - (2) 管理博客内容 - (3) 更新/修改已发布的文章 - (4) 导出/导入博客内容 - (5) 查看博客状态 - (6) 使用自然语言操作 Halo 博客时触发此技能 ---- - -# Halo 博客写作助手 - -本技能帮助 AI Agent 通过 Halo CLI 管理用户的 Halo 博客(http://192.168.5.8:8090)。 - -## 快速开始 - -### 已配置的环境 - -用户已通过 npm 全局安装 Halo CLI: -- 版本:1.3.0 -- Node.js:v22.14.0 -- 系统:Windows (PowerShell) - -### CLI 配置 - -当前激活的配置(profile)是 `local`,连接地址: -``` -http://192.168.5.8:8090 -``` - -认证方式:Bearer Token(已存储在系统 keyring 中) - -### 基本命令 - -```powershell -# 列出文章(分页显示) -halo post list --page 1 --size 20 - -# 查看单个文章详情 -halo post get - -# 创建文章 -halo post create --title "标题" --content "# 内容" --publish true - -# 更新文章 -halo post update --title "新标题" --content "新内容" - -# 导出文章为 Markdown -halo post export-markdown --output ./post.md - -# 导入 Markdown 文件创建文章 -halo post import-markdown --file ./post.md -``` - -## 写作工作流 - -### 场景 1:发布新文章 - -当用户提供文章内容时: -1. 接收文章内容(可能是文件路径或直接文本) -2. 提取标题和正文 -3. 判断是否立即发布(publish)还是仅创建草稿 -4. 执行 `halo post create` 命令 -5. 确认创建成功 - -```powershell -# 发布新文章 -halo post create --title "文章标题" --content "# 文章内容..." --publish true - -# 仅创建草稿 -halo post create --title "文章标题" --content "# 文章内容..." -``` - -### 场景 2:从 Markdown 文件发布 - -当用户提供 `.md` 文件时: -1. 读取文件内容 -2. 从文件名或第一行提取标题 -3. 执行导入命令 - -```powershell -halo post import-markdown --file d:\path\to\article.md -``` - -### 场景 3:更新已发布的文章 - -当用户要求修改某篇文章时: -1. 先用 `halo post list` 找到文章 -2. 获取文章 ID 或 name -3. 执行更新命令 - -```powershell -# 先查看文章列表 -halo post list --page 1 --size 20 - -# 获取文章详情 -halo post get - -# 更新文章 -halo post update --title "新标题" --content "新内容" -``` - -### 场景 4:批量操作 - -处理多篇文章: -1. 获取文章列表 -2. 逐个处理 -3. 报告完成状态 - -```powershell -# 获取所有文章(分页) -halo post list --page 1 --size 100 - -# 或者导出为 JSON 格式便于处理 -halo post list --page 1 --size 100 --json -``` - -## 文件路径约定 - -用户的博客文章通常保存在: -``` -d:\Code\OpenClaw\文件\博客\ -``` - -常见文件格式: -- `.md` - Markdown 文件 -- `.txt` - 纯文本文件 - -## 注意事项 - -### 内容格式 - -- Halo 支持 Markdown 和 HTML 格式 -- 默认使用 Markdown(`--raw-type markdown`) -- 也支持 `--raw-type html` - -### 发布状态 - -- `published` - 已发布 -- `draft` - 草稿 -- 可以在创建时指定 `--publish true` 立即发布 - -### 文章标识 - -文章可以用以下方式标识: -- **Name/Slug**:文章的唯一标识符(如 `019dbabc-86b9-74a0-a926-c25529309007`) -- **ID**:文章内部 ID - -### 错误处理 - -如果命令失败: -1. 检查网络连接 -2. 确认 CLI 配置正确(`halo auth current`) -3. 查看错误信息并重试 - -## 其他管理功能 - -```powershell -# 单页管理 -halo single-page list -halo single-page create --title "标题" --content "内容" - -# 插件管理 -halo plugin list - -# 主题管理 -halo theme list - -# 备份 -halo backup list -halo backup create - -# 评论管理 -halo comment list -halo comment reply --content "回复内容" -``` - -## 安全提醒 - -- Bearer Token 已安全存储在系统 keyring 中 -- 不要在命令输出中显示敏感信息 -- 谨慎执行删除操作(`halo post delete`) - -## 常用命令速查 - -| 操作 | 命令 | -|------|------| -| 查看文章列表 | `halo post list --page 1 --size 20` | -| 查看文章详情 | `halo post get ` | -| 创建文章 | `halo post create --title "标题" --content "内容"` | -| 发布文章 | `halo post create --title "标题" --content "内容" --publish true` | -| 更新文章 | `halo post update --title "标题" --content "内容"` | -| 删除文章 | `halo post delete --force` | -| 导出为 Markdown | `halo post export-markdown --output ./post.md` | -| 从文件导入 | `halo post import-markdown --file ./post.md` | - -## 博客标签和分类 - -### 现有标签(共 11 个) - -| 显示名称 | Slug | 颜色 | -|---------|------|------| -| 协会动态 | xie-hui-dong-tai | 🟢 #B8E986 | -| 5分钟速览 | 5fen-zhong-su-lan | 🔵 #50E3C2 | -| 技术深潜 | ji-shu-shen-qian | 🔷 #4A90E2 | -| 校园AI | xiao-yuan-ai | 🟣 #9013FE | -| 竞赛指南 | jing-sai-zhi-nan | 🟪 #BD10E0 | -| 项目实战 | xiang-mu-shi-zhan | 🟢 #417505 | -| 零基础入门 | ling-ji-chu-ru-men | 🟢 #7ED321 | -| 开源框架 | kai-yuan-kuang-jia | 🟤 #8B572A | -| 模型轻量化 | mo-xing-qing-liang-hua | 🟡 #F8E71C | -| 隐私计算 | yin-si-ji-suan | 🟠 #F5A623 | -| AI智能体 | aizhi-neng-ti | 🔴 #D0021B | - -### 标签使用建议 - -| 文章类型 | 推荐标签 | -|---------|---------| -| 技术教程 / 开发工具 | 技术深潜 | -| 团队协作 / 开源项目 | 项目实战 | -| AI/LLM/模型相关 | 技术深潜, AI智能体 | -| 编程入门教程 | 技术深潜, 零基础入门 | -| 比赛通知 / 大赛解读 | 竞赛指南 | -| 协会通知 / 动态 | 协会动态 | -| AI 科普 / 入门 | 零基础入门, AI智能体 | - -### 常用命令速查 - -| 操作 | 命令 | -|------|------| -| 查看所有标签 | `halo post tag list` | -| 查看所有分类 | `halo post category list` | -| 为文章添加标签 | `halo post update --tags "标签1,标签2"` | -| 更新文章分类 | `halo post update --category "分类名"` | -| 查看文章标签 | `halo post get --json` | - -详细命令参见 `references/command-reference.md` - -## 参考文档 - -- CLI 完整帮助:`halo --help`、`halo post --help` -- 配置信息:`halo auth profile list` -- 高级用法:参见 `references/advanced-usage.md` -- 标签和分类命令:参见 `references/command-reference.md` diff --git a/.trae/skills/halo-blog-writer/references/advanced-usage.md b/.trae/skills/halo-blog-writer/references/advanced-usage.md deleted file mode 100644 index 3926dc1..0000000 --- a/.trae/skills/halo-blog-writer/references/advanced-usage.md +++ /dev/null @@ -1,253 +0,0 @@ -# Halo CLI 高级用法 - -本文件包含 Halo CLI 的高级用法和示例场景。 - -## 内容目录 - -1. [批量操作](#批量操作) -2. [自动化脚本](#自动化脚本) -3. [内容迁移](#内容迁移) -4. [SEO 优化](#seo-优化) - ---- - -## 批量操作 - -### 批量导出文章 - -```powershell -# 导出所有文章为 JSON -halo post list --page 1 --size 100 --json > all_posts.json - -# 批量导出为 Markdown -$posts = halo post list --page 1 --size 100 --json | ConvertFrom-Json -foreach ($post in $posts.items) { - halo post export-markdown $post.name --output "./export/$($post.name).md" -} -``` - -### 批量更新标签 - -```powershell -# 给多篇文章添加标签 -$posts = halo post list --page 1 --size 20 --json | ConvertFrom-Json -foreach ($post in $posts.items) { - halo post update $post.name --tags "Python,教程,技术分享" -} -``` - -### 批量发布 - -```powershell -# 将所有草稿发布 -$posts = halo post list --page 1 --size 100 --json | ConvertFrom-Json -foreach ($post in $posts.items) { - if ($post.status -eq "draft") { - halo post update $post.name --publish true - Write-Host "已发布: $($post.title)" - } -} -``` - ---- - -## 自动化脚本 - -### 文章备份脚本 - -```powershell -# backup-halo-posts.ps1 -param( - [int]$PageSize = 50 -) - -$backupDir = ".\halo-backup\$(Get-Date -Format 'yyyy-MM-dd')" -New-Item -ItemType Directory -Path $backupDir -Force | Out-Null - -$page = 1 -$totalExported = 0 - -do { - Write-Host "正在导出第 $page 页..." - $result = halo post list --page $page --size $PageSize --json | ConvertFrom-Json - - foreach ($post in $result.items) { - $filename = "$backupDir\$($post.name).md" - halo post export-markdown $post.name --output $filename - $totalExported++ - } - - $page++ -} while ($result.hasNext) - -Write-Host "备份完成!共导出 $totalExported 篇文章到 $backupDir" -``` - -### 定时发布脚本 - -```powershell -# scheduled-publish.ps1 -param( - [string]$FilePath -) - -if (-not (Test-Path $FilePath)) { - Write-Error "文件不存在: $FilePath" - exit 1 -} - -$content = Get-Content $FilePath -Raw -$title = [System.IO.Path]::GetFileNameWithoutExtension($FilePath) - -Write-Host "正在发布: $title" -halo post create --title $title --content $content --publish true - -if ($LASTEXITCODE -eq 0) { - Write-Host "发布成功!" -} else { - Write-Error "发布失败" - exit 1 -} -``` - ---- - -## 内容迁移 - -### 从其他平台导入 - -```powershell -# 从 WordPress 导出文件导入 -halo post import-markdown --file .\wordpress-export\post-1.md -halo post import-markdown --file .\wordpress-export\post-2.md - -# 批量导入 -Get-ChildItem .\wordpress-export\*.md | ForEach-Object { - halo post import-markdown --file $_.FullName - Write-Host "已导入: $($_.Name)" -} -``` - -### 迁移到其他平台 - -```powershell -# 导出为标准格式 -halo post list --page 1 --size 100 --json > migration.json - -# 转换为其他 CMS 格式(如 Hugo、Jekyll) -# 需要额外的转换脚本处理 JSON 到目标格式 -``` - ---- - -## SEO 优化 - -### 检查文章 SEO - -```powershell -# 获取最近 10 篇文章的 SEO 信息 -$posts = halo post list --page 1 --size 10 --json | ConvertFrom-Json - -foreach ($post in $posts.items) { - $detail = halo post get $post.name --json | ConvertFrom-Json - - Write-Host "文章: $($detail.title)" - Write-Host " - 字数: $($detail.wordCount)" - Write-Host " - 标签: $($detail.tags -join ', ')" - Write-Host " - URL: $($detail.slug)" - Write-Host "" -} -``` - -### 批量更新 SEO 描述 - -```powershell -# 为所有文章添加 SEO 描述 -$posts = halo post list --page 1 --size 50 --json | ConvertFrom-Json - -foreach ($post in $posts.items) { - $detail = halo post get $post.name --json | ConvertFrom-Json - - # 生成描述(前 160 个字符) - $excerpt = $detail.content -replace '<[^>]+>', '' # 去除 HTML 标签 - $excerpt = $excerpt.Substring(0, [Math]::Min(160, $excerpt.Length)) - - halo post update $post.name --description $excerpt -} -``` - ---- - -## 高级配置 - -### 使用不同 Profile - -```powershell -# 列出所有配置 -halo auth profile list - -# 使用指定配置 -halo post list --profile production - -# 创建新配置 -halo auth login --profile aliyun --url http://101.133.128.193:8091 --auth-type bearer --token -``` - -### 环境变量配置 - -```powershell -# 临时修改配置目录 -$env:HALO_CLI_CONFIG_DIR = "d:\halo-config" - -# 使用代理 -$env:HTTPS_PROXY = "http://proxy:8080" - -# 禁用 SSL 验证(不推荐) -$env:HALO_CLI_INSECURE_SSL = "true" -``` - -### 调试模式 - -```powershell -# 显示详细输出 -halo post list --page 1 --size 20 --verbose - -# 查看配置 -halo auth current --verbose - -# 查看请求/响应 -halo post get --debug -``` - ---- - -## 常见问题解决 - -### Token 过期 - -```powershell -# 刷新 Token -halo auth login --profile local --url http://192.168.5.8:8091 --auth-type bearer --token -``` - -### 网络问题 - -```powershell -# 使用超时设置 -halo post list --timeout 30 - -# 重试机制 -$maxRetries = 3 -for ($i = 1; $i -le $maxRetries; $i++) { - halo post list --page 1 --size 20 - if ($LASTEXITCODE -eq 0) { break } - Start-Sleep -Seconds 5 -} -``` - -### 并发限制 - -Halo API 有请求限制,高并发操作可能导致失败。建议: -- 使用 `--rate-limit 10`(如果支持) -- 添加延迟:`Start-Sleep -Milliseconds 100` -- 批量操作使用 `--batch` 标志(如果支持) \ No newline at end of file diff --git a/.trae/skills/halo-blog-writer/references/command-reference.md b/.trae/skills/halo-blog-writer/references/command-reference.md deleted file mode 100644 index 35e475b..0000000 --- a/.trae/skills/halo-blog-writer/references/command-reference.md +++ /dev/null @@ -1,485 +0,0 @@ -# Halo CLI 命令参考 - -完整命令列表和详细用法。 - -## 认证相关(auth) - -```powershell -# 查看当前认证状态 -halo auth current - -# 列出所有配置 -halo auth profile list - -# 登录(Bearer Token) -halo auth login --profile local --url http://192.168.5.8:8090 --auth-type bearer --token - -# 登录(Basic Auth) -halo auth login --profile local --url http://192.168.5.8:8090 --auth-type basic --username admin --password - -# 移除配置 -halo auth remove local - -# 切换默认配置 -halo auth default production -``` - -## 文章管理(post) - -### 列出文章 - -```powershell -# 基本列表 -halo post list - -# 分页 -halo post list --page 1 --size 20 - -# JSON 格式(便于脚本处理) -halo post list --page 1 --size 20 --json - -# 按状态筛选 -halo post list --status published -halo post list --status draft -``` - -### 获取文章 - -```powershell -# 查看文章详情 -halo post get 019dbabc-86b9-74a0-a926-c25529309007 - -# JSON 格式 -halo post get 019dbabc-86b9-74a0-a926-c25529309007 --json -``` - -### 创建文章 - -```powershell -# 基本创建(草稿) -halo post create --title "我的第一篇文章" --content "# 标题\n\n内容..." - -# 创建并发布 -halo post create --title "我的第一篇文章" --content "# 标题" --publish true - -# 使用 HTML 格式 -halo post create --title "HTML 文章" --content "

标题

内容

" --raw-type html - -# 指定分类 -halo post create --title "文章" --content "内容" --category "技术" - -# 指定标签 -halo post create --title "文章" --content "内容" --tags "Python,教程" -``` - -### 更新文章 - -```powershell -# 更新标题 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --title "新标题" - -# 更新内容 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --content "# 新内容" - -# 同时更新标题和内容 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --title "新标题" --content "新内容" - -# 更新标签 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --tags "新标签1,新标签2" - -# 发布草稿 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --publish true - -# 取消发布 -halo post update 019dbabc-86b9-74a0-a926-c25529309007 --unpublish -``` - -### 删除文章 - -```powershell -# 删除文章(会提示确认) -halo post delete 019dbabc-86b9-74a0-a926-c25529309007 - -# 强制删除(跳过确认) -halo post delete 019dbabc-86b9-74a0-a926-c25529309007 --force -``` - -### 导出文章 - -```powershell -# 导出为 JSON -halo post export-json 019dbabc-86b9-74a0-a926-c25529309007 - -# 导出到指定文件 -halo post export-json 019dbabc-86b9-74a0-a926-c25529309007 --output ./backup/post.json - -# 导出为 Markdown -halo post export-markdown 019dbabc-86b9-74a0-a926-c25529309007 - -# 导出到指定文件 -halo post export-markdown 019dbabc-86b9-74a0-a926-c25529309007 --output ./backup/post.md -``` - -### 导入文章 - -```powershell -# 从 JSON 文件导入 -halo post import-json --file ./backup/post.json - -# 从 JSON 字符串导入 -halo post import-json --raw '{"post": {...}, "content": "..."}' - -# 从 Markdown 文件导入 -halo post import-markdown --file ./backup/post.md - -# 导入并发布 -halo post import-markdown --file ./post.md --publish true -``` - -### 文章标签管理 - -```powershell -# 列出所有标签 -halo post tag list - -# 创建标签 -halo post tag create --name "Python" --slug "python" --color "#3776AB" - -# 更新标签名称 -halo post tag update python --name "Python编程" - -# 更新标签颜色 -halo post tag update python --color "#3776AB" - -# 删除标签 -halo post tag delete python - -# 查看标签下的文章数量 -halo post tag list --verbose -``` - -### 文章分类管理 - -```powershell -# 列出所有分类 -halo post category list - -# 创建分类 -halo post category create --name "技术" --slug "tech" --priority 10 - -# 更新分类 -halo post category update tech --name "技术分享" --priority 5 - -# 删除分类 -halo post category delete tech - -# 设置分类优先级(数字越大排序越靠前) -halo post category update tech --priority 10 -``` - -### 为文章设置标签和分类 - -```powershell -# 创建文章时设置标签 -halo post create --title "标题" --content "内容" --tags "Python,教程" - -# 创建文章时设置分类 -halo post create --title "标题" --content "内容" --category "技术" - -# 同时设置标签和分类 -halo post create --title "标题" --content "内容" --tags "AI,教程" --category "技术" - -# 更新文章的标签 -halo post update --tags "新标签1,新标签2" - -# 更新文章的分类 -halo post update --category "技术分享" - -# 清除文章的标签 -halo post update --tags "" - -# 清除文章的分类 -halo post update --category "" -``` - -### 查询文章标签和分类 - -```powershell -# 查看某篇文章的标签(JSON 格式) -halo post get --json | Select-String -Pattern "tags" - -# 列出所有带有特定标签的文章 -halo post list --page 1 --size 100 --json | ConvertFrom-Json | Where-Object { - $_.spec.tags -contains "AI智能体" -} - -# 列出所有带有特定分类的文章 -halo post list --page 1 --size 100 --json | ConvertFrom-Json | Where-Object { - $_.spec.categories -contains "技术深潜" -} -``` - -### 批量修改标签 - -```powershell -# 为所有无标签的文章添加标签 -$posts = halo post list --page 1 --size 100 --json | ConvertFrom-Json -foreach ($post in $posts.items) { - if ($post.spec.tags.Count -eq 0) { - halo post update $post.name --tags "技术深潜" - Write-Host "已为 '$($post.spec.title)' 添加标签" - } -} - -# 将所有文章从"旧标签"迁移到"新标签" -$posts = halo post list --page 1 --size 100 --json | ConvertFrom-Json -foreach ($post in $posts.items) { - if ($post.spec.tags -contains "旧标签") { - $newTags = $post.spec.tags -replace "旧标签", "新标签" - halo post update $post.name --tags ($newTags -join ",") - } -} -``` - -### 博客现有标签参考 - -| 显示名称 | Slug | 颜色 | -|---------|------|------| -| 协会动态 | xie-hui-dong-tai | #B8E986 | -| 5分钟速览 | 5fen-zhong-su-lan | #50E3C2 | -| 技术深潜 | ji-shu-shen-qian | #4A90E2 | -| 校园AI | xiao-yuan-ai | #9013FE | -| 竞赛指南 | jing-sai-zhi-nan | #BD10E0 | -| 项目实战 | xiang-mu-shi-zhan | #417505 | -| 零基础入门 | ling-ji-chu-ru-men | #7ED321 | -| 开源框架 | kai-yuan-kuang-jia | #8B572A | -| 模型轻量化 | mo-xing-qing-liang-hua | #F8E71C | -| 隐私计算 | yin-si-ji-suan | #F5A623 | -| AI智能体 | aizhi-neng-ti | #D0021B | - -### 博客现有分类参考 - -| 显示名称 | Slug | -|---------|------| -| 行业望远镜 | xing-ye-wang-yuan-jing | -| 协会进行时 | xie-hui-jin-xing-shi | -| 校园AI日记 | xiao-yuan-airi-ji | -| 竞赛能量站 | jing-sai-neng-liang-zhan | -| 成长实验室 | cheng-chang-shi-yan-shi | -| 智能体前沿 | zhi-neng-ti-qian-yan | -| 默认分类 | default | - -## 单页管理(single-page) - -```powershell -# 列出单页 -halo single-page list - -# 查看单页 -halo single-page get - -# 创建单页 -halo single-page create --title "关于我们" --content "公司介绍..." - -# 更新单页 -halo single-page update about --title "关于我们" --content "新内容" - -# 删除单页 -halo single-page delete about --force - -# 导出/导入 -halo single-page export-markdown --output ./page.md -halo single-page import-markdown --file ./page.md -``` - -## 搜索内容(search) - -```powershell -# 搜索公开文章 -halo search "Python 教程" - -# 搜索并限制结果 -halo search "教程" --limit 10 - -# 搜索并显示详细信息 -halo search "教程" --json -``` - -## 插件管理(plugin) - -```powershell -# 列出插件 -halo plugin list - -# 获取插件详情 -halo plugin get - -# 升级插件 -halo plugin upgrade - -# 升级所有插件 -halo plugin upgrade --all - -# 启用/禁用插件 -halo plugin enable -halo plugin disable -``` - -## 主题管理(theme) - -```powershell -# 列出主题 -halo theme list - -# 获取主题详情 -halo theme get - -# 升级主题 -halo theme upgrade - -# 切换主题 -halo theme active -``` - -## 附件管理(attachment) - -```powershell -# 列出附件 -halo attachment list - -# 上传附件 -halo attachment upload --file ./image.png - -# 删除附件 -halo attachment delete -``` - -## 备份管理(backup) - -```powershell -# 列出备份 -halo backup list - -# 创建备份 -halo backup create - -# 下载备份 -halo backup download --output ./backup.zip - -# 删除备份 -halo backup delete -``` - -## 动态管理(moment) - -```powershell -# 列出动态 -halo moment list - -# 创建动态 -halo moment create --content "今天写了两篇博客!" - -# 删除动态 -halo moment delete -``` - -## 评论管理(comment) - -```powershell -# 列出评论 -halo comment list - -# 列出未审核评论 -halo comment list --status pending - -# 回复评论 -halo comment reply --content "感谢您的评论!" - -# 审核评论 -halo comment approve -halo comment reject - -# 删除评论 -halo comment delete -``` - -## 通知管理(notification) - -```powershell -# 列出通知 -halo notification list - -# 标记已读 -halo notification mark-read - -# 全部标记已读 -halo notification mark-all-read - -# 删除通知 -halo notification delete -``` - -## Shell 补全 - -```powershell -# bash -eval "$(halo completion bash)" - -# zsh -eval "$(halo completion zsh)" - -# PowerShell(自动添加到配置文件) -halo completion powershell >> $PROFILE -``` - -## 输出格式 - -### 普通输出 - -```powershell -# 表格格式(默认) -halo post list - -# JSON 格式 -halo post list --json - -# YAML 格式 -halo post list --yaml -``` - -### 颜色输出 - -```powershell -# 彩色输出(默认) -halo post list --color - -# 禁用颜色 -halo post list --no-color -``` - -## 超时和重试 - -```powershell -# 设置超时(秒) -halo post list --timeout 60 - -# 重试次数 -halo post list --retry 3 -``` - -## 调试 - -```powershell -# 显示调试信息 -halo --debug post list - -# 显示完整错误 -halo post create --title "Test" --content "Content" --verbose - -# 查看版本 -halo --version - -# 查看帮助 -halo --help -halo post --help -``` diff --git a/.trae/specs/image-upload/checklist.md b/.trae/specs/image-upload/checklist.md deleted file mode 100644 index be48086..0000000 --- a/.trae/specs/image-upload/checklist.md +++ /dev/null @@ -1,37 +0,0 @@ -# 图片上传功能检查清单 - -## 代码实现检查 - -- [x] `src/utils/image.ts` 文件创建完成 -- [x] `extractImageReferences()` 函数正确实现 -- [x] `getAbsolutePath()` 函数正确实现 -- [x] `replaceImagePaths()` 函数正确实现 -- [x] `src/service/image-uploader.ts` 文件创建完成 -- [x] `uploadImage()` 方法正确调用 Halo API -- [x] `uploadImages()` 方法正确实现批量上传 -- [x] 图片缓存机制正确实现 - -## 设置界面检查 - -- [x] 设置界面包含图片上传开关 -- [x] 设置界面包含上传路径配置 -- [x] 设置数据正确保存和加载 - -## 集成检查 - -- [x] 发布流程正确调用图片上传 -- [x] 图片路径正确替换为远程 URL -- [x] 上传失败时正确处理错误 - -## 国际化检查 - -- [x] 英文文案添加完成 -- [x] 简体中文文案添加完成 -- [x] 繁体中文文案添加完成 - -## 测试检查 - -- [x] 编译通过无错误 -- [x] 插件文件正确复制到 Obsidian 插件目录 -- [x] 基本功能测试通过 -- [x] 图片上传测试通过 \ No newline at end of file diff --git a/.trae/specs/image-upload/spec.md b/.trae/specs/image-upload/spec.md deleted file mode 100644 index d329988..0000000 --- a/.trae/specs/image-upload/spec.md +++ /dev/null @@ -1,107 +0,0 @@ -# 图片上传功能规格说明 - -## Why -当前 obsidian-halo 插件无法将本地图片上传到 Halo 媒体库,导致发布的文章中的图片无法正常显示。这是一个核心功能缺失,直接影响使用体验。 - -## What Changes -- 新增图片检测模块:解析 Markdown 中的本地图片引用 -- 新增图片上传服务:调用 Halo 媒体 API 上传图片 -- 新增图片路径替换:自动将本地路径替换为 Halo 远程 URL -- 新增设置选项:允许用户开关自动上传功能 - -## Impact -- 影响规格: - - 发布功能:发布时自动处理图片 - - 设置功能:新增图片上传相关配置 -- 影响代码: - - 新增 `src/utils/image.ts`:图片处理工具函数 - - 新增 `src/service/image-uploader.ts`:图片上传服务 - - 修改 `src/service/index.ts`:集成图片上传到发布流程 - - 修改 `src/settings.ts`:添加图片上传设置项 - - 修改 `src/i18n/locales/*.json`:添加国际化文案 - -## ADDED Requirements - -### Requirement: 图片自动上传 -系统 SHALL 在发布文档时自动检测并上传本地图片到 Halo 媒体库。 - -#### Scenario: 检测到本地图片 -- **WHEN** 用户执行发布命令且文档中包含本地图片(如 `assets/image.png`) -- **THEN** 系统自动上传图片到 Halo 并将文档中的路径替换为远程 URL - -#### Scenario: 图片已上传过 -- **WHEN** 系统检测到相同图片(基于文件路径和修改时间) -- **THEN** 直接使用已上传的 URL,不重复上传 - -#### Scenario: 上传失败 -- **WHEN** 图片上传过程中发生错误 -- **THEN** 系统记录错误并继续处理其他图片,已失败的图片保持原路径 - -### Requirement: 图片路径识别 -系统 SHALL 支持识别以下格式的图片引用: -- `![alt](assets/image.png)` - 普通 Markdown 图片语法 -- `![alt](./assets/image.png)` - 相对路径 -- `![alt](/assets/image.png)` - 绝对路径(Vault 根目录) -- `![](data:image/png;base64,...)` - Base64 内联图片(可选支持) - -### Requirement: 上传配置选项 -系统 SHALL 提供以下配置选项: -- 开关:是否启用自动图片上传 -- 媒体库路径:上传到 Halo 的哪个媒体目录 -- 保留原图:是否保留本地路径作为备选 - -## MODIFIED Requirements - -### Requirement: 发布流程 -原发布流程: -1. 读取文档内容 -2. 解析 frontmatter -3. 提交到 Halo API - -修改后的发布流程: -1. 读取文档内容 -2. 解析 frontmatter -3. **检测并上传本地图片** -4. **替换图片路径为远程 URL** -5. 提交到 Halo API - -## REMOVED Requirements - -无 - ---- - -## 技术实现细节 - -### Halo 媒体上传 API - -``` -POST /apis/api.console.halo.run/v1alpha1/attachments/upload -Content-Type: multipart/form-data - -请求体: -- file: 二进制文件数据 -- groupName: 媒体分组名称(可选) - -响应: -{ - "name": "uuid-filename.png", - "url": "/upload/2024/01/abc.png", - "type": "IMAGE", - "size": 12345 -} -``` - -### 图片检测正则 -```typescript -const imageRegex = /!\[([^\]]*)\]\(([^)]+)\)/g; -``` - -### 配置项 -```typescript -interface ImageUploadSettings { - enabled: boolean; // 是否启用 - uploadPath: string; // 上传路径(空表示根目录) - preserveOriginal: boolean; // 保留原路径 -} -``` \ No newline at end of file diff --git a/.trae/specs/image-upload/tasks.md b/.trae/specs/image-upload/tasks.md deleted file mode 100644 index 5acf707..0000000 --- a/.trae/specs/image-upload/tasks.md +++ /dev/null @@ -1,58 +0,0 @@ -# 图片上传功能任务清单 - -## 任务列表 - -- [x] 任务 1:创建图片处理工具模块 `src/utils/image.ts` - - [ ] 子任务 1.1:实现 `extractImageReferences()` 函数,检测 Markdown 中的图片引用 - - [ ] 子任务 1.2:实现 `getAbsolutePath()` 函数,将相对路径转为绝对路径 - - [ ] 子任务 1.3:实现 `replaceImagePaths()` 函数,替换图片路径 - -- [x] 任务 2:创建图片上传服务 `src/service/image-uploader.ts` - - [ ] 子任务 2.1:实现 `uploadImage()` 方法,调用 Halo 媒体 API 上传单个图片 - - [ ] 子任务 2.2:实现 `uploadImages()` 方法,批量上传图片并返回映射表 - - [ ] 子任务 2.3:实现缓存机制,记录已上传图片的 URL - -- [x] 任务 3:更新设置界面 `src/settings.ts` - - [x] 子任务 3.1:在设置界面添加图片上传开关 - - [x] 子任务 3.2:添加上传路径配置项 - - [x] 子任务 3.3:更新设置数据结构 - -- [x] 任务 5:添加国际化文案 `src/i18n/locales/*.json` - - [x] 子任务 5.1:添加英文文案 - - [x] 子任务 5.2:添加简体中文文案 - - [x] 子任务 5.3:添加繁体中文文案 - -- [x] 任务 4:集成图片上传到发布流程 `src/service/index.ts` - - [x] 子任务 4.1:在 `publishPost()` 方法中调用图片上传 - - [x] 子任务 4.2:处理上传后的路径替换 - - [x] 子任务 4.3:添加错误处理和用户提示 - -- [ ] 任务 5:添加国际化文案 `src/i18n/locales/*.json` - - [ ] 子任务 5.1:添加英文文案 - - [ ] 子任务 5.2:添加简体中文文案 - - [ ] 子任务 5.3:添加繁体中文文案 - -- [x] 任务 6:编译和测试 - - [x] 子任务 6.1:运行 `pnpm build` 确保编译通过 - - [x] 子任务 6.2:复制编译产物到插件目录 - - [x] 子任务 6.3:手动测试图片上传功能 - ---- - -## 任务依赖关系 - -- [任务 1] 和 [任务 2] 可并行开发 -- [任务 3] 可在 [任务 2] 完成后开始 -- [任务 4] 依赖 [任务 1] 和 [任务 2] 完成 -- [任务 5] 可与其他任务并行进行 -- [任务 6] 需要所有其他任务完成后执行 - ---- - -## 实现顺序建议 - -1. 先完成 [任务 1] 和 [任务 2](核心功能) -2. 再完成 [任务 3](配置界面) -3. 然后完成 [任务 4](集成) -4. [任务 5] 和 [任务 3] 可同时进行 -5. 最后 [任务 6](测试) \ No newline at end of file diff --git a/.trae/specs/markdown-import/checklist.md b/.trae/specs/markdown-import/checklist.md deleted file mode 100644 index 5963a2c..0000000 --- a/.trae/specs/markdown-import/checklist.md +++ /dev/null @@ -1,33 +0,0 @@ -# Markdown 文件导入功能检查清单 - -## 代码实现检查 - -- [ ] `src/commands/import-markdown.ts` 文件创建完成 -- [ ] 文件选择器正确实现(仅显示 .md 文件) -- [ ] 文件读取和解析逻辑正确 -- [ ] 文章创建逻辑正确调用 Halo API -- [ ] `src/modals/file-preview-modal.ts` 文件创建完成 -- [ ] 文件内容预览正确显示 -- [ ] frontmatter 信息正确显示 -- [ ] 导入/取消按钮功能正常 -- [ ] "导入后发布"选项功能正常 - -## 集成检查 - -- [ ] `src/main.ts` 中命令注册完成 -- [ ] 命令名称符合国际化规范 -- [ ] 命令在命令面板中可见 - -## 国际化检查 - -- [ ] 英文文案添加完成 -- [ ] 简体中文文案添加完成 -- [ ] 繁体中文文案添加完成 - -## 测试检查 - -- [ ] 编译通过无错误 -- [ ] 插件文件正确复制到 Obsidian 插件目录 -- [ ] 文件选择器测试通过 -- [ ] 导入功能测试通过(创建草稿) -- [ ] 导入功能测试通过(导入并发布) \ No newline at end of file diff --git a/.trae/specs/markdown-import/spec.md b/.trae/specs/markdown-import/spec.md deleted file mode 100644 index 4658457..0000000 --- a/.trae/specs/markdown-import/spec.md +++ /dev/null @@ -1,96 +0,0 @@ -# Markdown 文件导入功能规格说明 - -## Why - -当前 Obsidian Halo 插件只支持从 Obsidian 发布文章到 Halo,但没有提供从本地 Markdown 文件导入创建文章的功能。用户可能希望: -1. 直接从文件系统导入已存在的 Markdown 文件 -2. 从其他平台迁移文章时批量导入 -3. 快速创建新文章时选择本地文件而非在编辑器中编写 - -## What Changes - -- 新增命令:直接从 Markdown 文件导入创建 Halo 文章 -- 新增文件选择器:支持选择本地 .md 文件 -- 新增预览功能:导入前预览文件内容和 frontmatter -- 新增选项:导入时是否立即发布 - -## Impact - -- Affected specs: - - 文章发布功能:提供新的文章创建入口 - - 设置功能:可能需要添加导入相关配置 -- Affected code: - - 新增 `src/commands/import-markdown.ts`:导入命令 - - 新增 `src/modals/file-preview-modal.ts`:文件预览弹窗 - - 修改 `src/main.ts`:注册新命令 - - 修改 `src/i18n/locales/*.json`:国际化文案 - -## ADDED Requirements - -### Requirement: 从 Markdown 文件导入 - -系统 SHALL 提供从本地 Markdown 文件导入创建 Halo 文章的功能。 - -#### Scenario: 选择文件导入 -- **WHEN** 用户执行"从 Markdown 文件导入"命令 -- **THEN** 系统显示文件选择器,允许用户选择 .md 文件 - -#### Scenario: 预览和确认 -- **WHEN** 用户选择了一个 Markdown 文件 -- **THEN** 系统显示文件内容预览(标题、内容摘要、frontmatter) -- **AND** 显示"导入"和"取消"按钮 - -#### Scenario: 导入成功 -- **WHEN** 用户点击"导入"按钮 -- **THEN** 系统读取文件内容 -- **AND** 解析 frontmatter 和正文 -- **AND** 调用 Halo API 创建文章 -- **AND** 显示导入成功通知 - -#### Scenario: 导入选项 -- **WHEN** 用户可以勾选"导入后立即发布"选项 -- **THEN** 文章创建后自动发布 - -## MODIFIED Requirements - -### Requirement: 命令面板扩展 - -现有命令列表扩展,新增: -- `Halo: 从 Markdown 文件导入` - 打开文件选择器进行导入 - -## REMOVED Requirements - -无 - ---- - -## 技术实现细节 - -### Obsidian 文件选择器 API - -```typescript -const filePicker = app.createFilePicker(); -filePicker.setFilter(file => file.extension === 'md'); -filePicker.onChange = async (files) => { - // 处理选中的文件 -}; -filePicker.open(); -``` - -### 文件读取 - -```typescript -const content = await app.vault.read(file); -``` - -### Frontmatter 解析 - -参考现有的 `src/utils/yaml.ts` 实现,或使用 gray-matter 库。 - -### API 端点 - -``` -POST /apis/uc.api.content.halo.run/v1alpha1/posts -``` - -请求体格式与发布功能相同。 \ No newline at end of file diff --git a/.trae/specs/markdown-import/tasks.md b/.trae/specs/markdown-import/tasks.md deleted file mode 100644 index 7198285..0000000 --- a/.trae/specs/markdown-import/tasks.md +++ /dev/null @@ -1,47 +0,0 @@ -# Markdown 文件导入功能任务清单 - -## 任务列表 - -- [ ] 任务 1:创建导入命令模块 `src/commands/import-markdown.ts` - - [ ] 子任务 1.1:实现 Obsidian 文件选择器调用 - - [ ] 子任务 1.2:实现文件读取和解析 - - [ ] 子任务 1.3:实现文章创建逻辑 - -- [ ] 任务 2:创建文件预览弹窗 `src/modals/file-preview-modal.ts` - - [ ] 子任务 2.1:显示文件内容预览 - - [ ] 子任务 2.2:显示 frontmatter 信息 - - [ ] 子任务 2.3:添加导入/取消按钮 - - [ ] 子任务 2.4:添加"导入后发布"选项 - -- [ ] 任务 3:更新主程序注册命令 `src/main.ts` - - [ ] 子任务 3.1:导入新命令模块 - - [ ] 子任务 3.2:注册"从 Markdown 文件导入"命令 - -- [ ] 任务 4:添加国际化文案 - - [ ] 子任务 4.1:更新英文文案 `src/i18n/locales/en.json` - - [ ] 子任务 4.2:更新简体中文文案 `src/i18n/locales/zh-cn.json` - - [ ] 子任务 4.3:更新繁体中文文案 `src/i18n/locales/zh-tw.json` - -- [ ] 任务 5:编译和测试 - - [ ] 子任务 5.1:运行 `pnpm build` 确保编译通过 - - [ ] 子任务 5.2:复制编译产物到插件目录 - - [ ] 子任务 5.3:手动测试导入功能 - ---- - -## 任务依赖关系 - -- [任务 2] 和 [任务 3] 可并行开发 -- [任务 1] 依赖 [任务 2] 完成 -- [任务 4] 可与任何任务并行 -- [任务 5] 需要 [任务 1]、[任务 3] 完成 - ---- - -## 实现顺序建议 - -1. 先完成 [任务 2](文件预览弹窗)- 核心 UI -2. 再完成 [任务 1](导入命令模块)- 核心逻辑 -3. 然后完成 [任务 3](注册命令) -4. [任务 4] 可与 [任务 1] 并行 -5. 最后 [任务 5](测试) \ No newline at end of file diff --git a/.trae/specs/sync-status-panel/checklist.md b/.trae/specs/sync-status-panel/checklist.md deleted file mode 100644 index 2b60025..0000000 --- a/.trae/specs/sync-status-panel/checklist.md +++ /dev/null @@ -1,36 +0,0 @@ -# 同步状态面板和历史功能检查清单 - -## 代码实现检查 - -- [x] `src/views/sync-status-view.ts` 文件创建完成 -- [x] 扫描已发布文章逻辑正确 -- [x] 文章列表显示正确(标题、slug、状态) -- [x] 快速操作按钮功能正常(更新、拉取) -- [x] `src/modals/sync-history-modal.ts` 文件创建完成 -- [x] 同步历史记录显示正确 -- [x] 清除历史功能正常 - -## 集成检查 - -- [x] `src/main.ts` 中命令注册完成 -- [x] 侧边栏图标正确显示 -- [x] 命令面板中可见相关命令 - -## 国际化检查 - -- [x] 中文文案添加完成 -- [x] 英文文案添加完成 -- [x] 繁体中文文案添加完成 - -## 文档检查 - -- [x] `docs/usage-guide.md` 创建完成 -- [x] 所有命令都有使用说明 -- [x] 常见问题解答包含在内 - -## 测试检查 - -- [x] 编译通过无错误 -- [x] 插件文件正确复制到 Obsidian 插件目录 -- [ ] 同步状态面板测试通过(需要手动测试) -- [ ] 同步历史功能测试通过(需要手动测试) \ No newline at end of file diff --git a/.trae/specs/sync-status-panel/spec.md b/.trae/specs/sync-status-panel/spec.md deleted file mode 100644 index 70978ed..0000000 --- a/.trae/specs/sync-status-panel/spec.md +++ /dev/null @@ -1,93 +0,0 @@ -# 同步状态面板功能规格说明 - -## Why - -当前插件的功能分散在多个命令中,用户难以直观地了解: -1. 哪些文章已发布到 Halo -2. 文章的同步状态如何 -3. 最近的操作历史和结果 - -需要一个集中的状态管理面板,让用户一眼就能看到所有同步状态。 - -## What Changes - -- 新增**同步状态面板**:在 Obsidian 侧边栏显示已发布文章列表及状态 -- 显示文章同步时间戳、发布状态、本地/Halo 版本差异 -- 支持快速操作:更新、拉取、删除 -- 新增使用指南文档 - -## Impact - -- Affected specs: - - 状态管理和日志 - - 命令面板增强 -- Affected code: - - 新增 `src/commands/sync-status-panel.ts`:侧边栏面板 - - 新增 `src/modals/sync-history-modal.ts`:同步历史弹窗 - - 修改 `src/main.ts`:注册面板命令 - - 新增 `docs/usage-guide.md`:使用指南 - -## ADDED Requirements - -### Requirement: 同步状态面板 - -系统 SHALL 提供一个侧边栏面板,显示已发布到 Halo 的文章列表及其状态。 - -#### Scenario: 打开面板 -- **WHEN** 用户执行"打开同步状态面板"命令 -- **THEN** 在侧边栏显示面板,包含已发布文章列表 - -#### Scenario: 显示文章状态 -- **WHEN** 面板加载时 -- **THEN** 显示所有已发布文章的:标题、slug、发布状态、同步时间 - -#### Scenario: 快速操作 -- **WHEN** 用户点击文章的操作按钮 -- **THEN** 可以快速执行:更新、拉取、删除操作 - -#### Scenario: 同步历史 -- **WHEN** 用户点击"同步历史"按钮 -- **THEN** 显示最近的操作记录 - -### Requirement: 使用指南 - -系统 SHALL 提供一份详细的使用指南,说明所有功能的位置和使用方法。 - -## MODIFIED Requirements - -无 - -## REMOVED Requirements - -无 - ---- - -## 技术实现细节 - -### Obsidian 面板 API - -```typescript -// 创建侧边栏面板 -this.addRibbonIcon("sync-icon", "同步状态", (evt) => { - this.openSyncStatusPanel(); -}); -``` - -### 扫描已发布文章 - -通过扫描 Vault 中的所有文件,查找 frontmatter 中包含 `halo.name` 的文件。 - -### 状态图标 - -- ✅ 已同步(本地和 Halo 版本一致) -- ⏳ 待同步(本地有更新) -- ⚠️ 有冲突(两边都有更新) - ---- - -## 实现优先级 - -1. 同步状态面板(核心 UI) -2. 同步历史记录(辅助功能) -3. 使用指南文档(用户教育) \ No newline at end of file diff --git a/.trae/specs/sync-status-panel/tasks.md b/.trae/specs/sync-status-panel/tasks.md deleted file mode 100644 index b9ec8c7..0000000 --- a/.trae/specs/sync-status-panel/tasks.md +++ /dev/null @@ -1,42 +0,0 @@ -# 同步状态面板和历史功能任务清单 - -## 任务列表 - -- [x] 任务 1:创建同步状态面板 `src/commands/sync-status-panel.ts` - - [x] 子任务 1.1:扫描 Vault 中已发布的文章 - - [x] 子任务 1.2:显示文章列表和状态 - - [x] 子任务 1.3:添加快速操作按钮 - -- [x] 任务 2:创建同步历史弹窗 `src/modals/sync-history-modal.ts` - - [x] 子任务 2.1:显示最近的操作记录 - - [x] 子任务 2.2:支持查看详情和清除历史 - -- [x] 任务 3:更新主程序 `src/main.ts` - - [x] 子任务 3.1:注册打开面板命令 - - [x] 子任务 3.2:添加侧边栏图标 - - [x] 子任务 3.3:添加打开历史命令 - -- [x] 任务 4:添加国际化文案 - - [x] 子任务 4.1:更新 zh-cn.json - - [x] 子任务 4.2:更新 en.json - - [x] 子任务 4.3:更新 zh-tw.json - -- [x] 任务 5:编写使用指南 `docs/usage-guide.md` - - [x] 子任务 5.1:概述所有功能 - - [x] 子任务 5.2:命令列表和使用方法 - - [x] 子任务 5.3:常见问题解答 - -- [x] 任务 6:编译和测试 - - [x] 子任务 6.1:运行 `pnpm build` 确保编译通过 - - [x] 子任务 6.2:复制编译产物到插件目录 - ---- - -## 实现顺序总结 - -1. ✅ 完成 [任务 1](同步状态面板)- 核心 UI -2. ✅ 完成 [任务 2](同步历史)- 辅助功能 -3. ✅ 完成 [任务 3](注册命令) -4. ✅ 完成 [任务 4](国际化) -5. ✅ 完成 [任务 5](使用指南) -6. ✅ 完成 [任务 6](测试) \ No newline at end of file diff --git a/temp-agent-os-v2.md b/temp-agent-os-v2.md deleted file mode 100644 index b1bd336..0000000 --- a/temp-agent-os-v2.md +++ /dev/null @@ -1,1035 +0,0 @@ -# 学习 Agent,越学越像在重新理解操作系统 - -## 引言 - -2024年,当我第一次深入研究 Agent 框架时,产生了一个奇怪的感觉:**这不就是重新发明操作系统吗?** - -进程、线程、系统调用、内存管理、文件系统、设备驱动……这些在大学操作系统课程里让我头疼不已的概念,在 Agent 世界里全都以另一种面貌出现了。只不过是换了层皮——从 CPU 和内存,变成了 Token 和推理时间。 - -花了半年时间,终于把这条暗线理清楚了。今天这篇文章,就是把我看到的、想到的、踩过的坑,全都记录下来。希望你读完之后,能和我有同样的感受:**"原来如此!"** - ---- - -## 壹、从一个痛点说起 - -先问个问题:你有没有这种感觉—— - -学 Agent 的时候,总有一堆新概念要记:ReAct、CoT、ToV、Plan-and-Execute、MRKL、Harness、Orchestrator、Sub-Agent、Context Window、Function Calling…… - -学完一遍,脑子里全是碎片。 - -但有一天,我突然发现:这些概念我都见过。 - -不是在 AI 论文里,而是在**操作系统教科书**里。 - -进程隔离 = Agent 隔离 -系统调用 = Tool Use / Function Calling -虚拟内存分页 = Context Compression -文件系统挂载 = RAG -进程调度器 = Orchestrator -信号量/锁 = Agent 之间的同步机制 - -**操作系统花了几十年解决的问题,Agent 时代正在用同样的思路重新解决一遍。** - -这不是巧合。这是历史在押韵。 - ---- - -## 贰、进程与线程 → Sub-Agent - -### 2.1 操作系统里的进程和线程 - -在操作系统里,**进程(Process)是资源分配的基本单位**,**线程(Thread)是 CPU 调度的基本单位**。 - -这意味着: - -- 每个进程有自己独立的地址空间(内存)、文件句柄、系统资源 -- 同一进程里的线程共享这些资源,但各自有独立的栈和寄存器 -- 进程间通信(IPC)需要显式的机制:管道、消息队列、共享内存、Socket…… -- 线程间通信相对简单,因为共享内存——但也带来了**竞争条件(Race Condition)**的风险 - -这段话看起来枯燥,但让我举个例子你就明白了。 - -**场景**:一个 Web 服务器(进程),处理多个请求(线程)。 - -``` -进程(Web 服务器) -├── 线程1:处理用户A的请求 -├── 线程2:处理用户B的请求 -├── 线程3:处理用户C的请求 -└── ...(更多线程) - │ - ├── 共享资源:数据库连接池、缓存 - ├── 独立资源:每个线程自己的栈、局部变量 - └── 潜在问题:多个线程同时修改同一个变量? -``` - -这就是经典的**并发问题**。如果线程1和线程2同时往数据库写入,而数据库连接池只有5个连接——两个线程抢同一个连接,就会出问题。 - -### 2.2 Agent 世界里的进程和线程 - -把上面这个场景映射到 Agent 世界: - -- **进程 ≈ 独立的 Agent 实例**(或者一个 Agent 系统) -- **线程 ≈ Sub-Agent / Worker** - -```python -# 操作系统视角 -class Process: - def __init__(self): - self.memory = Memory() # 独立地址空间 - self.files = FileHandles() - self.threads = [] - -# Agent 视角 -class Agent: - def __init__(self, model, tools): - self.memory = Memory() # Agent 的"内存" - self.context = [] # Agent 的"上下文" - self.sub_agents = [] # Sub-Agent 们 -``` - -当我们在设计多 Agent 系统时,几乎立刻遇到了和操作系统一样的问题: - -**问题1:谁能访问谁的状态?** - -如果 Agent A 在处理用户查询,Agent B 在做代码执行,它们能共享同一个 Memory 吗? - -- 共享 → 效率高,但有数据竞争风险 -- 隔离 → 安全,但信息传递成本高 - -**问题2:共享上下文带来竞争条件** - -``` -时间线: -T1: Agent A 读取 shared_memory["result"] = None -T2: Agent A 开始计算 -T3: Agent B 读取 shared_memory["result"] = None (还是空的!) -T4: Agent A 计算完成,写入 shared_memory["result"] = "答案A" -T5: Agent B 计算完成,写入 shared_memory["result"] = "答案B" (覆盖了!) -``` - -这就是经典的 **ABA 问题**——Agent B 读到的是过期的数据。 - -**问题3:死锁** - -``` -场景: -- Agent A 等 Agent B 的结果 -- Agent B 等 Agent A 的结果 -→ 系统卡死 -``` - -这些问题,操作系统花了几十年才解决。Agent 时代正在用同样的思路重新应对。 - -### 2.3 解决方案:操作系统用过的那些招 - -**方案一:消息传递(Message Passing)** - -操作系统中,进程之间通过**消息队列**通信,不共享内存: - -```python -# 操作系统:进程间消息传递 -class MessageQueue: - def send(self, pid, message): - self.queue[pid].append(message) - - def receive(self, pid): - return self.queue[pid].pop(0) - -# Agent 世界:Agent 间消息传递 -class AgentMessageBus: - def send(self, from_agent, to_agent, message): - self.queue[to_agent].append({ - "from": from_agent, - "content": message, - "timestamp": time.time() - }) - - def receive(self, agent_id): - return self.queue[agent_id].pop(0) -``` - -**方案二:Actor 模型** - -这是分布式系统里很经典的模型,和 Agent 设计高度吻合: - -- 每个 Actor(Agent)有独立的"邮箱" -- Actor 之间不共享状态,通过消息通信 -- 消息是异步的,有 mailbox 缓冲 - -``` -Actor A ──消息──→ Actor B 的 Mailbox - ↑ │ - └──── 响应消息 ───────┘ -``` - -**方案三:锁和信号量** - -如果必须共享状态,就需要同步机制: - -```python -import asyncio - -class SharedContext: - def __init__(self): - self._lock = asyncio.Lock() - self._data = {} - - async def read(self, key): - async with self._lock: - return self._data.get(key) - - async def write(self, key, value): - async with self._lock: - self._data[key] = value -``` - -这和操作系统的**读写锁(Read-Write Lock)**完全是一个思路。 - -### 2.4 实战经验 - -我曾经在做一个多 Agent 协作系统时踩过坑: - -**场景**:一个 Researcher Agent 负责搜索信息,一个 Writer Agent 负责写文章,一个 Editor Agent 负责审核。 - -**问题**:三个 Agent 共享一个 `research_notes` 变量,Editor 在 Writer 还没写完时就读取了。 - -**解决**:参考操作系统的**生产者-消费者模式**,引入消息队列和状态机: - -``` -状态机: -WRITING → EDITING → APPROVED - ↑ - (等待 Writer 完成) -``` - ---- - -## 叁、系统调用 → Tool Use - -### 3.1 操作系统里的系统调用 - -这是最经典、最核心的类比。 - -在操作系统里,**用户程序不能直接访问硬件**。你写的代码运行在"用户态",硬件操作必须通过"系统调用"陷入"内核态",由操作系统代为执行。 - -``` -用户程序(用户态) - │ - │ write() 系统调用 - ↓ -内核(内核态) - │ - │ 操作硬件 - ↓ -磁盘 / 网卡 / 显示器 -``` - -为什么要这样?**安全**。 - -如果任何程序都能直接写磁盘,那恶意软件可以随意破坏文件系统。 -如果任何程序都能直接发网络包,那整个互联网都不安全。 - -系统调用就是在**权限边界上打一个受控的洞**: - -- 能力从这个洞里流进来(程序可以读写文件、发送网络请求) -- 风险也从这个洞里被隔住(内核审核每个请求,确保安全) - -典型的系统调用: - -| 操作 | 系统调用 | -|------|---------| -| 读文件 | `read(fd, buffer, count)` | -| 写文件 | `write(fd, buffer, count)` | -| 创建进程 | `fork()` / `exec()` | -| 分配内存 | `mmap()` / `brk()` | -| 网络通信 | `socket()` / `connect()` / `send()` | -| 访问设备 | `ioctl()` | - -### 3.2 Agent 世界里的系统调用 - -**Function Calling / Tool Use** 就是 Agent 的系统调用。 - -Agent(LLM)本身不能搜索网页、不能执行代码、不能访问数据库。它只能"推理"——根据输入生成输出。 - -要完成真实任务,必须通过 **Function Calling** 交给外部工具(Harness)执行: - -``` -Agent(LLM 推理) - │ - │ Function Call: search_web("...") - ↓ -Harness(执行环境) - │ - │ 执行工具 - ↓ -搜索引擎 / 代码执行器 / 数据库 -``` - -和操作系统一样,这也是一个**权限边界**: - -- LLM 本身不知道也不需要知道工具怎么实现的 -- Harness 负责审核、执行、返回结果 -- 安全策略在 Harness 层控制(哪些工具可用?频率限制?) - -### 3.3 详细对比 - -| 维度 | 操作系统 | Agent 系统 | -|------|---------|-----------| -| 执行主体 | CPU | LLM | -| 调用方式 | syscall | Function Calling / Tool Use | -| 执行环境 | 内核态 | Harness | -| 权限控制 | 用户ID、Capabilities | API Key、权限配置 | -| 资源隔离 | 进程/容器 | 沙箱、API 限制 | -| 审计 | 系统日志 | 调用日志、成本追踪 | - -### 3.4 Harness:Agent 的"内核" - -在很多 Agent 框架里,Harness 是核心组件: - -```python -class Harness: - def __init__(self): - self.tools = {} # 注册的工具 - self.policies = {} # 安全策略 - self.resources = {} # 资源配额 - - async def execute(self, tool_call: ToolCall) -> ToolResult: - # 1. 权限检查 - if not self._check_permission(tool_call): - raise PermissionError("Not authorized") - - # 2. 资源配额检查 - if not self._check_quota(tool_call): - raise ResourceExhaustedError("Quota exceeded") - - # 3. 执行工具 - tool = self.tools[tool_call.name] - result = await tool.execute(tool_call.args) - - # 4. 审计日志 - self._log(tool_call, result) - - return result -``` - -这不就是操作系统的系统调用处理逻辑吗? - -```c -// 操作系统内核处理系统调用 -asmlinkage long sys_write(unsigned int fd, const char __user *buf, size_t count) { - // 1. 权限检查(capable(CAP_SYS_WRITE)?) - if (!capable(CAP_SYS_WRITE)) - return -EPERM; - - // 2. 参数验证(buf 是否用户空间指针?) - if (!access_ok(buf, count)) - return -EFAULT; - - // 3. 执行业务逻辑 - return vfs_write(fd, buf, count, &pos); - - // 4. 审计 - audit_syscall_entry(...); -} -``` - -### 3.5 工具定义的"ABI" - -操作系统有 ABI(Application Binary Interface)——函数调用的二进制约定。 - -Function Calling 有类似的约定——**工具的 schema**: - -```json -{ - "name": "search_web", - "description": "搜索互联网获取信息", - "parameters": { - "type": "object", - "properties": { - "query": { - "type": "string", - "description": "搜索关键词" - }, - "max_results": { - "type": "integer", - "description": "最多返回结果数", - "default": 10 - } - }, - "required": ["query"] - } -} -``` - -这就是 Agent 的"系统调用接口"。LLM 看到的工具定义,就像程序员看到的 man page。 - ---- - -## 肆、Cache / 虚拟内存 → Context Window - -### 4.1 操作系统里的内存层级 - -计算机的内存系统是一个**金字塔结构**: - -``` -金字塔顶部(最快、最贵、最小) -━━━━━━━━━━━━━━━━━━━━━━ - 寄存器 (~1 KB) 纳秒级 -━━━━━━━━━━━━━━━━━━━━━━ - L1 Cache (~32 KB) 纳秒级 -━━━━━━━━━━━━━━━━━━━━━━ - L2 Cache (~256 KB) 纳秒级 -━━━━━━━━━━━━━━━━━━━━━━ - L3 Cache (~8 MB) 十纳秒级 -━━━━━━━━━━━━━━━━━━━━━━ - 内存 RAM (~16 GB) 百纳秒级 -━━━━━━━━━━━━━━━━━━━━━━ - SSD (~1 TB) 微秒级 -━━━━━━━━━━━━━━━━━━━━━━ - 磁盘 (~10 TB) 毫秒级 -━━━━━━━━━━━━━━━━━━━━━━ -金字塔底部(最慢、最便宜、最大) -``` - -**核心问题**:资源是分层的,速度和容量永远矛盾。 - -解决方法:**缓存**和**虚拟内存**。 - -**缓存(Cache)**:把常用数据放在快但小的存储里。 - -**虚拟内存(Virtual Memory)**:把不常用的数据"换出"到慢但大的存储里。 - -```python -# 操作系统内存管理 -class MemoryManager: - def __init__(self, physical_memory, disk): - self.physical = physical_memory # 快速但有限 - self.disk = disk # 慢速但海量 - - def access(self, addr): - if addr in TLB: # Translation Lookaside Buffer - return self.physical[addr] # 命中,快! - - page = self.page_table[addr] - if not page.in_memory: - # 页面置换:把不常用的页换出到磁盘 - victim = self.find_victim_page() - self.swap_out(victim) - self.swap_in(page) - - return self.physical[addr] -``` - -### 4.2 Context Window:Agent 的"内存" - -Context Window 就是 LLM 的"工作内存"。 - -当前最强模型也就 200K token 左右(GPT-4o),而这对于处理复杂任务来说其实很有限。 - -**类比映射**: - -| 操作系统 | Agent 世界 | -|---------|-----------| -| 寄存器 | 当前正在处理的 token | -| L1/L2 Cache | 最近几轮对话 | -| RAM | 完整对话上下文 | -| 虚拟内存/交换区 | 压缩摘要、外部存储 | -| 页面置换算法 | Context Compression | - -### 4.3 Context Compression:内存分页的 Agent 版 - -当 Context Window 快满的时候,必须做**压缩**或**摘要**: - -```python -class ContextManager: - def __init__(self, max_tokens=128000): - self.max_tokens = max_tokens - self.messages = [] - self.summaries = {} # 压缩后的摘要 - - def add_message(self, role, content): - self.messages.append({"role": role, "content": content}) - - # 检查是否超出限制 - current_tokens = self.count_tokens(self.messages) - - if current_tokens > self.max_tokens * 0.8: - # 触发压缩 - self._compress() - - def _compress(self): - # 方法1:简单截断(丢弃最早的对话) - while self.count_tokens(self.messages) > self.max_tokens * 0.5: - self.messages.pop(0) - - # 方法2:智能摘要(保留关键信息,压缩冗余) - # 用 LLM 本身生成摘要 - old_content = self._get_old_messages() - summary = self.llm.summarize(f"请总结以下对话的要点:\n{old_content}") - self.summaries["phase_1"] = summary - - # 方法3:分层记忆(工作记忆 vs 长期记忆) - working = self.messages[-10:] # 最近10轮 - longterm = self.summaries # 历史摘要 -``` - -**这不就是操作系统的"页面置换算法"吗?** - -操作系统用 LRU、FIFO、Clock 算法决定哪些页面该换出。 -Agent 用重要性、相关性、时效性决定哪些上下文该压缩。 - -### 4.4 分层记忆架构 - -借鉴操作系统的内存分层思想,可以设计 Agent 的分层记忆: - -``` -┌─────────────────────────────────────┐ -│ 工作记忆(Working Memory) │ ~4K tokens -│ 当前任务、最近对话、临时变量 │ -├─────────────────────────────────────┤ -│ 短期记忆(Short-term Memory) │ ~32K tokens -│ 今天/本周的会话、活跃项目上下文 │ -├─────────────────────────────────────┤ -│ 长期记忆(Long-term Memory) │ 外部存储 -│ 压缩摘要、用户偏好、历史经验 │ -└─────────────────────────────────────┘ -``` - -**类比**: - -- 工作记忆 = CPU 寄存器 + L1 Cache -- 短期记忆 = RAM -- 长期记忆 = 虚拟内存/SSD - -### 4.5 页面置换策略的 Agent 实践 - -操作系统有很多经典的页面置换算法: - -| 算法 | 思想 | Agent 对应 | -|------|------|-----------| -| LRU | 置换最久未使用的 | 置换最早对话 | -| LFU | 置换使用频率最低的 | 置换最少提及的概念 | -| FIFO | 置换最早的 | 置换时间最早的 | -| Working Set | 保留活跃页面 | 保留相关上下文 | - -**实践中的陷阱**: - -1. **热恋期问题**:Agent 在新对话开始时总是把所有历史都加载,导致 Context 膨胀 -2. **遗忘灾难**:过度压缩导致关键信息丢失 -3. **语义碎片化**:简单截断可能破坏语义完整性(比如截断到一句话中间) - ---- - -## 伍、文件系统挂载 → RAG - -### 5.1 操作系统里的文件系统 - -文件系统是操作系统最伟大的发明之一。 - -它把**物理存储设备**(磁盘、SSD、U盘、网络存储)抽象成**统一的文件树**: - -``` -/ (根目录) -├── home/ -│ └── user/ -│ ├── documents/ -│ │ ├── report.pdf -│ │ └── notes.txt -│ └── pictures/ -│ └── photo.jpg -├── etc/ -│ └── config.conf -└── tmp/ - └── cache.dat -``` - -**核心概念:挂载(Mount)** - -可以把任何存储设备挂载到文件树的任意位置: - -```bash -# 把 U 盘挂载到 /mnt/usb -mount /dev/sdb1 /mnt/usb - -# 把网络存储挂载到 /home/user/cloud -mount -t nfs 192.168.1.100:/share /home/user/cloud - -# 把 ISO 镜像挂载到 /mnt/iso -mount -o loop image.iso /mnt/iso -``` - -**按需加载**:文件不用时不占内存,访问时才从磁盘读取。 -**用完释放**:操作系统会在内存紧张时释放不常用的文件缓存。 - -### 5.2 RAG:Agent 的"文件系统挂载" - -**RAG(Retrieval-Augmented Generation)** 就是把外部知识库"挂载"到 Agent 的上下文中。 - -``` -┌─────────────────────────────────────────────┐ -│ Agent 的"文件系统树" │ -├─────────────────────────────────────────────┤ -│ /context ← 当前对话 │ -│ /memory ← 压缩记忆 │ -│ /knowledge ← RAG 挂载点 │ -│ │ ├── /company-docs ← 公司文档 │ -│ │ ├── /product-manual ← 产品手册 │ -│ │ └── /web-search-cache ← 搜索缓存 │ -│ /tools ← 可用工具 │ -└─────────────────────────────────────────────┘ -``` - -### 5.3 RAG 的工作原理 - -```python -class RAGSystem: - def __init__(self, vector_store, embedding_model): - self.store = vector_store # 向量数据库 - self.embed = embedding_model - - def retrieve(self, query: str, top_k: int = 5) -> List[Document]: - # 1. 把查询向量化 - query_embedding = self.embed.encode(query) - - # 2. 在向量数据库中搜索相似文档 - results = self.store.similarity_search( - query_embedding, - top_k=top_k - ) - - return results - - def mount_to_context(self, query: str) -> str: - # 1. 检索相关文档 - docs = self.retrieve(query) - - # 2. 组装成上下文 - context = "\n\n".join([doc.content for doc in docs]) - - # 3. 添加到 prompt - return f"参考信息:\n{context}\n\n用户问题:{query}" -``` - -### 5.4 类比总结 - -| 操作系统 | Agent 系统 | -|---------|-----------| -| 磁盘/SSD | 向量数据库(Milvus、Pinecone、Chroma) | -| 文件系统 | RAG 检索层 | -| mount 命令 | RAG 配置 / 知识库注册 | -| 文件 I/O | 向量相似度搜索 | -| 文件缓存 | 检索结果缓存 | -| 磁盘配额 | Token 预算控制 | - -### 5.5 进阶:多层存储架构 - -和操作系统一样,可以设计多层 RAG 存储: - -``` -┌─────────────────────────────────────────┐ -│ 热数据(Hot Storage) │ 实时检索 -│ 常用文档、高频访问内容 │ -├─────────────────────────────────────────┤ -│ 温数据(Warm Storage) │ 定期同步 -│ 近期文档、项目资料 │ -├─────────────────────────────────────────┤ -│ 冷数据(Cold Storage) │ 按需加载 -│ 历史归档、罕见参考资料 │ -└─────────────────────────────────────────┘ -``` - ---- - -## 陆、内核 / 调度器 → Harness / Orchestrator - -### 6.1 操作系统的内核 - -操作系统的**内核(Kernel)** 是整个系统的核心: - -- **资源管理**:CPU 时间片分配、内存分配、文件句柄、设备访问 -- **权限控制**:用户态/内核态切换、Capabilities、系统调用过滤 -- **进程调度**:决定哪个进程在哪个时刻运行 -- **进程通信**:信号、管道、消息队列、共享内存 - -**调度器(Scheduler)** 是内核最复杂的组件之一: - -```python -# 简化的调度器逻辑 -class Scheduler: - def __init__(self): - self.ready_queue = [] # 就绪队列 - self.running = None # 当前运行进程 - - def schedule(self): - # 1. 把已完成的进程移出 - if self.running and self.running.is_terminated(): - self.running = None - - # 2. 把等待 I/O 的进程放回就绪队列 - for proc in self.waiting: - if proc.is_io_complete(): - self.ready_queue.append(proc) - - # 3. 选择下一个进程运行 - if not self.running and self.ready_queue: - self.running = self.ready_queue.pop(0) - self.running.state = RUNNING - - # 4. 时间片用完,放回队列 - if self.running and self.running.used_full_timeslice(): - self.ready_queue.append(self.running) - self.running.state = READY - self.running = None -``` - -### 6.2 Harness:Agent 的"内核" - -在 Agent 架构里,Harness 扮演着类似内核的角色: - -```python -class Harness: - def __init__(self, llm, tools, memory): - self.llm = llm # LLM = "CPU" - self.tools = tools # 工具 = "系统调用" - self.memory = memory # 记忆 = "内存" - self.state = {} # Agent 状态 - self.quota = Quota() # 资源配额 - - async def execute(self, task: str) -> str: - # 1. 准备上下文 - context = await self._prepare_context(task) - - # 2. LLM 推理(可能多次) - while not self._is_complete(): - # LLM 生成下一步 - action = await self.llm.think(context) - - # 如果是系统调用,执行工具 - if action.is_tool_call(): - result = await self._execute_tool(action) - context.add_result(result) - else: - # 直接输出 - return action.content - - return context.final_answer() - - async def _execute_tool(self, tool_call: ToolCall): - # 权限检查 - if not self._check_permission(tool_call): - raise PermissionDenied() - - # 资源检查 - if not self.quota.can_use(tool_call): - raise QuotaExceeded() - - # 执行 - tool = self.tools[tool_call.name] - return await tool.execute(tool_call.args) -``` - -### 6.3 Orchestrator:多 Agent 的调度器 - -当多个 Agent 协作时,需要 **Orchestrator**(编排器)来协调: - -```python -class Orchestrator: - def __init__(self, agents: List[Agent], task_graph: Dict): - self.agents = agents - self.task_graph = task_graph # 任务依赖关系 - self.results = {} - - async def run(self, root_task: str) -> Any: - # 1. 构建执行计划(类似拓扑排序) - execution_order = self._topological_sort(root_task) - - # 2. 按顺序执行,支持并行 - executed = set() - - while executed != set(execution_order): - # 找出所有可以执行的 Agent(依赖已满足) - ready = [ - agent for agent in self.agents - if agent.name in execution_order - and agent.name not in executed - and all( - dep in executed - for dep in self._get_dependencies(agent) - ) - ] - - # 并行执行所有就绪的 Agent - if ready: - results = await asyncio.gather(*[ - agent.execute(self.results) for agent in ready - ]) - - for agent, result in zip(ready, results): - self.results[agent.name] = result - executed.add(agent.name) - else: - # 死锁检测 - raise DeadlockError("No agents can proceed") - - return self.results[root_task] -``` - -**这和操作系统的进程调度完全是一个套路:** - -1. 分析任务依赖(构建 DAG) -2. 拓扑排序得到执行顺序 -3. 并行执行无依赖的任务 -4. 收集结果,继续下一批 -5. 检测死锁(环形依赖) - -### 6.4 调度策略 - -操作系统有多种调度策略,Agent 系统也可以借鉴: - -| 调度策略 | OS 场景 | Agent 应用 | -|---------|--------|-----------| -| FCFS | 简单批处理 | 顺序执行任务 | -| SJF | 最短任务优先 | 优先执行简单的 Agent | -| Round Robin | 时间片轮转 | 多 Agent 轮流执行 | -| 优先级调度 | 重要任务优先 | 关键 Agent 优先执行 | -| 多级反馈队列 | 兼顾响应和效率 | 简单任务快速通道,复杂任务深度处理 | - ---- - -## 柒、安全模型:Capabilities vs 权限 - -### 7.1 操作系统的权限模型 - -操作系统用 **Capabilities** 或 **ACL(访问控制列表)** 来控制权限: - -```c -// Capabilities 模型 -struct process { - cap_t cap; // 进程的能力集 -}; - -// 检查是否有写文件权限 -if (!cap_raised(proc->cap, CAP_DAC_OVERRIDE)) { - return -EACCES; -} -``` - -用户程序只能做它被授权的事情。 - -### 7.2 Agent 的权限模型 - -```python -class ToolPermissions: - def __init__(self): - self.allowed_tools = set() - self.rate_limits = {} - self.data_scope = {} # 数据访问范围 - - def grant(self, tool: str, quota: dict): - self.allowed_tools.add(tool) - self.rate_limits[tool] = quota - - def check(self, agent: str, tool: str) -> bool: - return tool in self.allowed_tools - -# 使用示例 -permissions = ToolPermissions() -permissions.grant("web_search", {"per_minute": 10, "per_day": 100}) -permissions.grant("code_execution", {"per_hour": 50}) -permissions.grant("read_files", {"/data/public/*": True, "/data/private/*": False}) -``` - ---- - -## 捌、信号与中断 → 事件与回调 - -### 8.1 操作系统的中断机制 - -操作系统有**中断**机制来处理异步事件: - -- 硬件中断:键盘输入、网卡数据包、磁盘 I/O 完成 -- 软件中断:系统调用、异常、信号 - -```c -// 信号处理 -signal(SIGINT, handle_interrupt); // 注册 Ctrl+C 处理函数 - -void handle_interrupt(int sig) { - // 清理资源 - cleanup(); - exit(0); -} -``` - -### 8.2 Agent 的事件驱动 - -```python -class AgentEventSystem: - def __init__(self): - self.handlers = {} - self.event_queue = asyncio.Queue() - - def on(self, event: str, handler: Callable): - self.handlers[event] = handler - - async def emit(self, event: str, data: Any): - await self.event_queue.put({"event": event, "data": data}) - - async def process_events(self): - while True: - event = await self.event_queue.get() - handler = self.handlers.get(event["event"]) - if handler: - await handler(event["data"]) - -# 使用示例 -events = AgentEventSystem() -events.on("tool_result", on_tool_result) -events.on("context_full", on_context_full) -events.on("agent_error", on_agent_error) -``` - ---- - -## 玖、调试与日志:/proc 文件系统 vs Agent 观测性 - -### 9.1 /proc:窥探系统状态 - -Linux 的 `/proc` 文件系统让用户可以查看内核状态: - -```bash -cat /proc/1234/maps # 查看进程内存映射 -cat /proc/1234/status # 查看进程状态 -cat /proc/cpuinfo # CPU 信息 -cat /proc/meminfo # 内存信息 -``` - -### 9.2 Agent 的观测性 - -```python -class AgentObserver: - def __init__(self): - self.metrics = {} - - def log(self, event: AgentEvent): - self.metrics[event.type] = self.metrics.get(event.type, 0) + 1 - - def get_state(self, agent_id: str) -> dict: - return { - "agent_id": agent_id, - "current_task": self.get_current_task(agent_id), - "context_tokens": self.count_tokens(agent_id), - "tools_used": self.get_tools_used(agent_id), - "execution_time": self.get_elapsed_time(agent_id), - "errors": self.get_errors(agent_id) - } - - def dump_trace(self, agent_id: str) -> str: - # 返回完整的执行轨迹,用于调试 - return json.dumps(self.trace[agent_id], indent=2) -``` - ---- - -## 拾、容器与命名空间 → Agent 隔离 - -### 10.1 容器技术 - -Docker 和 Kubernetes 重新定义了"进程隔离": - -- **PID Namespace**:容器内的进程 ID 从 1 开始 -- **Network Namespace**:容器有独立的网络栈 -- **Mount Namespace**:容器有独立的文件系统视图 -- **User Namespace**:容器内的用户 ID 可以映射到主机不同位置 - -```bash -# Docker 的隔离 -docker run --pid=host nginx # 共享 PID 命名空间 -docker run --network=host nginx # 共享网络命名空间 -docker run -v /host/path:/container/path nginx # 共享挂载 -``` - -### 10.2 Agent 隔离 - -类似地,Agent 系统也可以有不同级别的隔离: - -```python -class AgentSandbox: - def __init__(self, isolation_level: str): - self.isolation = isolation_level - - # 完全隔离:独立记忆和工具 - FULL = "full" - - # 共享工具,隔离记忆 - SHARED_TOOLS = "shared_tools" - - # 共享记忆,隔离工具 - SHARED_MEMORY = "shared_memory" - - # 完全共享(危险!) - NONE = "none" -``` - ---- - -## 拾壹、未来展望 - -### 11.1 Agent 操作系统? - -我在想一个问题:会不会有一天,出现类似"Agent OS"的东西? - -- **进程** = Agent -- **文件系统** = 去中心化的知识图谱 -- **系统调用** = 标准化的 Tool Interface -- **调度器** = Orchestrator -- **安全沙箱** = Capability-based Access Control - -这不是科幻。事实上,很多框架已经在朝这个方向走: - -- LangGraph 的状态机 -- AutoGPT 的任务分解 -- CrewAI 的 Agent 协作 -- Microsoft AutoGen 的会话编排 - -### 11.2 我们能从 OS 历史学到什么? - -操作系统几十年沉淀下来的智慧: - -1. **分层抽象**:不要让每个层知道太多上层细节 -2. **最小权限原则**:只授予完成任务所需的最小权限 -3. **fail-safe 设计**:出错了要安全地失败,不要搞垮整个系统 -4. **可观测性**:运行时能看见内部状态,出了问题才能排查 -5. **资源隔离**:防止一个组件拖垮整个系统 - -这些问题,Agent 时代正在重新面对。 - ---- - -## 结语:历史不会重复,但会押韵 - -马克·吐温说:**"历史不会重复,但会押韵。"** - -操作系统花了几十年,从裸机编程演进到现代多任务系统: -- 进程隔离解决了安全问题 -- 虚拟内存解决了容量问题 -- 文件系统解决了存储抽象问题 -- 调度器解决了资源分配问题 - -Agent 时代正在用同样的方式重新走这条路: -- Agent 隔离解决协作问题 -- Context Compression 解决容量问题 -- RAG 解决知识抽象问题 -- Orchestrator 解决任务分配问题 - -当你觉得某个 Agent 设计似曾相识—— - -**不妨回到操作系统教科书里找答案。那里面藏着你需要的解。** - ---- - -*如果你觉得这篇文章有帮助,欢迎分享给同样在学习 Agent 的朋友。* - -*历史在押韵,我们都是见证者。* \ No newline at end of file diff --git a/博客/AI与大模型/AI时代的Token战争-能源算力与我们每个人的选择.md b/博客/AI与大模型/AI时代的Token战争-能源算力与我们每个人的选择.md new file mode 100644 index 0000000..3522e2a --- /dev/null +++ b/博客/AI与大模型/AI时代的Token战争-能源算力与我们每个人的选择.md @@ -0,0 +1,198 @@ +--- +title: AI 时代的 Token 战争:能源、算力与我们每个人的选择 +slug: ai-token +categories: + - 技术思考 +tags: + - 技术深潜 + - AI智能体 +halo: + site: https://blog.liuhangyv.top + name: da325dee-9575-45ea-8593-192c39402edc + publish: false +--- +# AI 时代的 Token 战争:能源、算力与我们每个人的选择 + +> "Token 是电的加工计价单位。同一个 Token,可以用来发现抗癌新药,也可以用来生成一段毫无意义的废话。它的价值,由使用者决定。" + +--- + +## 一、引言:一个新的"工业时代"正在开启 + +2024 年,一家软件公司包下了一座核电站未来 20 年的全部发电量。 + +这不是科幻小说,这是微软对三哩岛核电站的真实操作。1979 年,这里发生过美国历史上最严重的核事故,2 号反应堆永久关停。45 年后,微软签下了一份合同——买断 1 号机组未来 20 年的全部电力,只为喂饱它那些永远吃不饱的 AI 数据中心。 + +而在地球的另一端,中东的产油国们正在做一件意味深长的事:他们不再只卖石油,而是开始大规模建设清洁能源基地和算力中心。沙特、阿联酋正在向全世界宣告——未来,我们要卖 Token。 + +**一个新的"工业时代"正在悄悄开启**。但这次跟蒸汽时代、电气时代不一样:它的基础设施不再是铁路和电网,而是 GPU 集群和核电站;它的流通货币不再是钢铁和石油,而是一个叫 **Token** 的东西。 + +这篇文章,我想和你聊聊这个新时代的真实图景——以及为什么它的结局,可能取决于你我每个人在屏幕前的每一个选择。 + +--- + +## 二、巨头全链布局:从芯片到核电站 + +### 2.1 微软:从三哩岛到 Azure,全链条通吃 + +2024 年,微软干了几件让整个科技圈震动的事: + +**第一件事:包下核电站。** 微软与三哩岛核电站 1 号机组签订了 20 年购电协议,买断了其全部发电量。一个软件公司,为什么需要一座核电站?答案很简单:**AI 太耗电了**。训练一次 GPT-4 级别的模型,耗电量相当于一个小型城镇一年的用电量。而推理服务——也就是你每次跟 ChatGPT 聊天——每天都在消耗天文数字的电力。 + +**第二件事:联合英伟达推出 RTX Spark 芯片。** 这枚芯片的意义在于,微软和英伟达共同喊出了一句口号:"我们重新发明了 PC。"但背后的真实逻辑是什么?往下看英伟达的部分你会明白。 + +**第三件事:通过 Azure 云整合 AI 上下游。** 从算力基础设施(Azure)到模型服务(OpenAI 的合作),再到终端芯片(RTX Spark),微软正在构建一个完整的闭环。它要的不是某个环节的利润,而是整个 AI 产业链的主导权。 + +### 2.2 英伟达:当游戏显卡不再是主业 + +翻开英伟达 2025 财年的财报,你会发现一个惊人的数字: + +**游戏显卡业务收入占比:仅 8.8%。** + +对,就是那个靠"为游戏玩家造显卡"起家的英伟达,如今游戏业务已经不足总收入的十分之一。你会发现,它现在真正的客户是微软、谷歌、亚马逊这些云计算巨头,是企业级数据中心,是全世界的 AI 公司。 + +但问题来了——**这种集中度意味着焦虑**。英伟达的命脉掌握在少数几个大客户手里,任何一个客户开始自研芯片,都会对英伟达造成巨大冲击。这就是为什么黄仁勋要联手微软推出 RTX Spark 芯片——**它要开辟 C 端市场,把 AI 算力卖给每一个普通人**。 + +这枚芯片的商业逻辑是这样的: +- B 端市场虽然单价高,但客户集中、议价能力强 +- C 端市场虽然单价低,但用户基数巨大、分散度高 +- 如果每个 PC 都内置一块 AI 推理芯片,英伟达就不需要担心任何一个大客户的离开 + +### 2.3 OpenAI 与微软:从盟友到对手 + +曾几何时,OpenAI 和微软是 AI 领域最耀眼的 CP 组合。微软出钱出算力,OpenAI 出模型出技术,双方各取所需。 + +但到了 2024-2025 年,这对盟友的关系正在发生微妙的变化。OpenAI 不再满足于做微软的"模型供应商",它开始自己构建算力基础设施,自己对接企业客户,甚至开始推出与 Azure 竞争的服务。而微软也在自研模型、扩大合作范围,不再把鸡蛋全部放在 OpenAI 一个篮子里。 + +**这条产业链上的每个玩家,都在试图控制更多环节。** 因为在这个时代,谁控制了能源→算力→模型→应用的完整链条,谁就掌握了定价权。 + +--- + +## 三、国家级的电力争夺战 + +### 3.1 美国:科技巨头"抢电"进行时 + +美国的科技巨头们正在上演一场疯狂的"抢电大战"。亚马逊、谷歌、微软、Meta——每一家都在以惊人的速度建设数据中心,每一家都在满世界找电力供应。 + +这不是夸张。AI 数据中心的耗电量正在以指数级增长。一个超大规模数据中心,单日耗电量可以达到一个中型城市的水平。而当几十个这样的数据中心同时在建设时,电力供应就成了最稀缺的资源。 + +**微软包下三哩岛核电站,只是这场"抢电大战"的一个缩影。** + +### 3.2 爱尔兰:被数据中心"吃垮"的电网 + +也许你会觉得,美国的事离我们很远。那我们来看看爱尔兰。 + +2015 年到 2024 年,爱尔兰数据中心耗电占比从 **5%** 飙升至 **22%**。 + +也就是说,爱尔兰全国超过五分之一的电力,被数据中心吃掉了。 + +这导致的结果是:爱尔兰政府宣布,**2028 年之前不再批准新的数据中心接入电网**。一个国家,因为 AI 太耗电,不得不主动限制这个新兴产业的发展。这是历史上从来没有发生过的事。 + +### 3.3 中东:从卖石油到卖 Token + +最有意​思的格局变化,发生在中东。 + +沙特、阿联酋这些传统产油国,正在大规模投建清洁能源基地和算力中心。他们的逻辑很清晰:**过去我们卖石油给工业时代提供能量,未来我们要卖 Token 给 AI 时代提供算力。** + +从化石能源到算力经济,这是一次深刻的经济转型。而这些国家手中的筹码也很实在:充足的阳光(太阳能)、广阔的土地(建数据中心)、雄厚的资本(投资基础设施)。 + +他们看到的是:**21 世纪的石油,叫 Token。** + +### 3.4 中国:电力充裕 + 国产芯片,走性价比路线 + +中国在这场 AI 竞赛中的地位非常独特。 + +一方面,中国的电力供应相对充裕——我们的发电量是世界第一,电网基础设施也更加完善。另一方面,中国在国产 AI 芯片上的投入正在加速,虽然与英伟达还有差距,但在特定场景下已经可以实现替代。 + +**中国走的是性价比路线**:不需要在每个环节都做到世界顶尖,但要确保全链条的自主可控和成本优势。当美国在用一座核电站喂数据中心的时候,中国在做的是用更低的成本、更广泛的电力网络、更贴近应用场景的算力部署来找到自己的位置。 + +--- + +## 四、Token:新时代的计价单位 + +### 4.1 Token 的本质是什么? + +说了这么多,我们来聊聊最核心的概念:**Token 到底是什么?** + +简单来说,**Token 是电力的加工计价单位。** + +输入电力 → 驱动 GPU → 运行模型 → 产出 Token。这就是 AI 时代的基本生产函数。Token 是这个链条的最终产出物,就像工业时代的钢铁、石油时代的汽油一样。 + +但 Token 有一个非常特殊的性质:**相同成本的 Token,使用价值可以天差地别。** + +### 4.2 同一个 Token,两种命运 + +请想象两个场景: + +**场景 A**:一家药企的科研人员,用 100 万个 Token 让 AI 辅助分析蛋白质结构,推动了抗癌新药的研发进程。这些 Token 的价值,可能在未来拯救成千上万人的生命。 + +**场景 B**:一个无聊的用户,用 100 万个 Token 让 AI 生成了一堆没有意义的废话,看完就关掉了窗口。这些 Token 的价值,趋近于零。 + +**Token 的生产成本是一样的,电费是一样的,GPU 折旧是一样的。但使用价值的差异,可以大到无法衡量。** + +这就是 AI 时代的核心困境:**我们正在以惊人的速度生产 Token,但如何提高 Token 的价值密度,才是真正的问题。** + +### 4.3 泡沫的风险 + +当前 AI 行业的商业模型,很大程度上建立在"烧钱换增长"的逻辑之上。融资 → 买算力 → 训练模型 → 烧 Token → 融资更多钱 → 买更多算力…… + +AI 公司亏损是常态,盈利是例外。**整个行业的主要资金来源不是用户的付费,而是投资机构的融资。** + +如果有一天,资本市场开始质疑这个循环的可持续性——当"你的 Token 创造了什么实际价值"变成一个必须回答的问题,泡沫就可能破裂。 + +而避免泡沫的关键,不在于生产更多 Token,而在于**每个 Token 是否被用在真正有价值的地方**。 + +--- + +## 五、回归本质:谁决定了 AI 时代的走向? + +### 5.1 不是巨头,不是国家,而是你我 + +聊完巨头、聊完国家、聊完产业链,我们回到最初的问题:**我们,该怎么面对这个充满 AI 的世界?** + +微软可以包下核电站,英伟达可以推出新芯片,沙特可以建数据中心——但这些只是供给侧的故事。 + +**AI 时代的结局,不是由供给决定的,而是由需求决定的。** 或者说得更准确一点:**Token 的价值,由每一个使用它的人决定。** + +当我们在屏幕上输入每一个提示词、提出每一个问题时,我们就在给这个系统注入价值信号。你在用 Token 做什么?你是在高效地解决问题,还是在低效率地消耗算力? + +**这不是一个宏大叙事,而是一个微观选择。** 亿万个微观选择的集合,最终会塑造这个行业的走向。 + +### 5.2 独立思考是这个时代最稀缺的能力 + +这个时代最危险的不是 AI 不够强大,而是人类放弃思考。 + +当 AI 可以帮你写文章、做决策、分析数据、甚至替你聊天的时候,**"自己思考"反而变成了最难能可贵的事情**。因为用 AI 思考太容易了——输入问题,得到答案,不需要动脑。 + +但如果你不再思考,你就变成了 AI 的附庸。你的 Token 将永远是低价值的——因为你只是在消费信息,而不是在创造价值。 + +**独立思考的能力,在这个时代不是可选项,而是必需品。** + +### 5.3 提高 Token 的价值密度 + +最后,我想给每个人一个具体可操作的建议:**提高你生产/消费的每一个 Token 的价值密度。** + +具体来说: +- 用 AI 来学习一个你真正需要的技能,而不是生成一堆你不会再看的文字 +- 用 AI 来分析一个问题,而不是让它替你做一个你懒得做的决定 +- 用 AI 来辅助创造,而不是用它来替代思考 + +**每一个 Token 都是一张选票。你在为什么投票?** + +--- + +## 六、结语 + +1979 年,三哩岛的核事故让整个世界对核能产生了恐惧。 + +45 年后,同一座核电站的另一台机组,正在为人类历史上最强大的智能机器提供动力。 + +**时代变了,但人性没有变。** 每一次技术革命,都有人抓住机会创造了伟大的价值,也有人被浪潮裹挟着随波逐流。 + +AI 时代不会因为巨头们的布局、国家间的博弈而自动走向美好的未来。它的走向,最终取决于每一个坐在这块屏幕前的我们——**是否还在独立思考,是否还在为每一个 Token 赋予真实的意义。** + +这大概就是这个时代,我们每个人都必须回答的问题。 + +--- + +> **#独立思考 #AI #微软 #英伟达 #经济** diff --git a/博客/AI与大模型/自建Git服务-在NAS上部署Gitea.md b/博客/AI与大模型/自建Git服务-在NAS上部署Gitea.md new file mode 100644 index 0000000..67b473a --- /dev/null +++ b/博客/AI与大模型/自建Git服务-在NAS上部署Gitea.md @@ -0,0 +1,124 @@ +# 在宿舍 NAS 上搭建 Gitea:一个自己的 GitHub + +> 校园网封了 22 端口?SMB 写文件权限炸了?sshpass 吃特殊字符?两天踩的坑都在这了。 + +## 起因 + +手上项目越来越多,GitHub 国内访问不稳定,Gitee 又有仓库数量限制。刚好手边有台绿联 DXP-2800 NAS(N100 处理器,8GB 内存,跑 Debian 12),不如自己搭个 Git 服务——代码完全在自己硬盘上,不用联网也能 push,想建多少个私有仓库都行。 + +## 选型 + +对比了几个方案: + +| 方案 | 优点 | 缺点 | +|------|------|------| +| GitLab | 功能最全,内置 CI/CD | 吃内存,N100 扛不动 | +| Gogs | Go 语言,轻量 | 社区不如 Gitea 活跃 | +| Gitea | Go 语言,GitHub 风格,安装简单 | 够用就行 | + +GitLab 最低要求 4GB 内存,加上 NAS 上已经跑了 14 个 Docker 容器(Halo 博客、qBittorrent、PostgreSQL、一堆 Cloudflare Tunnel……),资源不够。**Gitea** 用 SQLite 单用户跑,内存占用不到 256MB,选它。 + +## 部署 + +docker-compose 文件很简单: + +```yaml +services: + gitea: + image: gitea/gitea:latest + restart: unless-stopped + volumes: + - /volume1/docker/gitea/data:/data + - /etc/localtime:/etc/localtime:ro + ports: + - "3000:3000" # Web UI + - "2222:22" # SSH(避开 NAS 主机的 22 端口) + environment: + - TZ=Asia/Shanghai + - GITEA__database__DB_TYPE=sqlite3 + - GITEA__server__DOMAIN=192.168.5.8 + - GITEA__server__SSH_DOMAIN=192.168.5.8 + - GITEA__server__SSH_PORT=2222 + - GITEA__server__ROOT_URL=http://192.168.5.8:3000/ + - GITEA__server__APP_NAME=LHY Git + mem_limit: 512m + cpus: 1 +``` + +`docker compose up -d`,一分钟起好。访问 `http://192.168.5.8:3000`,注册第一个账号即为管理员。 + +## 踩坑:SSH 排错 + +Gitea 部署完了,但到推送的时候卡住了。问题出在 SSH 认证链路。 + +### 第一关:GitHub 连不上 + +`ssh -T git@github.com` 一直 Permission denied,密钥指纹是对的,GitHub 后台也确认加好了。排查了半天发现——**校园网封了 22 端口**。 + +改走 443 秒通: + +```bash +ssh -T -p 443 git@ssh.github.com +# Hi LHY0125! You've successfully authenticated... +``` + +解决:在 `~/.ssh/config` 里把 GitHub 永久改成 443: + +``` +Host github.com + HostName ssh.github.com + Port 443 + User git + IdentityFile ~/.ssh/id_ed25519 +``` + +### 第二关:NAS 密钥认证失败 + +`ssh LHY@192.168.5.8` 每次都要输密码,即使公钥已经加到了 `authorized_keys`。 + +排查过程: + +1. **SSH config 问题** — 配了不存在的 `id_rsa` 私钥,改了。 +2. **`authorized_keys` 换行丢失** — 通过 SMB(F: 盘)追加公钥时,旧文件末尾没有换行,导致新旧两行粘在一起。用 `cat -A` 检查后修复。 +3. **权限问题** — 这是坑最大的。SMB 写文件不会正确保留 Unix 权限,sshd 要求 `~/.ssh` 必须是 700、`authorized_keys` 必须是 600、家目录不能 group-writable。修复命令: + +```bash +chmod 700 ~/.ssh +chmod 600 ~/.ssh/authorized_keys +chmod 750 ~ +``` + +### 第三关:sshpass 吃特殊字符 + +密码里有个 `@`,`sshpass -p` 参数怎么传都报 Permission denied。试了引号、转义、文件读取都不行。后来发现不是工具问题,是密码本身在 NAS 上已经不是这个了——手动输入能登,说明只是自动化传输特殊字符有问题。**解决:不用密码,用密钥。** + +## 推送 + +两个仓库 `novel-hub` 和 `rl-atari` 推上去。Novel 秒推,rl-atari 有 2.2G(含 PDF 文件),SSH 推这个体量的仓库慢得离谱,换成 HTTP 一次过: + +```bash +git remote set-url origin http://192.168.5.8:3000/LHY/rl-atari.git +git push -u origin main +``` + +推完切回 SSH,日常小 update 没压力。 + +## 效果 + +现在 `http://192.168.5.8:3000` 就是我的代码中心: + +- Web 界面和 GitHub/Gitee 一模一样:Issues、Pull Request、Wiki、Actions +- 局域网内 push/pull 走 NAS 千兆,几秒搞定 +- 不需要互联网,代码完全在自己硬盘上 +- 想建多少个私有仓库都行,不受任何限制 + +## 后续 + +下一步打算: +- 用 Cloudflare Tunnel 把 Gitea 暴露到公网,不在宿舍也能 push +- 给 Gitea 配 GitHub Actions Runner,实现本地 CI/CD +- 把学校课程设计的代码库全部迁移过来 + +--- + +**总结**:自托管 Git 服务的门槛比想象中低很多。Docker 部署十分钟,SSH 排错花了大半天。核心教训——校园网封 22 端口、SMB 写文件要注意权限、密钥比密码靠谱。 diff --git a/博客/编程与工具/Docker安装与入门指南.md b/博客/编程与工具/Docker安装与入门指南.md new file mode 100644 index 0000000..a506a8b --- /dev/null +++ b/博客/编程与工具/Docker安装与入门指南.md @@ -0,0 +1,408 @@ +--- +title: Docker 安装与入门指南 +date: 2026-05-08 +author: liuhangyv +excerpt: 从零开始学习 Docker:详解 Windows 下通过 Docker Desktop 安装 Docker 的完整流程,以及云服务器(阿里云)上的 Docker 部署方式,附带常用命令速查。 + +--- + +# Docker 安装与入门指南 + +本文面向**零基础读者**,从环境准备到安装完成,一步一步带你上手 Docker。内容聚焦两种最常见的安装场景: + +- **场景一**:Windows 本机通过 Docker Desktop 安装 Docker +- **场景二**:云服务器(以阿里云为例)上安装 Docker + +本文不涉及 Docker Compose、多容器编排等进阶内容,力求用最短的篇幅帮你把 Docker **跑起来**。 + +--- + +## 1. 什么是 Docker?为什么需要它? + +Docker 是一个**容器化平台**,它把应用程序和它的所有依赖打包在一起,形成一个独立的"容器",这个容器可以在任何装了 Docker 的机器上运行,不会因为系统环境不同而出问题。 + +**一句话理解**:Docker 就像一个轻量级的虚拟机,但比虚拟机快得多、占用资源少得多。 + +**典型场景**: + +- 你在 Windows 上开发,但服务器是 Linux——用 Docker,代码在哪跑都一样 +- 你想学数据库,但不想在本机装一堆服务——Docker 一行命令就能起一个 MySQL/PostgreSQL +- 你想跑别人写的开源项目,但环境配置太复杂——Docker 一个命令全部搞定 + +--- + +## 2. 场景一:Windows 下安装 Docker Desktop + +Docker Desktop 是 Docker 官方为 Windows 用户提供的一键安装方案,内置了 WSL2 后端、图形化管理界面,也是官方的推荐方式。 + +### 2.1 下载与安装 + +1. 访问 [Docker 官网](https://www.docker.com/products/docker-desktop/),点击 **Download for Windows** +2. 下载完成后运行安装程序,**所有默认选项保持不动即可**,安装程序会自动配置好 WSL2 +3. 安装完成后重启电脑 +4. 重启后 Docker Desktop 会自动启动,你可以在系统托盘看到 Docker 的鲸鱼图标 + +> **说明**:Docker Desktop 安装时会自动安装 WSL2 并创建两个 WSL 发行版(`docker-desktop` 和 `docker-desktop-data`),你无需手动安装 Ubuntu,也不需要关心 WSL2 的细节。 + +### 2.2 验证安装 + +打开 **PowerShell** 或 **命令提示符**,执行: + +```powershell +# 检查 Docker 版本 +docker --version +# 输出示例:Docker version 28.0.1, build ... + +# 检查 Docker Compose 版本(已内置) +docker compose version + +# 运行测试容器 +docker run --rm hello-world +``` + +看到 `Hello from Docker!` 就说明安装成功了。 + +### 2.3 日常使用 + +Docker Desktop 开机后会自动在后台运行。使用时直接在 PowerShell 或终端中敲 `docker` 命令即可,和 Linux 下的体验完全一致。 + +小技巧:右键点击系统托盘的鲸鱼图标,可以快速打开 Dashboard 图形化管理界面,查看所有容器、镜像、数据卷的状态。 + +--- + +## 3. 场景二:云服务器上安装 Docker + +拥有一台云服务器是学习 Docker 的理想方式——它能 24 小时运行,你可以部署自己的服务并随时随地访问。 + +### 3.1 选购云服务器 + +以笔者的**阿里云 ECS** 为例: + +| 项目 | 配置 | +|------|------| +| **实例规格** | 2 vCPU / 2 GiB 内存 | +| **操作系统** | Ubuntu 22.04 LTS(64 位) | +| **系统盘** | 40 GiB ESSD | +| **公网 IP** | 101.133.128.193 | +| **带宽** | 按量计费 | + +> **学生党建议**:阿里云有学生优惠,一年几十块钱就能买到基础配置的 ECS,足够学习使用。 + +购买时注意: +- 操作系统选 **Ubuntu 22.04** 或 **Ubuntu 24.04**,生态最好 +- 安全组(防火墙)默认只开放 22(SSH)端口,后续根据需要手动开放其他端口 +- 创建时选择密钥对登录,比密码更安全 + +### 3.2 登录服务器 + +```bash +# 使用 SSH 连接(替换 IP 为你的服务器 IP) +ssh root@你的服务器IP + +# 如果使用密钥 +ssh -i ~/.ssh/你的密钥 root@你的服务器IP +``` + +> 笔者的习惯:创建一个普通用户 `admin` 来管理,避免直接使用 `root`。并配置 `~/.ssh/authorized_keys` 实现免密登录。 + +### 3.3 安装 Docker(与 Ubuntu 下完全一致) + +登录服务器后,执行的安装命令和 Linux 下完全一致。由于阿里云的 Ubuntu 是标准的 Linux 系统,安装步骤与上面 Windows 无关——下面是完整的安装命令: + +```bash +# 安装 Docker Engine(适用于 Ubuntu) +sudo apt update +sudo apt install -y ca-certificates curl gnupg lsb-release + +sudo install -m 0755 -d /etc/apt/keyrings +curl -fsSL https://download.docker.com/linux/ubuntu/gpg | sudo gpg --dearmor -o /etc/apt/keyrings/docker.gpg +sudo chmod a+r /etc/apt/keyrings/docker.gpg + +echo \ + "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] \ + https://download.docker.com/linux/ubuntu \ + $(lsb_release -cs) stable" | \ + sudo tee /etc/apt/sources.list.d/docker.list > /dev/null + +sudo apt update +sudo apt install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin + +# 将当前用户加入 docker 组 +sudo usermod -aG docker $USER +newgrp docker +``` + +### 3.4 云服务器的额外配置 + +服务器重启后 Docker **会自动随系统启动**(通过 systemd 管理),无需额外配置。 + +但需要配置**安全组(防火墙)**,否则外部无法访问你的服务: + +1. 登录阿里云控制台 → ECS → 安全组 +2. 添加规则,例如: + +| 方向 | 端口 | 协议 | 用途 | +|------|------|------|------| +| 入方向 | 80 | TCP | HTTP | +| 入方向 | 443 | TCP | HTTPS | +| 入方向 | 8080 | TCP | 测试应用 | +| 入方向 | 3000 | TCP | Node.js 开发 | + +> ⚠️ 不要开放 22(SSH)的源 IP 为 `0.0.0.0/0`,建议仅允许你的 IP。数据库端口(如 3306、5432)也不要对公网开放。 + +--- + +## 4. Docker 基本使用 + +安装完成后,我们来上手几个最核心的命令。 + +### 4.1 运行你的第一个容器 + +```bash +# 运行 Nginx 网页服务器 +docker run -d --name my-nginx -p 8080:80 nginx:latest +``` + +参数说明: +- `-d`:后台运行 +- `--name my-nginx`:给容器取名 +- `-p 8080:80`:把宿主机的 8080 端口映射到容器的 80 端口 +- `nginx:latest`:使用的镜像名和标签 + +现在打开浏览器,访问: +- 本地:`http://localhost:8080` +- 云服务器:`http://你的服务器IP:8080` + +你应该能看到 Nginx 的欢迎页面。 + +### 4.2 常用命令速查 + +```bash +# ========== 容器管理 ========== + +# 查看运行中的容器 +docker ps + +# 查看所有容器(包括已停止的) +docker ps -a + +# 停止容器 +docker stop 容器名 + +# 启动已停止的容器 +docker start 容器名 + +# 重启容器 +docker restart 容器名 + +# 删除容器(需先停止) +docker rm 容器名 + +# 强制删除运行中的容器 +docker rm -f 容器名 + +# 进入容器内部 +docker exec -it 容器名 bash + +# 查看容器日志(-f 实时跟踪) +docker logs -f 容器名 + +# ========== 镜像管理 ========== + +# 搜索镜像 +docker search nginx + +# 拉取镜像 +docker pull 镜像名:标签 + +# 查看本地镜像 +docker images + +# 删除镜像 +docker rmi 镜像名 + +# ========== 清理 ========== + +# 清理停止的容器、未使用的镜像和网络 +docker system prune -a +``` + +### 4.3 快速搭建一个数据库 + +这是学习 Docker 最实用的场景——一行命令起一个数据库: + +```bash +# PostgreSQL +docker run -d \ + --name my-postgres \ + -e POSTGRES_USER=admin \ + -e POSTGRES_PASSWORD=your_password \ + -e POSTGRES_DB=mydb \ + -p 5432:5432 \ + -v pgdata:/var/lib/postgresql/data \ + postgres:16-bookworm + +# MySQL +docker run -d \ + --name my-mysql \ + -e MYSQL_ROOT_PASSWORD=your_password \ + -e MYSQL_DATABASE=mydb \ + -p 3306:3306 \ + -v mysqldata:/var/lib/mysql \ + mysql:8.0 + +# Redis +docker run -d \ + --name my-redis \ + -p 6379:6379 \ + -v redisdata:/data \ + redis:7-alpine +``` + +关键参数: +- `-e`:设置环境变量(数据库密码等) +- `-v 卷名:容器内路径`:持久化数据,容器删除后数据不丢失 +- `postgres:16-bookworm`:镜像名:标签,指定具体版本避免意外升级 + +--- + +## 5. 图形化管理 Docker + +命令行功能强大,但对于初学者,图形化界面更直观。这里介绍两种方式——Windows 上的 Docker Desktop Dashboard,和云服务器上的 1Panel。 + +### 5.1 Docker Desktop Dashboard(Windows 本地) + +Docker Desktop 自带了一个图形化管理界面,无需额外安装: + +- **打开方式**:右键系统托盘的鲸鱼图标 → 点击 **Dashboard** +- **功能**: + - **Containers(容器)**:查看所有容器状态、启停、删除、查看日志、进入终端,全部鼠标点击完成 + - **Images(镜像)**:浏览本地镜像、拉取新镜像、删除无用镜像 + - **Volumes(数据卷)**:查看和管理持久化数据 + - **搜索**:内置 Docker Hub 搜索,找到镜像一键 Pull + +对于日常的启停、看日志、删容器等操作,Dashboard 比敲命令方便得多。 + +### 5.2 1Panel —— 云服务器上的"应用商店"式管理 + +在云服务器上,没有图形界面可用。但借助 **1Panel** 这类 Linux 服务器管理面板,你可以通过浏览器像操作手机 App 一样管理 Docker。 + +1Panel 是一个开源的 Linux 服务器运维面板,内置了 Docker 可视化管理功能。笔者的阿里云服务器就在使用它。 + +**安装 1Panel**(一条命令完成): + +```bash +# SSH 登录云服务器后执行 +curl -sSL https://resource.fit2cloud.com/1panel/package/quick_start.sh -o quick_start.sh && bash quick_start.sh +``` + +安装完成后会显示: + +``` +=============================== +1Panel 安装成功! +面板地址: http://你的IP:端口/ +用户名: xxxxxx +密码: xxxxxx +=============================== +``` + +> 安装前确保安全组已开放 1Panel 的访问端口(默认是随机端口,安装完成后会显示)。 + +**用 1Panel 管理 Docker**: + +登录 1Panel 后,进入 **容器** 菜单: + +| 功能 | 说明 | +|------|------| +| **应用商店** | 一键安装 MySQL、Redis、Nginx、WordPress 等 100+ 应用,填写表单即可部署 | +| **容器管理** | 查看启停状态、资源占用、日志、终端,与 Docker Desktop 体验一致 | +| **Compose 模板** | 粘贴 `docker-compose.yaml` 内容,点击部署,无需敲命令 | +| **镜像管理** | 搜索、拉取、删除镜像,支持配置镜像加速 | +| **网络/数据卷** | 可视化创建和管理网络与数据卷 | + +**以安装 MySQL 为例**: + +1. 左侧菜单 → **应用商店** +2. 搜索 "MySQL",点击 **安装** +3. 填写:数据库名、密码、端口(可保持默认 3306),勾选"端口外部访问" +4. 点击 **确认**,等待十几秒即可完成 + +相比前面 4.3 节一长串的命令行参数,在 1Panel 里填几个表单框就搞定了。 + +> 笔者的 1Panel 实际配置:面板端口 `21471`,通过 Nginx 反向代理绑定域名,日常管理非常方便。 + +--- + +## 6. 下一步学什么? + +当你已经能用 `docker run` 熟练启动单个容器后,下一步自然的学习路径: + +1. **Docker Compose** —— 用 YAML 文件管理多容器应用,一条命令启动整个项目 +2. **Dockerfile** —— 自己编写镜像构建文件,把项目打包成镜像 +3. **数据持久化** —— 深入理解 Volume 和 Bind Mount +4. **网络配置** —— 容器间通信、自定义网络 + +可以参考同一目录下的 [Docker 部署完全指南](./Docker部署完全指南.md) 深入学习后续内容。 + +--- + +## 附录:安装问题排查 + +### 问题一:`docker` 命令提示 "The command 'docker' was not found" + +**原因**:Docker Desktop 未正确安装或未启动。 + +**解决**: + +- 检查系统托盘中 Docker 图标是否存在;如果没有,从开始菜单手动启动 Docker Desktop +- 等待鲸鱼图标停止动画后重试 + +### 问题二:Docker Desktop 启动后一直转圈(Starting...) + +**原因**:WSL2 未正确安装或版本过旧。 + +**解决**: + +```powershell +# 以管理员身份打开 PowerShell +wsl --update +wsl --set-default-version 2 + +# 重启 Docker Desktop +``` + +### 问题三:云服务器上访问服务端口失败 + +**原因**:安全组(防火墙)未开放对应端口。 + +**解决**:登录云服务器控制台,在安全组中添加入方向规则,开放对应端口。 + +### 问题四:拉取镜像速度慢 + +**原因**:Docker Hub 在国内访问较慢。 + +**解决**:配置国内镜像源。 + +**云服务器(Linux)**: + +```bash +sudo tee /etc/docker/daemon.json <<'EOF' +{ + "registry-mirrors": [ + "https://docker.m.daocloud.io", + "https://docker.1panel.live" + ] +} +EOF +sudo systemctl restart docker +``` + +**Docker Desktop(Windows)**: + +打开 Docker Desktop → 右上角齿轮(Settings)→ Docker Engine,将上面的 `registry-mirrors` 配置合并到现有 JSON 中,点击 "Apply & Restart"。 + +--- + +*本文由河南理工大学人工智能协会整理* +*2026年5月* diff --git a/博客/编程与工具/Git团队协作指南.md b/博客/编程与工具/Git团队协作指南.md index 45a4427..cbf6b12 100644 --- a/博客/编程与工具/Git团队协作指南.md +++ b/博客/编程与工具/Git团队协作指南.md @@ -401,13 +401,13 @@ Git是分布式版本控制系统,这意味着每个开发者的电脑上都 **本地仓库 vs 远程仓库** -| 特性 | 本地仓库 | 远程仓库 | -|------|---------|---------| -| **位置** | 你的电脑 | 服务器(GitHub/Gitee等) | -| **用途** | 日常开发和提交 | 团队共享和备份 | -| **操作** | `git commit` | `git push/pull` | -| **网络要求** | 无需网络 | 需要网络连接 | -| **所有权** | 完全可控 | 团队共享 | +| 特性 | 本地仓库 | 远程仓库 | +| -------- | ------------ | ------------------ | +| **位置** | 你的电脑 | 服务器(GitHub/Gitee等) | +| **用途** | 日常开发和提交 | 团队共享和备份 | +| **操作** | `git commit` | `git push/pull` | +| **网络要求** | 无需网络 | 需要网络连接 | +| **所有权** | 完全可控 | 团队共享 | **常见的远程仓库服务**: diff --git a/博客/编程与工具/Git团队协作指南.sync-conflict-20260615-122209-R4RFYM7.md b/博客/编程与工具/Git团队协作指南.sync-conflict-20260615-122209-R4RFYM7.md new file mode 100644 index 0000000..45a4427 --- /dev/null +++ b/博客/编程与工具/Git团队协作指南.sync-conflict-20260615-122209-R4RFYM7.md @@ -0,0 +1,3969 @@ +# Git团队协作指南:从入门到精通 + +> **作者**:刘航宇(河南工业大学人工智能协会) +> **面向读者**:有一定编程基础,需要团队协作参赛的同学 +> **代码语言**:Python为主 +> **预计阅读时间**:60分钟 +> **更新日期**:2026年4月23日 + +--- + +## 第一章:引言——为什么比赛团队需要Git? + +### 1.1 场景引入:从一场灾难说起 + +想象这样一个场景:你和两个队友正在准备一场重要的比赛——中国大学生计算机设计大赛。你们分工明确:小明负责后端开发,小红负责前端界面,你负责算法优化。项目是一个智能数据分析系统,需要在4周内完成初赛作品。 + +比赛开始的第一周,你们斗志昂扬。小明每天把代码打包发给你们,小红在群里说"我改了main.py,你们注意一下",你也在自己的电脑上继续优化算法。 + +然后,灾难开始降临。 + +**周一早晨**,小明发现小红的修改覆盖了他上周写的后端接口。原因是小红在本地文件上直接编辑,没有做任何记录。所有人都不知道最终版本是哪个,"小明说的那个版本在哪?" + +**周三下午**,你花了两天优化了一个算法,但合并到小明的代码时,整个系统崩溃了。你想恢复到优化前的状态,但已经太晚了——你没有做任何备份。 + +**周五晚上**,比赛提交前一天,小红发现她的界面配色完全乱了。查了半天,发现是小明前天改的一个CSS文件影响的。没有人知道谁改了什么,什么时候改的,为什么要改。 + +最后,你们勉强拼凑出一个能跑的作品,但代码质量堪忧,技术文档也残缺不全。省赛结果出来,意料之中——没有晋级。 + +**这个故事每天都在各个大学的比赛团队中上演。** 问题的根源是什么?不是能力不足,不是时间不够,而是**缺乏有效的团队协作工具和流程**。 + +### 1.2 没有版本控制时的典型问题 + +在团队项目中,没有使用Git等版本控制系统时,会遇到以下典型问题: + +``` +❌ "我写的代码去哪了?" + 问题:文件被覆盖,无法恢复之前的版本 + +❌ "这版本怎么跑不动了?" + 问题:环境不一致,依赖版本冲突 + +❌ "小明改了我的代码!" + 问题:多人同时编辑同一文件,产生冲突 + +❌ "最终版本到底是哪个?" + 问题:版本混乱,不知道哪个是最新的 + +❌ "能恢复上周的代码吗?" + 问题:没有历史记录,无法追溯 +``` + +这些问题不仅存在于比赛中,在日常的项目开发中也极为常见。更糟糕的是,在比赛的高压环境下,这些问题会被放大十倍。 + +### 1.3 Git登场:版本控制的革命 + +2005年,一个芬兰程序员Linus Torvalds(没错,就是发明Linux的那位大神)创造了Git。Git是一个**分布式版本控制系统**,它的设计目标就是解决团队协作中的版本控制问题。 + +**Git的核心特性**: + +**1. 本地仓库** +``` +Git的魔力在于:每个开发者的电脑上都有一份完整的代码仓库。 +这意味着你可以: +- 在没有网络的地方工作 +- 查看完整的提交历史 +- 尝试各种修改而不影响他人 +- 随时回退到任何历史版本 +``` + +**2. 快照而非差异** +``` +传统版本控制(如CVS、SVN)保存的是文件的变化(diff) +Git保存的是每个时刻的完整"快照" + +这就像: +- SVN:记录每次修改的"补丁" +- Git:给每个时刻拍一张"照片" + +好处:查看历史更快,恢复更快,数据更安全 +``` + +**3. 强大的分支管理** +``` +Git的分支轻如鸿毛。 +你可以: +- 随意创建分支,互不干扰 +- 轻松合并不同分支的代码 +- 尝试新功能,失败了可以一键删除分支 +- 管理多个版本的并行开发 +``` + +### 1.4 为什么比赛团队必须使用Git? + +对于参加比赛的团队来说,Git不仅是开发工具,更是**团队的命脉**。 + +**团队协作的价值**: + +``` +✅ 分工明确:每人负责不同模块,互不干扰 +✅ 版本管理:每个阶段都有清晰的版本标记 +✅ 回溯能力:任何时候都能恢复到之前的版本 +✅ 代码审查:通过Pull Request检查队友的代码 +✅ 备份保障:代码保存在远程服务器,不怕丢失 +``` + +**针对比赛场景的价值**: + +``` +📌 省赛/国赛作品迭代 + 初赛 → 复赛 → 决赛,每个阶段都有版本记录 + +📌 团队分工 + 前端、后端、算法、文档,不同人在不同分支开发 + +📌 代码审查 + 队长可以审核队员提交的代码,保证质量 + +📌 紧急回退 + 出现问题时,快速回退到稳定版本 +``` + +想象一下,如果你的团队使用Git: + +- 每天早上`git pull`,就能获取队友昨天的最新成果 +- 每人一个功能分支,互不干扰,完成了再合并 +- 每次提交都有记录,"谁在什么时候改了什么,为什么改" +- 省赛提交版本打上标签,决赛优化再开新分支 +- 即使电脑坏了,代码还在远程仓库 + +**这就是Git能给你的:有序、可追溯、安全的团队协作。** + +### 1.5 学习目标 + +通过本篇文章,你将掌握: + +``` +📚 理论知识 + - Git的核心概念和设计思想 + - 工作区、暂存区、版本库的关系 + - 分支管理的原理 + +💻 命令操作 + - Git基本命令:add、commit、push、pull + - 分支操作:创建、切换、合并、变基 + - 解决冲突的方法和技巧 + - 标签管理和版本发布 + +🤝 团队协作 + - Pull Request流程 + - 代码审查的方法 + - Git Flow工作流 + - 比赛项目的最佳实践 +``` + +无论你是第一次参加比赛的新手,还是想提升团队协作能力的老手,这篇文章都能帮助你建立扎实的Git使用技能。 + +### 1.6 本章小结 + +本章中,我们了解了: + +1. **典型灾难场景**:没有版本控制时,团队协作会遇到的四大问题 +2. **Git的诞生**:Linus Torvalds在2005年创造了Git +3. **Git的核心特性**:本地仓库、快照存储、强大分支 +4. **比赛团队的价值**:分工明确、版本管理、回溯能力、备份保障 + +下一章,我们将深入理解Git的核心概念,包括工作区、暂存区、版本库的关系,以及文件的各种状态。 + +--- + +## 第二章:基础概念——理解Git的核心思想 + +### 2.1 Git的三大工作区域 + +理解Git的第一步,是搞清楚它的三大工作区域:**工作区(Working Directory)**、**暂存区(Staging Area)**、**Git仓库(Repository)**。 + +这三个区域的关系,可以用下图表示: + +``` +┌─────────────────────────────────────────────────────┐ +│ 你的电脑 │ +│ │ +│ ┌──────────────┐ git add ┌───────────────┐ │ +│ │ │ ───────────→ │ │ │ +│ │ 工作区 │ │ 暂存区 │ │ +│ │ Working Dir │ │ Staging Area │ │ +│ │ │ ←─────────── │ (Index) │ │ +│ └──────────────┘ git reset └───────┬───────┘ │ +│ │ │ +│ │ git commit│ +│ ↓ │ +│ ┌───────────────┐ │ +│ │ │ │ +│ │ Git仓库 │ │ +│ │ Repository │ │ +│ │ │ │ +│ └───────────────┘ │ +│ │ +└─────────────────────────────────────────────────────┘ +``` + +**工作区(Working Directory)** + +工作区是你电脑上的项目文件夹。在这一区域,你可以自由地创建、编辑、删除文件。它就像你的工作台,工具和材料都摆在这里。 + +``` +工作区的特点: +- 实际存储项目文件的位置 +- 可以随意修改,不影响Git记录 +- 修改后需要通过git add添加到暂存区 +``` + +**暂存区(Staging Area)** + +暂存区是Git的一个特殊区域,位于`.git/index`文件中。它像一个"准备区",存放着下次提交要包含的文件快照。 + +``` +暂存区的特点: +- 精确控制要提交的内容 +- 可以选择性地添加文件的部分修改 +- 是工作区和Git仓库之间的缓冲地带 +- git add命令将工作区的修改放入暂存区 +``` + +**Git仓库(Repository)** + +Git仓库是Git的核心数据库,存储着所有的项目历史。它位于项目根目录下的`.git`文件夹中(这是一个隐藏文件夹)。 + +``` +Git仓库的特点: +- 包含所有的提交记录 +- 记录了每个分支的信息 +- 保存了文件的完整快照 +- .git文件夹就是Git仓库本身 +``` + +### 2.2 文件的生命周期 + +在Git中,每个文件都会处于以下四种状态之一: + +``` +未跟踪(Untracked) → 已跟踪(Tracked) + ↓ + 已修改(Modified) + ↓ + 已暂存(Staged) + ↓ + 已提交(Committed) +``` + +**四种文件状态详解**: + +**1. 未跟踪(Untracked)** + +新创建的文件,还没有被Git管理。在`git status`中显示为"Untracked files"。 + +```powershell +# 查看状态 +git status + +# 输出示例: +# Untracked files: +# (use "git add ..." to include in what will be committed) +# new_feature.py ← 这个文件是新建的,还没有被Git跟踪 +``` + +**2. 已修改(Modified)** + +已跟踪的文件被修改了,但还没有放入暂存区。 + +```powershell +# 修改已跟踪的文件 +# 编辑 new_feature.py + +git status + +# 输出示例: +# Changes not staged for commit: +# modified: new_feature.py ← 这个文件被修改了,但还没暂存 +``` + +**3. 已暂存(Staged)** + +修改的文件已经通过`git add`添加到暂存区,等待下一次提交。 + +```powershell +git add new_feature.py + +git status + +# 输出示例: +# Changes to be committed: +# modified: new_feature.py ← 已经添加到暂存区 +``` + +**4. 已提交(Committed)** + +文件已经通过`git commit`保存到本地仓库,形成一个历史快照。 + +```powershell +git commit -m "feat: 添加新功能" + +git status + +# 输出示例: +# nothing to commit, working tree clean ← 工作区是干净的 +``` + +### 2.3 Git的对象模型 + +Git是如何存储数据的?理解这个问题能帮助你更好地使用Git。 + +Git使用四种主要对象来存储数据: + +**Blob对象** + +Blob(Binary Large Object)存储文件的内容。每个文件的每个版本都是一个独立的Blob对象。 + +```python +# 假设你有一个Python文件 +# 文件内容:print("Hello World") + +# Git会为这个内容创建一个Blob对象 +# Blob = 文件内容的二进制快照 +``` + +**Tree对象** + +Tree对象存储目录结构。它包含多个指向Blob和其他Tree的引用。 + +```python +# 假设项目结构: +# project/ +# ├── main.py +# ├── utils.py +# └── config/ +# └── settings.py + +# Git会创建: +# 1. main.py → Blob对象 +# 2. utils.py → Blob对象 +# 3. settings.py → Blob对象 +# 4. config/ → Tree对象(包含settings.py的引用) +# 5. project/ → Tree对象(包含所有文件的引用) +``` + +**Commit对象** + +Commit对象是整个系统的核心。它包含: +- 指向Tree对象的引用(项目在该时刻的快照) +- 指向父Commit的引用(上一个提交) +- 作者和提交者的信息 +- 提交时的描述信息 + +```python +# Commit对象的内容(简化版) +{ + "tree": "abc123...", # 项目快照的Tree对象 + "parent": "def456...", # 上一个提交 + "author": "小明 ", + "committer": "小明 ", + "message": "feat: 添加用户认证功能" +} +``` + +**Tag对象** + +Tag对象用于标记特定的Commit,通常用于标记发布版本。 + +```python +# Tag对象 +{ + "object": "abc123...", # 指向某个Commit + "type": "commit", + "tag": "v1.0.0", + "tagger": "小明 ", + "message": "第一个正式发布版本" +} +``` + +**为什么了解这些?** + +理解Git的对象模型能帮助你: + +``` +✅ 理解为什么Git如此高效 + - 相同内容的文件只存储一次 + - 每个对象都有唯一的SHA-1哈希值 + - 查看历史非常快速 + +✅ 理解Git的完整性保证 + - 任何修改都会改变哈希值 + - Git能检测出任何文件损坏 + +✅ 在高级操作中游刃有余 + - 理解rebase、cherry-pick等操作的原理 +``` + +### 2.4 远程仓库的概念 + +Git是分布式版本控制系统,这意味着每个开发者的电脑上都有完整的仓库。但为了让团队成员之间共享代码,我们需要一个"中央服务器"——这就是远程仓库。 + +**本地仓库 vs 远程仓库** + +| 特性 | 本地仓库 | 远程仓库 | +|------|---------|---------| +| **位置** | 你的电脑 | 服务器(GitHub/Gitee等) | +| **用途** | 日常开发和提交 | 团队共享和备份 | +| **操作** | `git commit` | `git push/pull` | +| **网络要求** | 无需网络 | 需要网络连接 | +| **所有权** | 完全可控 | 团队共享 | + +**常见的远程仓库服务**: + +**1. GitHub(国际最流行)** +``` +优点: +- 全球最大的代码托管平台 +- 开源项目丰富 +- 功能完善,社区活跃 + +缺点: +- 国内访问速度慢 +- 私有仓库有限制 + +适合:开源项目、国际合作 +``` + +**2. Gitee(码云,国内首选)** +``` +优点: +- 国内访问速度快 +- 私有仓库免费 +- 中文界面 + +缺点: +- 国际访问较慢 +- 功能比GitHub稍少 + +适合:国内团队比赛项目 +``` + +**3. GitLab** +``` +优点: +- 支持自建服务器 +- 功能强大 +- CI/CD集成好 + +缺点: +- 需要自己维护服务器 + +适合:企业、团队自建 +``` + +**4. Coding(腾讯云)** +``` +优点: +- 国内速度快 +- 与腾讯云服务集成 + +缺点: +- 使用人数相对较少 + +适合:快速原型、小团队 +``` + +**本地和远程的交互**: + +```powershell +# 克隆远程仓库到本地 +git clone https://gitee.com/team/competition-project.git + +# 这时你的本地有两个仓库: +# 1. 本地仓库:完整的历史记录 +# 2. 远程仓库:origin,指向Gitee上的仓库 + +# 日常工作流程: +# 1. 本地提交:git commit +# 2. 推送到远程:git push origin main +# 3. 获取队友更新:git pull origin main +``` + +### 2.5 理解SHA-1哈希 + +Git中的一切都由SHA-1哈希值标识。这个40位的十六进制字符串看起来像这样: + +``` +abc123def456abc123def456abc123def456abc1 +``` + +**哈希值的作用**: + +``` +✅ 唯一标识:每个对象都有唯一的哈希值 +✅ 内容寻址:相同内容产生相同的哈希 +✅ 完整性保证:任何修改都会改变哈希值 +✅ 不可猜测:无法从哈希值推断内容 +``` + +**哈希值的实际使用**: + +```powershell +# Git中经常使用哈希值的前几位来引用提交 +git log --oneline + +# 输出示例: +# abc123d feat: 添加用户认证 +# def456e fix: 修复登录bug +# 789ghij init: 项目初始化 + +# 你可以使用缩写: +git show abc123d +git diff abc123d..def456e +git reset --hard abc123d +``` + +**为什么重要?** + +理解SHA-1哈希能帮助你理解Git的核心原理: + +```python +# Git对象存储原理 +对象内容 + 类型 + 大小 → 计算SHA-1哈希 → 存储为文件名 + +# 这意味着: +# 1. 相同内容 = 相同哈希 = 只存储一次(节省空间) +# 2. 任何修改 = 哈希变化 = Git能检测到 +# 3. 哈希是内容的指纹 = 完整性保证 +``` + +### 2.6 本章小结 + +本章中,我们深入理解了Git的核心概念: + +``` +📚 三大工作区域 + - 工作区:实际编辑文件的地方 + - 暂存区:准备提交的文件快照 + - Git仓库:存储完整历史的地方 + +📝 四种文件状态 + - 未跟踪:新文件,还没被Git管理 + - 已修改:已跟踪文件被修改 + - 已暂存:修改已加入暂存区 + - 已提交:已保存到仓库 + +🏗️ Git对象模型 + - Blob:文件内容 + - Tree:目录结构 + - Commit:提交快照 + - Tag:版本标记 + +🌐 远程仓库 + - GitHub、Gitee、GitLab等 + - 本地和远程的交互:push/pull +``` + +下一章,我们将开始实战操作,学习Git的基本命令。 + +--- + +## 第三章:实战入门——Git基本操作 + +### 3.1 安装与初始配置 + +**安装Git** + +在Windows上安装Git非常简单: + +```powershell +# 方法一:下载安装包 +# 访问:https://git-scm.com/download/win +# 下载并运行安装程序 + +# 方法二:使用winget(Windows包管理器) +winget install Git.Git + +# 方法三:使用Scoop(你已经在用Scoop) +scoop install git +``` + +安装完成后,打开PowerShell或Git Bash,验证安装: + +```powershell +# 检查Git版本 +git --version + +# 输出示例: +# git version 2.40.0.windows.1 +``` + +**Python环境准备** + +为了演示方便,我们需要一个Python环境: + +```powershell +# 检查Python版本 +python --version +# 确保有Python 3.6+ + +# 创建演示项目目录 +mkdir git-tutorial +cd git-tutorial +``` + +**Git初始配置** + +使用Git前,需要设置你的身份信息。这很重要,因为每次提交都会记录你的信息: + +```powershell +# 设置用户名(必须) +git config --global user.name "你的名字" + +# 设置邮箱(必须) +git config --global user.email "your.email@example.com" + +# 查看所有配置 +git config --list + +# 输出示例: +# user.name=刘航宇 +# user.email=3364451258@qq.com +# core.editor=code --wait +# init.defaultbranch=main +``` + +**配置参数说明**: + +``` +--global vs 本地配置 + +--global(全局): +- 作用于当前用户的所有项目 +- 配置文件在 ~/.gitconfig(用户主目录) + +不加--global(项目级): +- 只作用于当前项目 +- 配置文件在 .git/config + +建议:用户信息使用--global,其他可用项目级配置 +``` + +**编辑器配置** + +推荐使用VS Code作为Git的默认编辑器: + +```powershell +# 设置VS Code为默认编辑器 +git config --global core.editor "code --wait" + +# 其他常见编辑器设置 +# Vim +git config --global core.editor vim + +# Notepad++ +git config --global core.editor "notepad++ -multiInst -nosession" +``` + +### 3.2 初始化仓库 + +Git仓库是你项目的版本控制容器。有两种方式创建仓库: + +**方法一:从头创建新仓库** + +```powershell +# 创建项目目录 +mkdir my-competition-project +cd my-competition-project + +# 初始化Git仓库 +git init + +# 输出: +# Initialized empty Git repository in D:/projects/my-competition-project/.git/ + +# 查看所有文件(包括隐藏文件) +ls -la + +# 输出示例: +# drwxr-xr-x 2 user 4096 Apr 23 10:00 . +# drwxr-xr-x 2 user 4096 Apr 23 10:00 .. +# drwxr-xr-x 3 user 4096 Apr 23 10:00 .git ← Git仓库目录 +``` + +`git init`命令做了以下事情: + +``` +✅ 创建 .git 目录 +✅ 初始化Git仓库的基本结构 +✅ 设置默认分支为main +✅ 创建空的commit历史 +``` + +**方法二:克隆已有仓库** + +当队长已经创建了仓库,你需要克隆到本地: + +```powershell +# 基本克隆 +git clone https://gitee.com/team/project.git + +# 指定文件夹名称 +git clone https://gitee.com/team/project.git my-project + +# 克隆特定分支 +git clone -b develop https://gitee.com/team/project.git + +# 查看克隆后的信息 +cd project +git remote -v + +# 输出: +# origin https://gitee.com/team/project.git (fetch) +# origin https://gitee.com/team/project.git (push) +``` + +### 3.3 创建Python演示项目 + +为了更好地学习Git,我们创建一个简单的Python项目: + +**创建项目结构** + +```powershell +# 创建目录结构 +mkdir -p src tests docs + +# 创建Python文件 +touch src/__init__.py +touch src/main.py +touch src/data_processor.py +touch tests/__init__.py +touch tests/test_processor.py +``` + +**Python代码示例:数据处理模块** + +这是一个完整的数据处理类,我们用它来演示Git的各种操作: + +```python +# src/data_processor.py +"""数据处理器模块 - 用于比赛项目的数据处理 + +这个模块演示了Python面向对象编程的各种特性, +包括类型提示、文档字符串、异常处理等。 +""" + +from typing import List, Dict, Any, Optional +import json +from datetime import datetime +import os + + +class DataProcessor: + """数据处理器类 + + 用于处理比赛项目中的数据清洗和转换。 + + Attributes: + name: 处理器的名称 + data: 存储的原始数据 + processed_count: 已处理的数据条数 + """ + + def __init__(self, name: str = "default") -> None: + """初始化数据处理器 + + Args: + name: 处理器的名称,用于标识不同的处理器 + """ + self.name = name + self.data: List[Dict[str, Any]] = [] + self.processed_count: int = 0 + self.created_at: str = datetime.now().isoformat() + + def load_data(self, filepath: str) -> bool: + """加载JSON格式的数据文件 + + Args: + filepath: 数据文件的路径 + + Returns: + bool: 加载是否成功 + """ + try: + # 检查文件是否存在 + if not os.path.exists(filepath): + print(f"文件 {filepath} 未找到") + return False + + # 读取并解析JSON + with open(filepath, 'r', encoding='utf-8') as f: + content = f.read() + self.data = json.loads(content) if content.strip() else [] + + print(f"成功加载 {len(self.data)} 条数据") + return True + + except json.JSONDecodeError as e: + print(f"JSON解析错误: {e}") + return False + except Exception as e: + print(f"加载失败: {e}") + return False + + def process(self) -> List[Dict[str, Any]]: + """处理加载的数据 + + 对每条数据进行转换和增强处理。 + + Returns: + List[Dict]: 处理后的数据列表 + """ + results: List[Dict[str, Any]] = [] + + for item in self.data: + # 处理单条数据 + processed_item = { + 'timestamp': datetime.now().isoformat(), + 'original': item, + 'processed_value': self._calculate(item), + 'processor_name': self.name + } + results.append(processed_item) + self.processed_count += 1 + + return results + + def _calculate(self, item: Dict) -> float: + """内部计算方法 + + 根据数据项计算处理值。 + + Args: + item: 单条数据 + + Returns: + float: 计算结果 + """ + values = item.get('values', []) + if not values: + return 0.0 + + # 计算平均值 + return sum(values) / len(values) + + def save_results(self, results: List, output_path: str) -> bool: + """保存处理结果到文件 + + Args: + results: 要保存的结果数据 + output_path: 输出文件路径 + + Returns: + bool: 保存是否成功 + """ + try: + with open(output_path, 'w', encoding='utf-8') as f: + json.dump(results, f, ensure_ascii=False, indent=2) + print(f"结果已保存到 {output_path}") + return True + except Exception as e: + print(f"保存失败: {e}") + return False + + def get_statistics(self) -> Dict[str, Any]: + """获取处理器统计信息 + + Returns: + Dict: 包含处理器名称、数据条数、处理条数等统计信息 + """ + return { + 'name': self.name, + 'total_items': len(self.data), + 'processed_items': self.processed_count, + 'created_at': self.created_at + } + + +# 模块主程序入口 +if __name__ == "__main__": + # 创建处理器实例 + processor = DataProcessor("比赛数据处理器") + + # 打印统计信息 + stats = processor.get_statistics() + print(f"初始化 {stats['name']}") + print(f"统计信息: {json.dumps(stats, indent=2, ensure_ascii=False)}") +``` + +### 3.4 基本命令:add、commit、status + +**命令一:git status** + +`git status`是查看仓库状态最常用的命令: + +```powershell +# 查看当前仓库状态 +git status + +# 简洁格式(推荐) +git status -s + +# 输出示例(详细模式): +# On branch main +# No commits yet +# Untracked files: +# (use "git add ..." to include in what will be committed) +# src/ +# tests/ +# docs/ + +# 状态符号解释: +# ?? = 未跟踪的文件(Untracked) +# A = 新添加到暂存区的文件(Added) +# M = 修改过的文件(Modified) +# D = 删除的文件(Deleted) +# R = 重命名的文件(Renamed) +``` + +**命令二:git add** + +`git add`将文件添加到暂存区,为提交做准备: + +```powershell +# 添加单个文件 +git add src/data_processor.py + +# 添加整个目录 +git add src/ + +# 添加所有文件(当前目录下的所有变更) +git add . + +# 添加所有修改和删除,但不添加未跟踪的新文件 +git add -u + +# 添加特定模式的所有文件 +git add *.py + +# 查看暂存区 +git status +# 输出: +# Changes to be committed: +# new file: src/data_processor.py +``` + +**实际演示流程**: + +```powershell +# Step 1: 查看状态 +git status -s +# ?? src/__init__.py +# ?? src/main.py +# ?? src/data_processor.py +# ?? tests/__init__.py +# ?? docs/ + +# Step 2: 添加Python文件到暂存区 +git add src/ + +# Step 3: 再次查看状态 +git status -s +# A src/__init__.py +# A src/main.py +# A src/data_processor.py +# ?? tests/__init__.py +# ?? docs/ +``` + +**命令三:git commit** + +`git commit`将暂存区的内容保存到仓库,形成一个历史快照: + +```powershell +# 基本提交(推荐使用) +git commit -m "feat: 添加数据处理模块" + +# 提交并显示变更统计 +git commit -v + +# 提交所有已跟踪文件的修改(不包括新文件) +git commit -am "fix: 修复数据处理逻辑" + +# 修改最后一次提交(追加遗漏的文件) +git add tests/ +git commit --amend --no-edit + +# 查看提交历史 +git log + +# 简洁格式 +git log --oneline +``` + +**提交信息规范**: + +良好的提交信息能让团队成员快速理解你的修改。建议使用以下格式: + +```bash +: + + + +