镇上有位古怪的裁缝,店门口贴着一行字:恕不接口头吩咐。
你若上门说"把左袖改短两寸",他连头都不抬。他只收一样东西——改衣单。单子上不写话,只别着两块布样:一块叫"原样",是照着从衣服上剪下的那一小片,针脚、缩褶、走线,分毫不差地誊在样布上;另一块叫"改后",是你想要的新模样。
拿到单子,他做的第一件事不是动剪刀,而是拎起你那件衣服,把"原样"布样贴上去,顺着衣料一寸一寸地滑,直到找到一块和样布线线对得上的地方。只有在那儿——严丝合缝、一根纱都不差的那一处——他才下剪子,把旧料挖走,缝上"改后"。找不到对得上的?他绝不猜,也绝不"差不多就这儿吧"。他把整张单子原样别回去,附一句:"这片布,我在你这件衣服上找不到对得上的地方,你拿现在的衣服重描一张来。"
麻烦就出在"现在的衣服"。你上回描"原样"时,袖口那个词还绣着 Hello;送洗一遭,店家手一抖改成了 Hallo——就差这一个字母。你的样布还是老样子,裁缝拿着它满衣服找,哪一处都差着那一针,于是又是原样退回、又是那句话。他脾气还倔:同一张单子,他最多回你三趟,三趟还对不上,就撂下不做了。
有人嫌他死板:直接告诉他"袖口那个词改一下"不就得了?可他认死理——口头的话会走样,布样不会;他要的从来不是"你想改什么",而是"照着当下这件衣服,原原本本指给我看改哪儿"。
这位只认布样、不听人话、锚不上就退单重来的裁缝,正是 Aider 给模型装"手"的办法:模型不喊"调用某工具",而是在回话里写下一段 SEARCH/REPLACE 补丁——这就是它的编辑格式(edit format):一块"原样"(SEARCH)、一块"改后"(REPLACE),harness 先把 SEARCH 段逐字锚定到文件,对上了才落盘,对不上就把修复单塞回去让模型重描。
先猜揭晓前,先押一个猜测 · 你觉得这讲的是?
谜底 · The Concept
Other coding agents — Aider: the 'tool' is an edit format (SEARCH/REPLACE), not a tool_use loop; the harness parses the prose patch and anchors it to the file itself
Other Coding Agents · 他山之石
中文速览 · Quick read
Citations pinned at aider@5dc9490 (cloned 2026-07-08).
Aider is a single Python package. After setup, main() enters a plain
while True: coder.run() and catches a SwitchCoder exception to swap the live
coder between turns (aider/aider/main.py:1159-1177). The agent core is one class,
Coder, subclassed by ~14 registered "coders" — one per edit format — chosen at
runtime by matching a string against the coders.__all__ registry
(aider/aider/coders/base_coder.py:190-194, aider/aider/coders/init.py:18-34).
The defining fact, the one everything else follows from: Aider has no
model-driven tool loop. On the live path the model never emits a tool_use block.
Instead the harness pre-loads context, the model replies once in a strict edit
format (by default SEARCH/REPLACE blocks), and the harness parses that prose reply
and applies the edits itself. Delegation, when it happens, is likewise in-process: an
architect coder hands its plan to a freshly-built editor coder via Coder.create,
not to a spawned agent (aider/aider/coders/architect_coder.py:11-48). Every organ
Claude Code has is present here — it is just realized through parsing text, not
dispatching tools.
Coder.run() is the outer REPL: read user input, call run_one()
(aider/aider/coders/base_coder.py:876-892). run_one() is the real agentic turn
loop, and it is startlingly small:
while message:
self.reflected_message = None
list(self.send_message(message))
if not self.reflected_message:
break
if self.num_reflections >= self.max_reflections:
self.io.tool_warning(f"Only {self.max_reflections} reflections allowed, stopping.")
return
self.num_reflections += 1
message = self.reflected_message
The loop turns only if self.reflected_message got set during the turn, and is
hard-capped at max_reflections = 3 (aider/aider/coders/base_coder.py:924-944,
100-101). A turn itself — send_message — appends the user message, assembles and
token-checks the prompt, streams the completion, then post-processes the text reply
(aider/aider/coders/base_coder.py:1419-1431). That post-processing is where the
"agency" lives: apply_updates() parses and applies the edit blocks, and any failure
downstream — a non-matching patch, a lint error, a failed test, a file the model
asked to add — is funnelled back into reflected_message so the next iteration
carries it (aider/aider/coders/base_coder.py:1585-1623). So Claude Code's "detect
tool_use, execute, loop" becomes Aider's "parse edit blocks from prose, apply, and
loop only on failure feedback" — a single reflection channel with a 3-iteration
budget instead of an open-ended tool loop.
A "tool" in Aider is an edit format — a Coder subclass carrying the full
tool anatomy, only spelled differently:
- Name.
edit_format = "diff"(aider/aider/coders/editblock_coder.py:18); the dispatcher scans the registry for the subclass whoseedit_formatmatches (aider/aider/coders/base_coder.py:190-194). - Schema — in prose, not JSON. The system prompt mandates the exact syntax: "All changes to files must use this SEARCH/REPLACE block format. ONLY EVER RETURN CODE IN A SEARCH/REPLACE BLOCK!" (aider/aider/coders/editblock_prompts.py:8-30), reinforced with few-shot examples.
- Parser / validator.
get_edits()runsfind_original_update_blocks, which regex-scans the reply for , and markers (aider/aider/coders/editblock_coder.py:386-394, 439). - Executor.
apply_edits()anchors each SEARCH body against the file by exact line match (perfect_replace) — flexible only about uniform leading whitespace — and, if the target file doesn't match, retries the block against every other file in the chat (aider/aider/coders/editblock_coder.py:41-74, 146-154).
When a block fails to anchor, apply_edits raises a ValueError whose message is a
repair-grade report: # N SEARCH/REPLACE blocks failed to match!, the offending
block echoed back, a Did you mean to match some of these actual lines… hint drawn
from find_similar_lines, and the rule "The SEARCH section must exactly match an
existing block of lines including all white space, comments, indentation…"
(aider/aider/coders/editblock_coder.py:84-124, 602-628). apply_updates() catches
that ValueError and stores the whole report in self.reflected_message, so the
model gets to retry against the budget (aider/aider/coders/base_coder.py:2296-2316).
Notably, JSON-schema function-call tools exist in the tree but are dead code.
EditBlockFunctionCoder carries a full OpenAI functions schema — a replace_lines
call with path / original_lines / updated_lines
(aider/aider/coders/editblock_func_coder.py:10-58) — but its __init__ immediately
raises RuntimeError("Deprecated, needs to be refactored to support get_edits/apply_edits") (aider/aider/coders/editblock_func_coder.py:61), it is never
imported into the coders.__all__ registry, and a sibling
SingleWholeFileFunctionCoder is explicitly commented out
(aider/aider/coders/init.py:16). Tellingly, even that dead function coder imports
and re-uses the same do_replace (aider/aider/coders/editblock_func_coder.py:5):
the structured JSON would still have to anchor its original_lines to the file — the
format changes the parse surface, not the anchor requirement.
The gate. The single approval primitive is InputOutput.confirm_ask() — a
terminal Y/N prompt with (A)ll / (S)kip all / persistent (D)on't ask again
(aider/aider/io.py:807-925). Between a parsed edit and the filesystem sits
prepare_to_edit → allowed_to_edit: files already in the chat are pre-authorized
(the human added them — that was the grant), and any file not in the chat triggers
"Allow edits to file that has not been added to the chat?"
(aider/aider/coders/base_coder.py:2191-2240). Model-suggested shell commands get the
strictest form, explicit_yes_required=True
(aider/aider/coders/base_coder.py:2450-2463). There is no rules engine or settings
file; the deeper safety net is that every applied edit is auto-committed to git, so a
bad edit is one /undo away (aider/aider/coders/base_coder.py:2375-2395).
Every prompt is rebuilt from a fixed ChatChunks layering —
system + examples + readonly_files + repo + done + chat_files + cur + reminder
(aider/aider/coders/chat_chunks.py:6-26). History is two lists, done_messages (past
turns) and cur_messages (this turn); after edits apply, move_back_cur_messages
shifts cur into done (aider/aider/coders/base_coder.py:1036-1046). That shift is
the compaction trigger: when done-history exceeds max_chat_history_tokens —
computed per model as min(max(max_input_tokens / 16, 1024), 8192)
(aider/aider/models.py:355-358) — a background thread summarizes it, recursively
splitting at an assistant-message boundary and keeping a verbatim tail
(aider/aider/history.py:33-96).
The repo map is Aider's read-tool substitute. Rather than let the model call
Read/Grep, every prompt embeds a token-budgeted map of the rest of the repo as a
user/assistant message pair (aider/aider/coders/base_coder.py:750-761). RepoMap
turns tree-sitter def/ref tags into a graph and runs PageRank with
personalization — boosting identifiers mentioned in the conversation and references
from in-chat files (aider/aider/repomap.py:470-531) — then binary-searches how many
tags to include so the rendered tree fits the budget within ~15%
(aider/aider/repomap.py:676-706). Durable memory is the --read read-only file (a
CONVENTIONS.md, say) injected into the readonly_files chunk every prompt
(aider/aider/coders/base_coder.py:478-485).
Every organ maps to an existing page; the difference is always how it is realized.
| Aider organ | Realized as | Claude Code page |
|---|---|---|
Turn loop (run_one + reflected_message, budget 3) |
loop while the harness has failure feedback, not while the model returns tool_use |
/stories/loop/1/ · budget & stop: /stories/loop/2/ |
| Edit format as the "tool" (SEARCH/REPLACE) | prose schema + regex parser + anchor executor, one active write-tool per model | /stories/tools/1/ |
confirm_ask gate + git auto-commit |
(question, file) prompts in memory, not a persisted rules config | /stories/tools/2/ |
| ChatChunks + background summarizer | proactive, backgrounded, weak-model-first compaction | /stories/context/1/ |
--read conventions files + restored history |
always-in-context durable material (CLAUDE.md's role) | /stories/context/2/ |
Architect → editor handoff (Coder.create) |
fresh-context, different-model delegation — but sequential, user-gated, in-process | /stories/multiagent/1/ |
/ask→SwitchCoder mode round-trips |
staged-pipeline flow control by exception | /stories/multiagent/2/ |
The one organ with no Claude Code equivalent is the PageRank repo map: it does not add a tool, it replaces the read/grep tools of /stories/tools/1/ with an always-present, budgeted orientation map (aider/aider/repomap.py:470-531).
- The PageRank repo map. A token-budgeted map of the whole repo, always in context, kills the latency of on-demand read/grep for orientation: tree-sitter tags become a graph, PageRank ranks symbols with conversation-aware personalization (aider/aider/repomap.py:470-531), and a binary search fits the rendered tree to the budget within 15% (aider/aider/repomap.py:676-706).
- One reflection channel with a budget and repair-grade errors. Malformed blocks,
non-anchoring SEARCH sections with "did you mean" hints, lint errors, and test
failures all funnel into the single
reflected_messageretry loop, hard-capped at 3 (aider/aider/coders/base_coder.py:924-944, aider/aider/coders/editblock_coder.py:84-124) — a uniform, bounded self-repair path instead of ad-hoc error returns. - Prompt-cache-first context layout. The stable-first
ChatChunksorder exists socache_controlmarkers can be pinned at chunk boundaries (aider/aider/coders/chat_chunks.py:28-55), and a daemon thread re-pings the provider to keep the cache warm between human keystrokes (aider/aider/coders/base_coder.py:1348-1392). - Summarize-on-format-switch — real, but not what powers the architect→editor
handoff.
Coder.createdefaultssummarize_from_coder=True(aider/aider/coders/base_coder.py:131), and when the edit format changes it summarizes awaydone_messagesso the new model can't imitate the stale format (aider/aider/coders/base_coder.py:153-188, gate at :161) — a small contamination guard for multi-format systems. ButArchitectCoder.reply_completedexplicitly setskwargs["summarize_from_coder"] = Falsebefore building the editor coder (aider/aider/coders/architect_coder.py:32), so that gate never fires on the handoff; the editor's fresh context instead comes from a direct wipe,editor_coder.cur_messages = []; editor_coder.done_messages = [](aider/aider/coders/architect_coder.py:38-39). The summarizer's real trigger is the/chat-mode <format>path:cmd_chat_modeleavessummarize_from_coderat its defaultTrueunless switching to "code" or "ask" (aider/aider/commands.py:191-203), whereas the/ask/code/architect/contextshortcuts (_generic_chat_command) always force itFalse(aider/aider/commands.py:1206-1230).
classDiagram
class Coder {
<<Organ 1 turn loop>>
run REPL loop
run_one the turn
send_message one reply
sets reflected_message
max_reflections 3
}
class EditFormat {
<<Organ 2 the tool>>
edit_format diff
get_edits regex parse
apply_edits anchor
perfect_replace exact match
}
class ConfirmGate {
<<Organ 3 the gate>>
confirm_ask Y or N
allowed_to_edit
git auto commit for undo
}
class ContextAssembly {
<<Organ 4 context and memory>>
ChatChunks layering
background summarizer
RepoMap PageRank map
}
class ArchitectHandoff {
<<Organ 5 delegation>>
create fresh editor coder
history wiped on handoff
SwitchCoder in process
}
Coder ..> ContextAssembly : build every prompt
Coder ..> EditFormat : parse and apply the reply
EditFormat ..> ConfirmGate : side effect asks first
EditFormat ..> Coder : failure sets reflected_message
Coder ..> ArchitectHandoff : delegate in process 读法:Aider 没有模型驱动的工具循环。Organ 1 的 run_one 是真正的回合循环——
发一次 send_message,只有当这一轮置了 reflected_message 才再转,硬上限 3 次
(aider/aider/coders/base_coder.py:924-944)。模型不发 tool_use,而在散文里写
Organ 2 的 SEARCH/REPLACE 补丁;harness 正则解析、用 perfect_replace 把 SEARCH 段逐字锚定到文件
(editblock_coder.py:386-394, 146-154),锚不上就把修复单塞回 reflected_message
(base_coder.py:2296-2316)。每次落盘前过 Organ 3 的 confirm_ask 关卡、并自动 git 提交以备 /undo
(base_coder.py:2191-2240, 2375-2395)。Organ 4 把每个 prompt 按 ChatChunks 定序拼装,
后台弱模型摘要旧history、并嵌入 PageRank 仓库地图充当只读工具(chat_chunks.py:6-26,repomap.py:470-531)。
Organ 5 的架构师→编辑器交接是同进程内的 Coder.create,顺序、要人点头,不是并发子代理
(architect_coder.py:11-48)。
Aider 的模型不发 tool_use。它在回话里写一段 SEARCH/REPLACE 补丁:一块「原样」、一块「改后」。
harness 先把 SEARCH 段拿去和文件逐字锚定(perfect_replace,只对统一缩进宽容),对上了才落盘。
把文件拨动一个字符,SEARCH 就锚不上——看它退回一份修复单,塞进 reflected_message 让模型重描。
greeter.py 原样 diff 编辑格式replace_lines({
"edits": [{
"path": "greeter.py",
"original_lines": [" greeting = \"Hello, \" + name", " print(greeting)"],
"updated_lines": [" greeting = f\"Hello, {name}!\"", " print(greeting)"]
}]
}) - 传输层不同:结构化 JSON、按 schema 校验,没有
<<<<<<< SEARCH这类标记要从散文里正则抠出——「标记写坏」的解析失败不会发生(那套正则只针对散文补丁,editblock_coder.py:386-394)。 - 锚定要求相同:schema 里
original_lines写的是「从原文件逐字截取、含所有空白、不得跳行」,而这个 coder 用的还是同一个do_replace(editblock_func_coder.py:5, 10-58)——文件一漂,tool-call 编辑照样锚不上,和散文补丁一模一样。 - 而且在 Aider 里它是死的:
EditBlockFunctionCoder.__init__直接raise RuntimeError("Deprecated…")(editblock_func_coder.py:61),没进__all__注册表;活着的只有散文 SEARCH/REPLACE。
这就是 Aider 给模型装「手」的方式:编辑格式即工具。「工具」的 schema 是散文里的一句强制令
(ONLY EVER RETURN CODE IN A *SEARCH/REPLACE BLOCK*!,editblock_prompts.py:8-30),
执行器是把 SEARCH 段逐字锚定到文件的 perfect_replace(editblock_coder.py:146-154)。
锚不上不会崩——ValueError 修复单被 apply_updates() 存进 reflected_message,
run_one 据此重试,硬上限 3 次(base_coder.py:2296-2316, 924-944)。
The lay of the land
Citations pinned at aider@5dc9490 (cloned 2026-07-08).
Aider is a single Python package. After setup, main() enters a plain
while True: coder.run() and catches a SwitchCoder exception to swap the live
coder between turns (aider/aider/main.py:1159-1177). The agent core is one class,
Coder, subclassed by ~14 registered "coders" — one per edit format — chosen at
runtime by matching a string against the coders.__all__ registry
(aider/aider/coders/base_coder.py:190-194, aider/aider/coders/init.py:18-34).
The defining fact, the one everything else follows from: Aider has no
model-driven tool loop. On the live path the model never emits a tool_use block.
Instead the harness pre-loads context, the model replies once in a strict edit
format (by default SEARCH/REPLACE blocks), and the harness parses that prose reply
and applies the edits itself. Delegation, when it happens, is likewise in-process: an
architect coder hands its plan to a freshly-built editor coder via Coder.create,
not to a spawned agent (aider/aider/coders/architect_coder.py:11-48). Every organ
Claude Code has is present here — it is just realized through parsing text, not
dispatching tools.
The loop
Coder.run() is the outer REPL: read user input, call run_one()
(aider/aider/coders/base_coder.py:876-892). run_one() is the real agentic turn
loop, and it is startlingly small:
while message:
self.reflected_message = None
list(self.send_message(message))
if not self.reflected_message:
break
if self.num_reflections >= self.max_reflections:
self.io.tool_warning(f"Only {self.max_reflections} reflections allowed, stopping.")
return
self.num_reflections += 1
message = self.reflected_message
The loop turns only if self.reflected_message got set during the turn, and is
hard-capped at max_reflections = 3 (aider/aider/coders/base_coder.py:924-944,
100-101). A turn itself — send_message — appends the user message, assembles and
token-checks the prompt, streams the completion, then post-processes the text reply
(aider/aider/coders/base_coder.py:1419-1431). That post-processing is where the
"agency" lives: apply_updates() parses and applies the edit blocks, and any failure
downstream — a non-matching patch, a lint error, a failed test, a file the model
asked to add — is funnelled back into reflected_message so the next iteration
carries it (aider/aider/coders/base_coder.py:1585-1623). So Claude Code's "detect
tool_use, execute, loop" becomes Aider's "parse edit blocks from prose, apply, and
loop only on failure feedback" — a single reflection channel with a 3-iteration
budget instead of an open-ended tool loop.
Tools and the gate
A "tool" in Aider is an edit format — a Coder subclass carrying the full
tool anatomy, only spelled differently:
- Name.
edit_format = "diff"(aider/aider/coders/editblock_coder.py:18); the dispatcher scans the registry for the subclass whoseedit_formatmatches (aider/aider/coders/base_coder.py:190-194). - Schema — in prose, not JSON. The system prompt mandates the exact syntax: "All changes to files must use this SEARCH/REPLACE block format. ONLY EVER RETURN CODE IN A SEARCH/REPLACE BLOCK!" (aider/aider/coders/editblock_prompts.py:8-30), reinforced with few-shot examples.
- Parser / validator.
get_edits()runsfind_original_update_blocks, which regex-scans the reply for , and markers (aider/aider/coders/editblock_coder.py:386-394, 439). - Executor.
apply_edits()anchors each SEARCH body against the file by exact line match (perfect_replace) — flexible only about uniform leading whitespace — and, if the target file doesn't match, retries the block against every other file in the chat (aider/aider/coders/editblock_coder.py:41-74, 146-154).
When a block fails to anchor, apply_edits raises a ValueError whose message is a
repair-grade report: # N SEARCH/REPLACE blocks failed to match!, the offending
block echoed back, a Did you mean to match some of these actual lines… hint drawn
from find_similar_lines, and the rule "The SEARCH section must exactly match an
existing block of lines including all white space, comments, indentation…"
(aider/aider/coders/editblock_coder.py:84-124, 602-628). apply_updates() catches
that ValueError and stores the whole report in self.reflected_message, so the
model gets to retry against the budget (aider/aider/coders/base_coder.py:2296-2316).
Notably, JSON-schema function-call tools exist in the tree but are dead code.
EditBlockFunctionCoder carries a full OpenAI functions schema — a replace_lines
call with path / original_lines / updated_lines
(aider/aider/coders/editblock_func_coder.py:10-58) — but its __init__ immediately
raises RuntimeError("Deprecated, needs to be refactored to support get_edits/apply_edits") (aider/aider/coders/editblock_func_coder.py:61), it is never
imported into the coders.__all__ registry, and a sibling
SingleWholeFileFunctionCoder is explicitly commented out
(aider/aider/coders/init.py:16). Tellingly, even that dead function coder imports
and re-uses the same do_replace (aider/aider/coders/editblock_func_coder.py:5):
the structured JSON would still have to anchor its original_lines to the file — the
format changes the parse surface, not the anchor requirement.
The gate. The single approval primitive is InputOutput.confirm_ask() — a
terminal Y/N prompt with (A)ll / (S)kip all / persistent (D)on't ask again
(aider/aider/io.py:807-925). Between a parsed edit and the filesystem sits
prepare_to_edit → allowed_to_edit: files already in the chat are pre-authorized
(the human added them — that was the grant), and any file not in the chat triggers
"Allow edits to file that has not been added to the chat?"
(aider/aider/coders/base_coder.py:2191-2240). Model-suggested shell commands get the
strictest form, explicit_yes_required=True
(aider/aider/coders/base_coder.py:2450-2463). There is no rules engine or settings
file; the deeper safety net is that every applied edit is auto-committed to git, so a
bad edit is one /undo away (aider/aider/coders/base_coder.py:2375-2395).
Context and memory
Every prompt is rebuilt from a fixed ChatChunks layering —
system + examples + readonly_files + repo + done + chat_files + cur + reminder
(aider/aider/coders/chat_chunks.py:6-26). History is two lists, done_messages (past
turns) and cur_messages (this turn); after edits apply, move_back_cur_messages
shifts cur into done (aider/aider/coders/base_coder.py:1036-1046). That shift is
the compaction trigger: when done-history exceeds max_chat_history_tokens —
computed per model as min(max(max_input_tokens / 16, 1024), 8192)
(aider/aider/models.py:355-358) — a background thread summarizes it, recursively
splitting at an assistant-message boundary and keeping a verbatim tail
(aider/aider/history.py:33-96).
The repo map is Aider's read-tool substitute. Rather than let the model call
Read/Grep, every prompt embeds a token-budgeted map of the rest of the repo as a
user/assistant message pair (aider/aider/coders/base_coder.py:750-761). RepoMap
turns tree-sitter def/ref tags into a graph and runs PageRank with
personalization — boosting identifiers mentioned in the conversation and references
from in-chat files (aider/aider/repomap.py:470-531) — then binary-searches how many
tags to include so the rendered tree fits the budget within ~15%
(aider/aider/repomap.py:676-706). Durable memory is the --read read-only file (a
CONVENTIONS.md, say) injected into the readonly_files chunk every prompt
(aider/aider/coders/base_coder.py:478-485).
对照 Claude Code
Every organ maps to an existing page; the difference is always how it is realized.
| Aider organ | Realized as | Claude Code page |
|---|---|---|
Turn loop (run_one + reflected_message, budget 3) |
loop while the harness has failure feedback, not while the model returns tool_use |
/stories/loop/1/ · budget & stop: /stories/loop/2/ |
| Edit format as the "tool" (SEARCH/REPLACE) | prose schema + regex parser + anchor executor, one active write-tool per model | /stories/tools/1/ |
confirm_ask gate + git auto-commit |
(question, file) prompts in memory, not a persisted rules config | /stories/tools/2/ |
| ChatChunks + background summarizer | proactive, backgrounded, weak-model-first compaction | /stories/context/1/ |
--read conventions files + restored history |
always-in-context durable material (CLAUDE.md's role) | /stories/context/2/ |
Architect → editor handoff (Coder.create) |
fresh-context, different-model delegation — but sequential, user-gated, in-process | /stories/multiagent/1/ |
/ask→SwitchCoder mode round-trips |
staged-pipeline flow control by exception | /stories/multiagent/2/ |
The one organ with no Claude Code equivalent is the PageRank repo map: it does not add a tool, it replaces the read/grep tools of /stories/tools/1/ with an always-present, budgeted orientation map (aider/aider/repomap.py:470-531).
What to steal
- The PageRank repo map. A token-budgeted map of the whole repo, always in context, kills the latency of on-demand read/grep for orientation: tree-sitter tags become a graph, PageRank ranks symbols with conversation-aware personalization (aider/aider/repomap.py:470-531), and a binary search fits the rendered tree to the budget within 15% (aider/aider/repomap.py:676-706).
- One reflection channel with a budget and repair-grade errors. Malformed blocks,
non-anchoring SEARCH sections with "did you mean" hints, lint errors, and test
failures all funnel into the single
reflected_messageretry loop, hard-capped at 3 (aider/aider/coders/base_coder.py:924-944, aider/aider/coders/editblock_coder.py:84-124) — a uniform, bounded self-repair path instead of ad-hoc error returns. - Prompt-cache-first context layout. The stable-first
ChatChunksorder exists socache_controlmarkers can be pinned at chunk boundaries (aider/aider/coders/chat_chunks.py:28-55), and a daemon thread re-pings the provider to keep the cache warm between human keystrokes (aider/aider/coders/base_coder.py:1348-1392). - Summarize-on-format-switch — real, but not what powers the architect→editor
handoff.
Coder.createdefaultssummarize_from_coder=True(aider/aider/coders/base_coder.py:131), and when the edit format changes it summarizes awaydone_messagesso the new model can't imitate the stale format (aider/aider/coders/base_coder.py:153-188, gate at :161) — a small contamination guard for multi-format systems. ButArchitectCoder.reply_completedexplicitly setskwargs["summarize_from_coder"] = Falsebefore building the editor coder (aider/aider/coders/architect_coder.py:32), so that gate never fires on the handoff; the editor's fresh context instead comes from a direct wipe,editor_coder.cur_messages = []; editor_coder.done_messages = [](aider/aider/coders/architect_coder.py:38-39). The summarizer's real trigger is the/chat-mode <format>path:cmd_chat_modeleavessummarize_from_coderat its defaultTrueunless switching to "code" or "ask" (aider/aider/commands.py:191-203), whereas the/ask/code/architect/contextshortcuts (_generic_chat_command) always force itFalse(aider/aider/commands.py:1206-1230).
来源 · Source citations
- [1]
aider/aider/main.py:1159-1177 - [2]
aider/aider/coders/base_coder.py:190-194 - [3]
aider/aider/coders/__init__.py:18-34 - [4]
aider/aider/coders/base_coder.py:876-892 - [5]
aider/aider/coders/base_coder.py:924-944 - [6]
aider/aider/coders/base_coder.py:100-101 - [7]
aider/aider/coders/base_coder.py:1419-1431 - [8]
aider/aider/coders/base_coder.py:1585-1623 - [9]
aider/aider/coders/base_coder.py:2296-2316 - [10]
aider/aider/coders/editblock_prompts.py:8-30 - [11]
aider/aider/coders/editblock_coder.py:18 - [12]
aider/aider/coders/editblock_coder.py:386-394 - [13]
aider/aider/coders/editblock_coder.py:439 - [14]
aider/aider/coders/editblock_coder.py:41-74 - [15]
aider/aider/coders/editblock_coder.py:146-154 - [16]
aider/aider/coders/editblock_coder.py:84-124 - [17]
aider/aider/coders/editblock_coder.py:602-628 - [18]
aider/aider/coders/editblock_func_coder.py:5 - [19]
aider/aider/coders/editblock_func_coder.py:10-58 - [20]
aider/aider/coders/editblock_func_coder.py:61 - [21]
aider/aider/coders/__init__.py:16 - [22]
aider/aider/io.py:807-925 - [23]
aider/aider/coders/base_coder.py:2191-2240 - [24]
aider/aider/coders/base_coder.py:2450-2463 - [25]
aider/aider/coders/base_coder.py:2375-2395 - [26]
aider/aider/coders/chat_chunks.py:6-26 - [27]
aider/aider/coders/base_coder.py:1036-1046 - [28]
aider/aider/history.py:33-96 - [29]
aider/aider/models.py:355-358 - [30]
aider/aider/repomap.py:470-531 - [31]
aider/aider/repomap.py:676-706 - [32]
aider/aider/coders/base_coder.py:750-761 - [33]
aider/aider/coders/base_coder.py:478-485 - [34]
aider/aider/coders/architect_coder.py:11-48 - [35]
aider/aider/coders/architect_coder.py:32 - [36]
aider/aider/coders/architect_coder.py:38-39 - [37]
aider/aider/coders/base_coder.py:131 - [38]
aider/aider/coders/base_coder.py:153-188 - [39]
aider/aider/coders/base_coder.py:161 - [40]
aider/aider/commands.py:191-203 - [41]
aider/aider/commands.py:1206-1230 - [42]
aider/aider/coders/chat_chunks.py:28-55 - [43]
aider/aider/coders/base_coder.py:1348-1392 - [44]
aider/aider/coders/wholefile_coder.py:13