抛砖引玉: 介绍命令行AI编程工具aider以及emacs集成aider.el

是不是可以借鉴一下 embark 的思路,它会识别当前指针下面是什么,执行对应的操作。

获许可以在 aider-minor-mode 下稍微调整一下 C-c C-c 的逻辑,基于 pointer 的位置:

  1. 如果是单行或者段落,执行 block 的逻辑,发送一个 block 过去,只有一行的 block 就和 line by line 没区别。

  2. 如果是在 heading 上,则可以发送 heading 里的内容,整体发送过去

  3. 如果是 region,则发送 region

他们都可以用 {aider} 包裹,反正包了也不影响。

这样一个按键就能实现很多事情了,容易操作,也容易记忆。

确实有这样的场景需求。

考虑光标下是不是个aider prompt block再决定是否call 这个函数 还是org mode 已经有的功能 可能可行 这样就还能继续用C-c C-c了 我可以去看看怎么弄

多行prompt 包在aider tag里好像不按多个prompt处理 回头我可以再核实一下

现在单行的用第二个函数 和第一个 效果应该一样的 就是多了个tag block

我没明白你说的heading是什么意思 是org的标题么? 能麻烦举个例子吗

对,就是 heading 就是说的标题。

例如你的多行场景,可以放在 heading 下,直接执行 heading 完成,类似 line-by-line,省去了选择 region 的动作。

我觉得也不用强求都在一个命令里,对于 line by line 多行这种场景,可以保留一个快捷键去完成?

然后剩下的大部分都能用 C-c C-c 完成,这样就挺方便的。

确实不是两个都经常用 不过 我自己用的感觉是 大部分情况下单行prompt 够用了 很少需要用到复杂的多行prompt

不过有时候copy paste例子什么的进prompt 作为context帮助大模型理解 需要multiline prompt

Aider prompt org file现在仅仅是加了上面两个eval函数 具体怎么用好 可能还得再想想 虽然是prompt文件 但是里面也可能有prompt以外的内容 比如说描述如何解决某个task或者子task的思考 或者描述prompt做得怎么样 下一步怎么改进 这样回头看的时候 有具体的文字描述 这些内容不属于prompt 也不需要发给aider

另外单独给add文件开个标题 觉得标题有点太细了 我现在还是更习惯用标题描述具体任务

最近aider.el的一些改动

aider.el的基本假设之一就是aider的力量来自于用什么prompt. 而prompt是有可重复性的. 可以应用相同或者稍作修改的prompt来应用到不同的项目,类似的任务. 好的prompt可以反复使用,不需要每次手工输入,CLI在这点上做得不够.这里有优化的空间.

优化的方式基于

  1. 基于helm输入prompt (建议使用aider-helm.el) 这在诸多code change / discussion菜单项当中使用. helm的输入支持模糊匹配,所以可以比较容易的从以前自己输入的prompt里找到可以重复使用或者稍微改改就能用的prompt

    最近的修改提高了prompt可重用性: 仅仅保存修改指令进history, 不保存context部分比如改哪个函数.

  2. aider prompt file ~C-c a p~. 这个方式把prompt输入从aider session挪到的单独的aider prompt (org) file. 通过类似发送代码块的方式来和aider session交互. 这样容易管理项目, 以前同一repo的prompt也在同一文件里, 容易重用和修改. 另外发现copilot.el在aider prompt file里也很有帮助,因为copilot.el不但补全代码很好,补全prompt英文也很有用.

    最近的修改引入了yasnippets 支持 (感谢之前一位网友提到yasnippets,才想到可以这么用), 这样用户可以容易重用网络上认为比较好的coding prompt库,来应用到自己的项目上. 目前加了reddit上两个比较高分的prompt list. 欢迎大家根据自己的经验加其他的,或者改进现有的prompt snippet.

其他的用户端改动:

- 菜单: 简化菜单以适应屏幕并减少记忆: 感谢 Spike-Leung的建议和review

  • 将操作分组到同一菜单项中。不常用的操作绑定到 C-u

开发者端改动:

  • 弃用 aider-minor-mode,改用 aider-prompt-mode (major-mode)
    • aider-prompt-mode 继承自 org-mode. 这样定义键位比minor-mode更容易控制
  • 对 aider.el 进行重构,原先有接近1000行. 将其分解为几个小文件,以帮助未来的开发和维护
    • aider-core.el: 核心 comint aider 会话交互功能
      • 还有自定义变量
    • aider-file.el: 文件操作相关的功能
      • 依赖于 aider-core.el
    • aider-code-change.el: 代码更改相关的功能
      • 依赖于 aider-core.el 和 aider-file.el
    • aider-discussion.el: 讨论相关的功能
      • 依赖于 aider-core.el 和 aider-file.el
    • aider-prompt-mode.el: aider 提示文件的主模式
      • 依赖于 aider-core
    • aider.el: aider 会话管理和菜单
      • 依赖于以上所有文件

有用户提出希望有类似cursor的code review / accept功能, 回复了aider中对应怎么review / accept code change, 也许这里也有网友会有相同的问题. 详细回答在Feature Request or Doubt: accept code changes · Issue #98 · tninja/aider.el · GitHub

PS: 做这个插件的过程中,体会到虽然AI编码很快,但是想法和思路是根据使用经验和大家的反馈慢慢发展的. 这些都需要程序员的参与. 所以对于网络上或者某些名人宣称AI将来取代程序员, 我不信.

5 个赞
  • aider.el V0.5.0 的变化

  • 基于markdown-mode.el的aider comint session语法高亮显示. 这种方法不需要任何配置, 不但使得代码块高亮,而且使得文字输出也能得到格式化. 因为aider的输出默认就是markdown模式的. 我测试了sonnet, o3-mini, deepseek, 语法高亮都可以. 缺点是要稍微等一下, 也就1-2s

  • 补全: aider prompt file和aider comint input都增加了命令和文件名补全. aider prompt file增加了语法高亮. 文件名手工输入是挺麻烦的. 有补全会舒服一些. 它会在相关命令比如/add后自动触发. 另外aider prompt file和aider comint input支持使用helm补全prompt, 所以历史prompt就共享了, 说不定可以少敲一些字.

  • Diff review: 增加功能aider-pull-or-review-diff-file (C-c a v). 第一次按的时候, 请输入base branch比如main, 它会pull base…HEAD的diff, 也就是base到feature branch的diff (你也可以输入branch1…branch2来得到任意两个branch间的diff), 它会打开这个diff的buffer. 第二次按的时候, 它就会让aider来review这个diff. 这个功能是模仿cursor的, 用处是在发PR前检查自己的change. 当然它也可以用来review别人的diff

  • 增加了中文版的README

  • 加入melpa (第一次做, 谢谢网友的帮助) 以后可以用package-install安装. 软件包名字就叫做aider. 安装说明在README.org里. 安装后,如果需要helm input的话,请(require 'aider-helm)

  • 其他

    • 注意:Aider v0.77.0 会自动接受 /architect 命令的更改。如果您想按照以前的习惯在 aider.el 中的许多命令一样在接受更改之前审查代码更改,您可以在 aider-args 或 .aider.conf.yml 中使用 “–no-auto-accept-architect” 禁用。
    • aider.el 旨在成为操作简单的, 我们可以日常使用的, 稳定的生产力工具。我会在代码合并前仔细测试此库中的功能, 对于引入新的功能和依赖我也会比较小心。很多地方为了系统稳定性原因, linux版本可能相对不那么新, 因此不一定能安装最新版本的emacs, 考虑到这点, aider.el只需要emacs >= 26.1.
3 个赞

通过 melpa 安装 aider 会自动安装 helm 依赖。(因为 aider.el 里的 package-requires 注释声明了需要 helm,所以 helm 会被自动安装)。

这个是否不太合理呢?大部分的用户应该使用的是 ivy+counsel 或者 vertico+consult,仅仅是为了使用 aider 就要安装 helm 似乎不太合理。

两个思路:

  1. aider-helm 作为一个 单独的 package 发布,不再随 aider.el 一起发布
  2. aider.el 里不再将 helm 作为 package-require。当然因为 helm 不再是 dependency,因此 byte-compile 可能会出现问题,因此需要把所有调用的 helm 变量和函数都用 defvar 和 decalre-function 声明。
1 个赞

非常感谢反馈和建议 应该不需要安装helm比较合理 第二条建议很好用 已经改了 谢谢

2 个赞

试用了一下,不错的改进~

1 个赞

关于 drop file 和设置 readonly,我之前有个想法:

做成类似 magit rebase 的形式,每一行对应的就是当前的文件,然后可以标记是 readonly 还是 drop 还是别的状态。

不过可能有点小题大做啦。

感谢使用以及建议! 我试图理解一下这个需求, 这个工作涉及到很多文件是吗? 而且工作到工作切换希望drop一些, 增加一些? 确实有很多文件的话, 一个个增加去除是挺麻烦的

不知道我对use case的理解对不对, 如果不符合, 还请告诉我use case是什么样的, 如果是这样的use case,

我的理解是,如果一个更改涉及到蛮多文件,那还是有一定复杂度的. 最早我加了用dired来批量处理. 但是我也不能确定一次修改就弄对. Aider session一旦关了就丢了. 对于有复杂度的事情, 我会希望把它做的有可重复性 (reproducibility), 这样的话, 即使没有完成,回头也会很容易复盘 改进

aider prompt file (C-c a p) 可以管理一定的复杂度, 也可以做有重复性的事情, 比如, 开一个headline like this:

  • Task1 ## 下面的操作没有magit的rebase好看, 但也能凑合用, 而且有记录, 也随时可以重复执行, 选中多行文件操作后C-c C-n

/add file1

/read-only file2

/read-only file3

/architect prompt1

/architect prompt2

  • Task 2 ## 或许, Task到Task之间保持独立. 对我这样记忆力一般的人而言节省心智负担 不过每个人选择不一样

/drop file1 (or /reset)

/add file2

/read-only file3

不知道您看这样是否可以? 现在aider prompt file里有一些补全, 操作起来要省心一些了

我也确实是有点偷懒不是很想写很多代码. 希望能用较少的代码 以及借用已经有的well tested功能, 来服务较多的use case.

有一阵子我用的时候会频繁调整文件,我希望通过 /drop/readonly 一定程度上去控制这次 session 的上下文,减少 token 使用(sonnet 还是有点贵)(更彻底的办法可能是直接 reset 重来)

目前 /drop 有文件补全,比以前方便一些啦。

但文件补全是从 git 根目录开始的,我还需要敲对应的文件名。

当目录层次比较多的时候,就增加了需要记忆目录的心智负担,虽然看着 /ls 的文件路径慢慢敲也行,但还不够方便。

我之前的另一个办法和你说的差不多 (流程 A):

  1. 我通过 /ls 列出所有文件,复制到 prompt file
  2. 通过 string-rectangle 统一在前面添加 /drop ,个别文件我可能再改成 /readonly
  3. 修改之后执行 C-c C-n,让它们逐行执行

但光看我描述就会觉得上面的过程有点繁琐。

/drop 的肯定是 /ls 中包含的文件,或许部分文件还会想设置为 /readonly

所以我就在想,一个更方便的方法是:

  1. 先列出当前所有的文件(通过 /ls 返回所有当前的文件)
  2. 然后逐一标记 (类似 magit rebase 的方式,直接在行上输入对应的快捷键)
  3. 最后执行标记动作。

这样可以减少我上面流程 A 中,第 1,2 步要做的事情,更容易操作一些。

操作流和 magit rebase 一样的话,我想用过 magit rebase 的人只要用一次就会知道怎么用,不需要什么额外的引导。

我经常用 magit rebase,我觉得它的流程很好。

我选择从哪里开始 rebase,我选择的就是我想改的,它都给我列出来,然后选择每个 commit 对应需要的处理,最后执行 C-c C-c 确定,或者 C-c C-k 放弃。

不过或许是有点小题大做 :wink:

1 个赞

你的这个做法学习了,确实可以记录实现步骤,然后去调整 prompt,重新对 LLM 进行引导,有记录总归是好的。

1 个赞

aider add和drop文件确实是个比较添麻烦的事情, 我也看到其他人对比其他AI编码工具,抱怨过这点 你说的深层次目录里文件的增加删除 确实是麻烦 非常同意 再想想怎么简化这些事情 如果能智能一点就好了

对了, 如果在prompt里直接输入文件名, 比如xx文件里的yy class, 执行prompt的时候aider会询问是否要加入的

如果是已经加入的文件, 特别是在aider prompt file里之前的task有的, 应该比较容易copy paste

我看看怎么把你的流程结合到aider prompt file里面, 比如, magit rebase的时候可以一键切换pick / stash, etc, addressing:

所以我就在想,一个更方便的方法是:

先列出当前所有的文件(通过 /ls 返回所有当前的文件)
然后逐一标记 (类似 magit rebase 的方式,直接在行上输入对应的快捷键)
最后执行标记动作。

如果你觉得增加删除文件有些复杂度(complexity),可以了解一下/save/load

你建议的方法, 实现在这个PR里了: Feat: Add magit rebase style file operation in aider-prompt-mode, and send block line by line command by tninja · Pull Request #109 · tninja/aider.el · GitHub, 直接在aider prompt file里用C-c C-y cycle through /add, /read-only, /drop. 这些文件命令之间要求连续没有空行. 确认之后C-c C-b发送, 比如

file0 ## C-c C-y will insert /add ahead

/add file1 ## C-c C-y will change to /read-only

/read-only file2 ## C-c C-y will change to /drop

/drop file3 ## C-c C-y will go back to /add

也可以/ls粘过来 然后用C-c C-y来改

我个人的经验是task和task之间可能share很多相同的文件. 所以第一次在aider prompt file里做task后, 之后就可以copy paste.

同时也回复MatthewZMD的建议, load命令是可以批处理多个命令,不过我不太会想把code change之类的命令放在里面, 结果就是最后add文件和code change分在了不同的地方. 我自己还是更喜欢把add文件和code change命令放在aider prompt file里以task为单位的org headline里 方便记忆和复盘. aider prompt file里面可以放很多task, 这样copy paste起来也省事.

能节省 /ls 的步骤就更好了,懒人连执行 /ls 再复制都会觉得有点麻烦= =


另外提一个与此无关的建议:

文字中,英文和中文最好留一些空格,这样更好阅读。

以及可以避免太长的段落,也是提高可读性的一个技巧。

参考:

1 个赞

Helm不作为依赖,这个提醒没必要

谢谢 今晚回去我检查一下