公司一直在疯狂实践AI开发,不得不说,挑战太多太细!从熟练可控的软件工程模式进入AI First工程实践,就好像是小时候学会骑自行车后,撒开双手,然后结果未知:可能你大力蹬车还很平稳,但也可能摔个大跟头!
实践中,我们不仅仅要应付AI(大模型)自身的问题,如工具函数库太大塞不进上下文怎么办?各种规范限制如何引入AI?还有工程方面的问题,如AI写代码时发现重复模式,什么时候触发重构?提取出新方法后,怎么让AI在下一秒就用上它?并行子Agent各自为政写出重复代码怎么解?上下文老化后关键约束丢失怎么办?还有,这些问题有没有一个统一的解决框架?
以下内容梳理了2025-2026年间AI工程社区在架构护栏方面的实践与讨论——其中既有GitHub生产环境的教训、DoorDash万PR/周的架构选择、开源项目的精巧解法,也有尚在争论中的开放难题。文中的2026年数据来自已公开的预印本研究与行业趋势推演。
本文要点
一、工具函数库太大,怎么塞进上下文?
二、发现重复模式,什么时候触发重构?
三、提取出新方法,如何立即让AI用上?
四、并行子Agent各自为政,写出重复代码怎么办?
五、上下文老化,关键约束丢失怎么办?
六、有没有统一的解决框架?
问题一:工具函数库太大,怎么塞进模型上下文?
一个中大型项目的工具函数库(API客户端封装、锁管理器、缓存层、审计日志器、校验Schema、数据库访问层……)如果全部展开写进CLAUDE.md,轻易超过5000行。而2026年的实践数据显示,上下文文件始终加载超过100行后,AI对其中具体约束的遵循率显著下降,占据窗口40%以上时关键指令开始被遗漏。更糟的是,MCP工具的JSON schema定义也在抢占token预算——GitHub MCP单次加载就要26K tokens,20个工具加完整文档轻松突破40000 tokens。
这不是一个"写得更精简"就能解决的问题。如果AI不知道有哪些工具可用,它就会自己发明一个新的;如果全告诉它,上下文就爆了。
解法A:三层渐进式加载——从980 tokens管理50+工具
百度开发者社区在2026年初报告了一个企业实践(据该团队在拥有50+工具的中型项目中的实测):把工具信息拆成三层,初始只加载元数据,按需展开详细内容。结果将初始加载从42300 tokens压缩到980 tokens——压缩率97.6%,同时工具调用成功率从82%提升到99%。
三层渐进式加载结构
以一个大模型应用项目为例:CLAUDE.md中只保留工具函数的注册表元数据(名称 + 路径 + 一句话用途),共约15-20行。当AI需要调用具体工具时,它通过文件路径读取对应源码中的JSDoc来获取详细约束。关键洞察:AI读取JSDoc时,会同时读到其中的约束信息(如"这是修改共享配置的唯一入口")——工具签名本身就是约束载体,不需要额外提示词。这就是"元数据常驻 + 按需展开"的模式。
解法B:子Agent工具隔离——主Agent不需要知道所有工具
Claude Code的子Agent(Subagent)架构有一个关键设计:每个子Agent是一个全新的Claude实例,拥有独立的上下文窗口,父子之间只有一根数据通道——Task工具的prompt字符串。子Agent不继承父Agent的对话历史、已读文件或已加载技能。
这带来一个工程优势:你可以为不同类别的任务创建携带不同工具集的子Agent。例如,数据库迁移子Agent只携带db/migrations相关的工具描述和schema信息;前端组件子Agent只携带UI组件库的API和设计Token。主Agent的CLAUDE.md不需要包含所有工具——它在遇到特定任务时通过Task工具委派给对应的子Agent。
工具限制在Claude Code中是通过Agent定义文件的YAML前端元数据硬实施的(tools: [Read, Grep, Glob, Bash]),不是靠提示词软约束。这意味着一个被定义为只能读取的子Agent,结构上就不可能执行写操作。
解法C:语义搜索替代全量列举
当工具函数数量超过约30个时,即使元数据压缩后也会占据可观的上下文。此时可以引入语义搜索:将工具函数的名称、签名、JSDoc描述向量化存储在本地向量数据库中,AI在需要时通过语义查询找到相关工具,再按需加载完整定义。Anthropic的claude-context MCP服务器正是做这件事——它在编码前对代码库做分块和嵌入,让AI通过语义检索而非盲目遍历来定位代码。
一个有趣的边界思考:如果你把工具函数的JSDoc写得足够精准,AI在读取调用方代码时会自然读到import路径和类型签名,它也许不需要提前知道所有工具的存在——只需要在生成代码时读到的函数签名就能推断出正确用法。这是"让代码自己说话"的策略,但前提是每个工具函数的类型定义和JSDoc都足够清晰。
实践路径(问题一的落地建议)
1. 在CLAUDE.md中维护一个≤20行的"工具函数注册表",仅含路径+一句话用途
2. 每个工具的JSDoc中包含约束信息("这是修改sharedConfig的唯一入口"),让按需读取时自然携带规则
3. 为不同领域创建携带不同工具集的子Agent定义文件
4. 当工具数量超过30个时,评估是否引入语义搜索层
解决了"AI知道有哪些工具"之后,下一个挑战是:AI在编码过程中发现了重复逻辑——谁来决策是否提取、何时提取、以及怎么安全地提取?
问题二:AI写代码时发现了重复模式,什么时候触发重构?
这个问题比看起来复杂得多。因为"触发重构"在AI编码场景下不是一次性的决定,而是一条管线上的三个连续决策:检测 → 判断 → 执行。每一步都有坑。
检测层:文本重复 vs 语义重复
最简单的检测是文本级别的——AI在生成代码时看到自己写了和已有代码几乎一样的片段。但真正有价值的是语义重复检测:两段代码用不同的变量名、不同的实现方式,但做了同样的事。2026年的开源工具在这个方向上做出了实质进展:
GitHub的Serena(语义代码分析引擎)能识别不同文件中逻辑等效但实现不同的函数,自动创建[duplicate-code]类型的Issue
Anatoly使用双重嵌入(代码嵌入+NLP嵌入)通过LanceDB做混合相似度评分,能捕获"用不同库实现相同功能"的情况
Kodify的Scout Agent使用Vertex AI text-embedding-005 + Vector Search在MR创建时就检测语义重复
判断层:什么时候值得抽?触发条件怎么定?
GitHub在2026年1月公开发布了"持续重构工作流"的生产数据,提供了判断门槛的实证参考:
- 重复代码检测器(Duplicate Code Detector):用Serena做语义分析,在文件间发现重复模式,自动创建Issue。76/96个PR被合并,合并率79%——说明自动化重构在多数场景下可信,但仍有21%被人工拒绝,值得关注拒绝原因
- 语义函数重构器(Semantic Function Refactor):按语义目的对函数分组,识别放错文件的函数和潜在重复。遵循"一文件一功能"原则
- 自动代码简化器(Automatic Code Simplifier):每天分析最近修改的代码,提取重复逻辑为helper函数,创建PR
从这些实践中可以抽象出触发重构的评估维度:
跨度阈值:重复出现在≥2个文件?跨文件 > 同文件内(同文件内重复先考虑合并而非提取)
语义阈值:逻辑等价(不仅仅是文本相似)?用嵌入相似度区分"碰巧相同的代码"和"真正可替换的行为"
变更频率:重复代码所在的文件近期是否频繁修改?高变更区域中的重复优先级更高(因为它正在扩散)
风险路由:简单重复(如相同的null检查模式)→ 自动化处理;复杂重复(涉及业务逻辑)→ LLM分析后创建建议;高风险重复(涉及安全或数据完整性)→ 标记人工审查
最小尺寸:重复代码块至少≥3行并在≥2个文件中出现。避免为了提取两行重复代码而引入不必要的间接层
执行层:谁来重构?怎么保证安全?
Kodify的重构Agent提供了一个值得参考的安全执行模式:克隆分支 → 在沙箱中应用修改 → 运行lint/test/build → 只在验证通过后才提交diff。这个"修复-验证-提交"循环确保了自动重构不会引入回归。
DocuSign的"Elf" Agent更进一步:它能通过Jira工单、Slack工作流或直接请求三种方式触发,处理重复性重构任务,还能自主监控自己提交的PR并响应审查意见。这意味着重构触发不一定由AI在编码过程中自发完成——它可以是一个异步的后台流程。
一个关键的实践建议:把重构触发从"编码Agent的附带任务"中分离出来。让编码Agent专注生成功能代码,让独立的审查/重构Agent在事后检测重复并提议提取。这种"写审分离"(Writer/Reviewer split)模式是2026年AI编码社区最重要的设计原则之一——编码Agent在同一个上下文中评判自己刚写的代码,判断力会显著衰减。
确认了"应该提取"之后,下一个问题立刻出现:提取出来的新函数,怎么确保AI在下一刻就知道它的存在?
问题三:提取出新方法后,如何立即让AI用它?
这是AI-First开发中最容易被忽略的工程问题。假设你(或一个重构Agent)提取了一个新的工具函数 extractUserId(),放到了 src/utils/auth.ts 中。5分钟后,你让AI在另一个模块中实现一个新功能——它会调用这个刚刚提取出来的函数吗?
答案取决于你的上下文传播机制。如果没有任何机制,AI大概率会重新发明一个类似但不完全相同的实现——这正是"代码腐化"的主要来源。
机制一:Git Hook驱动的自动上下文更新
npm包aggentctx提供了一个精巧的解法:在每次git commit后,一个hook自动生成待审文件(含提交消息、变更文件列表和diff)。下一次AI会话启动时,CLAUDE.md中的指令引导它读取这个待审文件,分析变更,如果发现有新的可复用函数或API变更,就自动更新项目的功能特性文件(FEATURES.md)。
git commit↓hook → 写入 .agentctx/pending-review.md(提交消息 + 变更文件 + diff)↓下次会话启动 → CLAUDE.md引导AI读取pending-review.md↓AI分析diff → 发现新提取的函数 → 更新工具注册表
这个模式的核心洞察是:上下文更新不应该依赖人工记得去做这件事。把它绑定到git提交流程上,让每次代码变更自动触发上下文审查。
机制二:SessionStart钩子 + 状态文件重读
在Claude Code中,可以通过SessionStart钩子在每次会话启动时(包括/clear和/compact之后)自动重读项目状态文件:
{ "hooks": { "SessionStart": [{ "matcher": "startup|clear|compact", "hooks": [{ "type": "command", "command": "cat .claude/tool-registry.md" }] }] }}
配合这个钩子,你需要维护一个工具注册表文件(如 .claude/tool-registry.md),每次提取新函数后,更新这个文件。钩子确保AI在每次新会话中都能看到最新的注册表。关键是——这个更新动作本身也可以由AI在重构完成后自动完成:提取函数 → 更新注册表文件 → 提交。下一次AI启动时自动加载。
机制三:复合工程(Compound Engineering)的学习回环
复合工程(Compound Engineering)插件定义了一个多步流水线,最后一步是 /ce-compound:将刚完成的任务中学到的模式、提取的方法和踩过的坑写入 docs/solutions/ 目录。这样提取出来的方法不只是存在于代码中,还存在于AI下次任务会参考的文档里。
这本质上是一个"学习 → 编码 → 传播"的闭环:
学习回环的三步闭环
1. AI(或人)在编码过程中发现重复模式 → 提取为工具函数
2. 提取动作自动触发上下文更新:更新工具注册表文件 + 在FEATURES.md中记录新增函数
3. 下一次AI会话启动时(通过SessionStart钩子或/clear重读),新的工具函数已经在注册表中,AI在生成代码时会引用它
微软的auto-memory项目走得更远:它是一个后台系统,扫描已完成的编码会话,将学到的内容注入未来的会话中。如果你在会话结束时写了一段结构化的"学习总结",auto-memory会将其索引并在此后相关任务中自动调出。这相当于把"工具函数传播"从手动维护升级到了自动检索。
综合来看,这个问题的解法正在从"人记住要更新文档"演进到"代码变更自动触发上下文传播"——让工具系统自己保持新鲜度,而不是依赖人的纪律。
上下文传播解决的是"时间维度"的信息丢失——提取的函数怎么传播到下一次会话。但还有一个"空间维度"的问题:同一时刻运行的多个子Agent,怎么避免彼此重复造轮子?
问题四:并行子Agent各自为政,写出重复代码怎么办?
这是一个2026年才被充分认知到严重性的问题。在rjmurillo/ai-agents项目中有一个被充分记录的生产事故:5个并行Agent独立工作,产生了489行几乎完全相同的样板代码,分散在12个文件中。每个Agent都因为不知道其他Agent的输出而分别重新实现了相同的辅助逻辑。
MSR 2026学术会议上的一篇论文对7851个AI Agent创建的PR做了代码克隆分析,使用NiCad克隆检测器发现:497个PR中共有28425个代码克隆,其中320个PR存在"跨提交重复克隆"——意味着重复在审查周期内持续存在,没有被一次性清除。
这个问题有三个层次的解法。注意:问题一的子Agent隔离解决的是"减少主Agent上下文负担"——每个子Agent携带不同工具集,任务互不重叠;而这里要解决的是"并行Agent同时工作时的输出重叠"——它们的任务可能涉及同一批文件,但彼此不知道对方在写什么。二者的隔离方向不同:前者是垂直切分(按领域),后者是水平切分(按并行任务)。
层次一:事后合并——为并行工作流增加整合阶段
在并行Agent执行完毕后、PR创建之前,插入一个"整合Agent"(Consolidation Agent)。这个Agent收集所有输出,扫描跨文件的相同代码块(≥3行同时在≥2个文件中),提取为共享helper,然后更新所有引用。这是当前最主流的解法,成本最低,也最容易实施。
层次二:共享上下文——让并行Agent知道彼此的职责边界
在分发任务时,给每个Agent提供一份"并行任务地图"——列出其他Agent各自负责的模块和文件范围,以及它们预期会创建的工具函数声明。这样每个Agent在生成代码时至少知道自己不应该重新发明属于其他Agent职责范围的工具。
层次三:跨Agent共享工具注册表——运行时同步
最理想但也最复杂的方案:维护一个共享的工具注册表,Agent在生成代码时实时检查和写入。当一个Agent提取了新函数,注册表即时更新,其他Agent可以在下一次工具调用前读取到新条目。这需要一个支持并发读写的注册表后端,当前在Claude Code的子Agent架构中尚未原生支持——但可以通过文件级别的共享存储(如共享的.claude/tool-registry.md配合git merge策略)来近似实现。
在实践中,大多数团队从层次一开始:先加上整合阶段,看重复率是否降到可接受水平。如果并行工作流的规模持续扩大(5+ Agent并行),再考虑层次二和层次三。
前面四个问题都围绕"信息如何进入上下文"展开。但还有一个更根本的威胁:已经进入上下文的信息,会随着会话变长而逐渐"腐烂"——模型开始遗忘早期指令,约束悄悄失效。
问题五:上下文老化后,关键架构约束丢失怎么办?
AI编码会话的生命周期中有一个规律性的失败模式:会话开始时代理严格遵守规则,在第15-20轮交互后开始出现违反,到第30轮后明显遗忘关键约束。这不是模型能力衰减,而是"上下文腐烂"(Context Rot)——随着工具输出、对话历史、中间产物的积累,有效上下文逐渐逼近窗口极限,模型注意力开始从早期指令漂移到最近的输出上。
研究数据(基于Claude等主流长上下文模型的基准测试):在100万token的上下文窗口中,当占用达到约70万token时,检索准确率会跌到约30%。而一个标准的Claude Code会话在20轮交互后可以轻易积累15-20万token。
策略一:把关键约束从对话历史中移出,放入持久化上下文文件
对话历史会随/compact被摘要压缩,但CLAUDE.md和AGENTS.md在每次会话启动时重新读取。因此,任何"AI必须一直记住"的约束,都不应该只出现在对话消息中——必须写入持久化文件。Mitchell Hashimoto的失败日志模式之所以有效,正是因为它把约束固化在了不会被压缩的文件中。
策略二:主动压缩而非被动退化
OpenDev在2026年发表的论文中提出了"自适应上下文压缩":不是等到上下文爆满再被动压缩,而是在预设的阈值(70% → 80% → 85% → 90% → 99%)逐步触发不同强度的压缩策略。最终实现了峰值上下文降低约54%,会话长度从15-20轮扩展到30-40轮。
ACC(自适应上下文压缩)五级策略
70%利用率 → 警告提示,建议主动/clear
80%利用率 → 遮罩旧工具输出(观测遮罩)
85%利用率 → 快速剪枝低信息量内容
90%利用率 → 激进遮罩,仅保留关键工具结果
99%利用率 → LLM摘要压缩,保留核心约束信号
策略三:/clear + 状态恢复
配合问题三中提到的 SessionStart 钩子与工具注册表机制,一个更主动的策略是:在关键约束都已写入持久化文件后,主动执行 /clear 清空对话历史,然后依靠钩子自动重读注册表和状态文件来重建上下文。这并非妥协——它把 /clear 从清空上下文的应急操作变成了防止上下文腐烂的主动策略。
核心原则:把约束放进不会被压缩的文件,把状态放进可以被重读的文件,让/clear成为功能而不是失败。
问题六:这些问题有没有统一的解决框架?
前面的五个问题不是孤立的——它们共享同一个根因:AI编码代理的内存是有限的、非持久的、且在长时间运行中不可靠。所有策略本质上都在做同一件事:把关键信息从AI的短期记忆(对话上下文)迁移到外部持久化存储(文件系统),然后通过机制保证AI在需要时能找回它们。
2026年的实践已经收敛出以下几个相互补充的机制模块:
AI-First架构约束的五个机制模块
这五个模块不是"全都要"——它们适合不同的项目规模和复杂度。一个小型项目可能只需要持久上下文文件 + 上下文生命周期管理。一个中型项目加上自动上下文传播和写审分离。一个多人协作的大项目才需要全套五个模块。关键是按需引入,从最痛的地方开始。
核心原则
上述六个问题可归纳为两条核心原则:
1. 把约束从对话历史移到持久化文件——工具注册表、架构文档、CLAUDE.md,让AI在每次会话启动时自动重读,而不是依赖对话记忆。
2. 用独立的审查/整合Agent打破"自己写自己审"的盲区——编码Agent的判断力在同一上下文中会衰减,需要写审分离来兜底。
AI-First任重道远,本文真实抛砖,望同行能分享高见!再次与各位同僚共勉!
夜雨聆风