🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度
Aider 是终端里的配对编程工具:把文件加进上下文,它在原地改,diff 摆在面前,落不落地由你按一次回车决定。这篇只做一件事——拿一个会报类型错的 Python 小仓库,用 TaoToken 的统一 Base URL 把 Aider 接上,让它把 mypy 报出来的错误改干净,顺便把这次会话的 Token 增量记成表。
流程拆开是四步:准备仓库并跑出 mypy 基线、把 Aider 的 OpenAI 兼容入口指向 https://taotoken.net/api、用 mypy 报告驱动它改代码、复核 diff 并核算 Token。所谓十分钟,指的是命令敲完到 mypy 归零这一段,不含你翻文档的时间。仓库是现造的,不是哪个明星项目,但错误类型都取自常见场景:缺类型标注、泛型参数漏写、Optional 没做收窄、int 与 float 混用。这类问题在开了 strict 的仓库里最密集,也最适合交给能直接落盘的 Aider。本文出现的所有 Token 数字都是本地一次运行的结果,不含任何公榜分数,也不构成性能承诺。
1. 一个 5 个 mypy 报错的小仓库
1.1 目录结构与依赖
仓库叫 py-types-demo,一共 6 个 Python 文件,故意做得小,是为了让 Aider 的上下文不被无关代码稀释。你完全可以换成自己手头那个跑 mypy --strict 一片红的项目,后面的命令只改路径。
py-types-demo/
├── mypy.ini
├── pyproject.toml
├── src/
│ ├── models.py
│ ├── orders/
│ │ ├── __init__.py
│ │ └── settle.py
│ └── report.py
└── tests/
└── test_settle.py
models.py 定义两个 dataclass,注意 credit 是可选的,这正是后面 mypy 报警的根源之一:
from dataclasses import dataclass, field
@dataclass
class OrderItem:
sku: str
price: float
qty: int
@dataclass
class Order:
order_id: str
items: list[OrderItem] = field(default_factory=list)
credit: float | None = None
src/orders/settle.py 是出事现场。它有一个函数完全没写类型,另有一个函数用了裸 dict,还有一处直接把 float 和 float | None 相减:
from typing import Any
from src.models import Order
def total_of(order, discounts) -> float:
amount = 0
for item in order.items:
amount += item.price * item.qty
if discounts.get("vip"):
amount = amount * 0.9
return amount
def settle(order: Order, discounts: dict) -> float:
total = total_of(order, discounts)
return total - order.credit
mypy.ini 打开 strict,这样上面每一处偷懒都会被点名,而不是等到线上出现 TypeError: unsupported operand type(s) 才回头找:
[mypy]
python_version = 3.11
strict = True
环境用 Python 3.11 加一个虚拟环境就够了。Aider 本身用 pipx install aider-chat 或 pip install aider-chat 都行,版本号不用照抄别人的博客,装完跑 aider --version 确认能执行即可。mypy 和 pytest 装在同一个虚拟环境里,这样 Aider 通过 /run 调起来的解释器和你在终端里敲的是同一个。
1.2 先把 mypy 基线跑出来
很多人一上来就把整个仓库扔给 Aider 说「帮我修类型错误」,结果模型不知道你关心哪几个文件、按什么标准判对错。正确顺序是先本地跑一遍,把报告当成任务清单,再把清单和文件一起交给它。
cd py-types-demo
python -m mypy src
输出长这样:
src/orders/settle.py:6: error: Function is missing a type annotation [no-untyped-def]
src/orders/settle.py:6: error: Function is missing a type annotation for one or more arguments [no-untyped-def]
src/orders/settle.py:9: error: Incompatible types in assignment (expression has type "float", variable has type "int") [assignment]
src/orders/settle.py:15: error: Missing type parameters for generic type "dict" [type-arg]
src/orders/settle.py:17: error: Unsupported operand types for - ("float" and "None") [operator]
src/orders/settle.py:17: note: Right operand is of type "float | None"
Found 5 errors in 1 file (checked 4 source files)
五个错误其实是四类问题。第一类是 total_of 的两个参数没有标注,strict 模式下不允许隐式 Any;第二类是 amount = 0 被推断成 int,后面又加上 float,mypy 认为变量类型发生了不兼容变化;第三类是 discounts: dict 少了类型参数,等价于 dict[Any, Any];第四类是 order.credit 的类型是 float | None,直接拿去做减法,编译器不认。
判断标准也很清楚:改完之后 mypy src 必须零错误,pytest -q 必须全绿,而且不能靠 # type: ignore 糊过去。这句话要原样写进给 Aider 的指令里,否则它很可能选择成本最低的那条路——加注释屏蔽,而不是真的把类型补上。
1.3 为什么不让 Aider 一次看完整个仓库
Aider 会把加进上下文的文件做成一份仓库地图,地图本身也吃 Token。src/report.py、tests/ 这些和本次修复无关的文件如果一起加进去,除了让每次请求变贵,还会干扰模型对「该改哪里」的判断。更实际的做法是按错误文件逐个加:先 src/models.py 和 src/orders/settle.py,这两个是本次修复的全部范围,等第一轮 diff 通过之后再单独加测试文件。
还有一条底线:整个过程中 Aider 只在你本地的仓库副本里工作,改的是工作区文件,跑的是本地解释器。不要把它的 Base URL 指向任何连接生产数据库的脚本,也不要在对话里粘真实的订单数据。类型修复这种活,用脱敏样例和本地夹具就足够了。
2. 把 Aider 的默认供应商指向 TaoToken
2.1 Aider 的 OpenAI 兼容三个参数
Aider 支持多种模型接入方式,其中一路是通用的 OpenAI 兼容协议,对应三个参数:--openai-api-base 指定服务地址,--openai-api-key 指定凭证,--model 指定模型,且模型名要带 openai/ 前缀,Aider 才知道走兼容协议而不是某家厂商的原生 SDK。把 TaoToken 当默认供应商,本质就是把这三位填对。
需要注意两件事。第一,Base URL 就写 https://taotoken.net/api,末尾不要补 /v1,也不要把任何 UTM 参数拼上去,追踪参数只属于网页链接,塞进接口地址只会让请求 404。第二,Key 从带 UTM 的官网创建,占位符统一用 YOUR_API_KEY,别把自己的真实 Key 贴进任何要提交到 Git 的文件里。
2.2 三种写法:环境变量、启动参数、配置文件
最省事的是环境变量,Aider 会读 OPENAI_API_BASE 和 OPENAI_API_KEY:
export OPENAI_API_BASE=https://taotoken.net/api
export OPENAI_API_KEY=YOUR_API_KEY
如果你同时要给多个项目用不同的 Key,写成项目根目录的 .env,Aider 启动时会自动加载,记得把 .env 加进 .gitignore:
OPENAI_API_BASE=https://taotoken.net/api
OPENAI_API_KEY=YOUR_API_KEY
更喜欢显式的话,直接写进启动命令,这也是本文后面一直用的形式。把 Base URL 和 Key 都摆在参数里,换终端不会因为忘了 export 而报 401:
cd py-types-demo
aider \
--openai-api-base https://taotoken.net/api \
--openai-api-key YOUR_API_KEY \
--model openai/YOUR_MODEL_ID \
--no-auto-commits \
--map-tokens 1024
--no-auto-commits 关掉自动提交,Aider 改完只留在工作区,你看完 diff 再决定要不要 git commit,这对第一次接入的仓库很重要。--map-tokens 1024 限制仓库地图的预算,我们只关心两个文件,没必要把地图做大。
如果这个仓库以后长期用 Aider,可以在根目录放一份 .aider.conf.yml,把不含密钥的部分固化下来。Key 仍然走环境变量,配置文件只负责地址和模型:
openai-api-base: https://taotoken.net/api
model: openai/YOUR_MODEL_ID
no-auto-commits: true
map-tokens: 1024
2.3 模型 ID 从模型广场抄,不要凭记忆
YOUR_MODEL_ID 不是随便猜的。以模型广场为准,页面上写什么就填什么,前缀和大小写都照抄。凭记忆写一个名字看着像的型号,最典型的结果就是 Aider 收到一个 404 或者模型不存在的报错,然后你开始怀疑是 Base URL 写错了,白折腾半小时。
模型广场和模型对话页都在同一个入口下,进去之后先确认两件事:这个模型 ID 是否支持工具调用,以及它在 Aider 里配合哪种 edit format 更稳。Aider 需要模型能稳定返回结构化编辑指令,如果某个模型偏好整文件重写,就给它加 --edit-format whole;如果它擅长 unified diff,就用默认或 --edit-format diff。这一步是 Aider 特有的,和 Key、地址无关,但经常被忽略,然后奇怪为什么 diff 里全是无意义的重排。
配好之后有个快速自检方式:启动 Aider 不要 /add 任何东西,直接问一句「用一句话说明你能看到当前目录吗」。能正常返回,说明 Base URL、Key、模型 ID 三者都是通的;如果返回 401,是 Key 的问题;如果返回 404 或 model not found,先查模型 ID,再查地址有没有多写 /v1。把这个自检做在前面,后面修代码时就不用反复怀疑管道本身。
3. 用 mypy 报告驱动 Aider 出 diff
3.1 第一轮:加文件、贴报告、下指令
启动之后的第一条命令是 /add,把出错文件和它的依赖加进上下文:
/add src/models.py src/orders/settle.py
接着把刚才那份 mypy 输出原样粘进去,末尾补上验收标准。指令不用写得像论文,把约束讲清楚就行:
上面是 mypy --strict 的完整报告,5 个错误都在 src/orders/settle.py。
要求:
1. 只改类型标注和必要的表达式,不改业务语义,折扣逻辑保持原样。
2. 不要用 # type: ignore、不要用 cast 把错误压下去。
3. total_of 的 discounts 用 collections.abc.Mapping 而不是裸 dict。
4. order.credit 为 None 时按 0 处理。
改完展示完整 diff,不要自动提交。
关键在于第 1 条和第 2 条。如果没有第 2 条,模型有相当概率在 order.credit 那一行加 # type: ignore[operator],mypy 确实会归零,但类型问题一点没解决,下次换个调用点还会炸。第 3 条则是在告诉它「用抽象类型做参数」这个具体偏好,避免它把 Mapping 写成 dict[str, bool] 导致调用方传来别的映射类型时不兼容。
3.2 Aider 交回的 diff
第一轮返回的改动如下,你可以对照自己的仓库看结构,变量名不必完全一致:
--- a/src/orders/settle.py
+++ b/src/orders/settle.py
@@ -1,17 +1,17 @@
-from typing import Any
-
+from collections.abc import Mapping
+
from src.models import Order
-def total_of(order, discounts) -> float:
- amount = 0
+def total_of(order: Order, discounts: Mapping[str, bool]) -> float:
+ amount = 0.0
for item in order.items:
amount += item.price * item.qty
if discounts.get("vip"):
- amount = amount * 0.9
+ amount *= 0.9
return amount
-def settle(order: Order, discounts: dict) -> float:
+def settle(order: Order, discounts: Mapping[str, bool]) -> float:
total = total_of(order, discounts)
- return total - order.credit
+ credit = order.credit or 0.0
+ return total - credit
四处改动分别对应四类错误:参数补标注、0 改成 0.0 让推断类型稳定为 float、裸 dict 换成 Mapping[str, bool]、把可能为 None 的 credit 收敛成一个确定的 float。Any 的 import 顺手删掉了,这点值得表扬,因为 strict 模式下未使用的导入虽然不报错,但留着会让人误以为这里还有动态类型。
在 Aider 里用 /diff 复核,确认没有多余的文件被动过。如果发现它顺手改了 report.py 或格式化了不相关文件,用 /drop 把文件移出上下文,或者直接 git checkout 重来,然后重新下指令,明确写「只改 src/orders/settle.py」。
3.3 第二轮:让它自己跑 mypy 并补测试
类型改完不等于任务结束,还要验证。Aider 的 /run 会在本地执行命令并把输出带回上下文,这比你自己在另一个终端跑完再复制粘贴省事:
/run python -m mypy src
预期输出是 Success: no issues found in 4 source files。如果还有残余错误,直接把 /run 的输出连同「继续修,仍然不许加 ignore」一起发过去,通常一轮就能收敛。
接着补测试,这一轮加进去的是测试文件:
/add tests/test_settle.py
按这次修复的边界补三个用例:无折扣、VIP 折扣、credit 为 None。断言用精确相等,不要用近似。
返回的新增文件大概是这样:
--- /dev/null
+++ b/tests/test_settle.py
@@ -0,0 +1,16 @@
+from src.models import Order, OrderItem
+from src.orders.settle import settle, total_of
+
+
+def test_total_without_discount() -> None:
+ order = Order("o-1", [OrderItem("sku-a", 10.0, 2)])
+ assert total_of(order, {}) == 20.0
+
+
+def test_total_with_vip_discount() -> None:
+ order = Order("o-1", [OrderItem("sku-a", 10.0, 2)])
+ assert total_of(order, {"vip": True}) == 18.0
+
+
+def test_settle_with_none_credit() -> None:
+ order = Order("o-1", [OrderItem("sku-a", 10.0, 2)])
+ assert settle(order, {}) == 20.0
三个用例刚好压住三个改动点:Mapping 参数能接受普通 dict、折扣分支不被改坏、credit 为 None 时不再抛 TypeError。再跑一次 /run pytest -q,看到 3 passed 就可以收工。
3.4 这次会话的 Token 增量
Aider 每轮请求结束都会打印一行统计,包含本次发送和接收的 Token 数,用 /tokens 可以查看当前上下文占用。把三轮的数字抄出来,就是这次修复的增量:
| 轮次 | 我发的内容 | 发送 Token | 接收 Token |
|---|---|---|---|
| 第 1 轮 | /add 两个文件 + mypy 报告 + 修复指令 | 3,412 | 587 |
| 第 2 轮 | /run mypy src + 补测试指令 | 3,905 | 402 |
| 第 3 轮 | /run pytest -q | 4,180 | 96 |
| 合计 | 11,497 | 1,085 |
这组数字是本地一次运行中 Aider 终端打印值,来源就是终端本身,它既不是榜单成绩,也不代表你换个仓库还是这个量级。发送侧远大于接收侧是正常的,因为每轮都要重发仓库地图和文件内容;接收侧只有几百 Token,是因为补标注这种改动本身很短。如果你的仓库有几十个文件,先把 --map-tokens 压低,再考虑是不是要拆成多次会话来修。
4. mypy 归零之后:复核与对账
4.1 先看 mypy,再看 pytest,最后看语义
自动化检查通过之后,还有一步人工复核不能省。打开 git diff,逐行确认三件事:折扣系数还是 0.9 没有被改成别的值;credit 为 None 时被当作 0 处理,这个语义是你在指令里明确要求的,而不是模型自作主张;Mapping 参数没有引入任何隐式的运行时类型检查。
类型标注只影响静态检查,不会改变运行时行为,所以一个改动即使 mypy 全绿,也可能把业务逻辑改歪。上面那份 diff 之所以看着干净,是因为指令里写了「不改业务语义」。如果换个模型或者换个说法,很容易出现「顺手重构」——把循环换成推导式、把函数拆成两个、把 total_of 改名。这些改动本身不一定错,但它超出了本次任务的范围,会让 diff 难以评审。第一轮就把范围钉死,比事后回滚便宜得多。
另外提醒一点:测试里的浮点比较用了精确相等,在这个例子里是安全的,因为 20.0 * 0.9 和 10.0 * 2 * 0.9 都能被二进制精确表示。换成更复杂的折扣公式就未必了,但那是测试设计的问题,不要让 Aider 用 pytest.approx 去掩盖一个本来应该暴露的精度隐患。
4.2 Aider 的 /tokens 与 TaoToken 控制台的用量页
Aider 打印的统计是会话视角,控制台用量页是账号视角,两者对照着看能发现一些有意思的偏差。会话里显示发送 11,497,控制台可能略高,因为 Aider 的重试、被中断的请求、以及仓库地图的重复计入都会体现在服务端。差个百分之几属于正常,差出一个数量级就要查是不是 Key 被别的地方共用了。
做这件事的好处是建立成本直觉。修 5 个类型错误花了多少 Token,下次遇到 50 个错误大概心里有数;如果某次会话的发送量突然翻了十倍,多半是你不小心把整个仓库 /add 进去了,而不是模型变啰嗦了。按 Key 维度看用量,还能把实验性质的和日常开发的分开,月底对账时清楚哪部分支出对应哪类任务。
顺便说一句评测纪律:本文没有引用任何公榜分数,也不需要引用。Aider 的修复效果取决于模型、仓库复杂度、指令质量三个变量,单次运行只能说明「这条路径是通的」,不能说明「某个模型最强」。想比较不同模型,正确做法是同一把 Key、同一份 mypy 报告、同一段指令,跑完把 diff 和 Token 数据摆在一起看,并且明确标注这只是一次运行。
4.3 本篇配置里踩过的坑
第一个坑是地址拼接。有人习惯性地把 Base URL 写成带 /v1 的形式,结果请求打到不存在的路径上。TaoToken 这一侧给的统一入口是 https://taotoken.net/api,按这个写,不要自己加后缀。
第二个坑是模型名没加 openai/ 前缀。Aider 看到不带前缀的模型名,会去猜厂商 SDK,猜错就是一堆连接错误。规则很简单:走兼容协议,模型名就带前缀。
第三个坑是 Key 没生效。.env 写在子目录、启动时用了别的 shell、或者复制时带了尾随空格,都会得到 401。排查顺序是先 echo $OPENAI_API_KEY 确认非空,再用启动参数直接显式传一次,两者行为不一致就说明是环境变量加载的问题。
第四个坑和 Aider 本身有关:某些模型默认的 edit format 不适合当前仓库,表现是 diff 里出现大量无关行或者整文件重写。遇到这种情况不用怀疑地址和 Key,换成 --edit-format diff 或者 --edit-format whole 再试一轮,通常立刻见效。
5. 换一个仓库要改的三处
把上面的流程搬到自己的项目,真正需要改的只有三处:Base URL 保持 https://taotoken.net/api,Key 换成你自己的,模型 ID 按模型广场上写的填。其余部分——先跑 mypy 拿基线、按错误文件 /add、指令里钉死「不许 ignore、不许改语义」、改完 /run 验证——都是可复用的套路。
仓库更大时,把这个流程拆成多轮:一轮修一个模块,每轮结束跑一次 mypy 并把它输出记下来。Token 增量会随着仓库地图变大而上升,这时候 --map-tokens 和 /drop 就是两个主要的控制手段。想让同一条修复路径换个模型再跑一次做对照,先把这次的 diff 用 git stash 存起来,再换模型 ID 重跑,两次的 Token 表和 diff 放在一起看,比凭感觉判断靠谱。
要复现这次的对照,先到 控制台 创建一把 Key,再用 模型对话 确认广场上的模型 ID 和你要填的字符串完全一致;如果你打算把 Aider 长期挂在日常开发里,Coding Plan 里有额度与计费方式,具体售价和折扣以页面展示为准。创建完 Key 之后,回到仓库把启动命令里的 YOUR_API_KEY 和 YOUR_MODEL_ID 替换掉,跑一轮 mypy 归零,再回控制台看看这次调用有没有入账,整条链路就算走通了。
🚀 告别海外账号与网络限制!稳定直连全球优质大模型,限时半价接入中。 👉 点击领取海量免费额度




被折叠的 条评论
为什么被折叠?



