Full Learning Log

This archive is generated from Renyuan_Log.md so every original date section, table, code block, ASCII diagram, and explanation remains findable from the tech blog.

The original log is rendered as line-numbered source blocks on purpose. This avoids a malformed code fence in the source from swallowing later Markdown while still preserving every table, code block, flow diagram, link, command, and explanation in original order.

Coverage Index

DateSource LinesCode BlocksTable RowsDiagram BlocksFirst Subheading
Preamble1-4000稔远学习日志
2025-03-195-54101知识学习
2025-03-2055-68000知识学习
2025-03-2169-228706知识学习
2025-03-22229-414402实践
2025-03-23415-528502知识学习
2025-03-24529-693101实践
2025-03-25694-964400知识学习
2026-03-26965-1092201实践
2026-03-271093-1143000知识学习
2026-03-281144-1314300知识学习
2026-03-291315-1318000毕设毕设!
2026-03-301319-1420500知识学习
2026-04-011421-1440000知识学习
2026-04-021441-1485100知识学习
2026-04-031486-1540000知识学习
2026-04-041541-1550000知识学习
2026-04-051551-1554000毕设毕设!
2026-04-061555-1561000拯救计划很好看
2026-04-071562-1621000知识学习
2026-04-081622-1690000实践
2026-04-091691-1710000尝试更多的开源策略
2026-04-101711-1773000尝试更多的开源策略
2026-04-111774-18700290实践
2026-04-121871-1899101知识学习
2026-04-131900-2002200实践
2026-04-142003-2127101知识学习
2026-04-152128-2129000原文
2026-04-162130-2132000原文
2026-04-172133-2255202知识学习
2026-04-202256-2270000Encoder 输出Z矩阵的归宿:KV
2026-04-222271-2309000vLLM-Router 完整运行起来了
2026-04-262310-2393100CUDA
2026-04-272394-2410000远方 faraway
2026-04-282411-2474160CUDA 编程
2026-04-292475-2577000RoPE
2026-04-302578-2594000关于推理框架
2026-05-012595-2606000GPU操作
2026-05-042607-26460130vLLM 工程边界与目录地图
2026-05-052647-2818101不同的并行方式
2026-05-072819-2820000原文
2026-05-082821-2822000原文
2026-05-092823-2824000原文
2026-05-102825-2830000原文
2026-05-112831-3015090FSDP / ZeRO-3 和张量并行 TP 的区别
2026-05-123016-3022000mini-vllm 源码完结!
2026-05-133023-30680120尝试启动 DeepSeek V4 Flash 的推理服务
2026-05-143069-3082000手动配置一天的 DeepSeek V4 环境
2026-05-153083-3113000河套学院晟腾课程
2026-05-163114-33857391DeepSeek V4 Flash W8A8 部署总结
2026-05-173386-3500552CUDA kernel 与 device function
2026-05-203501-3511000原文
2026-05-213512-3538060算子学习 Chapter 3:数值处理与规约
2026-05-223539-3596240LayerNorm 和 RMSNorm 的几何理解

Artifact Index

TypeDateSource LinesLabel
code2025-03-1935-45fenced block
diagram2025-03-1935-45ASCII / flow diagram inside fenced block
code2025-03-2185-92fenced block
code2025-03-2193-116fenced block
diagram2025-03-2193-116ASCII / flow diagram inside fenced block
code2025-03-21130-135fenced block
diagram2025-03-21130-135ASCII / flow diagram inside fenced block
code2025-03-21149-169fenced block
diagram2025-03-21149-169ASCII / flow diagram inside fenced block
code2025-03-21172-184fenced block
diagram2025-03-21172-184ASCII / flow diagram inside fenced block
code2025-03-21189-197fenced block
diagram2025-03-21189-197ASCII / flow diagram inside fenced block
code2025-03-21200-225fenced block
diagram2025-03-21200-225ASCII / flow diagram inside fenced block
code2025-03-22242-292fenced block
diagram2025-03-22242-292ASCII / flow diagram inside fenced block
code2025-03-22307-309fenced block
code2025-03-22326-352fenced block
diagram2025-03-22326-352ASCII / flow diagram inside fenced block
code2025-03-22368-413fenced block
code2025-03-23421-423fenced block
code2025-03-23428-430fenced block
code2025-03-23431-433fenced block
code2025-03-23440-458fenced block
diagram2025-03-23440-458ASCII / flow diagram inside fenced block
code2025-03-23463-527fenced block
diagram2025-03-23463-527ASCII / flow diagram inside fenced block
code2025-03-24551-636fenced block
diagram2025-03-24551-636ASCII / flow diagram inside fenced block
code2025-03-25736-741fenced block
code2025-03-25744-759fenced block
code2025-03-25837-853fenced block
code2025-03-25861-912fenced block
code2026-03-26974-997fenced block
diagram2026-03-26974-997ASCII / flow diagram inside fenced block
code2026-03-261000-1063fenced block
code2026-03-281179-1191fenced block
code2026-03-281200-1256fenced block
code2026-03-281259-1279fenced block
code2026-03-301351-1356fenced block
code2026-03-301359-1363fenced block
code2026-03-301366-1370fenced block
code2026-03-301373-1378fenced block
code2026-03-301381-1390fenced block
code2026-04-021470-1478fenced block
table2026-04-111801-1820Markdown table
table2026-04-111824-1832Markdown table
code2026-04-121889-1898fenced block
diagram2026-04-121889-1898ASCII / flow diagram inside fenced block
code2026-04-131909-1928fenced block
code2026-04-131951-1957fenced block
code2026-04-142076-2103fenced block
diagram2026-04-142076-2103ASCII / flow diagram inside fenced block
code2026-04-172166-2175fenced block: python
diagram2026-04-172166-2175ASCII / flow diagram inside fenced block
code2026-04-172183-2202fenced block: RoPE
diagram2026-04-172183-2202ASCII / flow diagram inside fenced block
code2026-04-262352-2359fenced block: cpp
code2026-04-282427-2429fenced block: cpp
table2026-04-282438-2443Markdown table
table2026-05-042613-2625Markdown table
orphan_fence2026-05-052801-2801unpaired code fence marker within date section
code2026-05-052803-2814fenced block: 在 Megatron-LM / vLLM 中典型结构:
diagram2026-05-052803-2814ASCII / flow diagram inside fenced block
table2026-05-112840-2848Markdown table
table2026-05-133032-3039Markdown table
table2026-05-133045-3048Markdown table
table2026-05-163159-3168Markdown table
code2026-05-163256-3265fenced block: text
diagram2026-05-163256-3265ASCII / flow diagram inside fenced block
code2026-05-163277-3288fenced block: yaml
table2026-05-163292-3302Markdown table
code2026-05-163318-3320fenced block: cpp
code2026-05-163332-3334fenced block: cpp
table2026-05-163342-3347Markdown table
code2026-05-163351-3353fenced block: text
table2026-05-163357-3362Markdown table
code2026-05-163366-3368fenced block: text
table2026-05-163372-3377Markdown table
code2026-05-163381-3383fenced block: text
table2026-05-173409-3413Markdown table
code2026-05-173417-3423fenced block: cpp
code2026-05-173433-3435fenced block: text
diagram2026-05-173433-3435ASCII / flow diagram inside fenced block
code2026-05-173463-3467fenced block: cpp
code2026-05-173481-3483fenced block: text
diagram2026-05-173481-3483ASCII / flow diagram inside fenced block
code2026-05-173489-3491fenced block: text
table2026-05-213526-3531Markdown table
code2026-05-223567-3569fenced block: text
code2026-05-223575-3578fenced block: text
table2026-05-223584-3587Markdown table

Original Log

Preamble

Source lines: 1-4


0001  # 稔远学习日志
0002  
0003  仅个人学习与实践记录,便于回顾与整理。
0004  

2025-03-19

Source lines: 5-54


0005  # 2025-03-19
0006  
0007  ## 知识学习
0008  
0009  #### 图解Transformer
0010  非常清晰的图示教程,用矩阵拆解Transformer,难度梯度很合适
0011  [教程](https://jalammar.github.io/illustrated-transformer/)
0012  
0013  #### TypeScript/npm包管理
0014  
0015  #### TypeScript[(清华大学的AI教育项目)](https://github.com/Ryannnice/CUHK-LLM-Edu/edit/main/README.md)
0016  
0017  整个项目绝大部分使用TypeScript,文件结构、调用逻辑非常复杂
0018  TypeScript 是 JavaScript 的超集
0019  
0020  #### npm
0021  
0022  npm = Node Package Manager(Node.js 包管理器)
0023  
0024  安装库和工具/管理依赖(项目里用到的第三方包)/**运行脚本**(比如启动 Vue、React 或 Node 项目)
0025  *npm run <脚本名>: 运行 package.json 里的脚本*
0026  
0027  #### FastAPI python框架了解
0028  第一次实习,第一次接触偏工程的项目:不同于科研的是,可行性/整体模块的缝合、运行似乎比细致的优化更重要
0029  
0030  清华的开源教育项目未采用前后端分离架构
0031  
0032  使用.json格式请求体完成数据/信息传递
0033  
0034  前端直接通过函数调用REST API:
0035  ```
0036   fastapi_backend/
0037      └── static/
0038          ├── index.html        # 主页(需求输入 + 设置)
0039          ├── generate.html     # 生成流程页(SSE 大纲流 + 进度)
0040          ├── classroom.html    # 课堂播放页(幻灯片/测验/聊天)
0041          ├── app.js            # 全局工具:API 调用、设置存储、路由
0042          ├── generate.js       # 生成流程逻辑
0043          ├── classroom.js      # 课堂播放逻辑(幻灯片渲染、测验、聊天)
0044          └── style.css         # 全局样式
0045  ```
0046  
0047  ## 实践
0048  
0049  #### FastAPI
0050  在原本的REST API接口上,建立/fastapi_backend文件夹,用FastAPI封装全部18个功能的api
0051  原有.ts文件前端直接调用api的所有逻辑均保留,与Fast后端接口不冲突
0052  
0053  实现后端分离之后,建立/fastapi_backend/static文件夹,仅使用js/html初步实现前端功能,以验证FastAPI后端接口可行性
0054  

2025-03-20

Source lines: 55-68


0055  # 2025-03-20
0056  
0057  ## 知识学习
0058  
0059  #### 开源库OnlySpecs
0060  这是自动生成软件的agent系统,可能对项目第二部分*WorkShop*有帮助
0061  (上午团队实现workshop功能时发现直接调用LLM实现代码(软件编程)能力有限:贪吃蛇不成功,推箱子成功)
0062  试图部署该开源项目,接入我们的项目
0063  
0064  ## 实践
0065  
0066  #### Linux bash
0067  上午claude api爆了,以为是网络问题重新配置安装一遍windows WSL的linux的网络环境
0068  

2025-03-21

Source lines: 69-228


0069  # 2025-03-21
0070  
0071  ## 知识学习
0072  
0073  #### 开源库OnlySpecs
0074  
0075  #### node-pty
0076  
0077  pty.spawn("claude")
0078  
0079  相当于**在程序里打开**一个**终端**窗口
0080  
0081  #### AIEngine
0082  
0083  一个“可以驱动 Claude CLI 干活”的执行器
0084  
0085  ```
0086  return new Promise((resolve, reject) => {
0087      proc.onExit((e) => {
0088          if (e.exitCode === 0) resolve()
0089          else reject(new Error(...))
0090      })
0091  })
0092  ```
0093  ```
0094  ┌─────────────┐
0095  │ run() 调用  │
0096  │ await engine│
0097  └─────┬───────┘
0098        │
0099        ▼
0100  ┌─────────────┐
0101  │ Promise     │   <-- pending 状态
0102  │ resolve/reject 内部管子
0103  └─────┬───────┘
0104        │
0105        ▼
0106  ┌─────────────┐
0107  │ proc.onExit │  <-- Claude CLI 退出触发
0108  │ e.exitCode  │
0109  └─────┬───────┘
0110        │
0111        ▼
0112  if(exitCode==0) resolve()  else reject(error)
0113        │
0114        ▼
0115  Promise 状态变更 → 外层 await/then/catch 收到结果
0116  ```
0117  
0118  #### Shim
0119  
0120  是一种兼容层或适配器,用于在不修改原有代码的情况下,让新旧接口或系统之间能够协同工作
0121  
0122  这很适用于最小化更改,让该开源项目快速应用于我们的项目中,以此为起点吧
0123  
0124  #### Node.js Web 服务器
0125  
0126  Node.js 是一个运行环境,可以用 JavaScript 写服务端程序
0127  
0128  "Node.js Web 服务器"就是用 Node.js 写的 HTTP 服务,比如用 Express、Fastify、Koa 等框架搭建的后端,和阿里云服务器不冲突
0129  
0130  ```
0131  阿里云 ECS(服务器硬件/系统)
0132      └── Nginx(反向代理,监听 80/443 端口)
0133            └── Node.js 进程(监听 3000 端口)
0134                  └── 你的业务代码
0135  ```
0136  
0137  ## 实践
0138  
0139  #### Web版OnlySpecs功能测试
0140  
0141  已完成在web上的部署,使用简单的html转跳
0142  
0143  汉化前端菜单栏
0144  
0145  #### 融入大项目的Workshop部分
0146  
0147  **我们项目Workshop的原架构:**
0148  
0149  ```
0150  用户 → Vue
0151          │
0152          ▼
0153  FastAPI /generate
0154          │
0155          ▼
0156  DeepSeek 生成 HTML
0157          │
0158          ▼
0159  FastAPI /upload
0160          │
0161          ▼
0162  阿里云 OSS
0163          │
0164          ▼
0165  返回 URL
0166          │
0167          ▼
0168  Vue 展示
0169  ```
0170  
0171  **开源软件OnlySpecs的原架构:**
0172  ```
0173  Electron UI
0174      ↓
0175  Renderer (DOM + Monaco)
0176      ↓
0177  IPC
0178      ↓
0179  Main Process
0180      ↓
0181  node-pty
0182      ↓
0183  Claude CLI
0184  ```
0185  
0186  **新架构融合,两种方案:**
0187  
0188  ***方案一,分离式:***
0189  ```
0190   大项目
0191   ├── Vue 仪表盘(前端)   → Docker: Nginx 静态托管,端口 80
0192   ├── FastAPI 后端         → Docker: Uvicorn,端口 9000
0193   │   ├── /generate        → DeepSeek 流式生成 HTML
0194   │   └── /upload          → 阿里云 OSS 上传
0195   └── OnlySpecs(待加入)  → Docker: Node.js,端口 3579
0196       └── 功能:Specs 编写、Claude AI 代码生成、终端
0197  ```
0198  
0199  ***方案二,通过FastAPI使用功能,仅替换掉LLM,使用claude agent编写软件:***
0200  ```
0201  Vue 前端
0202      ↓ POST /generate-software { prompt }
0203    FastAPI
0204      ↓ 调用 OnlySpecs Node.js 服务(HTTP 或子进程)
0205    OnlySpecs Web Server
0206      ↓ 写 specs.md → 启动 Claude CLI
0207    Claude CLI(node-pty)
0208      ↓ 生成代码
0209    返回结果(文件路径 / OSS URL)
0210      ↑ 流式进度推送(SSE / WebSocket)
0211    Vue 前端展示
0212  
0213  
0214  FastAPI 端点设计
0215    # POST /generate-software
0216    # 输入:用户 prompt
0217    # 输出:SSE 流式进度 + 最终代码 URL
0218  
0219    @app.post("/generate-software")
0220    async def generate_software(prompt: str):
0221        # 1. 调用 OnlySpecs API 创建 specs 文件
0222        # 2. 触发 Generate from Specs
0223        # 3. 流式返回进度
0224        # 4. 完成后上传到 OSS,返回 URL
0225  ```
0226  
0227  先尝试方案二,先设计无头OnlySpecs的API
0228  

2025-03-22

Source lines: 229-414


0229  # 2025-03-22
0230  
0231  ## 实践
0232  
0233  FastAPI 编写完成,核心是/generate 根据用户指令来交给OnlySpecs,利用其功能生成
0234  
0235  api测试成功(文档:/home/ryan/OnlySpecs/docs/API_QUICKSTART.md,测试:终端运行 npm run test:api)
0236  
0237  接下来对接我们的项目第二部分Workshop:
0238  实现方式参考原框架,写出仿制的前端:/home/ryan/OnlySpecs/api-integration
0239  
0240  整个Pipeline:
0241  
0242  ```
0243    📁 Project Structure
0244  
0245    ~/OnlySpecs/api-integration/
0246    ├── app.py              # FastAPI backend (API proxy + SSE streaming)
0247    ├── requirements.txt    # Python dependencies
0248    ├── .env               # Environment configuration
0249    ├── .env.example       # Environment template
0250    ├── start.sh           # Quick start script
0251    ├── static/
0252    │   └── index.html     # Vue 3 frontend (312 lines)
0253    └── README.md          # Complete documentation
0254  
0255    🎯 Key Features Implemented
0256  
0257    Backend (FastAPI):
0258    - ✅ CORS-enabled API proxy to OnlySpecs API
0259    - ✅ SSE streaming for real-time log updates
0260    - ✅ Endpoints: /api/generate, /api/status, /api/logs, /api/tasks, /api/download
0261    - ✅ Error handling and timeout controls
0262  
0263    Frontend (Vue 3 + Tailwind):
0264    - ✅ Clean, responsive UI with Chinese localization
0265    - ✅ Real-time log display with auto-scroll
0266    - ✅ Task status tracking (pending/running/completed/failed)
0267    - ✅ History task list with click-to-load
0268    - ✅ Download generated code as ZIP
0269    - ✅ EventSource for SSE log streaming
0270  
0271    🚀 Quick Start
0272  
0273    # 1. Start OnlySpecs API (in one terminal)
0274    cd ~/OnlySpecs
0275    npm run api
0276  
0277    # 2. Start frontend (in another terminal)
0278    cd ~/OnlySpecs/api-integration
0279    ./start.sh
0280  
0281    Then visit: http://localhost:9000
0282  
0283    📝 Usage Flow
0284    1. Enter software requirements in the text area
0285    2. Click "开始生成" (Start Generation)
0286    3. Watch real-time Claude CLI logs
0287    4. Download code when complete or open in file explorer
0288    5. View history tasks in the collapsible section
0289  
0290    The implementation follows the plan exactly, using SSE for real-time updates and providing a simple, user-friendly interface for interacting
0291    with OnlySpecs.
0292  ```
0293  
0294  已经能完美运行,依靠简洁的web界面,通过FastAPI和OnlySpecs交互
0295  
0296  ***输入-->OnlySpecs-->Claude CLI-->输出***,用户只负责敲几个字:项目第二部分低代码的思想
0297  
0298  debug修复内容:
0299  
0300  1. claude开始但是不工作,代码写不进去项目文件夹 / claude 不动,接收不到指令:--print 标志可以完全跳过交互式 UI,直接输出结果。不需要 pty 模拟,改用子进程即可。用 spawn + --print 替换整个 pty 方案,彻底解决交互式 UI 问题。
0301  
0302  2. 下载 ZIP之后win系统打不开:之前是把 OnlySpecs API 返回的 JSON 当 ZIP 存的,当然打不开。现在后端拿到 codePath,用 shutil.make_archive 真正打包成 ZIP,Win11 可以直接解压。
0303  
0304  3. “在文件管理器中打开”的按钮点不动:新增了 /api/open/{task_id} 接口,调用 xdg-open 打开 Linux 文件管理器,同时在界面显示代码路径。
0305  WSL2 里 xdg-open 无法直接打开 Windows 文件管理器。需要用 explorer.exe 来打开,但路径要转换成 Windows 格式。
0306  转换出来是 \\wsl.localhost\Ubuntu\... 格式,Win11 的文件资源管理器可以直接打开这个 UNC 路径。
0307  ```
0308  \\wsl.localhost\Ubuntu\home\ryan\Documents\OnlySpecs\api-workspaces\task_1774168877875_1u1yudaz6\code_v0001
0309  ```
0310  
0311  ***全部修复***
0312  
0313  朝着更更更低代码平台进发:
0314  
0315  📋 计划总结
0316  
0317  核心功能: 在 Web 界面添加 4 种输出类型选择:
0318  
0319  1. 📄 源代码 - 可编辑的源文件
0320  2. 🌐 Web 应用 - 单文件 HTML,浏览器直接运行
0321  3. 💻 桌面程序 - Windows .exe 可执行文件(自动打包)
0322  4. 📱 手机应用 - PWA 渐进式 Web 应用
0323  
0324  用户使用Pipeline:
0325  
0326  ```
0327  用户浏览器
0328      │
0329      ▼
0330  Vue 前端
0331      │
0332      ▼
0333  FastAPI API
0334      │
0335      ▼
0336  OnlySpecs AI Engine (调用Claude CLI)
0337      │
0338      ▼
0339  生成代码
0340      │
0341      ▼
0342  Build Worker(Docker)
0343      │
0344      ▼
0345  编译为 .exe
0346      │
0347      ▼
0348  上传到 OSS
0349      │
0350      ▼
0351  用户下载
0352  ```
0353  
0354  本地项目列表已更新:
0355  
0356  新功能:
0357  1. 类型标签 - 每个项目左侧显示彩色标签:
0358    - 📄 源代码(紫色)
0359    - 🌐 Web应用(靛蓝色)
0360    - 💻 桌面程序(橙色)
0361    - 📱 手机应用(粉色)
0362  2. 对应按钮 - 根据项目类型显示不同的主功能按钮:
0363    - 源代码:📄 线上查看 + 📦 下载源码
0364    - Web应用:🌐 线上运行 + 📦 下载源码
0365    - 桌面程序:💻 下载 .exe + 📦 下载源码
0366    - 手机应用:📱 下载应用 + 📦 下载源码
0367  
0368  ```
0369    部署步骤:
0370  
0371    1. 在阿里云服务器上安装依赖:
0372    安装Node.js
0373    curl -fsSL https://deb.nodesource.com/setup_20.x | sudo -E bash -
0374    sudo apt-get install -y nodejs
0375  
0376    安装Python
0377    sudo apt-get install python3 python3-pip
0378  
0379    安装Docker
0380    curl -fsSL https://get.docker.com | sudo sh
0381    sudo usermod -aG docker $USER
0382  
0383    安装Claude CLI
0384    npm install -g @anthropic-ai/claude-code
0385  
0386    2. 配置Claude API密钥:
0387    export ANTHROPIC_API_KEY="your-api-key-here"
0388  
0389    3. 打包项目:
0390    cd ~/OnlySpecs
0391    tar -czf onlyspecs-deploy.tar.gz \
0392      src/ \
0393      api-integration/ \
0394      package.json\
0395      tsconfig.json
0396    4. 上传到服务器:
0397    scp onlyspecs-deploy.tar.gz user@your-server:/home/user/
0398  
0399    5. 在服务器上解压并启动:
0400    tar -xzf onlyspecs-deploy.tar.gz
0401    npm install
0402    docker pull cdrx/pyinstaller-windows
0403  
0404    启动服务
0405    npm run api &
0406    cd api-integration && python3 app.py &
0407  
0408    6. 配置防火墙:
0409    sudo ufw allow 3580
0410    sudo ufw allow 9000
0411  
0412    访问:http://your-server-ip:9000
0413  ```
0414  

2025-03-23

Source lines: 415-528


0415  # 2025-03-23
0416  
0417  ## 知识学习
0418  
0419  #### Docker
0420  
0421  ```
0422  https://www.bilibili.com/video/BV1THKyzBER6/?share_source=copy_web&vd_source=035cd776909e96dadfc9bbaeb1588cd4
0423  ```
0424  
0425  #### FastAPI
0426  
0427  FastAPI配合**后端开发**:
0428  ```
0429  https://www.bilibili.com/video/BV1eUxve7Ein/?share_source=copy_web&vd_source=035cd776909e96dadfc9bbaeb1588cd4
0430  ```
0431  ```
0432  https://fastapi.org.cn/python-types/#pydantic-models
0433  ```
0434  
0435  上下文工程 Context Engineering
0436  
0437  #### 谕书的Workshop框架和API
0438  
0439  项目框架采用FC容器(每个项目一个),而不是fs操作本地文件
0440  ```
0441  OSS+FC的架构:
0442  ┌─────────────────────────────────────────────────────┐
0443  │                    用户浏览器                         │
0444  │  左:对话+文件树    中:代码查看    右:预览iframe      │
0445  └──────────┬──────────────────────────────┬───────────┘
0446             │              │ iframe src
0447             ▼                             ▼
0448  ┌─────────────────┐            ┌──────────────────────┐
0449  │  你的FastAPI服务 │            │  FC容器(每个项目一个) │
0450  │  (项目管理/调度) │            │   dev server:9000    │
0451  └────────┬────────┘            └──────────┬───────────┘
0452           │                                │
0453      ┌────┴──────────────────────────┐     │
0454      │         阿里云基础设施         │     │
0455      │  OSS(代码+静态)  RDS(MySQL)    |◄───┘
0456      │  ACR(镜像仓库)   MNS(消息队列) │
0457      └───────────────────────────────┘
0458  ```
0459  这个线上部署运行架构值得融合,今天做这件事:
0460  
0461  ## 实践
0462  
0463  ```
0464  OnlySpecs 的现有架构梳理:
0465  
0466    四种运行模式:
0467    ┌──────────────┬───────────────┬──────┬─────────────────────┐
0468    │     模式     │     入口      │ 端口 │        说明         │
0469    ├──────────────┼───────────────┼──────┼─────────────────────┤
0470    │ 桌面应用     │ npm run dev   │ —    │ Electron + IPC      │
0471    ├──────────────┼───────────────┼──────┼─────────────────────┤
0472    │ 无头 API     │ npm run api   │ 3580 │ Express + 子进程    │
0473    ├──────────────┼───────────────┼──────┼─────────────────────┤
0474    │ Web 界面     │ npm run web   │ 3579 │ Express + WebSocket │
0475    ├──────────────┼───────────────┼──────┼─────────────────────┤
0476    │ FastAPI 前端 │ python app.py │ 9000 │ 代理 API + SSE      │
0477    └──────────────┴───────────────┴──────┴─────────────────────┘
0478    核心数据流:
0479  
0480    桌面模式:
0481    Renderer (Monaco/xterm) → IPC → ipc-handlers.ts → 文件系统 / node-pty / Claude SDK
0482  
0483    API模式:
0484    HTTP POST /generate → 创建任务 → 启动 claude CLI子进程 → 写入 code_v0001/
0485    HTTP GET /logs/:id→ 轮询日志
0486  
0487    Web 模式:
0488    浏览器 → WebSocket → web-server(复用 ipc-handlers 逻辑)→ 文件系统 / node-pty
0489    FastAPI 模式:
0490    Vue3 → FastAPI (9000) → HTTP → API Server (3580) → claude CLI↑ SSE 实时日志流
0491  
0492    关键文件
0493  
0494    ┌──────────────────────────┬─────────────────────────────────────────────┐
0495    │           文件           │                    作用                     │
0496    ├──────────────────────────┼─────────────────────────────────────────────┤
0497    │ src/main/ipc-handlers.ts │ 所有 IPC 处理(编辑器、终端、文件、GitHub) │
0498    ├──────────────────────────┼─────────────────────────────────────────────┤
0499    │ src/main/claude/sdk.ts   │ Claude Agent SDK 封装,流式输出             │
0500    ├──────────────────────────┼─────────────────────────────────────────────┤
0501    │ src/renderer/index.ts    │ 主App 类,组装所有 UI组件                   │
0502    ├──────────────────────────┼─────────────────────────────────────────────┤
0503    │ src/api-server/index.ts  │ 无头 API,任务管理 + 子进程调度             │
0504    ├──────────────────────────┼─────────────────────────────────────────────┤
0505    │ src/web-server/index.ts  │ WebSocket 服务,镜像 IPC 协议               │
0506    ├──────────────────────────┼─────────────────────────────────────────────┤
0507    │ api-integration/app.py   │ FastAPI 代理 + SSE + ZIP 下载 + 项目管理    │
0508    └──────────────────────────┴─────────────────────────────────────────────┘
0509  
0510    ---
0511    数据存储
0512  
0513    ~/Documents/OnlySpecs/
0514    ├── editors/          # 编辑器内容(每个 tab 一个 JSON)
0515    ├── config.json       # API Key、上次项目路径
0516    ├── api-workspaces/   # API 模式生成的代码
0517    │   └── task_*/
0518    │├── specs_v0001.md
0519    │       └── code_v0001/
0520    └── tmp/              # GitHub import 临时克隆目录
0521  
0522    两个独立的 Claude 调用路径:
0523    1. 桌面/Web 模式 — 通过 claude/sdk.ts 调用 @anthropic-ai/claude-agent-sdk,流式返回结果给 UI
0524    2. API 模式 — 直接 spawn claude CLI 子进程,用 node-pty 捕获输出,存入任务日志
0525  
0526    两条路径互相独立,不共享代码。
0527  ```
0528  

2025-03-24

Source lines: 529-693


0529  # 2025-03-24
0530  
0531  ## 实践
0532  
0533  #### Debug 网络代理
0534  
0535  #### 完善数据流向
0536  
0537  优化prompt以暴露onlyspecs原有复杂文档生成能力
0538  
0539  #### FastAPI
0540  
0541  对接谕书的架构,我第一次跑通了FastAPI的agent接口!
0542  
0543  已成功完成/generate接口,成功对接、上传到OSS
0544  
0545  目前,需完善FC系统
0546  
0547  #### FC容器
0548  
0549  #### 方案规划
0550  **potential solution:**
0551  ```
0552  完成 FC 容器调度与多文件生成验证
0553  
0554   Context
0555  
0556   当前状态:
0557   - Claude CLI 集成完成:POST /generate → Claude CLI → 收集文件 → OSS
0558   - 标准化 prompt 已实现:build_enhanced_prompt() 注入 scripts/ 目录要求
0559   - FC API 调度完成:POST /containers/{project_id}/start → 创建 FC 函数 → 返回 preview_url
0560   - OSS 存储完成:save_project() 上传文件 + manifest.json
0561  
0562   核心问题:
0563   FC 容器无法启动生成的代码,因为缺少容器内的启动逻辑:
0564   1. 缺少 entrypoint.sh - 容器启动脚本,负责从 OSS 下载代码并执行标准化脚本
0565   2. 缺少容器镜像 - base-node18/base-python39/base-fullstack 镜像不存在
0566   3. 无法验证多文件生成 - 没有端到端测试验证 Claude CLI 生成的复杂项目能否正常运行
0567  
0568   目标:
0569   完成 FC 调度链路,实现:用户 prompt → Claude 生成 → OSS 存储 → FC 容器运行 → 前端 iframe 预览
0570  
0571   ---
0572   实现方案
0573  
0574   方案 A:完整 FC 容器方案(生产级)
0575  
0576   需要实现:
0577   1. 创建 3 个 Dockerfile(base-node18, base-python39, base-fullstack)
0578   2. 编写 entrypoint.sh 脚本(下载 OSS 代码 → 执行 prepare.sh → 执行 dev.sh)
0579   3. 构建镜像并推送到 ACR
0580   4. 验证完整链路
0581  
0582   优点: 真实生产环境,完全符合 goal.md 架构
0583   缺点: 需要 Docker 环境、ACR 推送权限、FC 配额
0584  
0585   ---
0586   方案 B:本地验证方案(快速验证)
0587  
0588   只验证文件生成和脚本执行,不依赖 FC:
0589   1. 调用 /generate 生成项目
0590   2. 从 OSS 下载生成的文件到本地临时目录
0591   3. 本地执行 scripts/prepare.sh 和 scripts/dev.sh
0592   4. 验证服务能在 9000 端口启动
0593  
0594   优点: 快速验证,无需云资源
0595   缺点: 不是真实 FC 环境
0596  
0597   ---
0598   推荐方案:方案 B(本地验证)+ 方案 A 的容器脚本准备
0599  
0600   分两步走:
0601  
0602   第一步:本地验证多文件生成(立即可做)
0603  
0604   创建测试脚本 test_generation.py:
0605   - 调用 /generate API 生成简单项目(如 "创建一个 Hello World 网页")
0606   - 检查返回的 files 列表是否包含:
0607     - scripts/prepare.sh
0608     - scripts/dev.sh(包含端口 9000)
0609     - scripts/build.sh
0610     - scripts/start.sh
0611     - 业务代码文件(如 src/index.html)
0612   - 从 OSS 下载所有文件到 /tmp/test-{project_id}/
0613   - 执行 bash scripts/prepare.sh(安装依赖)
0614   - 后台执行 bash scripts/dev.sh(启动服务)
0615   - 验证 curl http://localhost:9000 返回 200
0616  
0617   验证目标: 确认 Claude CLI 生成的项目结构正确,脚本可执行
0618  
0619   ---
0620   第二步:准备 FC 容器资源(为生产部署做准备)
0621  
0622   创建 3 个容器镜像的 Dockerfile 和 entrypoint.sh:
0623  
0624   文件结构:
0625   Reference-framework/
0626     docker/
0627       base-node18/
0628         Dockerfile
0629         entrypoint.sh
0630       base-python39/
0631         Dockerfile
0632         entrypoint.sh
0633       base-fullstack/
0634         Dockerfile
0635         entrypoint.sh
0636  ```
0637  
0638  #### 本地运行测试
0639  
0640  为了验证代码树的运行可行性。之后再真正在云端FC环境运行代码
0641  
0642  先完成**本地测试**,之后“登录 ACR”(ACR 指的是 Alibaba Cloud Container Registry):
0643  
0644  把构建的 Docker 镜像**上传到阿里云的镜像仓库**,让 **FC 容器**可以拉取运行
0645  
0646  排查出的三个问题:
0647  
0648  问题 1:系统代理拦截了 localhost 请求 → 502
0649  
0650    http_proxy=http://172.26.224.1:7890
0651    所有 requests 发出的请求(包括 localhost)都走了代理,代理无法转发本地流量,返回 502
0652    解决:对 localhost 请求显式禁用代理,对公网 OSS 保留代理:
0653    PROXY_FOR_LOCALHOST = {"http": None, "https": None}
0654    PROXY_FOR_OSS = {"http": os.environ.get("http_proxy"), ...}
0655  
0656  问题 2:FastAPI 和生成项目端口冲突(都是 9000)
0657  
0658    测试脚本发完请求后,Step 6 验证的 http://localhost:9000 其实打到了 FastAPI 自己
0659    解决:FastAPI 改用 9001 启动:
0660    uvicorn app:app --port 9001
0661  
0662  问题 3:prepare.sh 写死 /workspace 本地不存在
0663  
0664    Claude 生成的脚本是为 FC 容器设计的(容器里代码在 /workspace),本地 /workspace 不存在
0665    解决:下载脚本后自动替换路径: patched = content.replace("/workspace", test_dir)
0666  
0667    验证结果:
0668    ┌───────────────────────────┬────────────────────────────┐
0669    │           步骤            │            结果            │
0670    ├───────────────────────────┼────────────────────────────┤
0671    │ Claude CLI 生成文件       │  7个文件包含所有必需脚本 │
0672    ├───────────────────────────┼────────────────────────────┤
0673    │ OSS 上传/下载             │  正常                    │
0674    ├───────────────────────────┼────────────────────────────┤
0675    │ prepare.sh(npm install) │  成功                    │
0676    ├───────────────────────────┼────────────────────────────┤
0677    │ dev.sh(npx serve :9000) │  成功                    │
0678    ├───────────────────────────┼────────────────────────────┤
0679    │ HTTP 200 验证             │  返回完整 HTML           │
0680    └───────────────────────────┴────────────────────────────┘
0681    整条链路 prompt → Claude → OSS → 脚本执行 → HTTP服务 验证完毕。
0682  
0683    端口分配:
0684    ┌────────────────────────────┬──────┬────────────────────────────┐
0685    │            用途             │ 端口 │            备注
0686    ├────────────────────────────┼──────┼────────────────────────────┤
0687    │ FastAPI(uvicorn app:app)  │ 9000 │ 正常启动,不变
0688    ├────────────────────────────┼──────┼────────────────────────────┤
0689    │ 本地测试生成项目            │ 8080 │ 脚本自动 patch,测完还原
0690    ├────────────────────────────┼──────┼────────────────────────────┤
0691    │ FC 容器里生成项目           │ 9000 │ 真实环境不 patch,保持原样
0692    └────────────────────────────┴──────┴────────────────────────────┘
0693  

2025-03-25

Source lines: 694-964


0694  # 2025-03-25
0695  
0696  ## 知识学习
0697  
0698  #### 更全面的Coding agent
0699  
0700                  Workshop UI
0701                       │
0702                       ▼
0703              FastAPI Agent Orchestrator
0704                       │
0705          ┌────────────┼────────────┐
0706          ▼            ▼            ▼
0707       Planner       Coder       Debugger
0708          │            │            │
0709          ▼            ▼            ▼
0710       spec.md      code tree      patch
0711          │            │            │
0712          └──────► OSS Storage ◄───┘
0713                        │
0714                        ▼
0715                  FC Container
0716                        │
0717                        ▼
0718                  Dev Server
0719                        │
0720                        ▼
0721                     iframe
0722  
0723  ## 实践
0724  
0725  Multi-Agent的使用!震撼!
0726  
0727  #### OpenAI Codex, WSL2 CLI 模式运行
0728  
0729  Claude的API实在不稳定,50%时间403/503 error,换用Codex分组:
0730  
0731  VS Code的json配置openai codex默认不读取,所以需配置文件:
0732  
0733  我使用终端模式在WSL2,配置两个文件后成功运行gpt 5.4 model(默认的gpt 5 拥挤,不行):
0734  
0735  **GNU nano 7.2    /home/ryan/.codex/auth.json**
0736  ```
0737  {
0738    "auth_mode": "apikey",
0739    "OPENAI_API_KEY": "<REDACTED>"
0740  }
0741  ```
0742  
0743  **GNU nano 7.2    /home/ryan/.config/openai/config.toml**
0744  ```
0745  disable_response_storage = true
0746  model = "gpt-5-codex"
0747  model_provider = "custom"
0748  model_reasoning_effort = "high"
0749  windows_wsl_setup_acknowledged = true
0750  
0751  [model_providers.custom]
0752  base_url = "https://hone.vvvv.ee/v1"
0753  name = "custom"
0754  requires_openai_auth = true
0755  wire_api = "responses"
0756  
0757  [notice]
0758  hide_full_access_warning = true
0759  ```
0760  
0761  #### 优化LLM-CLI
0762  
0763  整个调用流程已换成阿里千问百炼
0764  
0765  #### 沙箱
0766  
0767  容器实例已经被创建并注册成功,现在要让容器运行起来
0768  核心思想:AI 不直接运行代码,而是在 sandbox container 里运行。
0769  
0770  常规的AI Coding 沙箱架构:
0771  User
0772    │
0773    ▼
0774  Workshop UI (Web IDE)
0775    │
0776    ▼
0777  Agent Service
0778    │
0779    ▼
0780  Task Queue
0781    │
0782    ▼
0783  Sandbox Manager
0784    │
0785    ▼
0786  Container Runtime
0787    │
0788    ▼
0789  Dev Server
0790    │
0791    ▼
0792  Preview Router
0793    │
0794    ▼
0795  Preview URL
0796  
0797  流程:
0798  create container
0799        ↓
0800  git clone / oss pull
0801        ↓
0802  npm install
0803        ↓
0804  start dev server
0805        ↓
0806  expose preview url
0807  
0808  Response body
0809  Download
0810  {
0811    "project_ids": [
0812      "proj-1774117999499",
0813      "proj-1774118618839",
0814      "proj-1774118736018",
0815      "proj-1774118993314",
0816      "proj-1774119281456",
0817      "proj-1774119530520",
0818      "proj-1774261757601",
0819      "proj-1774334030907",
0820      "proj-1774336370281",
0821      "proj-1774345523559",
0822      "proj-1774365171381",
0823      "proj-1774365451436",
0824      "proj-1774365898657",
0825      "proj-1774366108059",
0826      "proj-1774366895437",
0827      "proj-1774367246631",
0828      "proj-1774402506350",
0829      "proj-1774403324560",
0830      "proj-1774424651687",
0831      "string"
0832    ]
0833  }
0834  
0835  已经添加排查各个项目信息的接口:**GET /projects/summary**
0836  **接口返回:**
0837  ```
0838  {
0839    "projects": [
0840      {
0841        "project_id": "proj-1774117999499",
0842        "manifest_exists": true,
0843        "template": "base-node18",
0844        "created_at": "2026-03-21T18:33:20Z",
0845        "preview_url": "http://snake.fortuneai.cc/projects/proj-1774117999499/index.html",
0846        "file_count": 12,
0847        "has_fc": false,
0848        "fc_status": "NotFound",
0849        "fc_preview_url": null,
0850        "manifest_error": null,
0851        "fc_error": null
0852      },
0853  ```
0854  排查之后,已经逐一删除现存的FC
0855  
0856  现在,添加删除某项目的功能
0857  
0858  已经全部删除!现在有很多新接口,皆可使用:
0859  
0860  **目前的 FastAPI 接口:**
0861  ```
0862  
0863  POST
0864  /generate
0865  Generate With Openai
0866  
0867  POST
0868  /upload
0869  Upload
0870  
0871  POST
0872  /save-project
0873  Save Project
0874  
0875  GET
0876  /projects
0877  Get Projects
0878  
0879  GET
0880  /projects/summary
0881  Get Projects Summary
0882  
0883  GET
0884  /projects/{project_id}
0885  Get Project
0886  
0887  DELETE
0888  /projects/{project_id}
0889  Delete Project
0890  
0891  GET
0892  /projects/{project_id}/tree
0893  Get Project Tree
0894  
0895  GET
0896  /projects/{project_id}/file
0897  Get Project File
0898  
0899  ***container***
0900  
0901  POST
0902  /containers/{project_id}/start
0903  Start Container
0904  
0905  GET
0906  /containers/{project_id}/status
0907  Get Container Status
0908  
0909  DELETE
0910  /containers/{project_id}
0911  Delete Container
0912  ```
0913  
0914  #### 实现较复杂的 Coding Agent(方案2)
0915  
0916  **Coding Agent方案2:** ***/generate/agent***
0917  
0918  把现在的“一次 LLM 调用吐全量文件”升级成
0919  “多轮:生成 -> 本地验证 -> 读错误 -> 修复 -> 再验证”
0920  的闭环,让产物在上传 OSS/跑 FC 前就尽量稳定
0921  
0922  架构规划:
0923  把它从“一个接口里把所有事做完”拆成三层,并且用“可组合的工作流 + 可插拔的工具”承载未来功能增长。
0924  
0925    1) 分层与边界(关键)
0926    - API 层(routers/*):只负责参数校验、创建任务、返回 run_id/结果,不写业务逻辑。
0927    - Agent 编排层(agent/*):状态机 + 工作流引擎。负责多轮循环、停止条件、重试、取消、超时、产物汇总。
0928    - 工具与基础设施层(tools/* + services/*):把所有副作用封装成工具接口(LLM、文件系统、命令执行、HTTP 探测、OSS、FC),Agent 只能通过工具做
0929      事。
0930  
0931    2) 推荐目录结构(可扩展)
0932    - Reference-framework/routers/agent.py:/generate/agent、/runs/{run_id}、/runs/{run_id}/events、/runs/{run_id}/cancel
0933    - Reference-framework/agent/core.py:Run 状态机、Step 约定、上下文(Context)
0934    - Reference-framework/agent/workflows/*.py:工作流定义(生成-only、生成+验证、生成+上传OSS、生成+上传+部署FC…)
0935    - Reference-framework/agent/steps/*.py:原子步骤(generate_draft、normalize、write_workspace、validate、llm_patch、package、upload_oss、
0936      deploy_fc…)
0937    - Reference-framework/agent/tools/*.py:工具接口与实现适配(llm_tool、exec_tool、fs_tool、oss_tool、fc_tool、http_probe_tool)
0938    - Reference-framework/agent/store/*.py:RunStore/EventStore/ArtifactStore(先本地文件或内存,后续可换 Redis/DB,不影响上层)
0939  
0940    3) 核心数据模型(未来加功能不乱)
0941    - Run:run_id、status(queued/running/succeeded/failed/canceled)、workflow_name、config、created_at、updated_at
0942    - RunEvent:按时间追加(step_start/step_end/tool_call/tool_result/log/error),用于调试和前端展示
0943    - Artifact:生成文件集、workspace 路径、打包产物、OSS key、FC 预览 URL 等(都挂在 run 上)
0944  
0945    4) 工作流设计(把“越来越多功能”变成组合)
0946    - 每个 workflow 只是一个 step 列表 + 分支条件,例如:
0947        - generate_only: LLM生成 -> 规范化 -> 返回文件
0948        - generate_validate: LLM生成 -> 规范化 -> 落盘 -> prepare/build/start -> 健康检查 -> (失败则 patch 循环N次) -> 返回文件+报告
0949        - generate_save_oss: 在通过验证后追加 upload_oss -> 写manifest
0950        - generate_save_deploy_fc: 再追加 deploy_fc -> 探测预览URL
0951    - 每个 step 输入/输出都只读写 Context,不直接互相调用;这样加新能力只要“加 step + 改 workflow 配置”,不会把代码越堆越硬。
0952  
0953    5) LLM 交互规范(降低一次性大生成的 bug)
0954    - 修复轮:强制返回“补丁格式”,只允许改动少量文件(例如 {patches:[{path,content,op}]}),避免模型每轮重写全仓导致漂移。
0955    - 观察输入:只喂“失败日志摘要 + 相关文件(有限数量/有限字节)+ 明确目标”,并设置 max_iters、timeout、stop_condition。
0956  
0957    6) 执行与安全(必须提前规划)
0958    - exec_tool 统一加:工作目录隔离、命令白名单/模板、CPU/时间/输出大小上限、端口占用检查、进程回收。
0959    - fs_tool 统一加:路径穿越防护、只允许写入 run 的 workspace。
0960    - 日志与密钥脱敏:event 里禁止落 API_KEY/AK/SK 原文。
0961  
0962    这个结构最小闭环:/generate/agent + RunStore(EventStore) + generate_validate 工作流(多轮 patch 修
0963    复),其余 workflow(上传 OSS、部署 FC)用同一套 step 机制往后叠加
0964  

2026-03-26

Source lines: 965-1092


0965  # 2026-03-26
0966  
0967  ## 实践
0968  
0969  #### Coding Agent的较复杂实现与调用
0970  
0971  **Coding Agent方案2:** ***/generate/agent***
0972  
0973  方案2**函数调用流程图:**
0974  ```
0975    POST /generate/agent
0976    -> routers/agent.generate_with_agent()
0977    -> RunStore.create_run()
0978    -> run_generate_validate()
0979    -> build_enhanced_prompt()
0980    -> OpenAI-compatible /chat/completions 生成 files[]
0981    -> normalize_generated_files()
0982    -> materialize_files() 写入 run workspace
0983    -> _validate_workspace()
0984       -> prepare.sh
0985       -> build.sh
0986       -> start.sh / dev.sh
0987       -> HTTP probe
0988    -> 如果失败且 auto_fix=true
0989       -> pick_context_files()
0990       -> OpenAI-compatible /chat/completions 生成 patches[]
0991       -> apply_patches()
0992       -> normalize_generated_files()
0993       -> materialize_files()
0994       -> 再次验证
0995    -> RunStore.set_result() / set_status()
0996    -> GET /runs/{run_id} 轮询结果
0997  ```
0998  
0999  方案2,根据大型agent框架构建的**数据流向:**
1000  ```
1001    1. 请求进入 generate_with_agent()。它把 prompt/template/model/max_iters/... 组装进 config,创建一个 run_id。见 Reference-framework/routers/
1002       agent.py:61、Reference-framework/routers/agent.py:78。
1003    2. RunStore.create_run() 会在本地创建目录:
1004       /tmp/reference-agent-runs/{run_id}/
1005       里面至少有:
1006    - run.json
1007    - events.jsonl
1008    - workspace/
1009      见 Reference-framework/agent/store.py:57、Reference-framework/agent/store.py:45。
1010    3. 如果 wait=false,路由只是 asyncio.create_task(coro) 把 workflow 丢到后台;如果 wait=true,当前请求会一直等到 workflow 结束。见 Reference-
1011       framework/routers/agent.py:95。
1012    4. workflow 入口是 run_generate_validate()。它先把 run 状态改成 running,然后调用 build_enhanced_prompt() 拼模板约束,再调用
1013       generate_project_with_openai()。见 Reference-framework/agent/workflows/generate_validate.py:230、Reference-framework/agent/workflows/
1014       generate_validate.py:252、Reference-framework/services/generation_rules.py:481。
1015    5. LLM 层当前不是 Responses API,也不是 Codex CLI 配置链路,而是后端自己读取 .env,直接请求 OPENAI_BASE_URL/chat/completions,并要求返回严格
1016       JSON schema。见 Reference-framework/services/openai_service.py:18、Reference-framework/services/openai_service.py:162、Reference-
1017       framework/services/openai_service.py:226。
1018    6. 生成结果被规范成 List[SaveFileItem],也就是最核心的内部文件格式:
1019    - path
1020    - content
1021      见 Reference-framework/models.py:140。
1022    7. 生成完以后,会执行 normalize_generated_files()。这一步会强改模板相关内容,比如 Node18 依赖版本、补齐 scripts/*.sh、补 vite.config.js、修
1023       index.html 位置。见 Reference-framework/services/generation_rules.py:538。
1024    8. 然后 materialize_files() 把内存里的 files[] 写进 workspace/。注意这里是“覆盖同名文件”,不是“同步整个目录”。旧文件不会被删。见 Reference-
1025       framework/agent/tools/fs_tool.py:34。
1026    9. 如果 run_validation=false,workflow 到这里就结束,直接把 files[] 写入 run.result。见 Reference-framework/agent/workflows/
1027       generate_validate.py:264。
1028    10. 如果开启验证,会进入 _validate_workspace():
1029    - 找一个空闲本地端口
1030    - 注入环境变量 WORKSPACE/HOST/PORT
1031    - 顺序跑 prepare.sh、build.sh
1032    - 再启动 start.sh,没有就退回 dev.sh
1033    - 用 HTTP 探测本地地址
1034      见 Reference-framework/agent/workflows/generate_validate.py:62、Reference-framework/agent/workflows/generate_validate.py:75、Reference-
1035      framework/agent/workflows/generate_validate.py:143。
1036    11. 验证时实际执行 shell 的地方在 exec_tool.py:
1037    - run_bash_script() 跑 prepare/build
1038    - start_bash_script() 起长跑进程
1039    - wait_http_ready() 轮询页面
1040    - 成功后马上 terminate_process()
1041      见 Reference-framework/agent/tools/exec_tool.py:37、Reference-framework/agent/tools/exec_tool.py:98、Reference-framework/agent/tools/
1042      exec_tool.py:191。
1043    12. 如果验证失败且 auto_fix=true,才会进入 patch 回路:
1044    - pick_context_files() 选少量关键文件
1045    - _build_fix_prompt() 拼失败日志和上下文
1046    - generate_project_patches_with_openai() 让模型返回 patches[]
1047    - apply_patches() 应用补丁
1048    - 再次 normalize_generated_files()
1049    - 再次 materialize_files()
1050    - 再验证
1051      见 Reference-framework/agent/workflows/generate_validate.py:323、Reference-framework/agent/tools/fs_tool.py:80、Reference-framework/
1052      services/openai_service.py:254。
1053    13. 当前 patch 的语义只有“按 path 覆盖/新增”,没有 delete,没有 rename,也没有“按最终 files 全量同步 workspace”。这正是你说要先修的 4。见
1054       Reference-framework/agent/tools/fs_tool.py:60。
1055    14. 成功或失败后,workflow 会把结果写回 run.json。result 里会直接带完整文件内容和 validation report。见 Reference-framework/agent/workflows/
1056       generate_validate.py:297、Reference-framework/agent/store.py:123。
1057    run 的状态和文件
1058    当前 run 的生命周期基本是:
1059    - queued
1060    - running
1061    - succeeded / failed / canceled
1062    状态变化和关键动作都会追加到 events.jsonl。见 Reference-framework/agent/store.py:119。
1063  ```
1064  
1065  现在简化Agent流程:
1066  
1067  **Coding Agent 方案3:** ***/runs***
1068  
1069  舍弃了大型agent的流程()简化为简单的workflow。
1070  **完整链路成功:**
1071    - 生成成功
1072    - 本机 prepare/build/start 全部成功
1073    - 发布成功
1074    - 已创建 OSS 项目和 FC
1075  关键结果是:
1076    - run_id: run-1774496744666-42124bb9
1077    - project_id: proj-1774496798261
1078    - 最终状态:succeeded
1079  
1080  简化成功debug成功。
1081  
1082  ### Docker打包成功
1083  
1084  (base) ryan@ENVYKatana:/mnt/c/Desktop/generate/Reference-framework$ curl --noproxy '*' -i http://127.0.0.1:9000/healthz
1085  HTTP/1.1 200 OK
1086  date: Thu, 26 Mar 2026 07:33:08 GMT
1087  server: uvicorn
1088  content-length: 166
1089  content-type: application/json
1090  
1091  {"ok":true,"service":"reference-framework","runs_dir":"/data/reference-agent-runs","checks":{"runs_dir_writable":true,"openai_configured":true,"oss_configured":true}}(base)
1092  

2026-03-27

Source lines: 1093-1143


1093  # 2026-03-27
1094  
1095  ## 知识学习
1096  
1097  ### 探讨Coding Agent架构
1098  
1099  最终我们决定先完成强大的agent,再通过微调workflow的结构,格式化输出内容,以对接OSS/FC
1100  
1101  而不是一开始根据OSS/FC的结构化需求设计Agent
1102  
1103  逻辑关系是本末倒置的
1104  
1105  ### TypeScript
1106  
1107  [TypeScript教程](https://www.runoob.com/typescript/ts-tutorial.html)
1108  
1109  非常多的开源项目用 TS 写成!
1110  
1111  ## 实践
1112  
1113  ### 设计能够格式化输出的Coding Agent
1114  
1115  按现在这个目标,不需要把主流程改成“先输出 {"files":[...]} 再落盘”,具体而言:
1116  
1117  现有 coding agent 的主链路:
1118  1. 用户输入进入 SessionPrompt.prompt()
1119  2. 主 agent 开始推理
1120  3. agent 在过程中调用 write / edit / apply_patch
1121  4. 文件直接写到当前工作目录,比如你指定的 TEST/
1122  
1123  所以要加的,其实只是前置的一层:
1124  1. 检测是否是“生成项目”意图
1125  2. 如果不是,原样走现有聊天/分析流程
1126  3. 如果是,先把简单 prompt 例如“生成贪吃蛇游戏”增强成一条完整、规范、工程化的 prompt
1127  4. 然后把这条增强后的 prompt 继续交给现有 coding agent
1128  5. 后面的文件创建仍然由现有 agent 正常完成
1129  
1130  落实到改动操作:
1131  1. IntentDetector
1132     输入简单 prompt,比如“生成贪吃蛇游戏”。
1133     输出结构化判断:intent=generate_project | normal_chat,以及 template=base-node18 | base-python39。
1134  2. ProjectPromptEnhancer
1135     只在 intent=generate_project 时运行。
1136     用专门的 enhancer agent 深度理解、补全约束、规划输出。
1137     输出结构化结果建议是:
1138     template
1139     enhancedPrompt
1140     constraints
1141  3. 把 enhancedPrompt 覆盖原始 text part,再把这条增强后的 user message 交给现有主生成链路。
1142     后面的文件生成仍然由 write/edit/apply_patch 完成,直接落到 TEST/
1143  

2026-03-28

Source lines: 1144-1314


1144  # 2026-03-28
1145  
1146  ## 知识学习
1147  
1148  ### Opencode
1149  
1150  Opencode 内部调用逻辑
1151  
1152  ## 实践
1153  
1154  ### Coding Agent
1155  
1156  我通过修改opencode的工作流,实现了我们项目所需的Coding Agent!
1157  
1158  ### 封装 Coding Agent 到 FastAPI
1159  
1160  我找到更稳的落点了:直接给 opencode 加一个正式 CLI 子命令,比如 generate-project --prompt ...
1161  这样 FastAPI 只需要调用这个命令拿 JSON,完全绕开“内部再起一个 HTTP 服务”的不稳定链路
1162  
1163  我已经封装成功!
1164  现在能稳定输出.json格式的文件。
1165  
1166  ### 删改opencode大项目结构
1167  
1168  这很像我一年前删除Colias项目文件的过程......
1169  已完成并存档。这是目前的baseline版本。
1170  
1171  ### 流式输出架构
1172  
1173  我的流式推送接口应能:
1174  1.我希望前端能展示出当前项目的文件结构文件树、已有的文件。
1175  2.我希望不仅能够展示工作状态,还要有一行或者几行,实时显示LLM正在生成的代码。
1176  3.我希望用“思考状态”展示agent的工作状态。
1177  
1178  **推荐接口:**
1179  ```
1180  
1181  - POST /generate/jobs
1182    提交生成任务,返回 job_id
1183  - GET /generate/jobs/{job_id}/stream
1184    SSE 流式输出
1185  - GET /generate/jobs/{job_id}/result
1186    获取最终 { "files": [...] }
1187  - GET /generate/jobs/{job_id}
1188    获取任务当前快照
1189  - DELETE /generate/jobs/{job_id}
1190    可选,取消任务
1191  ```
1192  
1193  为什么这样设计:
1194  - 前端更容易接。POST 提交,GET 用 EventSource 收流。
1195  - 能断线重连。
1196  - 能同时拿“状态流”和“最终结果”。
1197  - 不破坏现有 /generate。
1198  
1199  **SSE 事件设计:**
1200  ```
1201  建议统一 JSON,事件名固定。
1202  - job.created
1203    {"job_id":"gen_xxx","status":"queued"}
1204  - thinking.status
1205    不传原始 CoT,只传安全摘要
1206    {
1207      "job_id":"gen_xxx",
1208      "phase":"planning",
1209      "label":"正在规划项目结构",
1210      "detail":"分析需求,确定模板和最小文件集"
1211    }
1212  - project.tree
1213    前端文件树用这个
1214    {
1215      "job_id":"gen_xxx",
1216      "paths":["package.json","scripts/start.sh","src/main.js"]
1217    }
1218  - file.snapshot
1219    当前已有文件内容
1220    {
1221      "job_id":"gen_xxx",
1222      "path":"src/main.js",
1223      "content":"..."
1224    }
1225  - file.delta
1226    可选,给前端展示“刚写出来的几行代码”
1227    {
1228      "job_id":"gen_xxx",
1229      "path":"src/main.js",
1230      "append":"const app = ...\n"
1231    }
1232  - preview.code
1233    专门给你第二个需求
1234    {
1235      "job_id":"gen_xxx",
1236      "path":"src/main.js",
1237      "lines":["const app = ...","app.listen(...)"]
1238    }
1239  - session.status
1240    忙闲状态
1241    {
1242      "job_id":"gen_xxx",
1243      "status":{"type":"busy"}
1244    }
1245  - job.completed
1246    {
1247      "job_id":"gen_xxx",
1248      "file_count":9,
1249      "result_url":"/generate/jobs/gen_xxx/result"
1250    }
1251  - job.failed
1252    {
1253      "job_id":"gen_xxx",
1254      "error":"..."
1255    }
1256  ```
1257  
1258  **怎么满足 3 个需求:**
1259  ```
1260  
1261  - 文件树、已有文件:
1262    用 project.tree + file.snapshot
1263  - 实时显示 LLM 正在生成的代码:
1264    用 file.delta 或 preview.code
1265    这个不要依赖模型“口头输出代码”,而要监听真实文件写入
1266  - 用“思考状态”展示 agent 状态:
1267    用 thinking.status内容来自阶段摘要,不直接暴露模型原始推理后端实现建议基于现有 packages/opencode/src/generate-api.ts 和 packages/opencode/src/server/generate-projectts改:
1268  - Python FastAPI 负责:
1269      - 创建 job
1270      - 保存 job 状态
1271      - 返回 SSE StreamingResponse
1272  - Bun 生成器负责:
1273      - 输出 JSONL 事件到 stdout
1274      - 最后一条输出最终 files
1275  - 生成器内部增加 3 类 hook:
1276      - 阶段变化 hook:发 thinking.status
1277      - 文件变化 hook:发 project.tree、file.snapshot、preview.code
1278      - 结束 hook:发 job.completed
1279  ```
1280  
1281  最重要的一点:thinking.status 不建议传模型原始 reasoning。
1282  建议只传这种:
1283  - 分析需求中
1284  - 规划项目结构中
1285  - 正在编写 package.json
1286  - 正在修复启动脚本
1287  - 正在校验最终结果
1288  这样前端效果够好,也安全。
1289  
1290  已实现
1291  
1292    改动在 app.py、packages/opencode/src/generate-api.ts、packages/opencode/src/server/generate-project.ts、packages/opencode/
1293    test/generate-api.test.ts。
1294  
1295    现在接口是:
1296  
1297    - POST /generate:保留原来的同步返回 { files: [...] }
1298    - POST /generate/jobs:创建流式生成任务
1299    - GET /generate/jobs/{job_id}:拿当前快照,里面有文件树、已有文件、预览代码、thinking 状态
1300    - GET /generate/jobs/{job_id}/stream:SSE 流
1301    - GET /generate/jobs/{job_id}/result:任务完成后拿最终 { files: [...] }
1302  
1303    流式行为现在支持:
1304  
1305    - 文件树:project.tree
1306    - 文件内容快照:file.snapshot
1307    - 代码预览:preview.code
1308    - 安全的工作状态:thinking.status
1309    - 任务状态:job.status
1310    - 最终结果:job.result
1311  
1312    另外,Bun 内部生成入口加了 --stream-jsonl,FastAPI 会把它解析成 job 状态并转成 SSE。同步 /generate 不受影响。reasoning 原文没
1313    有直接暴露给前端,只保留了状态级事件。
1314  

2026-03-29

Source lines: 1315-1318


1315  # 2026-03-29
1316  
1317  ## 毕设毕设!
1318  

2026-03-30

Source lines: 1319-1420


1319  # 2026-03-30
1320  
1321  ## 知识学习
1322  
1323  #### 对现有工程的思考:我们的目的是什么?
1324  
1325  ***真正缺的,不是“再造一个能连续对话的 /generate”,而是把 /generate 里更强的那部分能力搬进 web 的标准 session 路径里***
1326  
1327  #### 如果是workshop,那重心在于强大的coding agent、在线运行。但“生成自己的openclaw”这样级别的要求难以实现
1328  “贪吃蛇”是远远不够的。目前,coding agent已经足够强大,但在云端运行“用户生成的程序”仍需细致对接
1329  
1330  #### 如果是能解决具体需求的agent,那重心在于SKILL.md, 理解需求、细致的构造pipeline,通过多个skill真正去解决问题
1331  
1332  #### TODO List
1333  
1334  - find skills的SKILL.md, 进一步的,一堆工具skills,如何调用
1335  
1336  - 需求分析的SKILL.md
1337  
1338  - 不同应用场景,写paper,写report,上网
1339  
1340  - AI降重:带上自己的思想;  降重 SKILL.md
1341  
1342  - 让claude触手可得
1343  
1344  ## 实践
1345  
1346  #### Coding Agent网页版在线运行
1347  
1348  在现有网页应用上加OSS/FC在线运行:
1349  
1350  #### 第一阶段
1351  ```
1352    - 从 packages/opencode/src/server/generate-project.ts 抽出“项目 contract 校验”模块。
1353    - 保留现有规则:模板识别、必需文件、scripts/*.sh 约束、Vite/Vue 版本约束、Python/FastAPI约束。
1354    - 目标文件建议新建为 packages/opencode/src/server/project-contract.ts 或相近位置。
1355    - /generate 和 web session 后续都复用这一个模块。
1356  ```
1357  
1358  #### 第二阶段
1359  ```
1360    - 在 web 端加“在线构建并在线运行模式”开关。
1361    - 入口优先放在 packages/app/src/components/prompt-input/submit.ts 所在提交链路附近。
1362    - 这一步只做“把模式标记传到后端 prompt 流程”,不要先做部署。
1363  ```
1364  
1365  #### 第三阶段
1366  ```
1367    - 扩展 packages/opencode/src/plugin/prompt-enhancer.ts,让它在该模式下启用更强 project contract。
1368    - 普通聊天不受影响。
1369    - 项目生成首条消息启用:固定模板规则、输出目录规则、运行脚本规则、可部署规则。
1370  ```
1371  
1372  #### 第四阶段
1373  ```
1374    - 在标准 session 流里接入“校验失败自动修复”。
1375    - 不再走临时 /generate session。
1376    - 在同一 session 内,首轮生成完成后校验;失败时自动追加 repair prompt;成功则结束。
1377    - 这样 web 的持续对话、流式状态、消息历史都天然保留。
1378  ```
1379  
1380  #### 第五阶段
1381  ```
1382    - 只约束部署必需接口,不先绑定具体云厂商实现细节。
1383    - 先稳定:
1384      scripts/prepare.sh
1385      scripts/build.sh
1386      scripts/start.sh
1387      HOST
1388      PORT
1389      WORKSPACE
1390  ```
1391  
1392  保留 /generate: 作为外部 API、作为 contract 回归测试入口、作为未来批量生成入口
1393  
1394  最值得先做的是第 1 步和第 2 步。做完这两步,主路径就从“独立 /generate”转成“web session + 强约束项目模式”。
1395  下一步直接改两处:packages/opencode/src/server/generate-project.ts、packages/opencode/src/plugin/、prompt-enhancer.ts,然后再接 web 的模式开关。
1396  
1397  现在 web 版已经接上这条链路了:
1398  新会话输入框里新增了一个持久化的 “Run Online / 在线构建运行” 开关
1399  打开后,首条普通 prompt 会带上在线运行 mode marker;后端prompt-enhancer会据此启用之前加的“在线构建并运行”约束
1400  相关改动在 packages/app/src/components/prompt-input.tsx
1401  packages/app/src/components/prompt-input/submit.ts
1402  packages/opencode/src/plugin/prompt-enhancer.ts
1403  以及新的共享模块 packages/util/src/project-mode.ts
1404  
1405  ## 知识学习
1406  
1407  #### Workshop数据流动、架构细节分析讨论
1408  
1409  #### Seminar: World Model
1410  
1411  ## 实践
1412  
1413  #### Debug Web应用
1414  
1415  已添加“构建项目”模式,并在前端展示现已生成的文件
1416  
1417  带前端版本的完整pipeline已跑通!
1418  
1419  现在,对接CUHKSZ的第二前端。
1420  

2026-04-01

Source lines: 1421-1440


1421  # 2026-04-01
1422  
1423  ## 知识学习
1424  
1425  #### Claude Code 框架
1426  
1427  Claude Code 源码泄露...
1428  
1429  学一波:[Bilibili: Claude Code源码泄露!首发解读51万行代码!](https://www.bilibili.com/video/BV11zXCBFEMo/?spm_id_from=333.1387.top_right_bar_window_default_collection.content.click)
1430  
1431  ## 实践
1432  
1433  #### [Workshop 架构重构:MVP 版本](https://github.com/Ryannnice/Agent-Do)
1434  
1435  我们非常简洁、高效地实现了workshop。
1436  
1437  效果很好,并且摒弃了opencode的庞大源代码植入。
1438  
1439  优化前端,现在反应简洁迅速美观。
1440  

2026-04-02

Source lines: 1441-1485


1441  # 2026-04-02
1442  
1443  ## 知识学习
1444  
1445  #### Docker
1446  
1447  [Bilibili Docker](https://www.bilibili.com/video/BV1THKyzBER6/?spm_id_from=333.1387.favlist.content.click&vd_source=487ef5084994b81a0ec05eeffa991ed2)
1448  
1449  #### 关于项目未来的讨论
1450  
1451  教育平台是核心竞争力
1452  
1453  下一阶段定位:用户每天早晨五分钟了解AI圈大事,甚至上手实践,学习一二。
1454  
1455  ## 实践
1456  
1457  #### Workshop 架构重构:MVP 版本
1458  
1459  Debug, 增添流式输出功能
1460  
1461  #### Claude Code 宠物系统修复
1462  
1463  已经成功部署运行[“开源”的Claude Code](https://github.com/Ryannnice/claude-code)!
1464  
1465  修复了桌面宠物 /buddy 功能!
1466  
1467  #### Git Branch
1468  
1469  常见 type(非常重要)
1470  ```
1471  feature	  新功能	  feature/payment-api
1472  fix   	  修复 bug  fix/order-null-pointer
1473  hotfix  	紧急修复	hotfix/login-crash
1474  refactor	代码重构	refactor/cache-module
1475  docs	    文档修改	docs/api-guide
1476  test	    测试代码	test/user-service
1477  chore	    杂项修改	chore/dependency-update
1478  ```
1479  
1480  #### Workshop
1481  
1482  尝试构建多项目运行时,并展示:
1483  
1484  我们发现似乎并不需要FC或者公网URL。
1485  

2026-04-03

Source lines: 1486-1540


1486  # 2026-04-03
1487  
1488  ## 知识学习
1489  
1490  #### git / docker
1491  
1492  - 用 `git pull` 和 `docker compose up --build -d` 熟悉基本更新与重建流程。
1493  
1494  这一天原本保留了整段学习对话,这里压缩为日志摘要:
1495  
1496  - 学习对象是 `assignment1-basics`,目标是把 Transformer 训练链路真正串起来理解。
1497  - 当天抓住的主线是:
1498    `文本 -> tokenizer -> token id -> embedding -> Transformer -> logits -> cross entropy -> 反向传播 -> 参数更新`
1499  - 核心代码入口包括:
1500    `cs336_basics/tokenizer.py`、
1501    `cs336_basics/model/modules.py`、
1502    `cs336_basics/model/transformer.py`、
1503    `cs336_basics/trainer/data_loading.py`、
1504    `cs336_basics/trainer/utils.py`、
1505    `cs336_basics/train.py`
1506  - 最重要的认识有 6 点:
1507    1. 语言模型训练的本质是“给定前文,预测下一个 token”。
1508    2. `inputs` 是当前 token 序列,`targets` 是右移一位后的监督信号。
1509    3. Transformer 本体负责把上下文表示加工成下一个 token 的打分 `logits`。
1510    4. block 的核心结构是 Pre-Norm + Attention + FFN + Residual。
1511    5. `Linear`、`Embedding`、`RMSNorm`、`SwiGLU`、self-attention、RoPE 是后续必须啃透的基础模块。
1512    6. 训练闭环可以概括为:采样 -> 前向 -> 计算 loss -> backward -> 更新参数。
1513  - 当天的直接产出:
1514    - 给 `trainer/data_loading.py`
1515    - 给 `trainer/utils.py`
1516      增加了逐行中文注释,作为第一部分学习基线。
1517  - 下一步计划:
1518    继续理解 `Embedding`、`Linear`、attention 与 `transformer.py` 的整体拼装。
1519  
1520  ## 实践
1521  
1522  #### Docker构建Claude Code
1523  
1524  遇到构建docker镜像时候地址错误问题:
1525  
1526  - Claude 看起来执行了
1527  - 但改动写进了错误挂载目标
1528  - 当前 session 的真实 workspace/ 还是空的
1529  - 然后 runtime/start 检测不到 index.html / package.json,就返回 400 当前 session 没有可在线运行的项目
1530  
1531  问题不是 HTTP 400 本身,而是 Agent-Do 之前把 Claude 子容器的 workspace 挂到了错误的宿主机路径。
1532  AGENT_DATA_HOST_ROOT 在 Backend/WorkShop/.env 里指向了旧机器上的 /root/internship-szdsjyjy/...,
1533  但这台机器真实路径是 /home/ryan/CUHKSZ/Education_Platform/Backend/Agent-Do/data。
1534  
1535  - 修正了 Agent-Do 容器内 Docker CLI 的集成,避免之前的 input/output error: 'docker'
1536  - 修正了 Backend/WorkShop/.env 里的 AGENT_DATA_HOST_ROOT,并重启了 agent-do
1537  
1538  当前 qwen3-coder-next 这条模型链路有时会“看起来成功,实际上没写任何文件”。
1539  我准备从 WorkShop 侧加兜底:当第一轮生成后没有形成可预览项目时,自动发一轮更强约束的修复 prompt,而不是直接把 runtime/start 400 暴露给前端。
1540  

2026-04-04

Source lines: 1541-1550


1541  # 2026-04-04
1542  
1543  ## 知识学习
1544  
1545  #### PageAttention
1546  [怎么加快大模型推理?10分钟学懂VLLM内部原理,KV Cache,PageAttention](https://www.bilibili.com/video/BV1kx4y1x7bu/?spm_id_from=333.1391.0.0&vd_source=487ef5084994b81a0ec05eeffa991ed2)
1547  
1548  #### Flash Attention
1549  [Flash Attention 为什么那么快?原理讲解](https://www.bilibili.com/video/BV1UT421k7rA/?spm_id_from=333.1391.0.0&vd_source=487ef5084994b81a0ec05eeffa991ed2)
1550  

2026-04-05

Source lines: 1551-1554


1551  # 2026-04-05
1552  
1553  ## 毕设毕设!
1554  

2026-04-06

Source lines: 1555-1561


1555  # 2026-04-06
1556  
1557  ## 拯救计划很好看
1558  我觉得可以和星际穿越媲美。
1559  太空的浪漫很纯粹。
1560  [Bilibili细节解析](https://www.bilibili.com/video/BV1oSQZBRE8j/?spm_id_from=333.337.search-card.all.click)
1561  

2026-04-07

Source lines: 1562-1621


1562  # 2026-04-07
1563  
1564  ## 知识学习
1565  
1566  #### AI Station
1567  
1568  [AI Station 教程](https://xxl9u0uq9y2.feishu.cn/wiki/LVHvw3GCWiMlV4kjH25clngHnVf)
1569  
1570  #### LLM Router
1571  
1572  谕书的完整Pipeline
1573  
1574  #### Router 综述
1575  
1576  #### LLM 路由的概念设计空间
1577  本综述涵盖的范式(参见 1.3 节)为组织和理解文献提供了基础 。
1578  在实践中,现实世界的系统往往同时借鉴了不止一种范式 。
1579  为了补充基于范式的组织方式,路由方法还可以从更广泛的维度进行分类 :
1580  
1581  #### 决策时机 (When):指路由决策何时做出 。
1582  路由系统可以依赖生成前 (Pre-generation) 决策或生成后 (Post-generation) 决策,也可以采用多阶段过程 。
1583  生成前路由在产生任何输出前选择模型,完全依赖于输入查询的属性;而生成后路由则在产生初始响应后,根据输出质量或置信度信号做出决定 。
1584  
1585  #### 使用信息 (What):路由机制使用的信号丰富程度各不相同 。最简单的方法仅基于查询本身,利用词法或语义特征来刻画请求 。
1586  更进阶的系统还会加入可用模型的元数据来指导选择,如成本、延迟或领域专长 。生成后方法则进一步引入响应级信号,如置信度得分、Token 概率或验证器输出 。
1587  
1588  #### 计算方式 (How):路由决策的计算复杂度差异显著 。一端是简单的阈值规则或基于成本的启发式方法,无需训练即可直接应用 ;
1589  另一端是基于历史表现数据训练的监督分类器,用于预测哪个模型最适合处理给定查询 。
1590  更复杂的方法采用自适应策略,通过与环境的持续交互来更新路由行为 。
1591  
1592  #### 主流技术路线
1593  1. 难度感知路由 (Difficulty-aware Routing)
1594    这是最直观的路线,核心是**“看题下菜”** 。
1595    原理:在推理前评估查询的复杂度,将简单题分给小模型,难题分给大模型 。
1596    评估手段:包括启发式规则(如文本长度、词汇稀缺度)、训练专门的分类器(如你计划使用的 0.5B 模型)或使用 “LLM 作为评判者” 。
1597    代表案例:BEST-Route(动态分配并选择采样策略)和 VLLM Semantic Router(识别是否需要开启昂贵的思维链推理) 。
1598  2. 人类偏好对齐路由 (Human Preference-aligned Routing)
1599    不看“对错”,看**“好坏”** 。
1600    原理:模拟人类的主观评价,预测大模型生成的答案是否会比小模型显著“更好” 。
1601    训练数据:利用 Chatbot Arena 等人类真实偏好数据或 LLM 自动生成的对比数据 。
1602    代表案例:RouteLLM(预测强模型是否会胜出)和 Arch-Router(允许用户自定义不同领域的路由偏好) 。
1603  3. 基于聚类的路由 (Clustering-based Routing)
1604    核心是**“找规律”** 。
1605    原理:利用无监督学习(如 K-means)将语义相似的查询聚类,并为每个簇分配表现最好的模型 。
1606    优势:具有极强的扩展性,添加新模型时无需重新训练路由器,只需测试新模型在各个簇上的表现即可 。
1607    代表案例:UniRoute 和 Avengers-Pro 。
1608  4. 强化学习路由 (Reinforcement Learning Routing)
1609    核心是**“实战进化”** 。
1610    策略优化:通过多步交互(思考 -> 路由 -> 再思考)迭代改进答案,适合复杂推理,但延迟较高(如 Router-R1) 。
1611    在线老虎机 (Bandit):在实时交互中通过用户反馈(点赞/踩)动态调整路由策略,平衡“探索新模型”与“利用已知强模型” 。
1612    代表案例:MixLLM(实现 97% 的 GPT-4 质量且仅需 24% 的成本) 。
1613  5. 基于不确定性的路由 (Uncertainty-based Routing)这是你项目中 Logprobs 熵值 策略的理论依据 。
1614    原理:监控模型对自身回答的“信心” 。如果内部数学信号(如概率分布)显示模型在犹豫,则触发升级 。
1615    关键点:研究证明,模型内部的探测信号(Logits)远比模型自己口头说的“我很确定”要准得多 。
1616    代表案例:CP-Router(利用共形预测处理不确定性) 。
1617  6. 级联系统 (Cascading)这是你项目中 Binary Gate 和逐级踢球架构的归属 。
1618    原理:顺序执行。先让小模型试,不行再给中模型,最后大模型保底 。
1619    核心逻辑:引入了“后悔药”机制,通过自我验证或外部评估器决定是否停止或升级 。
1620    代表案例:FrugalGPT(三大组件:路由器、质量评估器、停止判断器)和 AutoMix 。
1621  

2026-04-08

Source lines: 1622-1690


1622  # 2026-04-08
1623  
1624  ## 实践
1625  
1626  搭建了自己的完整Pipeline
1627  质量不变的情况下,cluster方法效果最好,成本降低了 ***10%***
1628  
1629  ## 知识学习
1630  
1631  #### Router 经典论文总结
1632  本文档面向后续逐篇复现,聚焦综述 《Dynamic Model Routing and Cascading for Efficient LLM Inference: A Survey》 中以下三节的代表性工作:
1633  
1634  Section 2: Difficulty-aware Routing
1635  Section 6: Uncertainty-based Routing
1636  Section 7: Cascades
1637  整理原则:
1638  
1639  只优先采用原论文、官方项目页、官方代码仓库、会议页面。
1640  如果某些细节在摘要页看不到,我会明确标注“需要补读 PDF/附录”。
1641  如果仓库 README 展示的是论文发布后的扩展结果,我会明确写成“仓库后续更新”,避免和论文主结果混淆。
1642  一页结论
1643  如果你接下来要逐一复现,我建议按这个顺序推进:
1644  
1645  AutoMix:代码、数据、任务说明最完整,最适合先跑通一个 cascade 基线。
1646  FrugalGPT:工程可用性强,官方仓库完整,适合改造成商业 API 版本。
1647  BEST-Route:代码完整,但包含 reward model、best-of-n、多阶段数据构造,工程复杂度高于前两者。
1648  GraphRouter:官方代码已放出,但图构建与数据预处理更复杂。
1649  EmbedLLM:数据和代码齐全,但更像“模型表示学习 + routing 下游头”,对实验环境要求更高。
1650  CP-Router:训练自由、思路清晰,但我当前未检索到官方代码,复现需要自己补实现。
1651  Self-REF / Learning to Route LLMs with Confidence Tokens:论文价值高,但目前未检索到官方公开代码。
1652  Confidence-Driven LLM Router:适合后续用商业 API 重做,但目前主要能拿到论文页面信息,代码未公开。
1653  
1654  #### 开源Router方案总结
1655  本文档对 4 个你点名的开源 router / router 模型做统一拆解:
1656  
1657  1. `RouteLLM`
1658  2. `semantic-router`
1659  3. `notdiamond-0001`
1660  4. `knn-router`
1661  整理维度尽量与 `经典论文.md` 保持一致:
1662  - 项目定位
1663  - 相关论文或技术来源
1664  - 数据集
1665  - 测试用大模型 / 候选模型池
1666  - router 模型 / 机制
1667  - 效果 / benchmark
1668  - 创新点
1669  - 实验与工程形态
1670  - 开源代码位置
1671  - 复现建议
1672  
1673  #### RouteLLM
1674  
1675  这一段原本保留了完整的操作指令,整理后保留关键信息:
1676  
1677  - `RouteLLM` 的 GSM8K 基本链路已经打通,2 题 smoke test 可以分别产出 strong / weak model 结果。
1678  - 当时最后的阻塞点只是 `outputs/` 目录不存在,后来已经补成自动创建。
1679  - 关键修复包括:
1680    - `bert` 路径不再强依赖 `OPENAI_API_KEY`
1681    - `openai_server.py` 不再在 import 阶段崩溃
1682    - `gsm8k.generate_responses` 支持自定义模型对和输出文件
1683    - 评测脚本可以直接读取自定义 GSM8K 响应 CSV 并做可视化
1684  - 固定执行顺序被整理成:
1685    1. 保持 `routellm.openai_server` 运行
1686    2. 先做 5 题 smoke test
1687    3. 成功后再跑全量生成
1688    4. 最后执行 evaluate 出图
1689  - 这一段最重要的收获不是命令本身,而是把 `RouteLLM` 的“响应生成 -> 评测 -> 可视化”链路真正跑通了。
1690  

2026-04-09

Source lines: 1691-1710


1691  # 2026-04-09
1692  
1693  ## 尝试更多的开源策略
1694  
1695  #### [RouteLLM](https://github.com/aurelio-labs/semantic-router)
1696  
1697  准确率(Accuracy):
1698  
1699  在 GSM8K 数据集上,不同策略的表现如下:
1700  策略              准确率 (Accuracy)    相比 Random 的提升                     评价
1701  Random (随机)        88.93%                  -                     基准线:无脑混合强弱模型的结果。
1702  Causal_LLM         0.52%+1.59%           显著胜出:                成功识别了模型专长,捕获了互补性 。
1703  MF (矩阵分解)      90.30%+1.37%           优于随机:                即使只有部分数据,也展现了预判能力。
1704  BERT/SW_Ranking     ~88.7%             -0.2%(负优化)           低于随机:说明这些路由器在数学逻辑上出现了误判。
1705  
1706  策略,               准确率 (Accuracy),   成本 (CNY),性能/成本效率评价
1707  Weak (7B),          85.90%,0.58,        成本极低,但存在能力天花板
1708  Strong (72B),       92.87%,1.77,        准确率最高,但成本是 7B 的 3.06 倍
1709  Causal_LLM (Router),90.52%,1.20,        最优解:用 67% 的成本换取了 97.5% 的最强性能
1710  

2026-04-10

Source lines: 1711-1773


1711  # 2026-04-10
1712  
1713  ## 尝试更多的开源策略
1714  
1715  #### 现有Pipeline
1716  
1717  - `llmrouter` 当前主链路是:
1718    数据合并与切分 -> 全模型 benchmark -> 自动打 tier 标签 -> 训练 classifier / 调 cascade 阈值 -> test 集统一评测 -> 出图。
1719  - benchmark 阶段才会真实调用各模型;训练和评测阶段主要基于已保存结果做监督训练或离线模拟。
1720  - 因此当前 accuracy / cost 基本可直接比较,但 flat router 自身的 routing latency 并没有被完整计入。
1721  - 当前主评测策略包括:
1722    `baseline-32b / 14b / 7b / 3b / 1.5b`、
1723    `random`、
1724    `oracle`、
1725    `cascade-default`、
1726    `cascade-optimized`、
1727    `classifier`、
1728    `binary-gate-logprobs`
1729  
1730  #### [Semantic-router](https://github.com/aurelio-labs/semantic-router)
1731  - 我把 `semantic-router` 理解成“检索式分类器”,它更适合先以 `query -> predicted_label` 的形式接入 Phase 3 evaluation,而不是直接改 benchmark 主干。
1732  - 接入思路被收敛为:
1733    - 用 `unified/train` + `routing_labels` 构建 5 路 semantic routes
1734    - 对 `unified/test` 做 semantic routing
1735    - 输出 `predicted_label`
1736    - 复用现有 `simulate_strategy` 和 metrics
1737  - 需要提前注意的风险:
1738    - 路径硬编码较多
1739    - 需要可用的 encoder backend
1740    - Python 3.13 对部分本地 encoder 兼容性一般
1741    - 当前延迟口径仍不是端到端 latency
1742  - 已完成远程单独评测:
1743    - Accuracy: 68.18%
1744    - Cost Ratio: 25.9%
1745    - Avg Latency: 857ms
1746    - P99 Latency: 8308ms
1747  - 相对位置:
1748    - 比 `classifier` 更准,但成本更高
1749    - 略低于 `cascade-optimized` 的准确率,但延迟明显更好
1750  - 远程结果和对比文件都已经单独保存,后续可以直接回看 summary / comparison 产物。
1751  
1752  #### Router Latency
1753  
1754  - 我专门确认了一个问题:论文和综述通常会提到 latency / overhead,但很少把 `router decision latency` 单独定义为最终实验指标。
1755  - 更常见的口径仍然是端到端响应时间,因此 router 本身的额外决策开销在很多对比里其实是模糊的。
1756  
1757  - 当前诊断已经比较明确:
1758    - route 分布严重不平衡,32B route 太小
1759    - hardest 样本识别不足
1760    - 排除 `all_wrong` 样本会进一步削弱 hardest route
1761  - 后续调参顺序也已经确定:
1762    1. 先加 `all_wrong`
1763    2. 再做 per-route cap,处理类不平衡
1764    3. 最后再调 `top-k` 和 aggregation
1765  - 已跑出的关键实验结果:
1766    - `semantic_router_gpu`: 68.18% / 25.9%
1767    - `include_all_wrong`: 68.91% / 33.2%
1768    - `include_all_wrong + cap2000`: 70.29% / 36.7%
1769    - `include_all_wrong + top5 + max`: 62.94% / 20.6%
1770    - `bge-m3 + include_all_wrong`: 68.72% / 31.8%
1771    - `bge-m3 + include_all_wrong + cap2000`: 69.34% / 33.7%
1772  - 这一轮最重要的结论是:真正决定效果的不是“semantic-router”这个名字,而是 route 形态、数据分布和 threshold 策略。
1773  

2026-04-11

Source lines: 1774-1870


1774  # 2026-04-11
1775  
1776  ## 实践
1777  
1778  #### 追求极致的正确率
1779  
1780  - 当前 best semantic-router: 79.61% / 97.2%
1781  - 提升是 +0.08 个点 accuracy,同时成本从 100% 降到 97.2%
1782  
1783  最新结果:
1784    跑完了 4 组cost <= 40% 的 semantic-router 新实验。当前最优是:
1785    - semantic_router_override14b_7b_meta_mpnet_cost40_fresh4xa100
1786    - Accuracy: 76.28%
1787    - Cost Ratio: 38.6%
1788    - Avg Latency: 1054ms
1789    - P99 Latency: 9744ms
1790  
1791    关键配置:
1792    - encoder: sentence-transformers/all-mpnet-base-v2
1793    - routing mode: 32b-override 这一套逻辑被我用作“base model default + semantic override”,这里 base 是 qwen2.5-14b
1794    - override candidate: qwen2.5-7b
1795    - text fields: dataset,subject,difficulty
1796    - tuned threshold: qwen2.5-7b = 0.8628563005596404
1797    - routing distribution: qwen2.5-14b = 4935, qwen2.5-7b = 1514
1798  
1799  #### 已知方案总体对比
1800  
1801  | 策略 | 准确率 | 相对成本 | 平均延迟 | P99 延迟 |
1802  |------|--------|----------|----------|----------|
1803  | Always 32B | 79.53% | 100.0% | 1539ms | 18354ms |
1804  | Always 14B | 76.73% | 43.7% | 1248ms | 13021ms |
1805  | Always 7B | 72.58% | 22.1% | 659ms | 4878ms |
1806  | Always 3B | 63.92% | 9.4% | 621ms | 4673ms |
1807  | Always 1.5B | 55.93% | 4.7% | 624ms | 4280ms |
1808  | Random | 69.64% | 36.6% | 897ms | 7977ms |
1809  | Oracle | 90.80% | 20.3% | 824ms | 6848ms |
1810  | Cascade (default) | 62.35% | 10.3% | 951ms | 6759ms |
1811  | Cascade (optimized) | 68.85% | 24.7% | 2053ms | 21973ms |
1812  | Binary Gate (logprobs) | 68.85% | 24.7% | 2053ms | 21973ms |
1813  | Classifier (0.5B) | 60.89% | 12.3% | 765ms | 6386ms |
1814  | Semantic Tiered MiniLM cap2000 | 70.71% | 37.2% | 968ms | 9624ms |
1815  | Semantic Tiered MiniLM cost33 | 70.12% | 33.8% | 942ms | 9019ms |
1816  | Semantic Tiered MiniLM cost35.5 | 70.62% | 36.8% | 963ms | 9503ms |
1817  | Semantic Override 32B->14B MPNet | 79.61% | 97.2% | 1525ms | 17398ms |
1818  | Semantic Override 14B->7B MPNet | 76.28% | 38.6% | 1054ms | 9744ms |
1819  | Semantic Override 14B->3B MPNet | 74.57% | 35.7% | 1017ms | 9050ms |
1820  | Semantic Override 14B->1.5B/3B MPNet | 73.67% | 35.1% | 1023ms | 8919ms |
1821  
1822  #### 最新 Semantic Router 内部对比
1823  
1824  | 方案 | Encoder | 路由形式 | 关键设置 | 准确率 | 成本 |
1825  |------|---------|----------|----------|--------|------|
1826  | Tiered MiniLM cap2000 | all-MiniLM-L6-v2 | 五路 tiered | include_all_wrong + cap2000 | 70.71% | 37.2% |
1827  | Tiered MiniLM tuned cost33 | all-MiniLM-L6-v2 | 五路 tiered | 32B 阈值调优,target cost=0.33 | 70.12% | 33.8% |
1828  | Tiered MiniLM tuned cost35.5 | all-MiniLM-L6-v2 | 五路 tiered | 32B 阈值调优,target cost=0.355 | 70.62% | 36.8% |
1829  | Override 32B->14B MPNet | all-mpnet-base-v2 | 32B 默认,命中后降到 14B | metadata: dataset/subject/difficulty,threshold=0.9364 | 79.61% | 97.2% |
1830  | Override 14B->7B MPNet | all-mpnet-base-v2 | 14B 默认,命中后降到 7B | metadata: dataset/subject/difficulty,threshold=0.8629 | 76.28% | 38.6% |
1831  | Override 14B->3B MPNet | all-mpnet-base-v2 | 14B 默认,命中后降到 3B | metadata: dataset/subject/difficulty,threshold=0.8585 | 74.57% | 35.7% |
1832  | Override 14B->1.5B/3B MPNet | all-mpnet-base-v2 | 14B 默认,命中后降到 1.5B/3B | metadata: dataset/subject/difficulty,threshold=0.8818 | 73.67% | 35.1% |
1833  
1834  #### 关键结论
1835  1. **Semantic Router 的当前最高准确率方案**是 `32B 默认 + 14B override + MPNet + metadata`,达到 **79.61% / 97.2%**。相比 Always 32B,准确率仅提高 **0.08** 个百分点,成本下降 **2.8** 个百分点,收益很小,但它证明 semantic override 已经可以做到几乎不掉点。
1836  2. **在 cost <= 40% 约束下,当前最优方案**是 `14B 默认 + 7B override + MPNet + metadata`,达到 **76.28% / 38.6%**。相比 Always 14B,准确率只低 **0.45** 个百分点,但成本少 **5.1** 个百分点。
1837  3. **旧的 tiered semantic-router 已经被 override 方案明显压制。** 最好的 tiered 版本是 `include_all_wrong + cap2000`,只有 **70.71% / 37.2%**;而 `14B->7B override` 在几乎相同成本下把准确率再拉高了 **5.57** 个百分点,成本只增加 **1.4** 个百分点。
1838  4. **真正带来提升的不是“semantic-router”这个名字本身,而是方案形态变化。** 从五路 tiered 改成“强模型默认 + 低一级模型 override”,再叠加 `all-mpnet-base-v2` 和结构化 metadata,效果才明显跃升。
1839  5. **如果目标是把 semantic-router 接到现有 llmrouter pipeline 做单独验证,当前最值得保留的候选只需要两条:**
1840     - `semantic-override32b-14b-mpnet-meta-acc`:用于验证语义路由的准确率上限。
1841     - `semantic-override14b-7b-mpnet-meta-cost40`:用于验证成本受限场景下的真实收益。
1842  
1843  #### 和已有 Router 方案的相对位置
1844  - 相比 `classifier`,`14B->7B semantic override` 准确率从 **60.89%** 提升到 **76.28%**,但成本也从 **12.3%** 提升到 **38.6%**。
1845  - 相比 `cascade-optimized` / `binary-gate-logprobs` 的 **68.85% / 24.7%**,`14B->7B semantic override` 在准确率上高出 **7.43** 个百分点,但成本也更高。
1846  - 在“可直接上线的简单策略”里,Always 14B 仍然是很强的朴素基线:**76.73% / 43.7%**。Semantic override 的价值在于把这条强基线压到 40% 左右成本时,仍能尽量保留准确率。
1847  
1848  ## 知识学习
1849  
1850  #### 参数矩阵 Checkpoint
1851  参数矩阵只保存某些checkpoint:
1852  训练时,**时间换空间**
1853  
1854  为什么 checkpoint 不只存第一层:
1855  反向传播需要 每一层的输入激活值。如果只存第一层:
1856  L1 (存)
1857  L2
1858  L3
1859  L4
1860  反向传播时会反复从 L1 重新算:
1861  L1→L2→L3
1862  L1→L2
1863  ...
1864  计算量会爆炸。因此实际做法是 每隔几层存一个:
1865  L1 (存)
1866  L2
1867  L3
1868  L4 (存)
1869  这样反向时最多只需要 重新算中间几层,计算量可控,同时减少显存。
1870  

2026-04-12

Source lines: 1871-1899


1871  # 2026-04-12
1872  
1873  ## 知识学习
1874  
1875  #### assignment1-basics/cs336_basics/trainer/utils.py
1876  
1877  #### assignment1-basics/cs336_basics/model/modules.py
1878  
1879  #### einsum()
1880  
1881  #### 常见显卡
1882  
1883  #### LLM参数量估算
1884  
1885  #### MoE模型
1886  
1887  #### PPO/GRPO/DPO
1888  
1889  ```
1890  # 定义前向传播:给定输入 x,输出线性变换后的结果。
1891  def forward(self, x: torch.Tensor) -> torch.Tensor:
1892      # 用 einsum 实现矩阵乘法。
1893      # 这里的含义是:
1894      # 输入 x 的最后一维是 d_in,
1895      # 权重 weight 的形状是 (d_out, d_in),
1896      # 输出的最后一维就变成 d_out。
1897      return einsum(x, self.weight, '... d_in,  d_out d_in -> ... d_out')
1898  ```
1899  

2026-04-13

Source lines: 1900-2002


1900  # 2026-04-13
1901  
1902  ## 实践
1903  
1904  1. Semantic Router 的流程
1905  
1906  这版实现入口在 llmrouter/src/router/semantic_router_strategy.py 和 llmrouter/src/evaluate/run_evaluation.py。
1907  
1908  **完整流程**
1909  ```
1910  1. 从训练集读取已标注样本。
1911     代码会把 /tangboyan/llmrouter/data/unified/train.jsonl 和 /tangboyan/llmrouter/results/labels/routing_labels.jsonl 对齐,只保留 unified train 里的 query。对应 load_train_labeled_queries()。
1912  2. 把每条训练样本变成 semantic text。
1913     默认就是 query 本身;如果开了 semantic-text-fields,会把 dataset/subject/difficulty 这类 metadata 也拼进去。对应 build_semantic_text()。
1914  3. 按路由目标组织成 route。
1915     当前支持两种模式:
1916      - tiered:5 路分类,直接建 1.5b / 3b / 7b / 14b / 32b 五个 route。
1917      - 32b-override:不是五路平权,而是“默认强模型 + 若干小模型 override”。例如 14B 默认,7B override。对应 prepare_route_training_records()。
1918  4. 用预训练 encoder 建索引。
1919     每个 route 里放一批 utterances,semantic-router 用 HuggingFaceEncoder 编码后建立向量索引。对应 build_routes() 和 build_semantic_router_from_train_records()。
1920  5. 推理时对测试 query 编码并检索。
1921     对测试 query 用同一个 encoder 编码,检索 top-k 相似 utterances,然后按 route 聚合分数。对应 score_routes_for_vector()。
1922  6. 决策。
1923      - 如果是普通 tiered 且没调阈值:直接取 router 返回的最佳 route。
1924      - 如果开了 threshold tuning:按阈值判断,没过阈值就 fallback 到默认大模型。
1925      - 如果是 32b-override:必须走 threshold 逻辑,否则代码直接报错。对应 choose_route_with_thresholds() 和 run_semantic_router_inference()。
1926  7. 评测。
1927     路由结果不会真实再调模型,而是去查已经离线跑好的 benchmark 结果,看被路由到的模型在该题上是否答对,然后统计 accuracy / cost / latency。对应 simulate_strategy() 和 compute_all_metrics()。
1928  ```
1929  
1930  2. 测试用了什么数据集?
1931  测试集是 unified_test,入口写在 llmrouter/src/evaluate/run_evaluation.py。
1932  具体是:
1933  - 测试切分文件:/tangboyan/llmrouter/data/unified/test.jsonl
1934  - 训练切分文件:/tangboyan/llmrouter/data/unified/train.jsonl
1935  - 评测时会读取 5 个模型在各数据集上的 benchmark 结果,再筛出 unified_test 里的 query
1936  - 当前这套 v2_5tier 评测覆盖的数据集,从结果里看是:
1937      - agieval
1938      - ceval
1939      - cmath
1940      - cmmlu
1941      - gsm8k
1942      - logiqa2
1943  当前统一测试集规模是 6449 条。
1944  
1945  3. Semantic Router 需要训练吗?
1946  结论:不需要像 classifier 那样做参数训练。
1947  - classifier:要训练一个新模型
1948  - semantic-router:不训练新分类器参数,只是“拿预训练 embedding 模型 + 训练集样本建语义路由索引”
1949  
1950  **semantic-router**
1951  ```
1952  - 需要一套已标注的训练样本,用来构建 route utterances
1953  - 需要一个预训练 encoder,例如你现在用过的:
1954      - sentence-transformers/all-MiniLM-L6-v2
1955      - sentence-transformers/all-mpnet-base-v2
1956  - 可选地需要做一次阈值调优,但这不是模型训练,只是用训练集里再切一小块验证集做搜索
1957  ```
1958  
1959  现在这版代码里,threshold tuning 也只是:
1960  - 切一部分 unified_train
1961  - 搜索阈值
1962  - 选 accuracy/cost 最优点
1963  不是 gradient finetune。
1964  
1965  ## 知识学习
1966  
1967  #### 为什么不直接存“参数矩阵的转置”?
1968  
1969  Y = X x W^T
1970  
1971  你可能会问:既然都要转置,为什么不直接把 self.weight 定义成 (in_features, out_features)?
1972  
1973  答案是:为了计算效率(和历史习惯)。
1974  逻辑直观:在 (out, in) 的存储方式下,weight[0](矩阵的第一行)直接对应于第一个输出神经元的所有权重。这在逻辑上非常清晰。
1975  算子优化:底层硬件(如 NVIDIA GPU)在执行 Linear 算子时,针对这种存储方式做了深度优化。
1976  
1977  #### 常见数据类型详解
1978  
1979  通过浮点数的三个组成部分来理解它们:
1980  符号位(Sign)、指数位(Exponent,决定范围)和尾数位(Fraction/Mantissa,决定精度)。
1981  
1982  #### FP32 (Full Precision / Single Precision)
1983  结构: 1位符号,8位指数,23位尾数。
1984  特点: 精度极高,数值范围广。
1985  LLM 中的角色: 曾经是标准。但在如今的 LLM 训练中,它通常只作为“主权重(Master Weights)”存在,用来在优化器更新时保持微小的梯度变化。
1986  
1987  #### FP16 (Half Precision)
1988  结构: 1位符号,5位指数,10位尾数。
1989  优点: 内存占用减半,计算速度极快。
1990  缺点: 数值范围窄(最大约 65504)。在训练 LLM 时,极易产生“梯度溢出(Overflow)”或“下溢(Underflow)”,导致训练崩溃。
1991  对策: 需要使用混合精度训练(Mixed Precision Training)和损失缩放(Loss Scaling)。
1992  
1993  #### BF16 (Brain Floating Point 16) —— LLM 的宠儿
1994  结构: **1**位符号,**8**位指数,**7**位尾数。
1995  特点: 它是 Google 为了深度学习专门设计的。它的指数位与 FP32 一样长。
1996  为什么好用: 它的精度(尾数)虽然不如 FP16,但它的数值范围(Range)和 FP32 完全一样。
1997  意义: 在训练 LLM 时,你不需要担心梯度溢出,不需要搞复杂的 Loss Scaling。目前主流的大模型(Llama 3, GPT-4 等)基本都采用 BF16 进行预训练。
1998  
1999  #### einsum()
2000  通过 einsum,即使输入是一个高维张量(例如 x 的形状是 (batch_size, L, d_model)),我们仍然可以通过 广播 规则来进行矩阵乘法(在这种情况下,广播会自动应用到批次维度和其他维度)。
2001  所以,即使 x 不是二维矩阵,einsum 也能处理高维张量并正确地进行矩阵运算,保证维度匹配。
2002  

2026-04-14

Source lines: 2003-2127


2003  # 2026-04-14
2004  
2005  ## 知识学习
2006  
2007  #### vLLM Semantic Router
2008  是一个面向 多模型系统 的“语义路由与运行控制层”,不是单纯的模型网关,也不是只做学术路由实验的分类器。
2009  它的官方定位是:在云、数据中心、边缘侧,为 Mixture-of-Models 提供系统级智能路由。README.md
2010  - 不同模型在能力、成本、延迟、隐私边界上差异很大,单一模型很难覆盖所有流量。
2011  - 真实请求不仅要“选模型”,还要同时处理安全、缓存、记忆、RAG、工具调用、回放审计等系统能力。
2012  - 路由逻辑不能只停留在一个分类器上,而要变成可配置、可验证、可部署、可观测的运行时系统。
2013  这个项目本质上更像一个 LLM 流量控制平面。它位于客户端和后端模型之间,理解请求,再决定走哪条路、用哪个模型、是否启用插件能力、是否需要额外的安全或工具策略。README.md docs/agent/repo-map.md
2014  
2015  #### 系统架构
2016  把“路由”拆成了几个清晰层次,而不是用一个黑盒分类器直接输出模型名:
2017  - signal evaluation
2018  - projection coordination
2019  - decision selection
2020  - model selection
2021  - plugin handling
2022  在 AMD 参考 profile 里,这条链路写得很明确:先做多种信号检测,再做投影/分区,再选路由决策,最后把请求转发到对应模型别名。deploy/amd/README.md
2023  
2024  - Signals:检测层。定义“识别到了什么”。支持关键词、语言、上下文长度、结构、权限、embedding、domain、complexity、fact-check、jailbreak、PII、preference、reask、user-feedback、knowledge base 等。website/docs/tutorials/signal/overview.md
2025  - Projections:协调层。把多个弱信号合成为可复用的中间事实,比如 intent partition、difficulty band、verification_required 这类 band,而不是把数值逻辑散落在每个 route 里。website/docs/tutorials/projection/overview.md
2026  - Decisions:策略层。用布尔规则、优先级、tier 选出一条 route。这里是“哪条策略赢”。website/docs/tutorials/decision/overview.md src/semantic-router/pkg/config/decision_config.go
2027  - Algorithms / Model Selection:候选模型选择层。一个 decision 可以挂多个候选模型,再用静态或学习式算法选最优,包括 static、elo、router_dc、automix、hybrid、rl_driven、gmtrouter、latency_aware,以及 looper 类的 confidence、ratings、remom。config/README.md src/semantic-router/pkg/extproc/req_filter_classification_runtime.go src/semantic-router/pkg/modelselection/selector.go
2028  - Plugins:路由后处理层。匹配到某条 route 后,可以附加 route-local 行为,比如 semantic cache、RAG、memory、router replay、tools、system prompt、request params、content safety、hallucination、response jailbreak、image generation 等。website/docs/tutorials/plugin/overview.md
2029  
2030  不只是“把问题分类到模型”,而是在做 信号驱动的策略编排:
2031  比如可以先识别“这是法律高风险请求”,再叠加“需要核验来源”“上下文很长”“用户在追问纠错”,最后才决定走 premium specialist 路线,并启用相应插件。
2032  
2033  #### 配置与运行方式
2034  这个项目的另一大特点是配置体系比较完整,而且是统一的。
2035  它采用一套 canonical YAML 合同:
2036  - version
2037  - listeners
2038  - providers
2039  - routing
2040  - global
2041  其中:
2042  - routing 负责语义路由本身,包括 modelCards、signals、projections、decisions
2043  - providers 负责具体部署绑定和默认模型
2044  - global 负责全局运行时能力,比如 observability、router replay、stores、tools、looper、modelcatalog 等。这套约定写在公开配置文档里,也被仓库测试强约束。website/docs/installation/configuration.md configREADME.md
2045  
2046  此外,这个项目同时支持两种配置视角:
2047  - YAML canonical config
2048  - DSL authoring surface
2049  也就是说,用户既可以直接写 config.yaml,也可以用 DSL/可视化编辑器去表达路由图,然后再编译回canonical YAML。这让它既适合工程部署,也适合调参和策略设计。
2050  website/docs/installation/configuration.md
2051  
2052  在部署侧,它不是单一路径,而是支持多种环境:
2053  - 本地 CPU 开发
2054  - 本地 AMD/ROCm 开发
2055  - Kubernetes / Helm / Operator
2056  - Dashboard 控制台
2057  - E2E profile 驱动的测试环境
2058  
2059  仓库文档给出的本地默认流程是:
2060  - make vllm-sr-dev
2061  - vllm-sr serve --image-pull-policy never
2062  对应 CPU / AMD 两套本地环境说明也很清楚。docs/agent/environments.md
2063  
2064  #### 仓库组成
2065  从代码组织上看,这个仓库已经不是一个单体 router,而是一整套平台:
2066  - src/semantic-router:Go 核心路由器,包含 config、classification、decision engine、Envoy extproc、selection、plugin runtime。
2067  - src/vllm-sr:Python CLI,负责本地启动、配置校验、Docker 编排、开发体验。
2068  - dashboard:前后端控制台,用于配置编辑、部署、状态查看、playground、可视化。
2069  - deploy/operator:Kubernetes Operator 和 CRD。
2070  - deploy/helm:Helm chart。
2071  - src/training:模型选择与分类相关训练脚本、数据、推理服务。
2072  - e2e:端到端测试框架,覆盖 routing、safety、cache、response-api、dashboard、authz、streaming 多 profile。
2073  - candle-binding ml-binding nlp-binding:Rust/native bindings,用于更底层的推理或 ML 能力接入。
2074  
2075  **架构图**
2076  ```
2077    Authoring / Control Plane
2078      Dashboard / DSL / YAML / CLI / Helm / Operator
2079            |
2080            v
2081    Canonical Config v0.3
2082      version / listeners / providers / routing / global
2083            |
2084            v
2085    Runtime Plane
2086      Client
2087        -> Envoy
2088        -> semantic-router extproc (OpenAIRouter)
2089           -> Signals
2090           -> Projections
2091           -> Decisions
2092           -> Algorithms / Looper
2093           -> Route-local Plugins
2094           -> Provider binding / endpoint selection / alias rewrite
2095           -> Upstream model backends
2096        <- Response filters / replay / cache / warnings / headers
2097            |
2098            v
2099    Observability / Replay / Dashboard Insight
2100  
2101    Validation / Support Plane
2102      E2E profiles / deploy recipes / training stack / Rust-native bindings
2103  ```
2104  这张图背后的关键点是:
2105  - 这套系统有一个统一配置合同,不是 CLI 一套、Dashboard 一套、Operator 一套。仓库明确把入口统一为 version / listeners / providers / routing / global,其中 routing 负责 `modelCards5), website/docs/tutorials/projection/overview.md:9, website/docs/tutorials/decision/overview.md:7, website/docs/tutorials/algorithm/overview.md:7, website/docs/tutorials/plugin/overview.md:5, deploy/amd/README.md:100)
2106  - 仓库形态也说明它是平台,不是单一 router binary。src/semantic-router 是 Go 路由内核,src/vllm-sr 是 Python CLI,dashboard/ 是控制台,deploy/operator/ 和 deploy/helm/ 是 K8s 部署面,e2e/ 是验证框架,src/training/ 和 Rust bindings 是算法/模型支持层。(docs/agent/repo-map.md:3)
2107  
2108  所以一句话说,它更像“LLM 流量控制平面 + 运行时策略编排层”,而不是“模型网关 + 少量规则”。
2109  
2110  #### 一次请求怎么被路由
2111  1. 启动阶段先由 vllm-sr serve 做 bootstrap,解析配置、选择 Docker/K8s backend、准备 runtime config,然后把本地或集群拓扑拉起来。(src/vllm-sr/cli/commands/runtime.py:57, src/vllm-sr/cli/commands/runtime.py:214)
2112  2. 真正请求进入时,Go 侧的 OpenAIRouter 作为 Envoy extproc server 工作。它不是只处理 request body,而是完整跑四个阶段:request headers -> request body -> response headers -> response body。(src/semantic-router/pkg/extproc/router.go:24, src/semantic-router/pkg/extproc/processor_core.go:48)
2113  3. request headers 阶段会先抓 request_id、:path、:method、streaming 预期、looper 内部请求标记等。也就是说,这里先决定“这是普通 chat、Response API、models 接口,还是 looper 内部调用”。(src/semantic-router/pkg/extproc/processor_req_header.go:17)
2114  4. request body 阶段先走一个快路径:如果是 Response API,就先翻译成 chat completions 形态;然后做 body 校验;再用 fast extractor 直接拿到 model / userContent / firstImageURL / stream,避免一开始就完整反序列化。(src/semantic-router/pkg/extproc/processor_req_body.go:22, /home/ryan/CUHKSZ/LLM-Router/V:61, src/semantic-router/pkg/decision/engine.go:60, src/semantic-router/pkg/decision/engine.go:199)
2115  5. decision engine 本身是个布尔规则树求值器。叶子节点是 type + name,支持 AND / OR / NOT,命中后会得到 confidence;多个 decision 都命中时,再按 tier -> confidence -> priority 或 priority -> confidence 选出最终 route。(src/semantic-router/pkg/config/decision_config.go:3, src/semantic-router/pkg/decision/engine.go:151, src/semantic-router/pkg/decision/engine.go:335)
2116  6. route 选出来以后,不一定立刻等于“最终模型已定”。
2117  如果用户显式指定模型,router 会保留原模型,但仍然保留 decision 结果给插件使用。
2118  如果用户走的是 auto model,router 才会根据 decision.modelRefs + decision.algorithm 去做候选选择。(src/semantic-router/pkg/extproc/req_filter_classification_runtime.go:138, src/semantic-router/pkg/extproc/req_filter_classification.go:61)
2119  7. 候选模型选择分两类。单模型选择算法走 selector registry,比如 static / elo / router_dc / automix / hybrid / rl_driven / gmtrouter / latency_aware / knn / kmeans / svm。多模型编排算法走 looper,比如 confidence / ratings / remom。(website/docs/tutorials/algorithm/overview.md:55, src/semantic-router/pkg/selection/factory.go:96, src/semantic-router/pkg/extproc/req_filter_looper.go:45)
2120  8. 在真正发往上游前,router 还会跑一组 route-local 行为:fast_response、rate limit、semantic cache short-circuit、RAG 检索、modality 处理、memory 注入、request params、system prompt、tools 选择。然后才做 endpoint 选择、alias 到 provider-specific model id 的映射,并把修改后的 body 发给上游。(src/semantic-router/pkg/extproc/processor_req_body_prepare.go:63, src/semantic-router/pkg/extproc/req_filter_rag.go:19, src/semantic-router/pkg/extproc/processor_req_body_routing.go:28, src/semantic-router/pkg/extproc/processor_req_body_routing.go:65, /home/ryan/CUHKSZ/LLM-Router/VLLM-sem)
2121  
2122  把这 12 步压成一句话就是:
2123  客户端只发出一次 OpenAI 兼容请求,但 router 在内部实际完成了:
2124  “请求理解、信号抽取、投影协调、策略命中、候选模型选择、插件执行、后端绑定、响应审计与告警”
2125  这整条系统链路。
2126  
2127  

2026-04-15

Source lines: 2128-2129


2128  # 2026-04-15
2129  

2026-04-16

Source lines: 2130-2132


2130  # 2026-04-16
2131  
2132  

2026-04-17

Source lines: 2133-2255


2133  # 2026-04-17
2134  
2135  ## 知识学习
2136  
2137  ### RoPE
2138  
2139  - 今天把 RoPE 的几何直觉重新想清楚了:一个 `D` 维向量会被拆成 `D/2` 个二维平面,每个平面都以原点 `(0, 0)` 为旋转中心。
2140  - 第 `k` 个平面由 `(x_{2k}, x_{2k+1})` 组成,位置编码的本质就是把这一对坐标绕原点旋转角度 `theta_k`。
2141  - 不同平面之间是正交、互不干涉的:
2142    - 平面 0 只影响 `(x0, x1)`
2143    - 平面 1 只影响 `(x2, x3)`
2144    - 各平面独立旋转,不会互相混入
2145  - 它们唯一的系统性联系是频率分布:
2146    - 低频平面旋转慢,更偏向捕捉长距离关系
2147    - 高频平面旋转快,更偏向捕捉短距离细节
2148  
2149  ### 维度与旋转
2150  
2151  - 另一个关键理解是:RoPE 不是“单维缩放”,而是二维成对旋转。
2152  - 在第 `k` 个平面中:
2153    - `x_{2k}` 是横坐标
2154    - `x_{2k+1}` 是纵坐标
2155    - 它们共同组成平面上的点 `P`
2156  - 当 token 位于第 `m` 个位置时,这个点会被旋转 `m * theta_k`。
2157  - 旋转后的核心性质:
2158    - 方向改变
2159    - 向量长度不变
2160    - 因而保留了幅值信息,同时把位置信息写进方向关系里
2161  - 这也解释了为什么必须“成对旋转”:
2162    只动一个维度会更像缩放;只有 `(x_{2k}, x_{2k+1})` 联动,才是真正的圆周旋转。
2163  
2164  对应代码理解:
2165  
2166  ```python
2167  x = rearrange(x, '... (s r) -> ... s r', r=2)
2168  
2169  [
2170    [x0, x1],   # 第 1 个平面的坐标
2171    [x2, x3],   # 第 2 个平面的坐标
2172    ...
2173    [x62, x63]  # 第 32 个平面的坐标
2174  ]
2175  ```
2176  
2177  这段代码本质上就是把一维向量按两维一组重排,显式变成“多个二维旋转平面”。
2178  
2179  
2180  
2181  ### 代码实现详解
2182  
2183  ``` RoPE
2184      def rotate_tensor(self, x: torch.Tensor) -> torch.Tensor:
2185          '''
2186          create a rotated tensor (x_2k, x_2k+1) -> (-x_2k+1, x_2k)
2187          '''
2188          # 先把最后一维按两两一组重排:
2189          # (..., Dh) -> (..., Dh/2, 2)
2190          # 最后那个长度为 2 的维度分别存放偶数位和奇数位。
2191          x = rearrange(x, '... (s r) -> ... s r', r=2)
2192  
2193          # 拆出每一对中的偶数位和奇数位。
2194          x_even, x_odd = x.unbind(dim=-1)
2195  
2196          # 完成二维平面旋转中的“正交向量”构造:
2197          # (x_even, x_odd) -> (-x_odd, x_even)
2198          x = torch.stack((-x_odd, x_even), dim=-1)
2199  
2200          # 再还原回原始最后一维的布局,方便和输入逐元素相乘。
2201          return rearrange(x, '... s r -> ... (s r)')
2202  ```
2203  
2204  1. 核心算子:从“一排”到“一对” (rearrange)
2205  在进行旋转前,必须将平铺的隐藏维度 Dh 进行两两分组。
2206  
2207  代码:x = rearrange(x, '... (s r) -> ... s r', r=2)
2208  
2209  形状流:(..., 64) -> (..., 32, 2)
2210  
2211  意义:物理上确立了 32 个平面。最后一维的 2 代表每个平面内的坐标点 (x_even, x_odd)。
2212  
2213  2. 拆解与重组:实现 90° 垂直旋转
2214  RoPE 的旋转公式中,关键在于构造 rotate_half(x)。其内部逻辑如下:
2215  
2216  A. 拆分 (unbind)
2217  操作:x_even, x_odd = x.unbind(dim=-1)
2218  
2219  维度变化:
2220  
2221  x (原变量): 保持 (..., 32, 2) 不变。
2222  
2223  x_even / x_odd (新变量): 变为 (..., 32)。最后那个 2 被拆掉了。
2224  
2225  直观理解:像是把一叠双层卡片拆成了“上层”和“下层”两堆。
2226  
2227  B. 取反与配对 (stack)
2228  操作:x_rotated = torch.stack((-x_odd, x_even), dim=-1)
2229  
2230  逻辑:这里的 stack 会新建一个维度,将 -x_odd 和 x_even 按位置重新配对。
2231  
2232  变换结果:[a, b] -> [-b, a]。
2233  
2234  几何意义:这在二维平面上对应一个标准的逆时针 90° 旋转。
2235  
2236  3. 全程形状流动图 (Shape Flow)
2237  这是理解 RoPE 变换最清晰的视角:
2238  
2239  原始输入:(B, H, S, 64)
2240  —— 64 个特征平铺。
2241  
2242  分组 (rearrange):(B, H, S, 32, 2)
2243  —— 形成 32 个平面坐标系。
2244  
2245  提取 (unbind):x_even: (B, H, S, 32) | x_odd: (B, H, S, 32)
2246  —— 坐标分量拆分。
2247  
2248  旋转 (stack):(B, H, S, 32, 2)
2249  —— 得到 [-x_odd, x_even] 组合。
2250  
2251  还原 (rearrange):(B, H, S, 64)
2252  —— 旋转后的向量重新进入后续点积计算。
2253  
2254  
2255  

2026-04-20

Source lines: 2256-2270


2256  # 2026-04-20
2257  
2258  ## Encoder 输出Z矩阵的归宿:KV
2259   
2260  内部循环:Z 矩阵是“中间产物”,负责特征的层层叠加。  
2261  对外接口:Encoder 整体的最终输出被视作一个 Memory(记忆库)。  
2262  
2263  KV 的功能分工:  
2264  Key (K):相当于 Encoder 给每个词打的“索引标签”,供 Decoder 查找。    
2265  Value (V):相当于 Encoder 给每个词提取的“语义精华”,供 Decoder 提取。   
2266  总结:Encoder 最后的 Z 矩阵就是 KV 的母体。在翻译模型中,我们常说 Encoder 将输入序列“编码成了一个 KV 缓存(KV Cache)”。  
2267  
2268  
2269  
2270  

2026-04-22

Source lines: 2271-2309


2271  # 2026-04-22
2272  
2273  ## vLLM-Router 完整运行起来了
2274  
2275  这次真正跑通后,我对当前配置的理解是:
2276  
2277  - `model: "MoM"` 时,router 才会接管选模;否则就是普通模型直连。
2278  - 现在的 `decision -> route -> model` 里,大多数 route 只挂了 1 个 `modelRef`,所以它本质上还是“先分类,再直接转发”,还不是“同一路由内多模型竞争”。
2279  - 全局虽然开了 `model_selection.method: static`,但在单 `modelRef` 配置下,这一层几乎没有发挥作用。
2280  
2281  当前路由大致可分为两类:
2282  
2283  - 强制标签路由:`#flash / #plus / #max / #deepseek / #kimi / #coder` 分别固定到对应模型。
2284  - 语义路由:
2285    - 代码 / 报错 / 编程类 -> `qwen3-coder-plus`
2286    - 深度分析 / 长上下文 -> `qwen3.6-max-preview`
2287    - 规划 / 路线图 / 分步骤执行 -> `kimi-k2.5`
2288    - 多问题分析 -> `deepseek-v3.2`
2289    - 简短简单问题 -> `qwen3.6-flash`
2290    - 兜底 -> `qwen3.6-plus`
2291  
2292  这条 pipeline 可以概括成:
2293  
2294  1. 客户端请求打到 `8899`,并指定 `model: "MoM"`。
2295  2. Router 先根据消息内容抽取 signals。
2296  3. Decision engine 用这些 signals 命中某条 route。
2297  4. 当前 route 里通常只有一个 `modelRef`,所以直接选中该模型并转发到对应 provider。
2298  
2299  我现在的判断:
2300  
2301  - 这套配置已经能稳定完成“按请求类型分流”。
2302  - 但它还不算真正的多模型选择系统,更像是“规则分类器 + 单模型映射”。
2303  - 如果要验证 model selection 的价值,下一步必须让同一条 decision 挂多个 `modelRef`,否则 selection 层基本没有实验意义。
2304  
2305  补充定位:
2306  - 路由规则主要看 `config.yaml`
2307  - 分类入口在 `src/semantic-router/pkg/extproc/req_filter_classification*.go`
2308  
2309  

2026-04-26

Source lines: 2310-2393


2310  # 2026-04-26
2311  
2312  ## CUDA
2313  
2314  今天主要先把 CUDA 的整体脉络理顺了,重点不是背文档,而是搞清楚它和 LLM 优化到底怎么接上。
2315  
2316  ### 先建立整体图景
2317  
2318  - CUDA 是让 CPU 发起、GPU 执行并行任务的编程模型;主机代码负责分配内存、发射 kernel、同步结果。
2319  - GPU 追求吞吐量,适合海量并行;CPU 追求低延迟和复杂控制。
2320  - CUDA 程序常见分层:高层框架 / 库(PyTorch、cuBLAS、cuDNN、Triton) -> CUDA Runtime / Driver -> PTX / cubin -> GPU 硬件。
2321  
2322  ### 我真正需要记住的执行模型
2323  
2324  - kernel 是站在“单个线程”的视角写的:先算出自己的全局索引,再决定自己处理哪一段数据。
2325  - 启动方式是 `<<<grid, block>>>`;`blockIdx`、`blockDim`、`threadIdx` 不是函数参数,而是 CUDA 提供的内置上下文。
2326  - `.x / .y / .z` 只是数据维度的映射方式:向量通常只用 `.x`,图像或矩阵才会自然用到 `.x + .y`。
2327  - `grid` 负责覆盖总任务量,`block` 负责组织线程协作;简单向量加法只用 thread 视角,矩阵乘法 / attention 这类问题必须引入 block 视角。
2328  - warp 是 32 个线程的执行单位,因此 block 大小通常尽量设成 32 的倍数,避免浪费 lane。
2329  - 不同 block 之间默认不能相互依赖;块内协作靠 shared memory 和 `__syncthreads()`。
2330  
2331  ### 内存与性能直觉
2332  
2333  - 全局内存大但慢,寄存器和 shared memory 小但快。
2334  - 线程多不等于快,常见瓶颈反而在内存访问。
2335  - 一个 kernel 的性能,往往取决于:
2336    - 是否减少了全局内存读写
2337    - 是否避免了 warp divergence
2338    - 是否让访问尽量 coalesced
2339    - 寄存器 / shared memory 占用是否把 occupancy 压得太低
2340  
2341  ### 从代码层面想明白的几个点
2342  
2343  - `(N + threads - 1) / threads` 是为了向上取整,保证任务不漏;多开的线程再用 `if (i < N)` 挡住。
2344  - `cudaDeviceSynchronize()` 是显式同步点。调试时很好用,也能暴露前面 kernel 的错误;但在性能敏感场景里不能滥用。
2345  - `extern "C"` 是为了关闭 C++ 名字修饰,方便被其他语言或动态加载逻辑找到。
2346  - `__global__` 表示“CPU 发起、GPU 执行”的 kernel 入口,必须 `void` 返回。
2347  
2348  ## CUDA 编程
2349  
2350  今天这部分最大的转变,是把“写循环”改成“做映射”。
2351  
2352  ```cpp
2353  __global__ void vector_add(const float* A, const float* B, float* C, int N) {
2354      int i = blockIdx.x * blockDim.x + threadIdx.x;
2355      if (i < N) {
2356          C[i] = A[i] + B[i];
2357      }
2358  }
2359  ```
2360  
2361  我现在的理解:
2362  
2363  - CPU 时代的思路是“for 循环遍历数组”。
2364  - CUDA 的思路是“每个线程只负责自己的那个索引”。
2365  - 所以 kernel 本质上是在写 SPMD:同一段程序,被很多线程拿去处理不同数据。
2366  
2367  ### 和 LLM 优化怎么接上
2368  
2369  - Python / PyTorch 负责模型结构、调度和实验;CUDA kernel 负责真正重的并行算子。
2370  - 真正值得自己写 kernel 的地方,通常不是标准 GEMM,而是:
2371    - Attention / KV Cache 这类特殊访问模式
2372    - 量化解码
2373    - 多个小算子的融合
2374  - 如果只是标准矩阵乘法,优先用 `cuBLAS`;如果要在 GEMM 周围融合逻辑,再考虑 `CUTLASS`;如果想先快速试验,自定义 kernel 之前可以先看 `Triton`。
2375  
2376  ### 目前的工程判断
2377  
2378  - 写 CUDA 的核心不是“会不会语法”,而是能不能判断瓶颈在算力还是带宽。
2379  - 定位瓶颈不能靠猜,至少要会用:
2380    - `Nsight Systems` 看整体时间线
2381    - `Nsight Compute` 看单 kernel 的 roofline、memory throughput、occupancy
2382    - `torch.profiler` 把 Python 层和 CUDA kernel 对上
2383  
2384  ### 这次学习后我给自己的路线
2385  
2386  1. 先把 thread / block / warp / memory hierarchy 彻底吃透。
2387  2. 用最小例子把 kernel launch、同步、索引映射跑顺。
2388  3. 再进入 LLM 相关的 Triton / PyTorch Extension / CUTLASS。
2389  4. 真做优化时,先判断是 memory bound 还是 compute bound,再决定要不要手写 kernel。
2390  
2391  
2392  
2393  

2026-04-27

Source lines: 2394-2410


2394  # 2026-04-27
2395  
2396  ## 远方 faraway
2397  
2398  基于腾讯云对接完整的后端
2399  
2400  [网页版APP](https://faraway-app-d0gpvf2ko79ceaba3-1426371841.tcloudbaseapp.com/)
2401  
2402  ### 安卓 APP 打包成功
2403  
2404  [目前版本 GihHub 仓库](https://github.com/Tt200411/faraway)
2405  
2406  待对接、开发更详细的后端功能
2407  
2408  
2409  
2410  

2026-04-28

Source lines: 2411-2474


2411  # 2026-04-28
2412  
2413  ## CUDA 编程
2414  
2415  ### 内存分配
2416  
2417  #### 今日要点
2418  
2419  - `cudaMemcpyDefault` 的核心是让 CUDA 驱动自动判断搬运方向。
2420  - `cudaMallocManaged` 的核心不是复制两份数据,而是统一地址空间下的按需页迁移。
2421  - `cudaMemcpy` 更像“复制 + 粘贴”,`cudaMallocManaged` 更像“同一份逻辑数据在 CPU / GPU 之间迁移”。
2422  
2423  #### `cudaMemcpy` 第一个参数永远是目标地址(Destination)
2424  
2425  示例:
2426  
2427  ```cpp
2428  cudaMemcpy(devA, A, vectorLength * sizeof(float), cudaMemcpyDefault);
2429  ```
2430  
2431  理解:
2432  
2433  - `cudaMemcpyDefault` 就是让 CUDA 驱动开启“自动驾驶”模式。
2434  - CUDA 驱动会通过 PCIe 总线自动把数据从内存搬到显存。
2435  
2436  常见搬运方向:
2437  
2438  | 源地址(Source) | 目的地址(Destination) | 驱动实际执行的操作 |
2439  | --- | --- | --- |
2440  | CPU(`A`) | GPU(`devA`) | `HtoD`(上传到显卡) |
2441  | GPU(`devA`) | CPU(`A`) | `DtoH`(下载到内存) |
2442  | GPU1(`devA`) | GPU2(`devB`) | `Peer-to-Peer`(显卡间直接对传) |
2443  | GPU1(`devA`) | GPU1(`devB`) | `Device Copy`(显存内部搬运) |
2444  
2445  #### `cudaMallocManaged`:原来位于 CPU 的数据还在吗?
2446  
2447  这是一个非常深刻的问题,涉及到操作系统的虚拟内存管理和 CUDA 驱动的数据一致性策略。
2448  
2449  简单来说:
2450  
2451  - 数据依然“存在”,但在物理上它可能已经从 CPU 内存中“搬”走了。
2452  
2453  为了理解这一点,需要把“数据”拆分为逻辑地址和物理位置来看:
2454  
2455  1. 逻辑上:它一直都在  
2456     对于你的程序代码来说,变量 `A` 指向的地址(比如 `0x7f8000`)始终有效。无论数据当前是在显存里还是在主存里,都可以通过这个指针访问它。
2457  
2458  2. 物理上:它是“按需移动”的  
2459     统一内存(Unified Memory)的核心机制是页迁移(Page Migration)。它的行为非常像操作系统里的“交换文件(Swap)”:
2460     - GPU 访问时:如果数据在 CPU 内存中,驱动会产生一个“页错误”(Pa
2461    ge Fault)。此时,驱动会将这一页数据(通常是 `4KB` 或 `2MB`)通过 PCIe 总线拷贝到显存,并更新 GPU 的页表。
2462     - 原来在 CPU 的副本:在大多数现代系统(如 Pascal 架构及之后的 GPU)上,为了保证数据一致性,CPU 端的这一页内存会被标记为“无效”或直接被物理释放。
2463  
2464  #### `cudaMemcpy` 和 `cudaMallocManaged` 的本质区别
2465  
2466  使用 `cudaMemcpy` 时,数据是“复制 + 粘贴”:
2467  
2468  - 物理存在:拷贝完成后,CPU 内存(地址 `A`)和 GPU 显存(地址 `devA`)中各有一份完整的数据副本。
2469  - 独立性:如果随后在 CPU 上修改了 `A[0]`,GPU 上的 `devA[0]` 不会跟着变。它们是两个完全独立的物理实体。
2470  - LLM 场景应用:在加载大模型权重时,通常把权重从主存(RAM)拷贝到显存。
2471  
2472  
2473  
2474  

2026-04-29

Source lines: 2475-2577


2475  # 2026-04-29
2476  
2477  ## RoPE
2478  
2479  $n$ 是动力源: 随着 $n$ 的增加(即 Token 越来越靠后),旋转的角度 $n\theta_i$ 就会越来越大。
2480  
2481  
2482  
2483  刚才不是说越大,频率越低,角度越小吗
2484  
2485  这是个非常棒的观察!这里其实涉及两个不同的变量在“赛跑”,你的直觉敏锐地捕捉到了它们的矛盾点。
2486  
2487  我们要区分 位置 n 和 频率 θ 
2488  i
2489  ​
2490    对最终角度的贡献:
2491  
2492  1. 核心公式:角度 = 位置 × 频率
2493  旋转的角度公式是:
2494  
2495  Angle=n×θ 
2496  i
2497  ​
2498   
2499  这里有两个变量:
2500  
2501  n (位置索引):指 Token 是第几个(0,1,2,…)。
2502  
2503  θ 
2504  i
2505  ​
2506    (频率):指某个维度旋转的“步长”(由 inv_freq 决定)。
2507  
2508  2. 回答你的疑问:为什么你觉得“矛盾”?
2509  你说的“越大越小”是指维度 i 越大,频率 θ 
2510  i
2511  ​
2512    越小:
2513  
2514  在 高维度(i 很大时),频率 θ 
2515  i
2516  ​
2517    极其微小(比如 0.0001)。
2518  
2519  这意味着在这个维度上,位置 n 每增加 1,角度只增加一点点。
2520  
2521  但我说的“越大越大”是指位置 n 增加时,角度会变大:
2522  
2523  即使频率 θ 
2524  i
2525  ​
2526    很小(比如 0.0001),当位置 n 从 1 变成 10000 时,角度也会从 0.0001 变成 1.0。
2527  
2528  3. 用“时钟”类比(解决你的困惑)
2529  想象一个时钟,里面有秒针、分针和时针。它们代表了不同的维度 i。
2530  
2531  频率 θ 
2532  i
2533  ​
2534   (由维度决定):
2535  
2536  秒针:频率最高(转得最快)。
2537  
2538  时针:频率最低(转得最慢,也就是你说的“维度越大,频率越低”)。
2539  
2540  位置 n(动力源):
2541  
2542  n 就是流逝的时间。
2543  
2544  随着时间 n 的流逝,所有的针都在往前转,角度都在变大。
2545  
2546  重点来了:
2547  
2548  当 n 增加时(时间流逝),秒针转得飞快,很快就转满一圈回到原点(容易产生你之前担心的“碰撞”)。
2549  
2550  当 n 增加时,时针转得极慢。虽然它的角度也在变大,但它转得很稳,能区分非常遥远的时间点。
2551  
2552  4. 总结:到底谁大谁小?
2553  变量增加	对频率的影响	对角度 (nθ 
2554  i
2555  ​
2556   ) 的影响	物理意义
2557  维度 i 变大	变小	变小	旋转步长变短(从秒针变成时针)
2558  位置 n 变大	无关	变大	随着 Token 靠后,总旋转量累积
2559  你的困惑点在于: 你把“维度带来的频率衰减”和“位置带来的角度累加”搞混了。
2560  
2561  维度决定了这根针转得有多快。
2562  
2563  位置决定了这根针转了多久。
2564  
2565  所以,在同一个维度下,Token 越靠后(n 越大),角度肯定越大;但在同一个位置下,维度越高,旋转的角度反而越小。
2566  
2567  
2568  你现在能理解为什么 NTK-aware Scaling 很有用了:
2569  当我们想把上下文从 2k 扩展到 8k 时,如果我们直接把所有维度都“调慢”(位置内插),那么原本就很快的高频维度变慢了,会导致模型在短距离内的分辨精度下降(模型变糊了)。
2570  
2571  所以聪明的做法是:保持高频维度不动,只把那些本来就很慢的低频维度变得更慢。 这样既保住了“近视”的清晰度,又获得了“远视”的广度。
2572  
2573  现在的逻辑是不是彻底闭环了?低频维度在短距离确实“废”,但它们是为长跑而生的。
2574  
2575  
2576  
2577  

2026-04-30

Source lines: 2578-2594


2578  # 2026-04-30
2579  
2580  ## 关于推理框架
2581  
2582  我们两个小时的组会讨论了各个主流的推理框架  
2583  我们排除了OmniInfer、ChiTu  
2584  最终,决定在vLLM、vLLM-Ascend、SGLang-Ascend、xLLM中选取  
2585  
2586  
2587  ## 港中深新生见面会
2588  
2589  七八个新的志同道合者  
2590  受益!  
2591  
2592  
2593  
2594  

2026-05-01

Source lines: 2595-2606


2595  # 2026-05-01
2596  
2597  ## GPU操作
2598  
2599  Gather  
2600  All-Gather  
2601  Reduce  
2602  All-Reduce  
2603  Reduce-Scatter  
2604  All-to-All  
2605  
2606  

2026-05-04

Source lines: 2607-2646


2607  # 2026-05-04
2608  
2609  ## vLLM 工程边界与目录地图
2610  
2611  理解 vLLM,先不要盯住某个 kernel;先看它把 serving runtime 切成了哪些稳定边界。
2612  
2613  | 层次 | 关键文件 | 主要契约 | 为什么关键 |
2614  | --- | --- | --- | --- |
2615  | 用户入口 | [`v1/engine/llm_engine.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/engine/llm_engine.py) | 请求规范化、输出回组装 | 把 API 面和 runtime 面隔开 |
2616  | EngineCore | [`v1/engine/core.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/engine/core.py) | `add_request()` / `step()` 主循环 | 是 V1 runtime 总装点 |
2617  | Scheduler | [`v1/core/sched/scheduler.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/sched/scheduler.py) | 本轮谁前进、前进多少、是否抢占 | continuous batching 的真正核心 |
2618  | KV 系统 | [`v1/core/kv_cache_manager.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/kv_cache_manager.py)、[`v1/core/block_pool.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/block_pool.py) | prefix hit、slot 分配、block 生命周期 | PagedAttention 的系统收益都在这里释放 |
2619  | 协议对象 | [`v1/request.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/request.py)、[`v1/core/sched/output.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/sched/output.py) | Request、SchedulerOutput、status 字段 | feature 越多,越要靠协议对象稳住边界 |
2620  | Worker / ModelRunner | [`v1/worker/gpu/model_runner.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/worker/gpu/model_runner.py)、[`v1/worker/gpu/input_batch.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/worker/gpu/input_batch.py) | 把 scheduler output 变成设备输入批次 | 调度和算子之间的翻译层 |
2621  | Attention backend | [`v1/attention/backend.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/attention/backend.py)、[`v1/attention/selector.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/attention/selector.py) | backend 选择、metadata 协议 | attention 不是单函数而是一套派发体系 |
2622  | Paged Attention 执行 | [`v1/attention/ops/paged_attn.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/attention/ops/paged_attn.py)、[`v1/worker/gpu/block_table.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/worker/gpu/block_table.py) | block table、slot mapping、decode 访存路径 | 把 block 化 KV 变成真实执行 |
2623  | 编译与图执行 | [`compilation/cuda_graph.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/compilation/cuda_graph.py)、[`compilation/passes/pass_manager.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/compilation/passes/pass_manager.py) | capture/replay、pass 重写、runtime wrapper | 压低 decode 高频小步固定开销 |
2624  | 执行器与分布式 | [`v1/executor/abstract.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/executor/abstract.py)、[`distributed/parallel_state.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/distributed/parallel_state.py) | 单进程/多进程/Ray、TP/EP/CP 进程组 | 把单卡 runtime 拉成服务系统 |
2625  | Connector / 外部缓存 | [`distributed/kv_transfer/kv_connector/base.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/distributed/kv_transfer/kv_connector/base.py)、[`distributed/ec_transfer/ec_transfer_state.py`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/distributed/ec_transfer/ec_transfer_state.py) | KV/encoder cache 搬运协议 | disaggregated serving 的关键拼图 |
2626  
2627  
2628  
2629  ## 一次请求在 vLLM 里如何被推进
2630  
2631  把一条请求主链拉直之后,很多“为什么快”都会落回同一条控制流。
2632  
2633  1. 用户请求经 [`LLMEngine`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/engine/llm_engine.py) 标准化,形成 engine request。
2634  2. [`EngineCore.add_request()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/engine/core.py) 把请求交给 runtime,进入 waiting queue。
2635  3. [`EngineCore.step()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/engine/core.py) 驱动一轮 scheduler + executor 主循环。
2636  4. [`Scheduler.schedule()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/sched/scheduler.py) 计算该请求本轮还能前进多少 token。
2637  5. [`KVCacheManager.get_computed_blocks()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/kv_cache_manager.py) 查 prefix hit,再由 [`allocate_slots()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/kv_cache_manager.py) 申请 block。
2638  6. Scheduler 产出 [`SchedulerOutput`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/core/sched/output.py),executor 把它下发到 worker。
2639  7. [`GPUModelRunner.prepare_inputs()`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/worker/gpu/model_runner.py) 构造 [`InputBatch`](https://github.com/vllm-project/vllm/blob/92a7c121b62a1484b68c0a27d1ecefd1a84f78fc/vllm/v1/worker/gpu/input_batch.py),再由 `prepare_attn()` 拼出 block tables 和 slot mappings。
2640  8. `model_state.prepare_attn()` 与 attention backend 生成 metadata,按 full graph / piecewise / eager 路径执行模型。
2641  9. `sample()` 或 rejection sampler 产出 token,`postprocess()` 更新 host/device 两侧状态镜像。
2642  10. output processor 把底层 token 流整理成用户侧可见结果。
2643  
2644  
2645  
2646  

2026-05-05

Source lines: 2647-2818


2647  # 2026-05-05  
2648  
2649  分层 AllReduce + SHARP合在一起的真实执行路径讲清楚,并给一个具体数值例子。重点关注:哪一步在节点内做、哪一步跨节点、谁在算、谁在发。
2650  
2651  场景设定(例子)
2652  2 个节点(Node A / Node B)
2653  每个节点 4 张 GPU(共 8 张)
2654  每张 GPU 有数据大小 B = 8 MB
2655  网络:
2656  节点内:NVLink / NVSwitch(很快)
2657  节点间:InfiniBand + SHARP(较慢但可做网络内归约)
2658  总体流程(一句话)
2659  
2660  先在节点内“压缩”(AllReduce),再把“压缩结果”交给交换机做全局归约,最后再分发回节点内。
2661  
2662  Step 1️⃣ 节点内 AllReduce(intra-node)⚡
2663  
2664  在 Node A 内(4 张 GPU):
2665  
2666  用 NCCL 做一次 AllReduce
2667  Node B 同样做一遍
2668  
2669  结果:
2670  
2671  Node A 的每张 GPU 都拿到:A 节点的归约结果(8 MB)
2672  Node B 同理
2673  
2674  👉 这里通信量确实是 B×N = 8MB × 4 = 32MB(节点内)
2675  但因为 NVLink 很快,这一步不是瓶颈
2676  
2677  Step 2️⃣ 选“代表”参与跨节点 🌐
2678  
2679  每个节点选一个“代表”(通常是一个 GPU 或 NIC):
2680  
2681  Node A:选 GPU0
2682  Node B:选 GPU0
2683  
2684  👉 注意:
2685  不再是 4 张 GPU 都出去通信,而是每个节点只出 1 份数据
2686  
2687  Step 3️⃣ GPU → 交换机(上行)⬆️
2688  Node A 的代表发送 8 MB
2689  Node B 的代表发送 8 MB
2690  
2691  👉 跨节点总发送:
2692  
2693  每节点只发 B = 8MB
2694  而不是 B×N = 32MB
2695  Step 4️⃣ 交换机内做 Reduce(SHARP 核心)🧠
2696  
2697  在支持 SHARP 的交换机里(由 NVIDIA 网络实现):
2698  
2699  收到来自 Node A / B 的数据
2700  直接在交换机 ASIC 里做:
2701  result = A + B
2702  
2703  👉 这一步原本是在 GPU 上做的,现在搬到网络里
2704  
2705  Step 5️⃣ 交换机 → GPU(下行,多播)⬇️
2706  交换机把结果(8 MB)广播回:
2707  Node A 的代表
2708  Node B 的代表
2709  Step 6️⃣ 节点内广播(intra-node broadcast)🔁
2710  
2711  在每个节点内部:
2712  
2713  代表 GPU 把结果发给本节点其他 GPU(NVLink)
2714  
2715  最终:
2716  
2717  所有 8 张 GPU 都拿到全局 AllReduce 结果(8 MB)
2718  📊 对比数据量(关键)
2719  ❌ 不用分层 + 不用 SHARP
2720  
2721  跨节点:
2722  
2723  4 张 GPU × 8MB = 32MB / 节点
2724  ✅ 分层 + SHARP
2725  
2726  跨节点:
2727  
2728  每节点只发 8MB
2729  
2730  👉 直接减少 4 倍(= N)
2731  
2732  🧠 直觉总结
2733  
2734  把整个过程想成“三段式”:
2735  
2736  ① 节点内(快)
2737  
2738  “我们自己先算好一个结果”
2739  
2740  ② 网络(SHARP)
2741  
2742  “交换机帮我们把各节点结果再合并”
2743  
2744  ③ 节点内(快)
2745  
2746  “把最终答案发给自己人”
2747  
2748  🔥 为什么这是大模型训练的标配
2749  
2750  在:
2751  
2752  Megatron-LM
2753  DeepSpeed
2754  大规模 DDP
2755  
2756  中都会这样做,因为:
2757  
2758  NVLink 带宽 ≫ IB 带宽
2759  跨节点才是瓶颈
2760  SHARP 能进一步减少 GPU 参与通信
2761  一句话总结
2762  
2763  分层 AllReduce 负责减少“谁跨节点通信”,SHARP 负责减少“通信时谁做归约 + 几轮通信”,两者结合把跨节点流量从 B×N 降到 B,并减少一轮计算/通信。
2764  
2765  
2766  
2767  4️⃣ 总体时间模型(关键)
2768  
2769  可以写成:
2770  
2771  不分层:
2772  T = T_slow(BN)
2773  
2774  分层 + SHARP:
2775  T = T_fast(BN) + T_slow(B)
2776  
2777  
2778  
2779  ## 不同的并行方式
2780  
2781  ### ColumnParallelLinear: 
2782  **按照列维度分开。 某个GPU计算完后,结果的*某个维度*是*最终结果*,但是某GPU只有这些局部维度的信息。所以最后通过通讯来收集别的GPU结果**
2783  
2784  ### RowParallelLinear:
2785  **按照行维度分开。 某个GPU计算完后,有*每个*维度的信息,但是完整的维度上,都不是最终结果。所以最后*每个维度都*要再与来自其他GPU的中间信息进行计算,得到最终结果**
2786  
2787  
2788    ┌────────────────────────────┬─────────────┬──────────┬──────────────────────────┬────────────┬────────────────────┐
2789    │             类             │  切哪一维   │ 输入状态 │         输出状态         │    通信    │      放在哪里      │
2790    ├────────────────────────────┼─────────────┼──────────┼──────────────────────────┼────────────┼────────────────────┤
2791    │ ReplicatedLinear           │ 不切        │ 完整     │ 完整                     │ 无         │ 非并行场景或小矩阵 │
2792    ├────────────────────────────┼─────────────┼──────────┼──────────────────────────┼────────────┼────────────────────┤
2793    │ ColumnParallelLinear       │ out (dim 0) │ 完整     │ 切开                     │ 无         │ 一段计算的入口     │
2794    ├────────────────────────────┼─────────────┼──────────┼──────────────────────────┼────────────┼────────────────────┤
2795    │ RowParallelLinear          │ in (dim 1)  │ 切开     │ 完整(需 all-reduce)    │ all-reduce │ 一段计算的出口     │
2796    ├────────────────────────────┼─────────────┼──────────┼──────────────────────────┼────────────┼────────────────────┤
2797    │ MergedColumnParallelLinear │ out (dim 0) │ 完整     │ 切开(由多个子矩阵拼成) │ 无         │ gate+up 合并       │
2798    ├────────────────────────────┼─────────────┼──────────┼──────────────────────────┼────────────┼────────────────────┤
2799    │ QKVColumnParallelLinear    │ out (dim 0) │ 完整     │ 切开(Q/K/V 三段)       │ 无         │ attention 的 QKV   │
2800    └────────────────────────────┴─────────────┴──────────┴──────────────────────────┴────────────┴────────────────────┘
2801  ```
2802  
2803  ```在 Megatron-LM / vLLM 中典型结构:
2804  X
2805   │
2806   ├── ColumnParallel + Merged (QKV / MLP expand)
2807   │
2808   ▼
2809   attention / activation
2810   │
2811   ├── RowParallel
2812   ▼
2813   Y
2814  ```
2815  
2816  
2817  
2818  

2026-05-07

Source lines: 2819-2820


2819  # 2026-05-07
2820  I can do this all day ...   

2026-05-08

Source lines: 2821-2822


2821  # 2026-05-08  
2822  赶路到广州    

2026-05-09

Source lines: 2823-2824


2823  # 2026-05-09  
2824  毕业照  

2026-05-10

Source lines: 2825-2830


2825  # 2026-05-10  
2826  赶路到深圳  
2827  
2828  
2829  
2830  

2026-05-11

Source lines: 2831-3015


2831  # 2026-05-11
2832  
2833  ## FSDP / ZeRO-3 和张量并行 TP 的区别
2834  
2835    FSDP:切“模型状态”,主要为了省显存
2836    TP:切“单层计算”,主要为了让多卡一起算一个大矩阵
2837  
2838    更具体地说:
2839  
2840    | 对比点 | FSDP / ZeRO-3 | 张量并行 TP |
2841    |---|---|---|
2842    | 切什么 | 参数、梯度、优化器状态 | 线性层/注意力层里的大矩阵 |
2843    | 激活怎么切 | 通常按 batch/token 切 | 通常按 hidden dim / intermediate dim 切 |
2844    | 每张卡算什么 | 每张卡处理不同 batch 数据 | 多张卡一起算同一个 token 的同一层 |
2845    | 主要目的 | 降低显存占用 | 降低单卡计算量,并让超大层能并行计算 |
2846    | 通信内容 | 主要通信权重/梯度 | 主要通信激活/中间结果 |
2847    | 常见通信 | AllGather 参数,ReduceScatter 梯度 | AllGather 激活,ReduceScatter 或 AllReduce 输出 |
2848    | 对模型结构的侵入 | 相对低 | 较高,需要改 Linear/Attention 的实现 |
2849  
2850    举个线性层例子:
2851  
2852    Y = X @ W
2853  
2854    假设:
2855  
2856    X: [B, D]
2857    W: [D, F]
2858    Y: [B, F]
2859  
2860    FSDP 的思路
2861  
2862    每张卡存一部分 W,但真正算这一层时,会先把完整 W 收集回来:
2863  
2864    平时:
2865    GPU0: W 第 0 片
2866    GPU1: W 第 1 片
2867    GPU2: W 第 2 片
2868    GPU3: W 第 3 片
2869  
2870    计算前:
2871    AllGather -> 每张卡临时拿到完整 W
2872  
2873    计算时:
2874    GPU0 算 batch 第 0 份
2875    GPU1 算 batch 第 1 份
2876    GPU2 算 batch 第 2 份
2877    GPU3 算 batch 第 3 份
2878  
2879    所以 FSDP 本质上还是偏 数据并行:每张卡处理不同数据,只是参数平时不完整保存。
2880  
2881    TP 的思路
2882  
2883    每张卡不收集完整 W,而是真的只用自己那片 W 参与计算:
2884  
2885    GPU0: W[:, 0:F/4]      -> 算 Y[:, 0:F/4]
2886    GPU1: W[:, F/4:F/2]    -> 算 Y[:, F/4:F/2]
2887    GPU2: W[:, F/2:3F/4]   -> 算 Y[:, F/2:3F/4]
2888    GPU3: W[:, 3F/4:F]     -> 算 Y[:, 3F/4:F]
2889  
2890    最后再通过通信把结果拼起来或规约。
2891  
2892    所以 TP 是真正把 一个矩阵乘法拆开计算。
2893  
2894    一个简单类比:
2895  
2896    FSDP:
2897    书太大,每个人平时只背几章。
2898    要读某一页时,大家临时把整本书凑出来,各自读自己的题。
2899  
2900    TP:
2901    一道题太大,一个人算不完。
2902    大家每人算这道题的一部分,最后合并答案。
2903  
2904  
2905  ## narrow
2906  
2907  narrow 是 PyTorch 里用来沿某一个维度取连续切片的方法。
2908  
2909    你的代码:
2910  
2911    sliced_weight = loaded_weights.narrow(0, start_index, shard_size)
2912  
2913    意思是:
2914  
2915    从 loaded_weights 的第 0 维开始切
2916    起点是 start_index
2917    长度是 shard_size
2918  
2919    等价于常见切片:
2920  
2921    sliced_weight = loaded_weights[start_index : start_index + shard_size]
2922  
2923    如果写完整一点:
2924  
2925    loaded_weights.narrow(dim, start, length)
2926  
2927    参数含义是:
2928  
2929    dim: 沿哪个维度切
2930    start: 从这个维度的哪个下标开始
2931    length: 取多少个元素
2932  
2933  
2934  ## slided_weight = loaded_weights.narrow(0, start_index, shard_size)
2935  
2936  slided_weight 不是“纯指针”,而是一个 Tensor 视图(view)对象。
2937  
2938    可以粗略理解成:
2939  
2940    slided_weight = 指向原始数据的一张“切片窗口”
2941  
2942    它本身是个 Tensor,带着:
2943  
2944    - 数据在哪
2945    - 形状是多少
2946    - 从原始 loaded_weights 的哪一段开始看
2947    - 步长怎么走
2948  
2949    但它通常不拷贝底层数据。
2950  
2951    例如:
2952  
2953    slided_weight = loaded_weights.narrow(0, start_index, shard_size)
2954  
2955    这里 slided_weight 和 loaded_weights 共享同一块底层存储,只是 slided_weight 只看其中一部分。
2956  
2957    所以你可以把它理解成:
2958  
2959    - 不是新开一份完整内存
2960    - 也不是 C 语言里那种裸指针
2961    - 更像“带形状信息的视图引用”
2962  
2963    真正把数据写进本地参数的是:
2964  
2965    param_data.copy_(slided_weight)
2966  
2967    这一步才发生拷贝。
2968  
2969    一句话:
2970  
2971    slided_weight 不是数据副本,而是原 tensor 上的一个切片视图。
2972  
2973  
2974  ## offset:
2975  
2976  offset = sum(self.output_sizes[:loaded_weight_id]) // self.tp_size ,// self.tp_size是什么作用
2977  
2978  // self.tp_size 的作用是:
2979  
2980    把“完整大矩阵里的偏移量”换算成“当前 rank 本地分片里的偏移量”。
2981  
2982    ———
2983  
2984    假设:
2985  
2986    output_sizes = [4096, 4096, 4096]
2987    tp_size = 4
2988  
2989    完整合并矩阵是:
2990  
2991    [ q: 4096 行 ][ k: 4096 行 ][ v: 4096 行 ]
2992  
2993    如果现在加载 k,也就是:
2994  
2995    loaded_weight_id = 1
2996  
2997    完整矩阵里的偏移是:
2998  
2999    sum(output_sizes[:1]) = 4096
3000  
3001    也就是 k 在完整大矩阵里从第 4096 行开始。
3002  
3003    但是当前 rank 本地只保存每个子矩阵的 1/4:
3004  
3005    rank 本地矩阵:
3006    [ q shard: 1024 行 ][ k shard: 1024 行 ][ v shard: 1024 行 ]
3007  
3008    所以 k 在本地矩阵里的起点不是 4096,而是:
3009  
3010    4096 // 4 = 1024
3011  
3012  
3013  
3014  
3015    

2026-05-12

Source lines: 3016-3022


3016  # 2026-05-12
3017  
3018  ## mini-vllm 源码完结!
3019  
3020  前海湾公园的海与落日很美 ...
3021  
3022  

2026-05-13

Source lines: 3023-3068


3023  # 2026-05-13
3024  
3025  ## 尝试启动 DeepSeek V4 Flash 的推理服务
3026  
3027  
3028  ## 1. 可用模型与硬件资源
3029  
3030  ### 可用模型版本(/models/share/)
3031  
3032  | 模型 | 路径 | 量化/精度 | 架构 | 推理框架 | 最低 NPU 需求 | 能否直接跑 |
3033  |------|------|----------|------|---------|-------------|-----------|
3034  | **DeepSeek-V4-Flash (W8A8)** | `DeepSeek-V4-Flash-w8a8-mtp/` | W8A8 Ascend 量化 | 43层/4096d/256专家 | vLLM-Ascend | 32 NPU (2节点x16) | 有现成 yaml,直接跑 |
3035  | **DeepSeek-V4-Flash (compressed-tensors)** | `deepseek-v4-flash-mtp/` | W8A8 compressed-tensors | 同上 | vLLM-Ascend | 32 NPU (2节点x16) | 改 MODEL_PATH + quantization 参数即可 |
3036  | DeepSeek-V4-Flash (BF16) | `DeepSeek-V4-Flash-bf16/` | BF16 原始精度 | 同上 | 自研 NPU 推理脚本 | 1 NPU(单卡验证) | adaption_test/ 下有 quick_verify.py |
3037  | **DeepSeek-V4-Pro** | `DeepSeek-V4-Pro-w4a8-mtp/` | W4A8 Ascend 量化 | 61层/7168d/384专家 | vLLM-Ascend | 32 NPU (2节点x16) | 有现成 yaml,直接跑 |
3038  | DeepSeek-R1-Distill-Qwen-1.5B | `DeepSeek-R1-Distill-Qwen-1.5B/` | BF16 | Qwen2 28层/1536d | vLLM-Ascend | 1 NPU | 有现成 yaml,直接跑 |
3039  | DeepSeek-V4-Flash-Base (BF16) | `DeepSeek-V4-Flash-Base-bf16/` | BF16 | 同 Flash | 无推理脚本 | — | 不适合评测(base 模型,无 instruct 对齐) |
3040  
3041  所有 V4 模型均为 MoE 架构,支持最大 1M token 上下文(max_position_embeddings=1048576),使用 YaRN RoPE 扩展。
3042  
3043  ### 可用 NPU 资源
3044  
3045  | 队列 | 类型 | 配额 (NPU) | 物理规格 | 状态 |
3046  |------|------|-----------|---------|------|
3047  | user-1-wangakang-compute | 个人 | 8 | 8卡 x 2chip = 16 Ascend910 chip | ok |
3048  | project-ascend-fit-wangakang | 项目 | 52 | 52卡 x 2chip = 104 Ascend910 chip | ok |
3049  
3050  硬件说明:每张 Ascend910 物理卡包含 2 个 AI 处理器(chip),每 chip 64GB HBM。ktp 调度以"NPU"(物理卡)为单位。vLLM 的 `--tensor-parallel-size` 等参数也以 NPU(卡)为单位。
3051  
3052  总计可用:**60 NPU**(个人 8 + 项目 52)。
3053  
3054  尝试使用镜像启动:镜像拉取失败。  
3055  尝试手动配置。
3056  
3057  ## 河套学院晟腾课程
3058  
3059  ### 关于 Linux 命令操作 ...
3060  
3061  在我们的个人 docker 运行实验
3062  
3063  ### Kerminal 自动适配部署大模型 
3064  
3065  跑了90分钟,最后还是成功了! 
3066  
3067  
3068  

2026-05-14

Source lines: 3069-3082


3069  # 2026-05-14
3070  
3071  ## 手动配置一天的 DeepSeek V4 环境
3072  
3073  各种包依赖、环境冲突、未更新问题  
3074  最棘手的是环境不支持  
3075  
3076  ## 结束所有 LeetGPU Easy 题目!
3077  
3078  感觉还行。  
3079  但是 Medium 题目一下就难起来了。  
3080  
3081  
3082  

2026-05-15

Source lines: 3083-3113


3083  # 2026-05-15
3084  
3085  ## 河套学院晟腾课程
3086  
3087  使用 Kerminal 写算子。  
3088  讨论了关于文件目录。  
3089  
3090  ### 讨论了部署需求
3091  
3092  - **Flash (W8A8)**: 2 节点 x 16 NPU = 32 NPU(TP=8, DP=2, Expert Parallel)— **最低要求,不可降低**
3093  - **Pro (W4A8)**: 2 节点 x 16 NPU = 32 NPU(TP=16, DP=2, Expert Parallel)
3094  - **R1-Distill-1.5B**: 1 NPU 即可
3095  - **Flash BF16 单卡验证**: 1 NPU(adaption_test,max_seq_len=2048)
3096  
3097  注意:经实测验证,Flash W8A8 模型在单节点 16 NPU 上无论 TP=8+DP=2 还是 TP=16 均会 OOM。必须使用双节点 32 NPU 部署。当集群只有一个 16-NPU 节点空闲时无法启动。
3098  
3099  项目队列 52 NPU 足够同时部署 Flash + 留余量做其他实验。
3100  
3101  
3102  ## 尝试使用现有配置启动 DS v4
3103  
3104  ### 当前阻塞问题(2026-05-16)
3105  
3106  经过多轮实测,发现以下问题:
3107  
3108  1. **镜像兼容性**:`qwen3_5-v0-a3` 镜像的 transformers 不认识 `deepseek_v4` 架构,必须用 `deepseekv4-a3` 镜像
3109  2. **单节点 OOM**:16 NPU 单节点无论 TP=8+DP=2 还是 TP=16 均 OOM,必须双节点
3110  3. **vLLM-Ascend bug**:双节点 DP=2 跨节点部署时,worker 在 KV cache 初始化阶段报 `AttributeError: 'list' object has no attribute 'merge'`(kv_cache_spec_values 类型错误)
3111  
3112  
3113  

2026-05-16

Source lines: 3114-3385


3114  # 2026-05-16
3115  
3116  ## DeepSeek V4 Flash W8A8 部署总结
3117  
3118  ### 今日结论
3119  
3120  - 任务 1058 已成功启动,服务地址为 `http://10.250.193.147:8005`。
3121  - 当前唯一验证过的可用配置是 cdy 的原版配置:`/models/share/task/cdy/deepseek-v4-flash.yaml`。
3122  - 正确镜像是 `quay.io/ascend/vllm-ascend:v0.13.0rc3-a3`。
3123  - 启动前必须在 `/vllm-workspace/vllm` 中应用 patch:`/models/share/DeepSeek-V4-Flash/deepseek-v4-agentic-support.patch`。
3124  - 单节点 16 NPU 可以跑通 DeepSeek V4 Flash W8A8,配置为 DP=2、TP=8、Expert Parallel。
3125  - CPU 内存需要 1000Gi,CPU 需要 500 核。
3126  
3127  ### 立即可做
3128  
3129  按照 `deepseek-v4-reasoning-eval.md` 中的测试矩阵,继续进行 DeepSeek V4 Flash 性能测试。
3130  
3131  ### 后续学习任务
3132  
3133  1. **服务器 NPU 资源调度**
3134  
3135     管理员解释:提交任务后,调度系统会分配空闲节点。每个节点有 16 张 910 显卡,不同节点已有的镜像缓存不同,这会影响是否需要重新拉镜像。
3136  
3137     后续需要进一步理解服务器节点运行方式、节点间通信和并行配置。可参考:
3138  
3139     - 网络基础说明:https://lqhl.github.io/scaling-book/gpus/#%E7%BD%91%E7%BB%9C
3140     - 配置脚本:`/models/share/task/cdy/start_dsv4.sh`
3141     - 本文档附录中的 NPU 集群调度方案
3142  
3143  2. **服务器集群镜像系统**
3144  
3145     关于“镜像拉取慢”的问题,需要学习服务器集群的镜像系统:https://luoss.nilpo.app/guide/image-storage。
3146  
3147     管理员建议先上传镜像到公开镜像池。上传完成后,服务器内部拉取镜像和模型权重都会明显变快:拉镜像约 10 秒,否则可能需要 10 分钟以上。
3148  
3149  ### 后续优化方向
3150  
3151  1. 管理员提到自己曾跑通过 SGLang,后续可以尝试用 SGLang 启动 DeepSeek V4 Flash。
3152  
3153  2. 管理员提到开源项目 [DFlash: Block Diffusion for Flash Speculative Decoding](https://github.com/z-lab/dflash)。该项目能显著提高解码速度,但目前似乎只能本机运行,不一定适合直接对外提供推理服务。后续可以考虑基于它改进 vLLM / SGLang 框架。
3154  
3155  ## 最终成功配置
3156  
3157  **任务 1058**:使用 cdy 的原版配置成功启动。
3158  
3159  | 项目 | 值 |
3160  | --- | --- |
3161  | yaml | `/models/share/task/cdy/deepseek-v4-flash.yaml` |
3162  | 镜像 | `quay.io/ascend/vllm-ascend:v0.13.0rc3-a3` |
3163  | 节点 | atlas-19(单节点 16 NPU) |
3164  | 配置 | DP=2, TP=8, Expert Parallel |
3165  | 端口 | 8005 |
3166  | 模型名 | deepseek-v4-flash |
3167  | max_model_len | 524288 |
3168  | 关键步骤 | 启动前先 `git apply` patch 到 `/vllm-workspace/vllm` |
3169  
3170  ## 这两天遇到的问题
3171  
3172  ### 1. 镜像不支持 `deepseek_v4` 架构
3173  
3174  **现象**:`The checkpoint has model type deepseek_v4 but Transformers does not recognize this architecture`
3175  
3176  **原因**:`qwen3_5-v0-a3` 和 `deepseekv4-a3` 镜像中的 transformers 库版本不包含 `deepseek_v4` 模型类型注册。
3177  
3178  **解决方案**:使用 `v0.13.0rc3-a3` 镜像 + cdy 脚本中的 `git apply` patch。patch 位于 `/models/share/DeepSeek-V4-Flash/deepseek-v4-agentic-support.patch`,它会修改 vLLM 代码,注册 `deepseek_v4` 相关组件。该镜像中的 vLLM 版本(v0.13)对 `model_type` 的检查逻辑与新版不同,patch 后即可通过。
3179  
3180  ### 2. `tool-call-parser deepseek_v4` 不支持
3181  
3182  **现象**:`invalid tool call parser: deepseek_v4`
3183  
3184  **原因**:`qwen3_5-v0-a3` 镜像的 vLLM 版本(v0.16.0rc2)不包含 `deepseek_v4` tool parser。
3185  
3186  **解决方案**:
3187  
3188  - 方案 A:使用 `v0.13.0rc3-a3` 镜像 + patch(cdy 方案,已验证)
3189  - 方案 B:去掉 `--tool-call-parser` 和 `--reasoning-parser` 参数(性能测试不需要)
3190  
3191  ### 3. `speculative-config deepseek_mtp` 不支持
3192  
3193  **现象**:`Unsupported speculative method: 'mtp'`
3194  
3195  **原因**:`deepseekv4-a3` 镜像的 vLLM 版本不支持 MTP 投机解码。
3196  
3197  **解决方案**:去掉 `--speculative-config` 参数,或使用 `v0.13.0rc3-a3` 镜像(支持 MTP)。
3198  
3199  ### 4. 单节点 16 NPU DP=2 TP=8 OOM(`deepseekv4-a3` 镜像)
3200  
3201  **现象**:Worker 进程被 terminated,报错 `WorkerProc was terminated`。
3202  
3203  **原因**:`deepseekv4-a3` 镜像的 vLLM 版本内存管理效率较低,DP=2 在单节点上 OOM。
3204  
3205  **解决方案**:使用 `v0.13.0rc3-a3` 镜像(vLLM v0.13 内存管理更高效),并分配 1000Gi CPU 内存。cdy 配置证明同样的 DP=2 TP=8 单节点 16 NPU 可以跑通。
3206  
3207  ### 5. 双节点调度失败
3208  
3209  **现象**:Worker pod 一直 Pending,无法分配第二个 16-NPU 节点。
3210  
3211  **原因**:集群中空闲的 16-NPU 节点不足两个。
3212  
3213  **解决方案**:使用单节点配置(cdy 方案证明可行)。
3214  
3215  ### 6. `deepseekv4-a3` 镜像双节点 KV cache bug
3216  
3217  **现象**:`AttributeError: 'list' object has no attribute 'merge'`
3218  
3219  **原因**:`deepseekv4-a3` 镜像中 vLLM-Ascend 的 KV cache 初始化代码在跨节点 DP 模式下有 bug。
3220  
3221  **解决方案**:不使用该镜像,改用 `v0.13.0rc3-a3` 镜像。跨节点 DP 模式相关配置还需要进一步学习,尤其是 `/models/share/task/cdy/start_dsv4pro-worker.sh` 中的参数。
3222  
3223  ### 7. `cd vllm-ascend` 路径问题
3224  
3225  **现象**:`cd: vllm-ascend: No such file or directory`
3226  
3227  **原因**:不同镜像的工作目录不同。
3228  
3229  **解决方案**:cdy 的脚本直接 `cd "$VLLM_REPO"`(即 `/vllm-workspace/vllm`),不需要进入 `vllm-ascend`。
3230  
3231  ### 8. 镜像拉取慢
3232  
3233  **现象**:Pod 长时间 Pending(10-20 分钟)。
3234  
3235  **原因**:`deepseekv4-a3` 和 `v0.13.0rc3-a3` 镜像在部分节点上没有缓存。
3236  
3237  **解决方案**:等待拉取完成,或多次提交,直到调度到已有缓存的节点。
3238  
3239  **管理员解决方案**:先上传镜像到公开镜像池,参考 https://luoss.nilpo.app/guide/image-storage。上传完成后,服务器内部拉取镜像和模型权重都会很快。
3240  
3241  ## 关键经验
3242  
3243  1. 正确镜像是 `quay.io/ascend/vllm-ascend:v0.13.0rc3-a3`。
3244  2. 必须先打 patch:`/models/share/DeepSeek-V4-Flash/deepseek-v4-agentic-support.patch`。
3245  3. 单节点 16 NPU 可以跑,配置为 DP=2 TP=8,不需要双节点。
3246  4. CPU 内存需要 1000Gi,200Gi / 800Gi 不够。参考管理员方案:`/models/share/task/cdy/deepseek-v4-flash.yaml` 第 17、18 行。
3247  5. CPU 需要 500 核。
3248  6. cdy 的脚本(管理员方案)是唯一验证过的可用配置,后续所有版本都应以此为基础。
3249  
3250  ## 附录:NPU 集群现有调度方案
3251  
3252  本集群使用 **Kubernetes + Volcano 调度器** 管理 NPU 资源,并通过 `ktp` CLI 工具操作。
3253  
3254  ### 层级结构
3255  
3256  ```text
3257  集群 (K8s Cluster)
3258   └── 节点 (Node): atlas-1, atlas-18, atlas-19, atlas-39, atlas-40, atlas-41 ...
3259        └── 每个节点有 16 NPU(8 张物理卡 x 2 chip)
3260             └── 每张卡 64GB HBM
3261  
3262  用户通过 Queue(队列)获得 NPU 配额:
3263   ├── 个人队列: user-1-wangakang-compute (8 NPU)
3264   └── 项目队列: project-ascend-fit-wangakang (52 NPU)
3265  ```
3266  
3267  ### 调度流程
3268  
3269  1. **提交任务**:执行 `ktp submit -f job.yaml`。yaml 中指定 queue、npu 数量、镜像和启动命令,任务类型为 `acjob`(Ascend Computing Job)。
3270  2. **调度器分配节点**:Volcano 调度器根据队列配额和节点空闲情况分配资源,无法手动指定节点。请求 16 NPU 会分配一个完整节点;请求 32 NPU 需要两个空闲节点同时可用。
3271  3. **Pod 创建**:每个 task 对应一个 Pod。Pod 运行在分配的节点上,并挂载 `/models/` 共享存储。平台会自动生成 `hccl.json`,Pod 内的 `init_env.sh` 等待该文件就绪后设置 `MASTER_IP` 等环境变量。
3272  4. **分布式通信初始化**:单节点时,Pod 内所有 NPU 通过 HCCL(华为集合通信库)直接通信;多节点时,通过 `data-parallel-address`(`MASTER_IP`)跨节点 RPC 通信。
3273  5. **任务生命周期**:状态流转为 Pending -> Running -> Succeeded / Failed。`resumable_training.enabled: true` 时,失败会自动重试(最多 `fault_retry_times` 次);`max_runtime_minutes` 到期后自动终止。
3274  
3275  ### yaml 配置与调度的关系
3276  
3277  ```yaml
3278  tasks:
3279    - name: master        # Pod 名称后缀
3280      replicas: 1         # 该角色的 Pod 数量
3281      cpu: "500"          # CPU 核数(影响调度,节点需有足够 CPU)
3282      memory: "1000Gi"    # 内存(影响调度,节点需有足够内存)
3283      npu: 16             # NPU 数量(决定分配几张卡/几个节点)
3284      command: "..."      # Pod 启动后执行的命令
3285    - name: worker        # 第二个 Pod(可选,用于多节点)
3286      replicas: 1
3287      npu: 16             # 又一个 16 NPU = 又一个完整节点
3288  ```
3289  
3290  ### 常用操作
3291  
3292  | 命令 | 作用 |
3293  | --- | --- |
3294  | `ktp queues` | 查看队列配额和使用情况 |
3295  | `ktp submit -f job.yaml` | 提交任务 |
3296  | `ktp list` | 列出所有任务 |
3297  | `ktp pods <ID>` | 查看任务的 Pod 状态和所在节点 |
3298  | `ktp logs <ID>` | 查看日志(默认最新 100 行) |
3299  | `ktp logs <ID> --follow` | 实时跟踪日志 |
3300  | `ktp stop <ID>` | 停止任务 |
3301  | `ktp restart <ID>` | 重启已停止的任务 |
3302  | `ktp watch <ID>` | 实时监控任务状态 |
3303  
3304  ### 注意事项
3305  
3306  - 不能指定调度到哪个节点,只能靠调度器自动分配。
3307  - 不同节点上可能缓存了不同版本的同名镜像(tag 相同但内容不同)。
3308  - 请求的 NPU 数量决定了需要几个节点:8 NPU = 半个节点,16 NPU = 一个节点,32 NPU = 两个节点。
3309  - 如果集群没有足够空闲节点,Pod 会一直 Pending。
3310  - `/models/` 是所有节点共享的 NFS 存储,脚本和权重文件对所有 Pod 可见。
3311  
3312  ## CUDA 编程实践:共享内存
3313  
3314  ### 核心概念
3315  
3316  每个 Block 都有自己独立的共享内存。在 CUDA 中,下面这句声明的是块内私有共享内存:
3317  
3318  ```cpp
3319  extern __shared__ float sdata[];
3320  ```
3321  
3322  也就是说,Block 0、Block 1 和 Block 2 各自都有一份独立的 `sdata` 数组,它们互不干扰。
3323  
3324  在这个例子中:
3325  
3326  - `blockDim.x = 4`
3327  - 每个 Block 的 `sdata` 长度都是 4
3328  - 每个 Block 内部的索引都是 `[0, 1, 2, 3]`
3329  
3330  当程序执行到下面这一行时:
3331  
3332  ```cpp
3333  sdata[tid] = (i < N) ? input[i] : 0.0f;
3334  ```
3335  
3336  每个线程会根据自己的局部 ID(`tid`)和全局 ID(`i`),把全局内存中的数据搬到自己 Block 的共享内存中。
3337  
3338  ### 数据映射关系
3339  
3340  #### Block 0(`blockIdx.x = 0`)
3341  
3342  | Thread | `tid` | 全局 `i` | 执行操作 |
3343  | --- | --- | --- | --- |
3344  | Thread 0 | 0 | 0 | `sdata[0] = input[0]`(1.0) |
3345  | Thread 1 | 1 | 1 | `sdata[1] = input[1]`(2.0) |
3346  | Thread 2 | 2 | 2 | `sdata[2] = input[2]`(3.0) |
3347  | Thread 3 | 3 | 3 | `sdata[3] = input[3]`(4.0) |
3348  
3349  此时 Block 0 的 `sdata` 为:
3350  
3351  ```text
3352  [1.0, 2.0, 3.0, 4.0]
3353  ```
3354  
3355  #### Block 1(`blockIdx.x = 1`)
3356  
3357  | Thread | `tid` | 全局 `i` | 执行操作 |
3358  | --- | --- | --- | --- |
3359  | Thread 0 | 0 | 4 | `sdata[0] = input[4]`(5.0) |
3360  | Thread 1 | 1 | 5 | `sdata[1] = input[5]`(6.0) |
3361  | Thread 2 | 2 | 6 | `sdata[2] = input[6]`(7.0) |
3362  | Thread 3 | 3 | 7 | `sdata[3] = input[7]`(8.0) |
3363  
3364  此时 Block 1 的 `sdata` 为:
3365  
3366  ```text
3367  [5.0, 6.0, 7.0, 8.0]
3368  ```
3369  
3370  #### Block 2(`blockIdx.x = 2`)
3371  
3372  | Thread | `tid` | 全局 `i` | 执行操作 |
3373  | --- | --- | --- | --- |
3374  | Thread 0 | 0 | 8 | `sdata[0] = input[8]`(9.0) |
3375  | Thread 1 | 1 | 9 | `sdata[1] = input[9]`(10.0) |
3376  | Thread 2 | 2 | 10 | `sdata[2] = input[10]`(11.0) |
3377  | Thread 3 | 3 | 11 | `sdata[3] = input[11]`(12.0) |
3378  
3379  此时 Block 2 的 `sdata` 为:
3380  
3381  ```text
3382  [9.0, 10.0, 11.0, 12.0]
3383  ```
3384  
3385  

2026-05-17

Source lines: 3386-3500


3386  # 2026-05-17
3387  
3388  ## CUDA kernel 与 device function
3389  
3390  ### 问题
3391  
3392  为什么到了 `reduce_max_kernel` 才说 “kernel 1”?前面的几个函数不是 kernel 吗?
3393  
3394  前面的函数包括:
3395  
3396  - `warpReduceMax`
3397  - `warpReduceSum`
3398  - `blockReduceMax`
3399  - `blockReduceSum`
3400  
3401  ### 结论
3402  
3403  这些函数都不是 CUDA kernel,而是 device function(设备函数)。
3404  
3405  在 CUDA 中,**kernel = GPU 并行执行入口**,也就是能被 CPU 端用 `<<<blocks, threads>>>` 启动的函数。
3406  
3407  ### CUDA 中三类关键函数
3408  
3409  | 类型 | 示例 | 运行位置 | 是否 kernel | 是否能用 `<<<>>>` 启动 |
3410  | --- | --- | --- | --- | --- |
3411  | `__global__` | `__global__ void reduce_max_kernel(...)` | GPU | 是 | 是 |
3412  | `__device__` | `__device__ float warpReduceMax(float val)` | GPU | 否 | 否 |
3413  | 普通 CPU 函数 | `extern "C" void solve(...)` | CPU | 否 | 否 |
3414  
3415  `__global__` 函数是真正的 GPU 启动入口,例如:
3416  
3417  ```cpp
3418  __global__ void reduce_max_kernel(...) {
3419    // GPU kernel body
3420  }
3421  
3422  reduce_max_kernel<<<blocks, threads>>>(...);
3423  ```
3424  
3425  `__device__` 函数只能被 GPU 代码调用,它是 GPU 内部的辅助函数,不是执行入口。
3426  
3427  ## Reduction 规约操作
3428  
3429  ### 执行层级
3430  
3431  CUDA 的执行层级是:
3432  
3433  ```text
3434  Grid -> Block -> Warp -> Thread
3435  ```
3436  
3437  关键限制:
3438  
3439  - 一个 warp 固定 32 个线程。
3440  - 一个 block 最多 1024 个线程。
3441  - 因此一个 block 最多只有 32 个 warp。
3442  
3443  ### Warp-level reduction
3444  
3445  Warp 内通信主要使用 `__shfl_down_sync`。它允许线程直接读取其他 lane 的寄存器数据,比 shared memory 更快。
3446  
3447  Warp reduction 的本质是:**信息向低 lane 聚合**。最终只有 `lane 0` 一定保存整个 warp 的规约结果。
3448  
3449  ### Block-level reduction
3450  
3451  Block reduction 通常采用两级结构:
3452  
3453  1. 每个 warp 内部先做 reduction。
3454  2. 每个 warp 把自己的结果写入 `shared[32]`。
3455  3. 第一个 warp 继续对这些 partial results 做 reduction。
3456  
3457  `shared[32]` 足够的原因是:一个 block 最多只有 32 个 warp,而第一个 warp 正好有 32 个 lane,可以覆盖全部 warp partial results。
3458  
3459  ## Grid-Stride Loop
3460  
3461  Grid-Stride Loop 是 CUDA 中处理超大数据的经典模式:
3462  
3463  ```cpp
3464  for (int i = idx; i < N; i += stride) {
3465    // process input[i]
3466  }
3467  ```
3468  
3469  其中:
3470  
3471  - `idx` 是当前线程的全局编号。
3472  - `stride = blockDim.x * gridDim.x`,表示整个 grid 的线程总数。
3473  - 一个线程会循环处理多个元素。
3474  
3475  ### `local_max` 的含义
3476  
3477  `local_max` 不是全局最大值,而是当前线程负责的数据分片中的局部最大值(thread-local max)。
3478  
3479  完整规约路径是:
3480  
3481  ```text
3482  thread-local max -> warpReduceMax -> blockReduceMax -> global reduction -> final max
3483  ```
3484  
3485  ### CUDA reduction 优化直觉
3486  
3487  优先级通常是:
3488  
3489  ```text
3490  register > shuffle > shared memory > global memory
3491  ```
3492  
3493  因此优化方向是:
3494  
3495  - warp 内尽量使用 shuffle。
3496  - warp 间使用 shared memory。
3497  - 尽可能减少 global memory 访问。
3498  
3499  
3500  

2026-05-20

Source lines: 3501-3511


3501  # 2026-05-20
3502  
3503  Chapter2:简单融合算子与激活函数 (softmax, relu, silu, sigmoid)	"录制: Wang Akang (SRIBD)预定的会议
3504  日期: 2026-05-20 13:57:08
3505  录制文件:https://meeting.tencent.com/crm/2BYebVgo61
3506  密码:JAIW"	算子学习第二节课复盘_融合算子与FusedSoftmax_整理与补充版.pdf	session5(20min):基本融合算子:softmax	朱子为	以 fused-softmax为例,讲一下融合算子(fused softmax,不是 softmax)
3507  			session6:融合的“模型”	杨明哲	把 fused softmax 的数据流动画出来,讲讲为什么要融合,数学本质是什么(函数复合,一次加载多次计算)
3508  			session7(20min):融合算子练习	占贺深	relu、gelu、x*sigmoid(x)融合与不融合的版本、x + sigmoid(x) + silu(x)(如何加载一次 x 就算 3 个值)
3509  
3510  
3511  

2026-05-21

Source lines: 3512-3538


3512  # 2026-05-21
3513  
3514  ## 算子学习 Chapter 3:数值处理与规约
3515  
3516  ### 课程主题
3517  
3518  - `log softmax`
3519  - `relu softmax`
3520  - `softmax dropout`
3521  - softmax 与 element-wise 操作的融合
3522  - block 划分与规约
3523  
3524  ### Session 安排
3525  
3526  | Session | 负责人 | 主题 | 重点 |
3527  | --- | --- | --- | --- |
3528  | session8 | 刘欣 | 简单的算子优化方法 | 以 `log-softmax` 为例,展示简单算子优化方法 |
3529  | session9(20min) | 付谕书 | softmax 与 element-wise 的组合 | 以 `log-softmax + nll_loss`、`softmax + dropout` 为例,理解 softmax 与 element-wise 的融合方式 |
3530  | session10 | 崔诺拉 | 融合算子中的 block 划分与规约 | 实现 softmax 分块版本(不 fused) |
3531  | session11 | 刘稔远 | 实现 `relu(softmax(x))` | 讲解 Triton 实现代码,涉及 block 内部 program 计算和 block 之间的规约 |
3532  
3533  ### 今日关注
3534  
3535  - softmax 相关算子不仅要理解数学形式,也要理解内存读写路径。
3536  - softmax 与 element-wise 操作融合时,关键问题是哪些中间结果不需要写回 global memory。
3537  - block 划分会直接影响规约方式,需要同时考虑 block 内 program 计算和 block 间结果合并。
3538  

2026-05-22

Source lines: 3539-3596


3539  # 2026-05-22
3540  
3541  ## LayerNorm 和 RMSNorm 的几何理解
3542  
3543  ### 今日结论
3544  
3545  - RMSNorm 后的数据分布在完整的 $M$ 维超球面上,自由度为 $M-1$。
3546  - LayerNorm 后的数据分布在被超平面切开的“大圆”上,自由度为 $M-2$。
3547  - RMSNorm 只去掉向量长度信息;LayerNorm 同时去掉向量长度信息和平移基准(直流分量)。
3548  - 从 Triton 算子角度看,RMSNorm 计算开销更低,因为它只需要维护平方和累加器;LayerNorm 需要同时维护均值和方差。
3549  
3550  ### 几何直觉
3551  
3552  当一个超平面去切割一个超球面,并且这个平面正好穿过球心时,切出来的交集是一个大圆(Great Circle)。
3553  
3554  在 $M$ 维空间里,这个交集可以理解为一个 $M-2$ 维的子超球面。
3555  
3556  因此:
3557  
3558  - RMSNorm:只把数据投影到完整超球面上。
3559  - LayerNorm:先要求数据落在超球面上,又要求数据落在过球心的超平面上。
3560  
3561  ### 三维空间例子($M=3$)
3562  
3563  假设特征维度为 3,一行数据为 $[x, y, z]$。
3564  
3565  RMSNorm 的约束是:
3566  
3567  ```text
3568  x^2 + y^2 + z^2 = 3
3569  ```
3570  
3571  这对应一个普通的三维球面。
3572  
3573  LayerNorm 的约束是:
3574  
3575  ```text
3576  x^2 + y^2 + z^2 = 3
3577  x + y + z = 0
3578  ```
3579  
3580  也就是说,LayerNorm 不仅要求数据落在球面上,还要求数据落在过球心的平面上。最终数据只能落在球面和平面的交线上,也就是一条圆形轨道。
3581  
3582  ### 对大模型和 Triton 算子的意义
3583  
3584  | 归一化方式 | 几何形态 | 损失的信息 | Triton 计算开销 |
3585  | --- | --- | --- | --- |
3586  | RMSNorm | 完整的超球面 | 向量的绝对长度 | 低,只需维护 1 个平方和累加器 |
3587  | LayerNorm | 超球面上的“平切圆” | 向量的绝对长度 + 平移基准(直流分量) | 高,需要维护均值和方差 2 个累加器 |
3588  
3589  ### 物理本质
3590  
3591  从几何上看:
3592  
3593  - RMSNorm 是“只缩放长度”,保留方向和平移基准。
3594  - LayerNorm 是“去均值 + 缩放长度”,同时去掉平移基准和长度尺度。
3595  
3596  这也是为什么在大模型推理和 Triton kernel 实现中,RMSNorm 往往比 LayerNorm 更轻量。