[{"content":"Vim 功能 命令行文本编辑器：Vim 是一个强大的命令行文本编辑器，适用于多种操作系统。 自动识别编程语言：根据文件扩展名自动识别编程语言，支持代码高亮、自动缩进等功能。 使用方式： 命令：vim filename 如果文件存在，则打开该文件；如果文件不存在，则创建一个新文件并命名为 filename。 模式 Vim 包含三种主要模式：\n一般命令模式（默认模式）： 用于执行复制、粘贴、删除等操作。 通过键盘输入特定命令实现不同功能。 编辑模式： 按 i 从一般命令模式进入编辑模式，可输入文本。 按 ESC 退出编辑模式，返回一般命令模式。 命令行模式： 在一般命令模式下按 :, /, 或 ? 进入。 显示在屏幕底部，用于查找、替换、保存、退出或配置编辑器。 操作 以下为常用操作命令，均在 一般命令模式 下执行，除非特别说明：\n光标移动 h 或 左箭头：光标左移一个字符。 j 或 下箭头：光标下移一行。 k 或 上箭头：光标上移一行。 l 或 右箭头：光标右移一个字符。 n\u0026lt;Space\u0026gt;：光标右移 n 个字符（n 为数字）。 0 或 Home 键：光标移至本行开头。 $ 或 End 键：光标移至本行末尾。 G：光标移至文件最后一行。 :n 或 nG：光标移至第 n 行（n 为数字）。 gg：光标移至文件第一行（等同于 1G）。 n\u0026lt;Enter\u0026gt;：光标下移 n 行（n 为数字）。 查找 /word：向下查找第一个匹配 word 的字符串。 ?word：向上查找第一个匹配 word 的字符串。 n：重复上一次查找（同方向）。 N：反向重复上一次查找。 :noh：关闭查找关键词高亮。 替换 :n1,n2s/word1/word2/g：在第 n1 行到第 n2 行间，将 word1 替换为 word2。 :1,$s/word1/word2/g：全文将 word1 替换为 word2。 :1,$s/word1/word2/gc：全文替换 word1 为 word2，替换前逐一确认。 编辑 i：进入编辑模式（在光标前插入）。 ESC：退出编辑模式，返回一般命令模式。 v：进入可视模式，选择文本。 d：删除选中文本。 dd：删除当前行。 y：复制选中文本。 yy：复制当前行。 p：粘贴复制的内容到光标后/下一行。 u：撤销上一步操作。 Ctrl + r：恢复撤销的操作。 \u0026gt;：选中文本右缩进一次。 \u0026lt;：选中文本左缩进一次。 gg=G：格式化全文代码（自动调整缩进）。 保存与退出 :w：保存文件。 :w!：强制保存文件。 :q：退出 Vim。 :q!：强制退出，不保存更改。 :wq：保存并退出。 配置 :set paste：进入粘贴模式，禁用自动缩进，便于粘贴代码。 :set nopaste：取消粘贴模式，恢复自动缩进。 :set nu：显示行号。 :set nonu：隐藏行号。 其他 Ctrl + q：当 Vim 卡死时，取消当前执行的命令。 异常处理 .swp 文件问题： 每次编辑文件时，Vim 会生成一个 .filename.swp 临时文件。 若打开文件时提示 .swp 文件已存在，说明文件可能正在被其他 Vim 实例编辑。 解决方法： 找到正在编辑该文件的 Vim 进程并关闭。 直接删除 .filename.swp 文件（确保无其他进程编辑该文件）。 注意事项：删除 .swp 文件前，确认没有其他 Vim 实例正在编辑，避免丢失未保存的更改。 注意事项 区分大小写：Vim 命令对大小写敏感，如 G 和 g 功能不同。 多键组合：部分命令需连续输入（如 dd, yy），确保快速准确输入。 保存习惯：编辑完成后，养成使用 :w 保存的习惯，避免意外退出丢失更改。 学习曲线：Vim 操作需练习，建议初学者使用 vimtutor（命令行输入 vimtutor）学习基础操作。 配置文件：可通过编辑 ~/.vimrc 自定义 Vim 设置，如默认显示行号、设置主题等。 Nano 模式 Nano 的操作较为直观，没有 Vim 那样的复杂模式切换，主要在单一模式下操作：\n默认编辑模式：启动 Nano 后直接进入编辑模式，可立即输入或编辑文本。 命令模式：通过快捷键（如 Ctrl 组合键）执行保存、退出、查找等操作，快捷键提示显示在屏幕底部。 操作 Nano 的操作主要通过 Ctrl 或 Alt 组合键完成，常用命令如下（^ 表示 Ctrl 键，M- 表示 Alt 键）：\n光标移动 ^F 或 右箭头：光标右移一个字符。 ^B 或 左箭头：光标左移一个字符。 ^N 或 下箭头：光标下移一行。 ^P 或 上箭头：光标上移一行。 ^A：光标移至本行开头。 ^E：光标移至本行末尾。 M-G：跳转到指定行（输入行号后回车）。 ^Y：向上翻页。 ^V：向下翻页。 查找 ^W：查找字符串（输入字符串后回车）。 M-W：重复上一次查找（向下）。 ^W 后按 ^R：反向查找（向上）。 ^T（部分版本）：在查找时启用正则表达式。 替换 ^W 后按 M-R：进入替换模式，输入要替换的字符串和新字符串。 替换时可选择逐一确认或全部替换。 编辑 直接输入：Nano 默认处于编辑模式，可直接输入文本。 ^D：删除光标所在位置的字符。 ^K：剪切当前行（删除整行）。 ^U：粘贴剪切的行到光标位置。 M-U：撤销上一步操作。 M-E：恢复撤销的操作。 ^J：格式化当前段落（调整文本对齐，适用于文本文件）。 M-}：增加缩进（选中文本后）。 M-{：减少缩进（选中文本后）。 保存与退出 ^O：保存文件。 ^X：退出 Nano（若文件有更改，会提示是否保存）。 M-D：强制保存（覆盖已有文件，部分版本支持）。 配置 M-N：显示/隐藏行号。 M-P：启用/禁用软换行（文本自动换行）。 M-8：启用/禁用自动缩进。 ^G：显示帮助文档，列出所有快捷键。 其他 ^C：显示当前光标位置（行、列等信息）。 M-/：取消当前操作（若 Nano 卡住）。 异常处理 .nano 文件问题： Nano 编辑文件时会创建一个临时文件（通常以 .save 或 .nano 结尾）。 若打开文件时提示临时文件存在，可能是上次编辑未正常退出。 解决方法： Nano 会提示是否恢复临时文件，选择 Y（是）以恢复未保存的内容。 若不需要恢复，可选择 N（否），或手动删除临时文件（通常位于 ~/.nano/ 目录）。 注意事项：恢复临时文件前，确认是否需要保存上次未保存的更改，以免覆盖。 注意事项 快捷键提示：Nano 界面底部始终显示常用快捷键（如 ^X Exit 表示 Ctrl+X 退出），方便初学者使用。 区分大小写：查找和替换操作默认区分大小写，可通过 ^W 后按 M-C 切换大小写敏感模式。 配置文件：可通过编辑 ~/.nanorc 自定义 Nano 设置，如默认显示行号、启用语法高亮等。 学习资源：Nano 内置帮助文档（^G）是快速学习的好工具，建议初学者阅读。 局限性：Nano 功能较 Vim 简单，适合轻量级编辑，不支持复杂脚本或多文件操作。 版本差异：不同版本的 Nano 可能支持不同快捷键或功能（如正则表达式），建议检查版本（nano --version）。 Tmux 核心概念 Tmux 的结构主要包含三个层级：\n会话 (Session)： 一个会话是 Tmux 管理的最高层级，可以看作是一个独立的工作区。 每个会话包含一个或多个窗口。 窗口 (Window)： 一个窗口占据整个终端屏幕，类似于应用程序中的一个标签页。 每个窗口包含一个或多个窗格。 窗格 (Pane)： 一个窗格是窗口中的一个分割区域，它是一个独立的终端 Shell。 用户可以在一个窗口内创建多个窗格，实现分屏效果。 Tmux 外部命令 (从 Shell 执行) 注意：以下命令直接在的常规终端（例如 Bash, Zsh）中输入并执行，而不是在 Tmux 会话内部。\n创建会话 (Creating Sessions) tmux new -s \u0026lt;session_name\u0026gt;：创建一个名为 \u0026lt;session_name\u0026gt; 的新会话并立即进入。 tmux new：创建一个以数字自动编号的新会话。 tmux new -d -s \u0026lt;session_name\u0026gt;：在后台创建一个名为 \u0026lt;session_name\u0026gt; 的新会话，但不立即进入。这在系统启动脚本中非常有用。 连接会话 (Attaching to Sessions) tmux ls：列出所有正在后台运行的 Tmux 会话及其信息。 tmux attach -t \u0026lt;session_name\u0026gt;：连接（Attach）到名为 \u0026lt;session_name\u0026gt; 的已存在会话。 tmux a -t \u0026lt;session_name\u0026gt;：attach 命令的简写形式，功能完全相同。 tmux attach：如果当前只有一个正在运行的会话，可省略会话名，直接连接。 关闭会话 (Killing Sessions) tmux kill-session -t \u0026lt;session_name\u0026gt;：关闭名为 \u0026lt;session_name\u0026gt; 的指定会话。 tmux kill-session -a：关闭所有正在运行的会话。 tmux kill-server：关闭所有会话并完全终止 Tmux 服务进程。 警告：这是一个彻底的操作，所有会话及其内部运行的程序都将被立即终止，未保存的工作会丢失。 操作 所有操作都需要先按下 前缀键 (Prefix)，Tmux 的默认前缀键是 Ctrl + b。按下前缀键后松开，再按后续的命令键。\n会话管理 (Session) tmux new -s \u0026lt;session_name\u0026gt;：创建一个名为 \u0026lt;session_name\u0026gt; 的新会话。 tmux attach -t \u0026lt;session_name\u0026gt; 或 tmux a -t \u0026lt;session_name\u0026gt;：重新连接到名为 \u0026lt;session_name\u0026gt; 的会话。 tmux ls：列出所有正在运行的 Tmux 会话。 Ctrl + b + s：在 Tmux 内部以交互式列表显示所有会话，并可进行切换。 Ctrl + b + d：分离 (Detach) 当前会话，返回到常规终端（会话在后台继续运行）。 Ctrl + b + $：重命名当前会话。 tmux kill-session -t \u0026lt;session_name\u0026gt;：在 Tmux 外部关闭指定的会话。 tmux kill-server：关闭所有会话并终止 Tmux 服务。 窗口管理 (Window) Ctrl + b + c：在当前会话中创建一个新窗口。 Ctrl + b + w：以交互式列表显示当前会话的所有窗口，并可进行切换。 Ctrl + b + p：切换到上一个窗口。 Ctrl + b + n：切换到下一个窗口。 Ctrl + b + 0-9：根据窗口编号快速切换到指定窗口（例如 Ctrl + b + 1 切换到 1 号窗口）。 Ctrl + b + ,：重命名当前窗口。 Ctrl + b + \u0026amp;：关闭当前窗口（会进行确认）。 窗格管理 (Pane) Ctrl + b + \u0026quot;：将当前窗格水平分割（上下布局）。 Ctrl + b + %：将当前窗格垂直分割（左右布局）。 Ctrl + b + 方向键 (↑, ↓, ←, →)：在不同窗格之间切换。 Ctrl + b + o：按顺序在当前窗口的窗格间循环切换。 Ctrl + b + x：关闭当前窗格（会进行确认）。 Ctrl + b + z：将当前窗格最大化/恢复原状 (Zoom)。 Ctrl + b + Space：在预设的窗格布局之间切换。 Ctrl + b + q：短暂显示所有窗格的编号，然后按下编号可快速切换到对应窗格。 Ctrl + b + {：将当前窗格与上一个窗格交换位置。 Ctrl + b + }：将当前窗格与下一个窗格交换位置。 Ctrl + b + Alt + 方向键：按住 Ctrl + b 不放，同时按 Alt 和方向键，可以调整窗格大小。 其他 Ctrl + b + t：在当前窗格显示一个大时钟。 Ctrl + b + [：进入复制模式 (Copy Mode)。 在此模式下，可以使用类似 Vim 的键位移动光标、滚动屏幕。 按 Space 开始选择，移动光标后按 Enter 复制所选内容。 按 q 退出复制模式。 Ctrl + b + ]：粘贴最近一次复制的内容。 Config 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # ~/.tmux.conf # tmux source-file ~/.tmux.conf 使配置生效 set -g mouse on set -g default-terminal \u0026#34;xterm-256color\u0026#34; set -ga terminal-overrides \u0026#34;,xterm-256color:Tc:clipboard\u0026#34; set -g status-position top set -g status-bg default set -g status-fg white set -g status-left \u0026#39;#[fg=brightcyan]#S\u0026#39; set -g status-left-length 20 # 右下角 set -g status-right \u0026#39;#[fg=brightcyan][%H:%M]\u0026#39; # 状态栏颜色 set -g status-style fg=brightgreen # 消息颜色 set -g message-style fg=brightgreen # 默认窗口标题颜色 set -w -g window-status-format \u0026#39;#[dim]#I:#W\u0026#39; # 当前窗口标题颜色 set -w -g window-status-current-format \u0026#39;#[fg=brightcyan,bold]#I:#W\u0026#39; # pane border set -g display-panes-active-colour brightgreen set -g display-panes-colour brightyellow 异常处理 无法连接会话： 问题：使用 tmux attach 时提示 \u0026ldquo;no sessions\u0026rdquo; 或找不到指定的会话。 解决方法： 先使用 tmux ls 确认会话是否真的存在以及其确切名称。 如果会话意外终止，需要重新使用 tmux new -s \u0026lt;session_name\u0026gt; 创建。 前缀键冲突： 问题：Ctrl + b 可能与其他应用程序（如某些 IDE 的快捷键）冲突。 解决方法： 可以通过编辑 ~/.tmux.conf 配置文件来修改前缀键。例如，将 set -g prefix C-a 添加到文件中，可以将前缀键改为 Ctrl + a。 修改后需要重新加载配置 (tmux source-file ~/.tmux.conf) 或重启 Tmux 服务。 注意事项 前缀键操作：几乎所有 Tmux 命令都需要先按下前缀键 Ctrl + b。这是一个组合操作，即先按下 Ctrl + b 并松开，然后再按下一个命令键（例如 c, d, % 等）。 鼠标模式：Tmux 支持鼠标操作（如选择窗口、调整窗格大小），但默认通常是关闭的。可以在 ~/.tmux.conf 中添加 set -g mouse on 来开启它，这对于新手非常友好。 配置文件：通过创建和编辑 ~/.tmux.conf 文件，可以高度自定义 Tmux 的行为和外观，例如更改快捷键、设置状态栏样式、颜色主题等。 嵌套会话：应避免在 Tmux 会话中再次启动一个新的 Tmux 会- 话。这会导致前缀键命令混乱，因为内部和外部的 Tmux 实例都会尝试捕获它。如果需要操作外部会话，可以先分离内部会话。 ","permalink":"https://haleuan.github.io/post/linux-survival-guide-terminal-tools/","summary":"\u003ch2 id=\"vim\"\u003eVim\u003c/h2\u003e\n\u003ch3 id=\"功能\"\u003e功能\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003e命令行文本编辑器\u003c/strong\u003e：Vim 是一个强大的命令行文本编辑器，适用于多种操作系统。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e自动识别编程语言\u003c/strong\u003e：根据文件扩展名自动识别编程语言，支持代码高亮、自动缩进等功能。\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e使用方式\u003c/strong\u003e：\u003c/li\u003e\n\u003cli\u003e命令：\u003ccode\u003evim filename\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e如果文件存在，则打开该文件；如果文件不存在，则创建一个新文件并命名为 \u003ccode\u003efilename\u003c/code\u003e。\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"模式\"\u003e模式\u003c/h3\u003e\n\u003cp\u003eVim 包含三种主要模式：\u003c/p\u003e","title":"Linux 生存指南：Vim, Nano 与 Tmux"},{"content":"Crontab 常用速查指南（OpenWRT / Linux） 1) 基本概念 cron：定时任务守护进程 crontab：用户级任务表（每个用户一份） 任务格式：分 时 日 月 周 命令 2) 常用命令 查看当前用户任务：crontab -l 编辑当前用户任务：crontab -e 删除当前用户任务：crontab -r 查看 cron 服务状态（OpenWRT）：/etc/init.d/cron status 启动 cron（OpenWRT）：/etc/init.d/cron start 开机自启（OpenWRT）：/etc/init.d/cron enable 重启 cron（OpenWRT）：/etc/init.d/cron restart 3) 时间字段速记（分时日月周） 分：0-59 时：0-23 日：1-31 月：1-12 周：0-7（0/7 都表示周日；1=周一 … 6=周六） 4) 通配与步进写法 *：任意值 ,：列举多个值（如 1,15） -：范围（如 1-5） /：步进（如 */5 表示每 5 分钟） 示例：\n每 5 分钟：*/5 * * * * cmd 每天 3:00：0 3 * * * cmd 每周一到周五 9:30：30 9 * * 1-5 cmd 每月 1 号 0:10：10 0 1 * * cmd 5) 常用时间模板（直接套用） 每分钟：* * * * * cmd 每 10 分钟：*/10 * * * * cmd 每小时整点：0 * * * * cmd 每天 02:00：0 2 * * * cmd 每天 23:59：59 23 * * * cmd 每周日 04:00：0 4 * * 0 cmd 每工作日 08:00：0 8 * * 1-5 cmd 每月 1 号 06:30：30 6 1 * * cmd 每年 1 月 1 日 00:00：0 0 1 1 * cmd 6) 输出与日志（强烈推荐） 丢弃输出（不产生日志）：cmd \u0026gt;/dev/null 2\u0026gt;\u0026amp;1 追加输出到日志：cmd \u0026gt;\u0026gt; /root/task.log 2\u0026gt;\u0026amp;1 分开记录标准输出/错误： cmd \u0026gt;\u0026gt; /root/out.log 2\u0026gt;\u0026gt; /root/err.log 7) 环境与坑位（务必注意） cron 环境变量很少：尽量使用绝对路径（如 /usr/bin/curl） 脚本要可执行：chmod +x /root/myscript.sh 脚本首行解释器：#!/bin/sh PATH 可在 crontab 顶部自定义： PATH=/usr/sbin:/usr/bin:/sbin:/bin 需要等待网络就绪：脚本内加检测与重试（如 ping 网关/公网 DNS） 8) 示例：OpenWRT 每天定时跑登录脚本 编辑：crontab -e 添加（每天 07:05 执行，记录日志）： 5 7 * * * /root/netlogin.sh \u0026gt;\u0026gt; /root/netlogin.log 2\u0026gt;\u0026amp;1 9) 排错清单（任务不执行时） 是否保存了 crontab：crontab -l cron 服务是否在跑：/etc/init.d/cron status 命令/脚本是否用绝对路径 权限是否可执行：ls -l /root/netlogin.sh 手动执行脚本是否正常：/root/netlogin.sh 日志是否有输出：tail -n 50 /root/netlogin.log systemd systemd 的基本概念 要理解 systemd 的用法，你需要先了解几个核心概念：\n单元（Units）： systemd 将所有需要管理的对象都抽象为单元。每个单元都有一个配置文件，以 .unit_type 结尾，比如 .service、.socket、.device 等。 目标（Targets）： 目标是一种特殊的单元，用于将其他单元分组。例如，multi-user.target 表示一个多用户的、非图形化的运行级别，而 graphical.target 则表示一个带有图形界面的运行级别。这类似于 SysVinit 中的运行级别（runlevels）。 下面是一些常见的单元类型：\nservice：服务，比如一个 Web 服务器 (Nginx) 或数据库 (MySQL)。 socket：套接字，用于进程间的通信。 device：设备，比如硬盘或网卡。 mount：挂载点，比如 /home 目录。 target：目标，用于将多个单元分组。 timer：定时器，类似于 cron，用于定时执行任务。 常用的 systemd 命令 所有与 systemd 相关的命令都以 systemctl 开头，这是你最常用到的工具。\n管理服务 这是 systemd 最常见的用法。你可以使用 systemctl 命令来启动、停止、重启、查看状态等。\n启动服务： 1 2 sudo systemctl start [服务名称].service # 例如：sudo systemctl start nginx.service 通常情况下，.service 后缀可以省略。\n停止服务： 1 2 sudo systemctl stop [服务名称] # 例如：sudo systemctl stop nginx 重启服务： 1 2 sudo systemctl restart [服务名称] # 例如：sudo systemctl restart nginx 查看服务状态： 1 2 systemctl status [服务名称] # 例如：systemctl status nginx 这个命令会显示服务的运行状态、进程 ID (PID)、内存占用、最近的日志等详细信息。\n开机自启 如果你希望某个服务在系统启动时自动运行，需要启用它。\n启用服务（开机自启）： 1 2 sudo systemctl enable [服务名称] # 例如：sudo systemctl enable nginx 这个命令会在 /etc/systemd/system/ 目录下创建一个指向服务配置文件的软链接。\n禁用服务（取消开机自启）： 1 2 sudo systemctl disable [服务名称] # 例如：sudo systemctl disable nginx 检查服务是否已启用： 1 systemctl is-enabled [服务名称] 列出所有单元 你可以使用 list-units 命令来查看当前系统中所有单元的状态。\n列出所有正在运行的单元： 1 systemctl list-units 列出所有已安装的单元（包括未运行的）： 1 systemctl list-units --all 按类型筛选单元： 1 2 3 4 systemctl list-units --type=service # 列出所有服务单元 systemctl list-units --type=target # 列出所有目标单元 创建自定义服务 当你需要为自己的应用程序创建一个 systemd 服务时，可以编写一个 .service 配置文件。这些文件通常存放在 /etc/systemd/system/ 目录下。 以下是一个简单的示例，假设你的应用程序是一个名为 my-app.py 的 Python 脚本。\n创建服务文件 1 sudo nano /etc/systemd/system/my-app.service 编辑文件内容 1 2 3 4 5 6 7 8 9 10 [Unit] Description=My Custom Python Application After=network.target [Service] User=your_username ExecStart=/usr/bin/python3 /path/to/your/my-app.py WorkingDirectory=/path/to/your/ Restart=always [Install] WantedBy=multi-user.target [Unit] 部分：定义了服务的描述和依赖关系。After=network.target 表示这个服务会在网络服务启动之后再启动。 [Service] 部分：定义了如何运行服务。 User：指定运行服务的用户。 ExecStart：指定启动服务的命令。 WorkingDirectory：指定工作目录。 Restart：指定服务异常退出时的重启策略，always 表示总是重启。 [Install] 部分：定义了服务如何被“安装”，也就是如何与目标单元关联。WantedBy=multi-user.target 表示当系统进入多用户模式时，会自动启用此服务。 重新加载 systemd 配置 创建或修改服务文件后，你需要让 systemd 重新加载配置。 1 sudo systemctl daemon-reload 启动并启用你的新服务 1 2 sudo systemctl start my-app.service sudo systemctl enable my-app.service 其他有用的命令 查看日志： systemd 将所有服务的日志都通过 journald 统一管理。 1 2 journalctl -u [服务名称] # 例如：journalctl -u nginx 这个命令会显示特定服务的所有日志。你可以使用 -f 选项实时跟踪日志：journalctl -u nginx -f。\n掩盖服务（Masking）： mask 命令可以阻止一个服务被启动，即使有其他服务依赖它。 1 2 sudo systemctl mask [服务名称] # 例如：sudo systemctl mask nginx 这会在 /etc/systemd/system/ 下创建一个指向 /dev/null 的软链接。要取消掩盖，使用 unmask。\n挂载点（Mounts）： systemd 也可以管理文件系统的挂载点，你可以使用 systemctl --type=mount 来查看。 ","permalink":"https://haleuan.github.io/post/linux-survival-guide-process/","summary":"\u003ch2 id=\"crontab-常用速查指南openwrt--linux\"\u003eCrontab 常用速查指南（OpenWRT / Linux）\u003c/h2\u003e\n\u003ch3 id=\"1-基本概念\"\u003e1) 基本概念\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003cstrong\u003ecron\u003c/strong\u003e：定时任务守护进程\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003ecrontab\u003c/strong\u003e：用户级任务表（每个用户一份）\u003c/li\u003e\n\u003cli\u003e\u003cstrong\u003e任务格式\u003c/strong\u003e：\u003ccode\u003e分 时 日 月 周  命令\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"2-常用命令\"\u003e2) 常用命令\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e查看当前用户任务：\u003ccode\u003ecrontab -l\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e编辑当前用户任务：\u003ccode\u003ecrontab -e\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e删除当前用户任务：\u003ccode\u003ecrontab -r\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e查看 cron 服务状态（OpenWRT）：\u003ccode\u003e/etc/init.d/cron status\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e启动 cron（OpenWRT）：\u003ccode\u003e/etc/init.d/cron start\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e开机自启（OpenWRT）：\u003ccode\u003e/etc/init.d/cron enable\u003c/code\u003e\u003c/li\u003e\n\u003cli\u003e重启 cron（OpenWRT）：\u003ccode\u003e/etc/init.d/cron restart\u003c/code\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"3-时间字段速记分时日月周\"\u003e3) 时间字段速记（分时日月周）\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e分：0-59\u003c/li\u003e\n\u003cli\u003e时：0-23\u003c/li\u003e\n\u003cli\u003e日：1-31\u003c/li\u003e\n\u003cli\u003e月：1-12\u003c/li\u003e\n\u003cli\u003e周：0-7（0/7 都表示周日；1=周一 … 6=周六）\u003c/li\u003e\n\u003c/ul\u003e\n\u003ch3 id=\"4-通配与步进写法\"\u003e4) 通配与步进写法\u003c/h3\u003e\n\u003cul\u003e\n\u003cli\u003e\u003ccode\u003e*\u003c/code\u003e：任意值\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e,\u003c/code\u003e：列举多个值（如 \u003ccode\u003e1,15\u003c/code\u003e）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e-\u003c/code\u003e：范围（如 \u003ccode\u003e1-5\u003c/code\u003e）\u003c/li\u003e\n\u003cli\u003e\u003ccode\u003e/\u003c/code\u003e：步进（如 \u003ccode\u003e*/5\u003c/code\u003e 表示每 5 分钟）\u003c/li\u003e\n\u003c/ul\u003e\n\u003cp\u003e示例：\u003c/p\u003e","title":"Linux 生存指南：进程与定时任务"},{"content":"nmcli 连接 Wi-Fi 命令备忘录 任务 命令 扫描网络 nmcli device wifi list 连接网络 sudo nmcli dev wifi connect \u0026quot;SSID\u0026quot; password \u0026quot;PASS\u0026quot; 查看活动连接 nmcli con show --active 查看所有已存连接 nmcli con show 断开连接 sudo nmcli dev disconnect wlan0 连接已存网络 sudo nmcli con up \u0026quot;连接名\u0026quot; 删除已存网络 sudo nmcli con del \u0026quot;连接名\u0026quot; 关闭/开启 Wi-Fi nmcli radio wifi off/on 方法一：使用 nmcli 创建热点 (推荐) nmcli 是 NetworkManager 的命令行客户端，是管理网络连接的现代标准。用它来创建热点相对简单直观。\n步骤 1：确保 NetworkManager 正在管理的无线设备 Ubuntu Server 24.04 默认使用 netplan 来配置网络，而 netplan 后端可以是 systemd-networkd 或 NetworkManager。要使用 nmcli，我们需要确保 NetworkManager 拥有对无线网卡的控制权。\n检查 netplan 配置：查看 /etc/netplan/ 目录下的 .yaml 配置文件。 1 2 3 4 # # 查看 netplan 配置文件内容 # cat /etc/netplan/*.yaml 确保 renderer 设置为 NetworkManager，或者为的无线设备 (wifis) 指定 renderer: NetworkManager。\n示例配置 /etc/netplan/01-netcfg.yaml:\n1 2 3 4 network: version: 2 # 将 NetworkManager 设置为默认的渲染器 renderer: NetworkManager 修改并应用配置：如果的配置不是 NetworkManager，请修改文件，然后运行： 1 2 3 4 # # 应用新的 netplan 配置 # sudo netplan apply 步骤 2：创建热点连接 现在，我们可以使用 nmcli 创建一个热点。这个过程只需要一条命令。\ncon add：添加一个新连接。 type wifi：连接类型是 Wi-Fi。 ifname wlan0：指定使用哪个无线接口（请替换成的接口名）。 con-name MyHotspot：给这个热点连接起个名字，方便管理。 autoconnect yes：设置为开机自动启动热点（如果需要）。 ssid \u0026quot;MyServerHotspot\u0026quot;：这是的热点名称（Wi-Fi 名称）。 wifi-sec.key-mgmt wpa-psk：设置安全模式为 WPA2-Personal。 wifi-sec.psk \u0026quot;YourStrongPassword\u0026quot;：设置的 Wi-Fi 密码（请务必使用一个强度高、至少8位的密码！）。 将以上参数组合成一条命令：\n1 2 3 # 创建一个新的 Wi-Fi 热点连接 # 请将 wlan0, MyHotspot, MyServerHotspot 和 YourStrongPassword 替换为自己的设置 sudo nmcli con add type wifi ifname wlp33s0 con-name MyHotspot autoconnect yes ssid \u0026#34;hpz8g4workstation\u0026#34; -- wifi-sec.key-mgmt wpa-psk wifi-sec.psk \u0026#34;cqnu123...\u0026#34; 步骤 3：启动热点 通常，创建后热点会自动启动。如果没启动，可以用以下命令手动启动：\n1 2 # 启动名为 MyHotspot 的连接 sudo nmcli con up MyHotspot 现在，可以用手机或其他设备搜索 Wi-Fi，应该能找到名为 \u0026ldquo;MyServerHotspot\u0026rdquo; 的网络，使用设置的密码即可连接。\n管理的热点 查看连接状态： 1 2 # 查看所有网络连接及其状态 nmcli con show 关闭热点： 1 2 # 关闭（断开）热点连接 sudo nmcli con down MyHotspot 删除热点： 1 2 # 删除热点配置 sudo nmcli con delete MyHotspot Ubuntu Server Wi-Fi 使用 Netplan 首次连接到 Wi-Fi 在这一步，我们将在没有 nmcli 且无网络的情况下，使用系统自带的 netplan 工具完成首次网络连接。此时，netplan 默认的后端是 systemd-networkd。\n查找无线网卡接口名 首先，我们需要知道系统的无线网卡叫什么名字。 1 2 # 列出所有网络接口 ip a 在输出中找到的无线网卡，其名称通常是 wlan0 或 wlpXsY 的形式。请记下这个名字。\n再使用 iw 命令扫描可用 Wi-Fi，iw 是现代 Linux 系统中用于配置无线设备的标准命令行工具，是旧的 iwconfig 的替代品。它能够直接与无线网卡硬件通信，进行扫描。假设前面得到的无线接口名是 wlan0。 执行扫描\n1 2 # 格式: sudo iw dev [接口名] scan sudo iw dev wlan0 scan 这条命令的输出会非常详细，它会列出范围内每个接入点（AP）的全部信息，包括 BSSID (MAC地址), 频率, 信号强度, 支持的速率, 加密方式等等。\n由于原始输出太长，我们通常使用 grep 命令来筛选出我们最关心的信息——网络名称 (SSID)。\n1 2 3 # | (管道符) 会将前一个命令的输出作为后一个命令的输入 # grep SSID: 会筛选出所有包含 \u0026#34;SSID: \u0026#34; 的行 sudo iw dev wlan0 scan | grep SSID: 输出示例：\n1 2 3 4 SSID: MyHomeWiFi SSID: Guest-Network SSID: Another-WiFi-Here SSID: 可能会看到一个空的 SSID:，这通常是隐藏网络。\n同时查看信号强度，可以这样做：\n1 2 3 # -i 参数让 grep 不区分大小写 # -E 参数表示使用扩展正则表达式，可以用 | (或) 来匹配多个模式 sudo iw dev wlan0 scan | grep -iE \u0026#39;SSID:|signal:\u0026#39; 信号强度解读：这个值是 dBm，它是一个负数。数字越大（越接近0），表示信号越强。例如 -45 dBm 是一个非常强的信号，而 -80 dBm 则是一个很弱的信号。\n创建 Netplan 配置文件 我们将为 Wi-Fi 连接创建一个新的 netplan 配置文件。 1 2 # 使用 nano 编辑器创建一个新的配置文件 sudo nano /etc/netplan/02-wifi.yaml 写入 Wi-Fi 配置信息 将以下内容复制并粘贴到编辑器中。注意：YAML 格式对缩进极其敏感，请务必使用空格并保持层级正确。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 # Netplan Wi-Fi 配置文件 network: version: 2 wifis: # # 将 wlan0 替换为的无线网卡接口名 # wlan0: dhcp4: true access-points: # # 替换为要连接的 Wi-Fi 名称 (SSID) # \u0026#34;Your_WiFi_SSID\u0026#34;: # # 替换为的 Wi-Fi 密码 # password: \u0026#34;Your_WiFi_Password\u0026#34; 修改完成后，保存并退出 (Ctrl + X, Y, Enter)。\n应用配置并验证连接 让配置生效并检查网络状态。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 # # 测试配置，如果120秒内没有确认，配置会自动回滚 # sudo netplan try # # 应用 netplan 配置 # sudo netplan apply # # 稍等片刻，检查是否获取了 IP 地址 # ip a # # 测试外网连通性 # ping -c 4 baidu.com 当在 wlan0 接口下看到 inet 开头的 IP 地址，并且 ping 命令成功时，恭喜，服务器已经成功连接到互联网！\n安装 NetworkManager 现在我们有了网络连接，可以安装更强大的网络管理工具 network-manager，它会提供 nmcli 命令行程序。\n更新软件包列表 1 2 3 4 # # 确保的软件包列表是最新 # sudo apt update 安装 NetworkManager 1 2 3 4 # # 安装 network-manager 软件包 # sudo apt install network-manager 将网络控制权移交给 NetworkManager 安装完成后，我们需要告诉 netplan，以后请让 NetworkManager 来当“网络管家”。\n修改 Netplan 配置文件 再次打开我们之前创建的 netplan 配置文件。 1 2 3 4 # # 编辑配置文件 # sudo nano /etc/netplan/02-wifi.yaml 简化配置，指定渲染器 将文件中的所有内容删除，替换为以下更简洁的配置。 1 2 3 4 5 6 7 8 9 10 # # Netplan 配置文件 - 指定 NetworkManager 为全局渲染器 # network: version: 2 # # 这一行是核心，它命令 Netplan 使用 NetworkManager # 来管理所有的网络设备 # renderer: NetworkManager 这个配置意味着 netplan 不再负责具体的连接细节，而是把所有工作都交给了 NetworkManager。保存并退出。\n应用新配置并验证 1 2 3 4 5 6 7 8 9 # # 应用新的配置，网络服务会重启 # 注意：的 SSH 连接可能会短暂中断 # sudo netplan apply # # 验证 NetworkManager 是否已成功接管 # nmcli device status 如果输出中，的无线网卡（wlan0）和有线网卡的 STATE 不再是 unmanaged，则说明切换成功。\n使用 nmcli 轻松管理 Wi-Fi 连接 现在，可以开始享受 nmcli 带来的便利了。\n扫描周边的 Wi-Fi 网络 1 2 3 4 # # 列出所有可用的 Wi-Fi 热点 # nmcli device wifi list 使用 nmcli 连接到网络 之前的 netplan Wi-Fi 配置在切换后已失效，我们需要用 nmcli 重新连接一次。这次连接后，配置将被 NetworkManager 自动永久保存。 1 2 3 4 # # 使用 nmcli 连接到的 Wi-Fi # sudo nmcli device wifi connect \u0026#34;Your_WiFi_SSID\u0026#34; password \u0026#34;Your_WiFi_Password\u0026#34; 查看当前连接状态 1 2 3 4 # # 查看当前活动的连接 # nmcli connection show --active 断开连接 1 2 3 4 # # 断开无线网卡的连接 # sudo nmcli device disconnect wlan0 系统优化 - 处理 systemd-networkd-wait-online 服务失败 在切换到 NetworkManager 后，可能会在系统日志或 Cockpit 界面中看到 systemd-networkd-wait-online 服务启动失败。这是一个无害的副作用，因为 systemd-networkd 已经“失业”了。我们可以禁用这个多余的服务来清理系统并略微加快开机速度。\n禁用多余的服务 1 2 3 4 # # 禁用该服务，使其不再开机启动 # sudo systemctl disable systemd-networkd-wait-online.service 重启验证（可选） 可以重启服务器，之后该启动失败的提示就会消失。 1 sudo reboot ","permalink":"https://haleuan.github.io/post/linux-survival-guide-network/","summary":"\u003ch2 id=\"nmcli\"\u003enmcli\u003c/h2\u003e\n\u003ch3 id=\"连接-wi-fi-命令备忘录\"\u003e连接 Wi-Fi 命令备忘录\u003c/h3\u003e\n\u003ctable\u003e\n\t\u003cthead\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003cth\u003e任务\u003c/th\u003e\n\t\t\t\t\t\u003cth\u003e命令\u003c/th\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/thead\u003e\n\t\u003ctbody\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e扫描网络\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003enmcli device wifi list\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e连接网络\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esudo nmcli dev wifi connect \u0026quot;SSID\u0026quot; password \u0026quot;PASS\u0026quot;\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e查看活动连接\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003enmcli con show --active\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e查看所有已存连接\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003enmcli con show\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e断开连接\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esudo nmcli dev disconnect wlan0\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e连接已存网络\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esudo nmcli con up \u0026quot;连接名\u0026quot;\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e删除已存网络\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003esudo nmcli con del \u0026quot;连接名\u0026quot;\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\t\t\u003ctr\u003e\n\t\t\t\t\t\u003ctd\u003e\u003cstrong\u003e关闭/开启 Wi-Fi\u003c/strong\u003e\u003c/td\u003e\n\t\t\t\t\t\u003ctd\u003e\u003ccode\u003enmcli radio wifi off/on\u003c/code\u003e\u003c/td\u003e\n\t\t\t\u003c/tr\u003e\n\t\u003c/tbody\u003e\n\u003c/table\u003e\n\u003ch3 id=\"方法一使用\"\u003e\u003cstrong\u003e方法一：使用 \u003ccode\u003enmcli\u003c/code\u003e 创建热点 (推荐)\u003c/strong\u003e\u003c/h3\u003e\n\u003cp\u003e\u003ccode\u003enmcli\u003c/code\u003e 是 NetworkManager 的命令行客户端，是管理网络连接的现代标准。用它来创建热点相对简单直观。\u003c/p\u003e","title":"Linux 生存指南：网络配置与管理"},{"content":"mv, cp 进度显示 rsync 复制单个文件：\n1 2 3 4 # -a: 归档模式，保留文件所有属性（权限、时间戳等） # -h: human-readable，以人类可读的格式显示大小 (e.g., GB, MB) # -P: 显示进度 (progress) 并且支持断点续传 (partial) rsync -ahP /path/to/source/very_large_dataset.zip /path/to/destination/ 复制整个目录：\n1 rsync -ahP /path/to/source_directory/ /path/to/destination_directory/ pv (通用管道查看器，非常灵活) pv (Pipe Viewer) 是一个终端工具，专门用来测量数据在管道（pipe）中的流向。可以把它插入到任意两个命令之间，用来监控数据传输的进度。它是一个通用工具，不仅能监控文件复制，还能监控 tar 打包、gzip 压缩、数据库导出 dd 镜像等任何通过管道传输数据的过程。 安装：\n1 sudo apt install pv 使用：\n1 2 # 基本格式: pv [源文件] \u0026gt; [目标文件] pv /path/to/source/large_model.pth \u0026gt; /path/to/destination/large_model.pth progress (监控已在运行的命令) 它可以在不中断命令的情况下，去监控系统中正在运行的 cp, mv, dd, tar 等命令的进度。 安装：\n1 sudo apt install progress 使用：\n在第一个终端窗口，正常执行复制命令： 1 cp very_large_file.iso /mnt/usb/ 打开第二个终端窗口，运行 progress 命令： 1 2 # -m 参数表示持续监控 progress -m 输出示例： progress 会自动找到正在运行的 cp 进程，并显示它的 PID、文件名、进度、速度等信息。\n1 2 [12345] cp /path/to/very_large_file.iso 4.1 GiB (55.1%) of 7.5 GiB at 98.7 MiB/s eta 0:35 ","permalink":"https://haleuan.github.io/post/linux-survival-guide-file-transfer/","summary":"\u003ch2 id=\"mv-cp-进度显示\"\u003e\u003ccode\u003emv\u003c/code\u003e, \u003ccode\u003ecp\u003c/code\u003e 进度显示\u003c/h2\u003e\n\u003ch3 id=\"rsync\"\u003e\u003ccode\u003ersync\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003e复制单个文件：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# -a: 归档模式，保留文件所有属性（权限、时间戳等）\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# -h: human-readable，以人类可读的格式显示大小 (e.g., GB, MB)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# -P: 显示进度 (progress) 并且支持断点续传 (partial)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003ersync -ahP /path/to/source/very_large_dataset.zip /path/to/destination/\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e复制整个目录：\u003c/p\u003e","title":"Linux 生存指南：文件传输与进度监控"},{"content":"Quickstart: Creating Models 使用 accelerator 进行加速：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 import torch import torch.nn as nn device = torch.accelerator.current_accelerator().type if torch.accelerator.is_available() else \u0026#34;cpu\u0026#34; print(f\u0026#34;Using {device} device\u0026#34;) # Define model class NeuralNetwork(nn.Module): def __init__(self): super().__init__() self.flatten = nn.Flatten() self.linear_relu_stack = nn.Sequential( nn.Linear(28*28, 512), nn.ReLU(), nn.Linear(512, 512), nn.ReLU(), nn.Linear(512, 10) ) def forward(self, x): x = self.flatten(x)\t# model 的实际输入是在这里发生 logits = self.linear_relu_stack(x)\treturn logits model = NeuralNetwork().to(device) print(model) 在 PyTorch（以及大多数深度学习框架）的习惯里，forward 函数接收的输入默认是整个 batch 的数据，而不是单个样本。\nCross Entropy 以以下为例：\n1 2 3 4 5 6 7 8 9 import torch from torch.nn import functional as F logits = torch.tensor([[2, 0.1, 0.1], [0.1, 1, 0.1]]) labels = torch.tensor([0, 1]) loss = F.cross_entropy(logits, labels) print(loss.item()) # output: 0.4283800423145294 其中：[2, 0.1, 0.1] 是对第一个样本（样本 0）的三个预测结果，对应 3 个类别的原始得分（logits）。假设分类中，一共有 3 类：类别 0，类别 1，类别 2。那么有：\n2 是类别 0 的得分。 0.1 是类别 1 的得分。 0.1 是类别 2 的得分。 同样，[0.1, 1, 0.1] 是对第二个样本（样本 1）的预测结果，分别对应 3 个类别的得分。\nPyTorch 的 cross_entropy 会自动将 logits 输入到 softmax 函数，计算各预测类别的概率，然后再算出交叉熵损失，因此我们不需要手动添加 softmax 层。\nDataset 和 DataLoader 在使用如下方式加载数据时：\n1 2 for sample_id, query_embedding, pos_embeddings_list, neg_embeddings_list in tqdm(dataloader): pass 需要注意 DataLoader 会自动给每个样本加上一维 batch。在写 forward 函数时需要注意传入样本数据的维度。\n除此之外，DataLoader 既然会将多个样本组合成一个 batch，那么很自然地，batch 中的各个样本 shape 需要是一致的。如果不一致，需要手动传入 collate_fn 函数，供 DataLoader 控制 batch 中样本的处理和补齐。\n示例：自定义 collate_fn 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 def simple_collate_fn(batch): queries = torch.stack([item[0] for item in batch]) # [B, 4] max_len = max(len(item[1]) for item in batch) B = len(batch) facts = torch.zeros(B, max_len, 4) labels = torch.zeros(B, max_len) mask = torch.zeros(B, max_len) for i, (q, fact_list, label_list) in enumerate(batch): for j in range(len(fact_list)): facts[i, j] = fact_list[j] labels[i, j] = label_list[j] mask[i, j] = 1 return queries, facts, labels, mask Tensor 操作 torch.stack torch.stack 沿着新维度连接张量序列（区别于 torch.cat()，后者是沿着现有维度连接给定的序列）。\n1 2 3 4 5 6 7 8 9 10 11 12 \u0026gt;\u0026gt;\u0026gt; x = torch.randn(2, 3) \u0026gt;\u0026gt;\u0026gt; x tensor([[ 0.3367, 0.1288, 0.2345], [ 0.2303, -1.1229, -0.1863]]) \u0026gt;\u0026gt;\u0026gt; torch.stack((x, x)) # same as torch.stack((x, x), dim=0) tensor([[[ 0.3367, 0.1288, 0.2345], [ 0.2303, -1.1229, -0.1863]], [[ 0.3367, 0.1288, 0.2345], [ 0.2303, -1.1229, -0.1863]]]) \u0026gt;\u0026gt;\u0026gt; torch.stack((x, x)).size() torch.Size([2, 2, 3]) 广播机制 (Broadcast) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 mat = torch.tensor([[1, 2, 3, 4], [1, 2, 2, 2], [2, 3, 3, 3]]) mat.sum(dim=1, keepdim=True), mat / mat.sum(dim=1, keepdim=True) # \u0026gt;\u0026gt;\u0026gt; ( # \u0026gt;\u0026gt;\u0026gt; tensor([[10], # \u0026gt;\u0026gt;\u0026gt; [7], # \u0026gt;\u0026gt;\u0026gt; [11]]), # \u0026gt;\u0026gt;\u0026gt; tensor( # \u0026gt;\u0026gt;\u0026gt; [ # \u0026gt;\u0026gt;\u0026gt; [0.1000, 0.2000, 0.3000, 0.4000], # \u0026gt;\u0026gt;\u0026gt; [0.1429, 0.2857, 0.2857, 0.2857], # \u0026gt;\u0026gt;\u0026gt; [0.1818, 0.2727, 0.2727, 0.2727], # \u0026gt;\u0026gt;\u0026gt; ] # \u0026gt;\u0026gt;\u0026gt; ), # \u0026gt;\u0026gt;\u0026gt; ) 这其中的过程是：\n1 2 3 4 5 6 mat_sum = tensor([[10], [7], [11]]) # 经过 broadcast 机制变成了: mat_sum_broadcast = tensor([[10, 10, 10, 10], [7, 7, 7, 7], [11, 11, 11, 11]]) 所以广播就是：在满足特定限制的前提下，较小的数组“广播至”较大的数组，使两者形状互相兼容。 可广播的一对张量需满足以下规则：\n每个张量至少有一个维度。 迭代维度尺寸时，从尾部的维度开始，维度尺寸满足： 或者相等 或者其中一个张量的维度尺寸为 1 或者其中一个张量不存在这个维度。 梯度 (Gradient) 在训练中: W.grad 就是指 $\\frac{\\partial(Loss)}{\\partial(w_i)}$，但是这里并不是一个数学表达式，而是一个具体的数值（即当前输入下的梯度数值）。 带符号的表达式，例如 $\\frac{dx^2}{dx}=2x$，这个叫做符号求导。\nMask 机制 为什么在 loss 函数中 mask 乘以 padding 可以屏蔽掉梯度？ 举个简单的例子： 无 mask 时：\n$z=w\\cdot x$，$z$ 为预测值 $L=\\frac 12 (z-\\hat z)^2=\\frac12(w\\cdot x-\\hat z)^2$，$\\hat z$ 为真实值；在链式法则中，中间变量为 $y_1=w\\cdot x$ 有 mask 时：\n$z=w\\cdot x\\cdot \\text{mask}$ $L=\\frac 12 (z-\\hat z)^2=\\frac12(w\\cdot x\\cdot \\text{mask}-\\hat z)^2$，$\\hat z$ 为真实值；在链式法则中，中间变量为 $y_1'=w\\cdot x\\cdot \\text{mask}$ 我们更新 $w$ 的方式为：\n$$w \\longleftarrow w - \\eta \\cdot \\frac{\\partial L}{\\partial w}$$展开来：\n$$w - \\eta \\cdot \\frac{\\partial L}{\\partial w} = w - \\eta \\cdot \\frac{\\partial L}{\\partial y_1}\\frac{\\partial y_1}{\\partial w}$$在有 mask 的情况下展开结果为：\n$$ w - \\eta \\cdot \\frac{\\partial L}{\\partial w} = w - \\eta \\cdot \\frac{\\partial L}{\\partial y_1'}\\frac{\\partial y_1'}{\\partial w} = w - \\eta \\cdot \\frac{\\partial L}{\\partial y_1'}\\frac{\\partial (w\\cdot x\\cdot \\text{mask})}{\\partial w} $$其中，如果对应的 token 是 padding（即 $\\text{mask}=0$），$\\frac{\\partial (w\\cdot x\\cdot 0)}{\\partial w} = 0$，这样就等于 $w = w - \\eta \\cdot 0$，梯度也就没有发生更新。\n更广泛地来讲，通过使用 mask，$m_i=0$ 就使得整个梯度变为 $0$。而如果不使用 mask 仅仅做零填充（即使 $x=0$），当损失函数不为 MSE（例如使用了 Cross Entropy）时，依然会有梯度产生，无法彻底消除填充部分对梯度的影响。\nMask 的弊端／风险 虽然 mask 是屏蔽 padding 的可靠手段，但也并非在任何场景都能“无脑”使用：\n统计层面不兼容：某些层（BatchNorm、LayerNorm）在训练时会根据全 batch 统计均值／方差，mask 无法阻止它们把 padding 的零纳入统计，从而造成偏差。需额外用 nn.SyncBatchNorm、mask-aware norm 或把这些层置为 eval 模式。 硬件与库支持限制：一些加速库（如 cuDNN 原生卷积）不支持 ragged/masked 输入。这导致必须先做 padding 再统一计算，mask 只能作为后处理。 分母为 0 的风险：如果一个 batch 全部是 padding（$\\sum m_j = 0$），计算平均损失时会出现除以 0 错误。需要在 sampler 或 collate 时保证每 batch 至少有一个有效样本。 代码复杂度与调试成本：需要在多个地方（collate_fn、loss、attention、Pooling）统一使用同一份 mask，漏一处就会有隐蔽的 bug。 ","permalink":"https://haleuan.github.io/post/pytorch-notes/","summary":"\u003ch2 id=\"quickstart-creating-models\"\u003eQuickstart: Creating Models\u003c/h2\u003e\n\u003cp\u003e使用 \u003ccode\u003eaccelerator\u003c/code\u003e 进行加速：\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e14\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e15\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e16\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e17\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e18\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e19\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e20\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e21\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e22\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e23\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e24\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e25\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e26\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003etorch\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"nn\"\u003etorch.nn\u003c/span\u003e \u003cspan class=\"k\"\u003eas\u003c/span\u003e \u003cspan class=\"nn\"\u003enn\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003etorch\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaccelerator\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003ecurrent_accelerator\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003etype\u003c/span\u003e \u003cspan class=\"k\"\u003eif\u003c/span\u003e \u003cspan class=\"n\"\u003etorch\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eaccelerator\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eis_available\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e \u003cspan class=\"k\"\u003eelse\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;cpu\u0026#34;\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"sa\"\u003ef\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Using \u003c/span\u003e\u003cspan class=\"si\"\u003e{\u003c/span\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e\u003cspan class=\"si\"\u003e}\u003c/span\u003e\u003cspan class=\"s2\"\u003e device\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# Define model\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003eclass\u003c/span\u003e \u003cspan class=\"nc\"\u003eNeuralNetwork\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eModule\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"fm\"\u003e__init__\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"nb\"\u003esuper\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"fm\"\u003e__init__\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eflatten\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eFlatten\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elinear_relu_stack\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eSequential\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLinear\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e28\u003c/span\u003e\u003cspan class=\"o\"\u003e*\u003c/span\u003e\u003cspan class=\"mi\"\u003e28\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e512\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReLU\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLinear\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e512\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e512\u003c/span\u003e\u003cspan class=\"p\"\u003e),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eReLU\u003c/span\u003e\u003cspan class=\"p\"\u003e(),\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e            \u003cspan class=\"n\"\u003enn\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eLinear\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"mi\"\u003e512\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"mi\"\u003e10\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eforward\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003ex\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eflatten\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\t\u003cspan class=\"c1\"\u003e# model 的实际输入是在这里发生\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"n\"\u003elogits\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"bp\"\u003eself\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003elinear_relu_stack\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003ex\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\t\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e        \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"n\"\u003elogits\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003emodel\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eNeuralNetwork\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\u003cspan class=\"o\"\u003e.\u003c/span\u003e\u003cspan class=\"n\"\u003eto\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003edevice\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nb\"\u003eprint\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003emodel\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e在 PyTorch（以及大多数深度学习框架）的习惯里，\u003cstrong\u003e\u003ccode\u003eforward\u003c/code\u003e 函数接收的输入默认是整个 batch 的数据\u003c/strong\u003e，而不是单个样本。\u003c/p\u003e","title":"PyTorch 核心概念与踩坑记录"},{"content":"Quickstart e.g\n1 2 3 4 5 6 7 8 9 10 11 12 13 from typing import Union from fastapi import FastAPI app = FastAPI() @app.get(\u0026#34;/\u0026#34;) def read_root(): return {\u0026#34;Hello\u0026#34;: \u0026#34;World\u0026#34;} @app.get(\u0026#34;/items/{item_id}\u0026#34;) def read_item(item_id: int, q: Union[str, None] = None): return {\u0026#34;item_id\u0026#34;: item_id, \u0026#34;q\u0026#34;: q} 创建 FastAPI 实例： 1 app = FastAPI() 在这一步，创建了一个 FastAPI 应用的实例，它将用于定义和管理应用的各个组件，包括路由。FastAPI 是FastAPI框架的主要类。 2. 定义根路径 / 的路由操作\n1 2 3 @app.get(\u0026#34;/\u0026#34;) def read_root(): return {\u0026#34;Hello\u0026#34;: \u0026#34;World\u0026#34;} 这个路由操作使用了 @app.get(\u0026quot;/\u0026quot;) 装饰器，表示当用户通过 GET 请求访问根路径时，将执行 read_root 函数。函数返回一个包含 {\u0026quot;Hello\u0026quot;: \u0026quot;World\u0026quot;} 的字典，这个字典会被 FastAPI 自动转换为JSON格式并返回给用户。 3. 定义带路径参数和查询参数的路由操作\n1 2 3 @app.get(\u0026#34;/items/{item_id}\u0026#34;) def read_item(item_id: int, q: Union[str, None] = None): return {\u0026#34;item_id\u0026#34;: item_id, \u0026#34;q\u0026#34;: q} 这个路由操作使用了 @app.get(\u0026quot;/items/{item_id}\u0026quot;) 装饰器，表示当用户通过 GET 请求访问 /items/{item_id} 路径时，将执行 read_item 函数。 函数接受两个参数：\nitem_id: 路径参数，指定为整数类型。 q: 查询参数，指定为字符串类型或空（None）。 函数返回一个字典，包含传入的 item_id 和 q 参数。 两个核心组件 Pydantic 负责数据校验，例如传入或者传出的数据类型是否符合要求\nHTTP协议的4大特点 HTTP是基于TCP/IP的一个上层协议，通信双方是client(浏览器)和server。 HTTP协议的特性 基于请求-响应模式 协议规定，请求从client发出，server接收到请求并返回响应。也就是说，肯定是从client开始建立通信的，server在没有接收到请求之前，是不会发送响应的。 不同于socket是全双工通信，socket协议中client和server谁先给谁发都是可以的。所以对应HTTP协议的这一特点，还衍生出了一个WebSocket协议，WebSocket协议是双向的，能够让server给client推送消息。 同时，HTTP有请求也必须有响应，即使请求错误，也必须返回报错响应\n无状态保存 当client给server发送请求，server根据请求作出响应后，server并不对此请求作任何相关记录。但实际上在很多场景下我们是需要这种记录的，比如在某个网站上登陆过一次后，我们后续访问该网站就不需要再次登陆，这一点光凭HTTP协议是无法做到的，所以这也就需要cookie-session机制。\n短连接 HTTP1.0默认使用的是短连接。浏览器和服务器每进行一次HTTP操作，就建立一次连接，任务结束就中断连接。 HTTP/1.1起，默认使用长连接。要使用长连接，客户端和服务器的HTTP首部的Connection都要设置为keep-adive，才能支持长连接 HTTP长连接，指的是复用TCP连接。多个HTTP请求可以复用同一个TCP连接，这就节省了TCP连接建立和断开的消耗。 HTTP协议格式 请求格式 1 2 3 4 5 POST /api/v1/auth/password/login/?loginWay=password HTTP/1.1 # 请求首行 content-type: application/json # 请求头 user-agent: Chrome/104.0.0.0 Safari/537.36 # 请求头 # 空行 {username: \u0026#34;user\u0026#34;, password: \u0026#34;123\u0026#34;} # 请求体 POST请求和GET请求的区别在于，发送请求时的数据部分放在什么的位置上：\nGET请求会将数据放在请求首行中的路径后面，作为一个请求参数，并没有请求体。 POST请求会有一个请求体，讲数据放在请求体中 在登陆的时候需要输入密码，由于GET只能将数据放在路径当中，而路径是明文放在地址栏中的，所以并不安全，登陆时也就因此选用POST方法，虽然POST方法也可以看到密码，但是可以进一步进行加密处理。 响应协议 1 2 3 4 5 6 7 8 9 10 11 12 HTTP/1.1 200 OK content-type: application/json date: Sun, 28 Aug 2022 13:43:25 GMT content-length: 73 # 空行 { \u0026#34;code\u0026#34;: -1, \u0026#34;msg\u0026#34;: \u0026#34;校验错误\u0026#34;, \u0026#34;data\u0026#34;: { \u0026#34;global_error\u0026#34;: [\u0026#34;密码错误\u0026#34;] } } ","permalink":"https://haleuan.github.io/post/fastapi-basics/","summary":"\u003ch2 id=\"quickstart\"\u003eQuickstart\u003c/h2\u003e\n\u003cp\u003ee.g\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e 1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 5\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 6\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 7\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 8\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e 9\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e10\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e11\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e12\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e13\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003etyping\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eUnion\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"kn\"\u003efrom\u003c/span\u003e \u003cspan class=\"nn\"\u003efastapi\u003c/span\u003e \u003cspan class=\"kn\"\u003eimport\u003c/span\u003e \u003cspan class=\"n\"\u003eFastAPI\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFastAPI\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@app.get\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eread_root\u003c/span\u003e\u003cspan class=\"p\"\u003e():\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;Hello\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;World\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"nd\"\u003e@app.get\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;/items/\u003c/span\u003e\u003cspan class=\"si\"\u003e{item_id}\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e)\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"k\"\u003edef\u003c/span\u003e \u003cspan class=\"nf\"\u003eread_item\u003c/span\u003e\u003cspan class=\"p\"\u003e(\u003c/span\u003e\u003cspan class=\"n\"\u003eitem_id\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"nb\"\u003eint\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"n\"\u003eq\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eUnion\u003c/span\u003e\u003cspan class=\"p\"\u003e[\u003c/span\u003e\u003cspan class=\"nb\"\u003estr\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e]\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"kc\"\u003eNone\u003c/span\u003e\u003cspan class=\"p\"\u003e):\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e    \u003cspan class=\"k\"\u003ereturn\u003c/span\u003e \u003cspan class=\"p\"\u003e{\u003c/span\u003e\u003cspan class=\"s2\"\u003e\u0026#34;item_id\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eitem_id\u003c/span\u003e\u003cspan class=\"p\"\u003e,\u003c/span\u003e \u003cspan class=\"s2\"\u003e\u0026#34;q\u0026#34;\u003c/span\u003e\u003cspan class=\"p\"\u003e:\u003c/span\u003e \u003cspan class=\"n\"\u003eq\u003c/span\u003e\u003cspan class=\"p\"\u003e}\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003col\u003e\n\u003cli\u003e创建 FastAPI 实例：\u003c/li\u003e\n\u003c/ol\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-python\" data-lang=\"python\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"n\"\u003eapp\u003c/span\u003e \u003cspan class=\"o\"\u003e=\u003c/span\u003e \u003cspan class=\"n\"\u003eFastAPI\u003c/span\u003e\u003cspan class=\"p\"\u003e()\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003cp\u003e在这一步，创建了一个 FastAPI 应用的实例，它将用于定义和管理应用的各个组件，包括路由。\u003ccode\u003eFastAPI\u003c/code\u003e 是FastAPI框架的主要类。\n2. 定义根路径 \u003ccode\u003e/\u003c/code\u003e 的路由操作\u003c/p\u003e","title":"FastAPI 入门与 HTTP 协议基础"},{"content":"前言 马尔可夫链（Markov Chain）可以说是机器学习和人工智能的基石，在强化学习、自然语言处理、金融领域、天气预测、语音识别方面都有着极其广泛的应用\nThe future is independent of the past given the present\n未来独立于过去，只基于当下。\n这句人生哲理的话也代表了马尔科夫链的思想：过去所有的信息都已经被保存到了现在的状态，基于现在就可以预测未来。\n虽然这么说可能有些极端，但是却可以大大简化模型的复杂度，因此马尔可夫链在很多时间序列模型中得到广泛的应用，比如循环神经网络 RNN，隐式马尔可夫模型 HMM 等，当然 MCMC 也需要它。\n随机过程 马尔可夫链是随机过程这门课程中的一部分，先来简单了解一下。\n简单来说，随机过程就是使用统计模型一些事物的过程进行预测和处理，比如股价预测通过今天股票的涨跌，却预测明天后天股票的涨跌；天气预报通过今天是否下雨，预测明天后天是否下雨。这些过程都是可以通过数学公式进行量化计算的。通过下雨、股票涨跌的概率，用公式就可以推导出来 N 天后的状况。\n俄国数学家 Andrey Andreyevich Markov 研究并提出一个用数学方法就能解释自然变化的一般规律模型，被命名为马尔科夫链（Markov Chain）。马尔科夫链为状态空间中经过从一个状态到另一个状态的转换的随机过程，该过程要求具备“无记忆性”，即下一状态的概率分布只能由当前状态决定，在时间序列中它前面的事件均与之无关。这种特定类型的“无记忆性”称作马尔可夫性质。\n数学定义 假设我们的序列状态是 $...X_{t−2}, X_{t−1}, X_t, X_{t+1}...$，那么在 $X_{t+1}$ 时刻的状态的条件概率仅依赖于前一刻的状态 $X_t$，即：\n$$ P(X_{t+1} \\mid \\ldots X_{t−2}, X_{t−1}, X_t) = P(X_{t+1} \\mid X_t) $$既然某一时刻状态转移的概率只依赖于它的前一个状态，那么我们只要能求出系统中任意两个状态之间的转换概率，这个马尔科夫链的模型就定了。\n转移概率矩阵 通过马尔科夫链的模型转换，我们可以将事件的状态转换成概率矩阵（又称状态分布矩阵），如下例：\n上图中有 $A$ 和 $B$ 两个状态，$A$ 到 $A$ 的概率是 $0.3$，$A$ 到 $B$ 的概率是 $0.7$；$B$ 到 $B$ 的概率是 $0.1$，$B$ 到 $A$ 的概率是 $0.9$。\n初始状态在 $A$，如果我们求 2 次运动后状态还在 $A$ 的概率是多少？非常简单：\n$P = A \\rightarrow A \\rightarrow A + A \\rightarrow B \\rightarrow A = 0.3 \\times 0.3 + 0.7 \\times 0.9 = 0.72$ 如果求 2 次运动后的状态概率分别是多少？初始状态和终止状态未知时怎么办呢？这时就要引入转移概率矩阵，可以非常直观的描述所有的概率。 $$\\begin{aligned} P \u0026= \\begin{bmatrix} \u0026 A \u0026 B \\\\ A \u0026 0.3 \u0026 0.7 \\\\ B \u0026 0.9 \u0026 0.1 \\end{bmatrix}, \\\\ \u0026 \\text{两次运动后:} \\\\ P \u0026= \\begin{bmatrix} \u0026 A \u0026 B \\\\ A \u0026 0.3 \u0026 0.7 \\\\ B \u0026 0.9 \u0026 0.1 \\end{bmatrix} \\begin{bmatrix} \u0026 A \u0026 B \\\\ A \u0026 0.3 \u0026 0.7 \\\\ B \u0026 0.9 \u0026 0.1 \\end{bmatrix} \\\\ \u0026= \\begin{bmatrix} \u0026 A \u0026 B \\\\ A \u0026 0.72 \u0026 0.28 \\\\ B \u0026 0.36 \u0026 0.64 \\end{bmatrix} \\end{aligned}$$有了状态矩阵，我们可以轻松得出以下结论：\n初始状态 $A$，$2$ 次运动后状态为 $A$ 的概率是 $0.72$； 初始状态 $A$，$2$ 次运动后状态为 $B$ 的概率是 $0.28$； 初始状态 $B$，$2$ 次运动后状态为 $A$ 的概率是 $0.36$； 初始状态 $B$，$2$ 次运动后状态为 $B$ 的概率是 $0.64$； 状态转移矩阵的稳定性 状态转移矩阵有一个非常重要的特性，经过一定有限次数序列的转换，最终一定可以得到一个稳定的概率分布，且与初始状态概率分布无关。例如：\n假设我们当前股市的概率分布为：$[0.3，0.4, 0.3]$，即 $30\\%$ 概率的牛市，$40\\%$ 概率的熊盘与 $30\\%$ 的横盘。然后这个状态作为序列概率分布的初始状态 $t_0$，将其带入这个状态转移矩阵计算 $t_1, t_2, t_3, \\dots$ 的状态。代码如下：\n1 2 3 4 5 6 7 8 9 10 11 import numpy as np matrix = np.matrix([[0.9, 0.075, 0.025], [0.15, 0.8, 0.05], [0.25, 0.25, 0.5]], dtype=float) vector1 = np.matrix([[0.3, 0.4, 0.3]], dtype=float) for i in range(100): vector1 = vector1 * matrix print(\u0026#39;Current round: {}\u0026#39;.format(i+1)) print(vector1) 部分输出结果如下：\n1 2 3 4 5 6 7 8 Current round: 1 [[ 0.405 0.4175 0.1775]] ... Current round: 60 [[ 0.625 0.3125 0.0625]] ... Current round: 100 [[ 0.625 0.3125 0.0625]] 可以发现，从第 60 轮开始，我们的状态概率分布就不变了，一直保持 $[0.625, 0.3125, 0.0625]$，即 $62.5\\%$ 的牛市，$31.25\\%$ 的熊市与 $6.25\\%$ 的横盘。\n非马尔科夫链过程的例子 只有满足马尔科夫链的特性，才属于马尔科夫链过程。例如对于不放回的袋中取球问题：\n显然当前取球的概率，不仅和最后一次取的球的颜色有关，也和之前每一次取球的颜色有关，所以这个过程不是一个马尔科夫链过程。\n如果是放回的袋中取球问题，这就建立了一个马尔科夫随机过程。\n隐马尔科夫模型 (Hidden Markov Model) 盒子摸球实验 假定我们有三个完全相同的盒子 $box$，每个 $box$ 中装有一些球，分布如下：\n$box_1: 3\\ \\text{black},\\ 8\\ \\text{white}$ $box_2: 6\\ \\text{black},\\ 4\\ \\text{white}$ $box_3: 4\\ \\text{black},\\ 6\\ \\text{white}$ 实验过程如下：\n随机取一个 $box_i$ 从取出的 $box_i$ 中拿出一个球 记录球的颜色并放回（古典概型） 可以看出，每次过程中只有取出的球的颜色是可以观察的，但是取出的 $box$ 的编号是无法被观察的。\n所以整个盒子摸球实验中关系如下：\n这幅图能够很好的说明隐马尔科夫模型当中的两个核心关键词：一个关键词是隐，指的就是状态序列，也就是盒子序列，它是我们无法观测得知的，是隐含的；另一个关键词是马尔科夫，指的是整个隐含状态序列，隐含状态之间的转换是一个马尔科夫过程，隐含状态之间是有特定的转换概率的。\n参考 简述马尔可夫链【通俗易懂】 什么是马尔可夫链？ MCMC(二)马尔科夫链 什么是隐马尔科夫模型 ","permalink":"https://haleuan.github.io/post/markov-chain/","summary":"\u003ch2 id=\"前言\"\u003e前言\u003c/h2\u003e\n\u003cp\u003e马尔可夫链（Markov Chain）可以说是机器学习和人工智能的基石，在强化学习、自然语言处理、金融领域、天气预测、语音识别方面都有着极其广泛的应用\u003c/p\u003e","title":"马尔可夫链与隐马尔可夫模型"},{"content":"Metadata Title: Simple Is Effective: The Roles of Graphs and Large Language Models in Knowledge-Graph-Based Retrieval-Augmented Generation\nVenue: ICLR 2025\nSource Code: Github\nTL;DR Motivation： Retriever效率和准确性之间的平衡。 知识的结构信息没有被充分利用 Sections The SubgraphRAG Framework 图的建立 Given a training set of question-answer pairs D, the subgraph retriever learning problem can be formulated as the following problem:\n$$\\max_\\theta\\mathbb{E}_{(q,\\mathcal{A}_q)\\thicksim\\mathcal{D},\\mathcal{G}_q\\thicksim\\mathbb{Q}_\\theta(\\mathcal{G}_q|q,\\mathcal{G})}\\mathbb{P}(\\mathcal{A}_q\\mid\\mathcal{G}_q,q).$$ 对于 $\\mathbb{E}_{(q,\\mathcal{A}_q)\\thicksim\\mathcal{D},\\mathcal{G}_q\\thicksim\\mathbb{Q}_\\theta(\\mathcal{G}_q|q,\\mathcal{G})}\\mathbb{P}(\\mathcal{A}_q\\mid\\mathcal{G}_q,q)$，其中 $\\mathcal{G}_q\\thicksim\\mathbb{Q}_\\theta(\\mathcal{G}_q|q,\\mathcal{G})$ 表示 $\\mathcal{G}_q$ 从条件分布 $\\mathbb{Q}_\\theta(\\mathcal{G}_q \\mid q, \\mathcal{G})$ 中采样,即模型以参数 $\\theta$ 控制生成过程，输出 $\\mathcal{G}_q$（例如推理路径、中间结构等）。所以就是要控制 $\\theta$，使得该期望最大。\nThis problem is hard to solve due to the complexity of the LLM ($\\mathbb P$).\n这里的意思是说，大模型输出 $\\mathcal{A}_q$ 的概率（即，$\\mathbb{P}(\\mathcal{A}_q\\mid\\mathcal{G}_q,q)$）无法计算，$\\frac{d\\mathbb{P}}{d\\mathcal{G}_q}$ 也就更无法计算了。要使 $\\mathbb{P}(\\mathcal{A}_q\\mid\\mathcal{G}_q,q)$ 最大，可以这样想： 在机器学习中，我们要使损失函数 $L$ 最小，是一个求最值的问题（虽然实际上很多情况下求出来的是一个极值）的过程。同理，使 $\\mathbb P$ 最大，也是一个求最值的问题，当然，$\\mathbb P$ 是需要有上界的，这里的 $\\mathbb P$ 是表示概率，所以必定是有上界的。\nTo solve the problem in Eq. 1, we adopt the following strategy.\n$$\\max_\\theta\\mathbb{E}_{(q,\\mathcal{A}_q)\\sim\\mathcal{D},\\mathcal{G}_q\\sim\\mathbb{Q}_\\theta(\\mathcal{G}_q|q,\\mathcal{G})}\\mathbb{P}(\\mathcal{A}_q\\mid\\mathcal{G}_q,q).$$ 既然我们无法优化LLM的输出，那么我们可以优化检索的信息，也就是找到的信息实体图中的最佳子图。既然不能优化回答器（LLM），那我们就优化它的输入，也就是“给它看的那部分知识图谱”。\nHowever, getting $\\mathcal{G}_q^∗$ for an even known question-answer pair $(q,\\mathcal{A}_q)$ is computationally hard and LLM-dependent.Instead, we use (q, Aq) to construct surrogate subgraph evidence with heuristics $\\tilde{\\mathcal{G}}(q,\\mathcal{A}_q)$ and train the retriever based on MLE: $$\\max_\\theta\\mathbb{E}_{(q,\\mathcal{A}_q)\\thicksim\\mathcal{D}}\\mathbb{Q}_\\theta(\\tilde{\\mathcal{G}}_q\\mid\\mathcal{G},q),\\quad\\mathrm{where~}\\tilde{\\mathcal{G}}_q=\\tilde{\\mathcal{G}}(q,\\mathcal{A}_q).$$ 但是即使有了($q$, $\\mathcal{A}_q$)，怎么得出最佳子图 $\\mathcal{G}_q^*$ 仍然是个问题，并且该问题还依赖于LLM。所以文中采用启发式训练，将 $(q,\\mathcal{A}_q)$ 作为训练资料，利用MLE训练检索器：\n$$\\max_\\theta\\mathbb{E}_{(q,\\mathcal{A}_q)\\thicksim\\mathcal{D}}\\mathbb{Q}_\\theta(\\tilde{\\mathcal{G}}_q\\mid\\mathcal{G},q),\\quad\\mathrm{where~}\\tilde{\\mathcal{G}}_q=\\tilde{\\mathcal{G}}(q,\\mathcal{A}_q).\\quad (1)$$ 理解启发式训练：所谓的启发式，就是预先人为定义一个 $\\tilde{\\mathcal{G}}$（文中举了一个例子，$\\mathcal T_q$ 和 $\\mathcal A_q$ 之间所有最短路组成的子图），这里的 $\\tilde{\\mathcal{G}}$ 充当了一个伪标签的角色。\nTriple Factorization We propose to adopt a retriever that allows a subgraph distribution factorization over triples given some latent variables $z_\\tau=z_\\tau(\\mathcal{G},q)$ (to be elaborated later):\n$$\\mathbb{Q}_\\theta(\\mathcal{G}_q\\mid\\mathcal{G},q)=\\prod_{\\tau\\in\\mathcal{G}_q}p_\\theta(\\tau\\mid z_\\tau,q)\\prod_{\\tau\\in\\mathcal{G}\\setminus\\mathcal{G}_q}\\left(1-p_\\theta\\left(\\tau\\mid z_\\tau,q\\right)\\right).\\quad (2)$$ 这里的 $z_\\tau=z_\\tau(\\mathcal{G},q)$ 是一个latent variable，用于在建立 $\\tilde{\\mathcal{G}}_q$ 时，对应的三元组是否被纳入该子图。 公式解读： $\\mathcal{G} \\backslash \\mathcal{G}_q$ 可以看作是 $\\mathcal{G}_q$ 在 $\\mathcal{G}$ 中的补集（虽然严格来说陪集分解会更复杂一些）。这个公式可以看作是一个判别模型，用于判断群 $\\mathcal{G}$ 中的每个元素 $\\tau$ 是否属于子群 $\\mathcal{G}_q$：\n如果 $\\tau \\in \\mathcal{G}_q$，我们希望 $p_\\theta(\\tau \\mid z_\\tau, q)$ 接近 1。 如果 $\\tau \\notin \\mathcal{G}_q$，我们希望 $p_\\theta(\\tau \\mid z_\\tau, q)$ 接近 0，因此 $1 - p_\\theta(\\tau \\mid z_\\tau, q)$ 接近 1。 所以结合 $Eq.2$ 和 $Eq.3$，训练目的就是使得 $\\mathbb{Q}_\\theta(\\mathcal{G}_q\\mid\\mathcal{G},q)$ 的期望最大。几个变量的关系如下： $$\\mathcal{z}_{(h,r,t)}\\longrightarrow \\mathcal{z}_\\tau\\longrightarrow p_\\theta(\\tau\\mid z_\\tau,q) \\longrightarrow \\mathbb{Q}_\\theta(\\mathcal{G}_q\\mid\\mathcal{G},q)$$ Directional Distance Encoding (DDE) $\\mathcal{z}_\\tau$ 刻画了在给定图 $\\mathcal G$ 和查询 $q$ 的情况下，三元组 $\\tau=(h,r,t)$ 和 $q$ 之间的某种关系。文章认为，在传统的GNN中一个严重的限制是无法计算图实体和查询实体之间的“距离”，这对于多跳任务来讲十分不利。文章将DDE作为 $\\mathcal{z}_\\tau(h,r,t)$。设 $q$ 中的所有实体（因为一个问题中可能有多个实体）为 $\\mathcal{T}_q$，对于任一实体 $e\\in\\mathcal{T}_q$，先使用one-hot编码成 $\\mathbf{s}_e^{(0)}$，再经过多次传播，将所有结果concat起来，得到 $\\mathbf{s}_e=[\\mathbf{s}_e^{(0)}\\|\\mathbf{s}_e^{(1)}\\|\\cdots\\|\\mathbf{s}_e^{(r,1)}\\|\\cdots]$。 也就是对 $z_\\tau(\\mathcal{G},q)=[\\mathbf{s}_h||\\mathbf{s}_t]$ 来说：\n$\\mathbf{s}_h=[\\mathbf{s}_h^{(0)}\\|\\mathbf{s}_h^{(1)}\\|\\cdots\\|\\mathbf{s}_h^{(r,1)}\\|\\cdots]$ $\\mathbf{s}_t=[\\mathbf{s}_t^{(0)}\\|\\mathbf{s}_t^{(1)}\\|\\cdots\\|\\mathbf{s}_h^{(r,1)}\\|\\cdots]$ 现在有了 $\\mathcal{z}_\\tau(h,r,t)$，为了计算 $Eq.2$，还需要进一步计算 $p_\\theta(\\cdot|z_\\tau(\\mathcal{G},q),q)$。\n问题(1)：在 $E.q2$ 中可以看出，需要计算的是 $p_\\theta(\\tau\\mid z_\\tau,q)$，但是文中明确指出： A Lightweight Implementation For $p_\\theta(\\cdot|z_\\tau(\\mathcal{G},q),q)$ 所以，为什么两者形式不一样？它们是同一个东西吗？\n可以这样理解（为了方便阐述，设$A:p_\\theta(\\tau\\mid z_\\tau,q),\\,B:p_\\theta(\\cdot|z_\\tau(\\mathcal{G},q),q)$ ，两个表达式都是在说，对于给定的图结构 $\\mathcal G$ 和查询 $q$，该三元组 $\\tau$ 出现在该图中的概率。其实，$B$ 是对 $A$ 的实现版本，重点是在表达实现训练的时候，参数是如何使用的，$A$ 只是说明了哪些参数会影响到该概率。所以 $B$ 也甚至可以是$p_\\theta(\\cdot|z_\\tau(\\mathcal{G},q),q,q,\\ldots)$\n所以文中提到用于训练MLP的数据为：$[z_q\\|z_h\\|z_r\\|z_t\\|z_\\tau]$，中查询不仅进行了单独编码（这其实是工程中的 trick —— 冗余地输入 $q$，是为了让模型更好地学习 query 与 triple 的关系），各编码的来源汇总如下：\n$z_q,z_h,z_t,$：嵌入模型gte-large-en-v1.5编码得到 $z_r$：一个查固定表得到的编码。 $z_\\tau$：计算DDE得到 ","permalink":"https://haleuan.github.io/post/paperdive-simpleiseffective/","summary":"\u003ch2 id=\"metadata\"\u003eMetadata\u003c/h2\u003e\n\u003cp\u003e\u003cstrong\u003eTitle\u003c/strong\u003e: \u003ca href=\"https://arxiv.org/abs/2410.20724\"\u003eSimple Is Effective: The Roles of Graphs and Large Language Models in Knowledge-Graph-Based Retrieval-Augmented Generation\u003c/a\u003e\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eVenue\u003c/strong\u003e: ICLR 2025\u003c/p\u003e\n\u003cp\u003e\u003cstrong\u003eSource Code\u003c/strong\u003e: \u003ca href=\"https://github.com/Graph-COM/SubgraphRAG\"\u003eGithub\u003c/a\u003e\u003c/p\u003e\n\u003ch2 id=\"tldr\"\u003eTL;DR\u003c/h2\u003e\n\u003ch3 id=\"motivation\"\u003eMotivation：\u003c/h3\u003e\n\u003col\u003e\n\u003cli\u003eRetriever效率和准确性之间的平衡。\u003c/li\u003e\n\u003cli\u003e知识的结构信息没有被充分利用\u003c/li\u003e\n\u003c/ol\u003e\n\u003chr\u003e\n\u003ch2 id=\"sections\"\u003eSections\u003c/h2\u003e\n\u003ch3 id=\"the-subgraphrag-framework\"\u003eThe SubgraphRAG Framework\u003c/h3\u003e\n\u003ch4 id=\"图的建立\"\u003e图的建立\u003c/h4\u003e\n\u003cul\u003e\n\u003cli\u003e\u003c/li\u003e\n\u003c/ul\u003e\n\u003cblockquote\u003e\n\u003cp\u003eGiven a training set of question-answer pairs D, the subgraph retriever learning problem can be formulated as the following problem:\u003c/p\u003e","title":"论文精读 | Simple Is Effective"},{"content":"AutoGen的安装 基于conda\n1 2 3 4 5 conda create -n pyautogen python=3.10 conda activate pyautogen # (optional, run on jupyter) conda install ipykernel -y # (optional, formatter plugin) conda install black -y pip install pyautogen 常见概念 OAI_CONFIG_LIST 调用OpenAI的API常用配置文件，用于指定如何连接到OpenAI的服务，比如API密钥、端点URL等信息。\ntop_p和temperature参数 top_p有时也称nucleus sampling，这两个参数都是用来控制生成文本的随机性和多样性，但它们的作用方式有所不同。\ntemperature temperature 参数决定了模型从候选词汇中选择下一个词的方式。它调整了概率分布的形状，从而影响模型的选择倾向。通常设定范围为 $[0,1]$ 当 temperature 接近0时，模型倾向于选择最有可能的词，这导致生成的文本更可预测但可能较乏味。 当 temperature 较高时，模型更倾向于随机选择，这样增加了生成文本的多样性和新颖性，但可能导致生成的句子不够连贯或者含有更多错误。 top_p (Nucleus Sampling 核心采样) top_p 是一种采样策略，它并不改变概率分布本身，而是首先从概率分布中选取累积概率总和达到 p 值的最高概率词汇子集。 比如说，如果 top_p 设置为0.9，那么模型将会只考虑那些累积概率之和达到或超过90%的词汇。 在这些词汇中，再根据它们相对的概率分布进行采样。这种方式既保持了一定的多样性，又避免了选择那些极不可能出现的词汇，有助于提升生成文本的质量。 联系 它们都是为了调整生成文本的随机性与创造性之间的平衡。 使用者可以通过调整这两个参数来优化生成的文本质量，使其更符合特定的应用场景。 区别 temperature 是通过改变概率分布来影响生成结果，而 top_p 则是在不改变分布的情况下，仅选择部分高概率词汇进行采样。 temperature 更多地控制了输出的探索性（exploration），即在概率分布中的冒险程度；而 top_p 则更多地控制了输出的稳健性（exploitation），即在已知的高概率选项中进行选择。 不建议两者同时修改 LLM配置 Official Documentation 建议采用OAI_CONFIG_LIST方式，方便多个API和模型的切换\n1 2 3 4 5 6 7 8 9 config_list = autogen.config_list_from_json( env_or_file=\u0026#34;OAI_CONFIG_LIST\u0026#34;, filter_dict={\u0026#34;model\u0026#34;:[\u0026#34;gpt-4\u0026#34;]} # 选择模型 ) llm_config = { \u0026#34;config_list\u0026#34;: config_list \u0026#34;timeout\u0026#34;:120 } 在配置时，最好加入\u0026quot;timeout\u0026quot;字段，由于网络连接的缘故，如果不设定此字段，容易出现connection error问题 如果api调用过慢，建议使用gpt-3.5系列模型以到达低时延的目的。(但是3.5版本比较笨，只能用于学习测试，其他目的慎用)，即使是gpt-4o-mini也会出现知识幻觉，这一系列模型感觉都是更加注重增加可传入token的数量，而在智能程度上还不够 终止Agents之间对话的方式 Documentation\n在initiate_chat函数中设定参数 e.g.\n1 result = joe.initiate_chat(cathy, message=\u0026#34;Cathy, tell me a joke.\u0026#34;, max_turns=2) 代码中的max_turns即表示代理进行多少轮(例如max_turns=2代表每个agent说出两句话)对话后停止对话。\n在创建agent时设定参数 有以下两个参数可以进行设定：\nmax_consecutive_auto_reply：在ConversableAgent类中可以设定此参数，当此代理对同一个发信代理的响应次数(注意不是接受消息次数)到达一定数量时，就终止对话。(疑问：终止的方式是什么？是直接不说话，还是说出结束语？) is_termination_msg：检查收到的回复中是否存在结束语，e.g. 1 2 3 4 5 6 7 8 9 joe = ConversableAgent( \u0026#34;joe\u0026#34;, system_message=\u0026#34;Your name is Joe and you are a part of a duo of comedians.\u0026#34;, llm_config={\u0026#34;config_list\u0026#34;: [{\u0026#34;model\u0026#34;: \u0026#34;gpt-4\u0026#34;, \u0026#34;temperature\u0026#34;: 0.7, \u0026#34;api_key\u0026#34;: os.environ.get(\u0026#34;OPENAI_API_KEY\u0026#34;)}]}, human_input_mode=\u0026#34;NEVER\u0026#34;, # Never ask for human input. is_termination_msg=lambda msg: \u0026#34;good bye\u0026#34; in msg[\u0026#34;content\u0026#34;].lower(), ) result = joe.initiate_chat(cathy, message=\u0026#34;Cathy, tell me a joke and then say the words GOOD BYE.\u0026#34;) 人工参与输入 人工输入的逻辑 人工输入处于agent_auto_reply的逻辑流程前面，也就是人工输入可以对传入消息进行拦截。 (个人理解)：例如，Agent A给Agent B发送一条消息msg_1，但是可以通过人工输入将msg_1替换成msg_2，并将msg_2传给Agent B。\n输入模式 通过Conversable构造函数中的human_input_mode来设定\nhuman_input_mode=NEVER TERMINATE：当终止条件满足时(例如：max_consecutive_auto_reply或者is_termination_msg满足时)，便请求人工输入。并且在max_consecutive_auto_reply情况下，人工输入后计数器会被重置。反之，如果人工选择跳过人工输入，那么agent将会继续auto_reply机制。另外，人工也可以选择终止对话 ALWAYS：可以选择跳过人工输入并依然使用auto_reply，或者拦截auto_reply并提供反馈(包括终止对话)。在此模式下，max_consecutive_auto_reply将失效 示例 以下例子充分证明了，gpt-3.5、gpt-4o-mini的推理能力较差，只有gpt-4能够正确地完成猜数游戏。\nNEVER 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 agent_with_number = ConversableAgent( \u0026#34;agent_with_number\u0026#34;, system_message=\u0026#34;You are playing a game of guess-my-number. You have the \u0026#34; \u0026#34;number 53 in your mind, and I will try to guess it. \u0026#34; \u0026#34;If I guess too high, say \u0026#39;too high\u0026#39;, if I guess too low, say \u0026#39;too low\u0026#39;. \u0026#34;, llm_config=llm_config, is_termination_msg=lambda msg: \u0026#34;53\u0026#34; in msg[\u0026#34;content\u0026#34;], # terminate if the number is guessed by the other agent human_input_mode=\u0026#34;NEVER\u0026#34;, ) agent_guess_number = ConversableAgent( \u0026#34;agent_guess_number\u0026#34;, system_message=\u0026#34;I have a number in my mind, and you will try to guess it. \u0026#34; \u0026#34;If I say \u0026#39;too high\u0026#39;, you should guess a lower number. If I say \u0026#39;too low\u0026#39;, \u0026#34; \u0026#34;you should guess a higher number. \u0026#34;, llm_config=llm_config, human_input_mode=\u0026#34;NEVER\u0026#34;, ) result = agent_with_number.initiate_chat( agent_guess_number, message=\u0026#34;I have a number between 1 and 100. Guess it!\u0026#34;, ) 在agent_guess_number的system_message中，限定让其使用二分法(binary search)，可使其更快更准确得出答案，这就是RAG的一个例子\nTERMINATE 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 agent_with_number = ConversableAgent( \u0026#34;agent_with_number\u0026#34;, system_message=\u0026#34;You are playing a game of guess-my-number. \u0026#34; \u0026#34;In the first game, you have the \u0026#34; \u0026#34;number 53 in your mind, and I will try to guess it. \u0026#34; \u0026#34;If I guess too high, say \u0026#39;too high\u0026#39;, if I guess too low, say \u0026#39;too low\u0026#39;. \u0026#34;, llm_config=llm_config, max_consecutive_auto_reply=1, # maximum number of consecutive auto-replies before asking for human input is_termination_msg=lambda msg: \u0026#34;53\u0026#34; in msg[\u0026#34;content\u0026#34;], # terminate if the number is guessed by the other agent human_input_mode=\u0026#34;TERMINATE\u0026#34;, # ask for human input until the game is terminated ) agent_guess_number = ConversableAgent( \u0026#34;agent_guess_number\u0026#34;, system_message=\u0026#34;I have a number in my mind, and you will try to guess it. \u0026#34; \u0026#34;If I say \u0026#39;too high\u0026#39;, you should guess a lower number. If I say \u0026#39;too low\u0026#39;, \u0026#34; \u0026#34;you should guess a higher number. \u0026#34;, llm_config=llm_config, human_input_mode=\u0026#34;NEVER\u0026#34;, ) result = agent_with_number.initiate_chat( agent_guess_number, message=\u0026#34;I have a number between 1 and 100. Guess it!\u0026#34;, ) 此例子的逻辑是，利用人工输入后会重置max_consecutive_auto_reply这一特性，agent_with_number只能回复一次too high或者too low，如果猜错则由人工接管回答too high或者too low。\nALWAYS 人工扮演猜数者\n1 2 3 4 5 6 7 8 9 10 11 human_proxy = ConversableAgent( \u0026#34;human_proxy\u0026#34;, llm_config=False, # no LLM used for human proxy human_input_mode=\u0026#34;ALWAYS\u0026#34;, # always ask for human input ) # Start a chat with the agent with number with an initial guess. result = human_proxy.initiate_chat( agent_with_number, # this is the same agent with the number as before message=\u0026#34;10\u0026#34;, ) Code Executor Code Executor组件接收输入消息(例如，包含代码块的消息)，执行并输出带有结果的消息。AutoGen 提供了两种类型的内置代码执行程序\n命令行代码执行程序，在 UNIX shell 等命令行环境中运行代码 在交互式 Jupyter 内核中运行代码。 对于每种类型的Code Executor，都有两种执行方式：本地执行和在Docker中执行。 CLI执行和Jupyter执行的选择 命令行和 Jupyter 代码执行程序之间的选择取决于代理对话中代码块的性质。如果每个代码块都是一个“脚本”，它不使用以前代码块中的变量，那么命令行代码执行程序是一个不错的选择。如果某些代码块包含昂贵的计算（例如，训练机器学习模型和加载大量数据），并且希望将状态保留在内存中以避免重复计算，则 Jupyter 代码执行程序是更好的选择。\n本地执行逻辑以及VSCode中Jupyter执行 Jupyter in VSCode执行 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 from autogen.coding import CodeBlock from autogen.coding.jupyter import JupyterCodeExecutor, LocalJupyterServer from autogen import ConversableAgent with LocalJupyterServer() as server: executor = JupyterCodeExecutor(server) code_executor_agent = ConversableAgent( \u0026#34;code_executor_agent\u0026#34;, llm_config=False, # Turn off LLM for this agent. code_execution_config={ \u0026#34;executor\u0026#34;: executor }, # Use the local command line code executor. human_input_mode=\u0026#34;NEVER\u0026#34;, # Always take human input for this agent for safety. ) message_with_code_block = \u0026#34;\u0026#34;\u0026#34;This is a message with code block. The code block is below: ```python print(\u0026#34;Jupyter Code Executor test\u0026#34;) ``` This is the end of the message. \u0026#34;\u0026#34;\u0026#34; # Generate a reply for the given code. reply = code_executor_agent.generate_reply( messages=[{\u0026#34;role\u0026#34;: \u0026#34;user\u0026#34;, \u0026#34;content\u0026#34;: message_with_code_block}] ) print(reply) \u0026#39;\u0026#39;\u0026#39; output: \u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt;\u0026gt; EXECUTING CODE BLOCK (inferred language is python)... exitcode: 0 (execution succeeded) Code output: Jupyter Code Executor test \u0026#39;\u0026#39;\u0026#39; Note:\n在jupyter中使用Code Executor相较于CLI执行.py文件，要多一步连接Jupyter内核，也就是with LocalJupyterServer() as server这一步骤。连接内核后，再用此连接创建JupyterCodeExecutor。 在使用Jupyter执行时要先安装依赖 pip install 'pyautogen[jupyter-executor]' Azure OpenAI API的Azure for Students无法执行Use Code Executor in Coversation的代码，原因是token超出限制(1k tokens per minutes)。虽然可以修改code_writer_system_message，尽量精简化其内容使得token数量少一点可以成功运行示例代码，但是prompt不够会导致结果不够准确。 Tool Use Agent写出的代码可靠性并不高，所以需要借助一些工具。AutoGen中tools是一些agent可以使用的已经预定义好了的函数。Agent能借助工具完成诸如搜索网页、计算、读取文件或者调用远程API的功能。\n创建工具 Tools可以像创建普通Python函数一样进行创建：\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 from typing import Annotated, Literal Operator = Literal[\u0026#34;+\u0026#34;, \u0026#34;-\u0026#34;, \u0026#34;*\u0026#34;, \u0026#34;/\u0026#34;] def calculator(a: int, b: int, operator: Annotated[Operator, \u0026#34;operator\u0026#34;]) -\u0026gt; int: if operator == \u0026#34;+\u0026#34;: return a + b elif operator == \u0026#34;-\u0026#34;: return a - b elif operator == \u0026#34;*\u0026#34;: return a * b elif operator == \u0026#34;/\u0026#34;: return int(a / b) else: raise ValueError(\u0026#34;Invalid operator\u0026#34;) Tips: 最好多使用类型提示来定义函数的参数和返回值，这也是对agent提供的一种prompt\n注册工具 创建好某个tool后，再向agent注册该tool就可以在对话中调用此tool\n1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 import os from autogen import ConversableAgent # Let\u0026#39;s first define the assistant agent that suggests tool calls. assistant = ConversableAgent( name=\u0026#34;Assistant\u0026#34;, system_message=\u0026#34;You are a helpful AI assistant. \u0026#34; \u0026#34;You can help with simple calculations. \u0026#34; \u0026#34;Return \u0026#39;TERMINATE\u0026#39; when the task is done.\u0026#34;, llm_config={\u0026#34;config_list\u0026#34;: [{\u0026#34;model\u0026#34;: \u0026#34;gpt-4\u0026#34;, \u0026#34;api_key\u0026#34;: os.environ[\u0026#34;OPENAI_API_KEY\u0026#34;]}]}, ) # The user proxy agent is used for interacting with the assistant agent # and executes tool calls. user_proxy = ConversableAgent( name=\u0026#34;User\u0026#34;, llm_config=False, is_termination_msg=lambda msg: msg.get(\u0026#34;content\u0026#34;) is not None and \u0026#34;TERMINATE\u0026#34; in msg[\u0026#34;content\u0026#34;], human_input_mode=\u0026#34;NEVER\u0026#34;, ) # Register the tool signature with the assistant agent. # (calculator)是装饰器的用法 assistant.register_for_llm(name=\u0026#34;calculator\u0026#34;, description=\u0026#34;A simple calculator\u0026#34;)(calculator) # Register the tool function with the user proxy agent. user_proxy.register_for_execution(name=\u0026#34;calculator\u0026#34;)(calculator) Tips: 合理添加注释，帮助agent的LLM理解tool的作用\n对话模式 AutoGen提供多个agent对话的组件。\n概览 two-agent chat sequential chat: 两个agent之间一系列的对话，对话可以将上一个对话的摘要带入下一个对话的上下文 Group Chat: 一个包含多个agent的对话，在这种情况下有一个很重要的问题：下一个说话的应该是哪一个agent？为了应对多种场景，autogen有多种方式来组织agent进行对话： 多种选择下一个agent：round_robin, random, manual(人工选择)，以及自动选择(默认方式，使用一个LLM来决定) 提供约束条件来限制下一个发言agent 传入一个函数来自定义下一个agent。利用此特性，我们可以建立一个 StateFlow 来制定一个工作流。 Nested Chat: 将一个工作流打包进一个agent，并在其他工作流中重复使用该agent ","permalink":"https://haleuan.github.io/post/note-of-autogen/","summary":"\u003ch2 id=\"autogen的安装\"\u003eAutoGen的安装\u003c/h2\u003e\n\u003cp\u003e\u003cem\u003e基于\u003ccode\u003econda\u003c/code\u003e\u003c/em\u003e\u003c/p\u003e\n\u003cdiv class=\"highlight\"\u003e\u003cdiv class=\"chroma\"\u003e\n\u003ctable class=\"lntable\"\u003e\u003ctr\u003e\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode\u003e\u003cspan class=\"lnt\"\u003e1\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e2\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e3\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e4\n\u003c/span\u003e\u003cspan class=\"lnt\"\u003e5\n\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\n\u003ctd class=\"lntd\"\u003e\n\u003cpre tabindex=\"0\" class=\"chroma\"\u003e\u003ccode class=\"language-bash\" data-lang=\"bash\"\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003econda create -n pyautogen \u003cspan class=\"nv\"\u003epython\u003c/span\u003e\u003cspan class=\"o\"\u003e=\u003c/span\u003e3.10\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003econda activate pyautogen\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (optional, run on jupyter) conda install ipykernel -y\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003e\u003cspan class=\"c1\"\u003e# (optional, formatter plugin) conda install black -y\u003c/span\u003e\n\u003c/span\u003e\u003c/span\u003e\u003cspan class=\"line\"\u003e\u003cspan class=\"cl\"\u003epip install pyautogen\n\u003c/span\u003e\u003c/span\u003e\u003c/code\u003e\u003c/pre\u003e\u003c/td\u003e\u003c/tr\u003e\u003c/table\u003e\n\u003c/div\u003e\n\u003c/div\u003e\u003ch2 id=\"常见概念\"\u003e常见概念\u003c/h2\u003e\n\u003ch3 id=\"oai_config_list\"\u003e\u003ccode\u003eOAI_CONFIG_LIST\u003c/code\u003e\u003c/h3\u003e\n\u003cp\u003e调用OpenAI的API常用配置文件，用于指定如何连接到OpenAI的服务，比如API密钥、端点URL等信息。\u003c/p\u003e","title":"AutoGen学习笔记"}]