智能架构决策记录:用 AI 管理 ADR 的编写、检索与演进
一、架构决策记录的困境:写了但没人看,看了但已过期
ADR(Architecture Decision Record)是记录架构决策的轻量级文档,通常包含背景、决策、后果和相关上下文。在实践中,ADR 面临三个核心问题:
第一,编写门槛高。写好一份 ADR 需要梳理背景、列举备选方案、论证决策理由、描述后果,这需要相当的写作能力和时间投入。开发者在紧张的迭代节奏中,往往推迟甚至跳过 ADR 的编写。
第二,检索困难。随着决策数量增长(一个成熟项目可能有上百个 ADR),团队新成员不知道有哪些决策存在,现有成员在类似场景需要做决策时也难以找到历史参考。
第三,过期无人维护。ADR 是"快照"性质的文档——记录的是"当时为什么做了这个决定"。架构演进后,原来的决策可能已被推翻,但旧 ADR 仍原样存在,误导后来者。
AI 在这三个问题上都有明确的切入点:辅助编写(将讨论记录转化为标准 ADR)、智能检索(基于语义搜索历史决策)、自动检测过期(追踪代码变更与 ADR 描述的偏差)。
二、AI 增强 ADR 的三阶段架构
三阶段环环相扣:先解决"写"的问题,再解决"用"的问题(检索),最后解决"维护"的问题(演进追踪)。
三、工程实现:ADR 全生命周期管理
3.1 ADR 数据模型
// adr/types.ts export enum ADRStatus { PROPOSED = 'proposed', // 提议中 ACCEPTED = 'accepted', // 已接受 DEPRECATED = 'deprecated', // 已废弃(有新决策取代) SUPERSEDED = 'superseded', // 已取代 REJECTED = 'rejected' // 被拒绝 } export interface ADR { id: number; title: string; status: ADRStatus; date: string; // 核心内容 context: string; // 决策背景——是什么问题驱动了这个决策 decision: string; // 决策内容——我们决定做什么 alternatives: string[]; // 备选方案——考虑过但未选择的方案 consequences: { // 后果——这个决策带来的影响 positive: string[]; // 正面后果 negative: string[]; // 负面后果 risks: string[]; // 需要注意的风险 }; // 关联 supersededBy?: number[]; // 被哪些 ADR 取代 supersedes?: number[]; // 取代了哪些 ADR relatedDecisions?: number[]; // 相关知识 tags: string[]; // 标签(用于分类检索) affectedComponents: string[];// 影响的代码组件(用于过期检测) // AI 增强字段 embedding?: number[]; // 文本嵌入向量 aiGeneratedSummary?: string; // AI 生成的摘要 lastVerifiedAt?: string; // 最后一致性验证时间 } export interface ADRSearchQuery { keywords?: string; tags?: string[]; status?: ADRStatus[]; component?: string; dateRange?: { start: string; end: string }; similarityThreshold?: number; }3.2 AI 辅助 ADR 生成
将讨论内容和代码变更为输入,AI 生成 ADR 草稿:
// adr/ADRGenerator.ts interface GeneratorInput { source: 'discussion' | 'commit' | 'design_doc'; title: string; rawContent: string; // 原始讨论/commit/文档内容 relatedFiles?: string[]; proposedTags?: string[]; } class ADRGenerator { private aiEndpoint: string; constructor(aiEndpoint: string) { this.aiEndpoint = aiEndpoint; } /** * 从讨论内容生成 ADR 草稿 */ async generateFromDiscussion(input: GeneratorInput): Promise<Partial<ADR>> { const prompt = ` 你是一位架构决策记录(ADR)专家。根据以下团队讨论,生成一份标准 ADR。 讨论标题:${input.title} 讨论内容: ${input.rawContent} 相关文件:${input.relatedFiles?.join(', ') || '无'} 请输出 JSON 格式的 ADR,包含: 1. context: 清晰的决策背景(用技术语言描述) 2. decision: 最终做出的决策(一句话 + 详细说明) 3. alternatives: 至少列出 2 个被考虑但拒绝的备选方案及拒绝原因 4. consequences.positive: 这个决策带来的正面影响(3-5条) 5. consequences.negative: 负面影响或技术债(2-3条,诚实描述) 6. consequences.risks: 需要注意的风险 7. tags: 5-8 个技术标签 8. affectedComponents: 可能受影响的代码模块/组件 输出格式: \`\`\`json { ... } \`\`\` `; try { const response = await this.callAIModel(prompt); const parsed = this.parseADRResponse(response); return { title: input.title, status: ADRStatus.PROPOSED, date: new Date().toISOString().split('T')[0], context: parsed.context, decision: parsed.decision, alternatives: parsed.alternatives, consequences: parsed.consequences, tags: parsed.tags, affectedComponents: parsed.affectedComponents, aiGeneratedSummary: parsed.summary }; } catch (error) { console.error('ADR 生成失败:', error); throw new Error(`ADR 生成异常: ${error instanceof Error ? error.message : '未知错误'}`); } } /** * 从 Commit 信息提炼 ADR */ async generateFromCommit( commitMessage: string, diffSummary: string, relatedFiles: string[] ): Promise<Partial<ADR>> { const prompt = ` 你是一位架构决策记录(ADR)专家。根据以下代码变更,判断是否需要创建一条 ADR。 Commit 信息:${commitMessage} 变更概要:${diffSummary} 变更文件:${relatedFiles.join(', ')} 如果这不是一个架构级别的决策(只是 bug 修复、small feature),请返回:{"skip": true, "reason": "..."} 如果是架构决策,请生成 ADR,格式同上。 `; const response = await this.callAIModel(prompt); const parsed = this.parseADRResponse(response); if (parsed.skip) { return { title: 'SKIP: ' + parsed.reason } as Partial<ADR>; } return { title: commitMessage, status: ADRStatus.PROPOSED, date: new Date().toISOString().split('T')[0], context: parsed.context, decision: parsed.decision, alternatives: parsed.alternatives, consequences: parsed.consequences, tags: parsed.tags, affectedComponents: relatedFiles }; } private async callAIModel(prompt: string): Promise<string> { const response = await fetch(this.aiEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ model: 'gpt-4', messages: [{ role: 'user', content: prompt }], temperature: 0.3, max_tokens: 3000 }) }); if (!response.ok) { throw new Error(`AI 模型调用失败: ${response.status}`); } const data = await response.json(); return data.choices[0].message.content; } private parseADRResponse(response: string): Record<string, any> { const jsonMatch = response.match(/```json\n?([\s\S]*?)\n?```/); const jsonStr = jsonMatch ? jsonMatch[1] : response; try { return JSON.parse(jsonStr); } catch (error) { console.error('ADR JSON 解析失败:', error); throw new Error('AI 生成的 ADR 格式无效'); } } }3.3 语义检索引擎
基于向量相似度的 ADR 搜索:
// adr/ADRSearcher.ts class ADRSearcher { private adrs: ADR[] = []; /** * 简单的 TF-IDF 文本相似度搜索 * 生产环境建议使用向量数据库(如 Qdrant、Milvus) */ search(query: ADRSearchQuery, allADRs: ADR[]): ADR[] { let results = [...allADRs]; // 状态过滤 if (query.status && query.status.length > 0) { results = results.filter(adr => query.status!.includes(adr.status)); } // 标签过滤 if (query.tags && query.tags.length > 0) { results = results.filter(adr => query.tags!.some(tag => adr.tags.some(t => t.toLowerCase().includes(tag.toLowerCase()) )) ); } // 组件过滤 if (query.component) { results = results.filter(adr => adr.affectedComponents.some(c => c.toLowerCase().includes(query.component!.toLowerCase()) ) ); } // 关键词相似度排序 if (query.keywords) { const keywords = query.keywords.toLowerCase().split(/\s+/); const scored = results.map(adr => { const text = [ adr.title, adr.context, adr.decision, ...adr.tags, ...adr.affectedComponents ].join(' ').toLowerCase(); let score = 0; keywords.forEach(word => { // 简单的词频分数 const regex = new RegExp(word.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'), 'gi'); const matches = text.match(regex); score += matches ? matches.length : 0; }); // TF-IDF 风格的归一化(除以文档长度) const normalizedScore = score / Math.log(text.length + 1); return { adr, score: normalizedScore }; }); scored.sort((a, b) => b.score - a.score); results = scored.map(s => s.adr); } return results; } /** * 基于决策内容的相似推荐 * "当前场景与历史上的哪些决策最相似" */ findSimilarDecisions( currentContext: string, allADRs: ADR[], limit: number = 5 ): ADR[] { // 将当前上下文拆分为词频向量 const queryTerms = this.tokenize(currentContext); const scored = allADRs .filter(adr => adr.status === ADRStatus.ACCEPTED) .map(adr => { const adrText = [ adr.context, adr.decision, ...adr.alternatives, ...adr.tags ].join(' '); const adrTerms = this.tokenize(adrText); // 计算余弦相似度的简化近似(Jaccard相似度) const intersection = queryTerms.filter(t => adrTerms.has(t)).length; const querySize = queryTerms.length; const adrSize = adrTerms.size; // Jaccard + TF 加权 const jaccard = intersection / (querySize + adrSize - intersection); return { adr, score: jaccard }; }) .filter(s => s.score > 0.05) .sort((a, b) => b.score - a.score) .slice(0, limit); return scored.map(s => s.adr); } private tokenize(text: string): string[] { return text .toLowerCase() .replace(/[^\w\s]/g, ' ') .split(/\s+/) .filter(w => w.length > 1); } }3.4 决策过期检测
自动检查 ADR 是否与当前代码状态一致:
// adr/ADRConsistencyChecker.ts interface ConsistencyResult { adrId: number; isConsistent: boolean; issues: Array<{ type: 'file_missing' | 'pattern_violation' | 'deprecated_usage' | 'superseded'; description: string; severity: 'warning' | 'error'; }>; lastVerified: string; } class ADRConsistencyChecker { /** * 检查单个 ADR 与代码库的一致性 */ async checkConsistency(adr: ADR, codebaseRoot: string): Promise<ConsistencyResult> { const issues: ConsistencyResult['issues'] = []; // 检查 1: affectedComponents 是否还存在 for (const component of adr.affectedComponents) { const filePath = resolve(codebaseRoot, component); try { if (!existsSync(filePath)) { issues.push({ type: 'file_missing', description: `ADR 引用的文件已不存在: ${component}。可能决策已不再适用`, severity: 'warning' }); } } catch (error) { console.warn(`文件检查失败 [${component}]:`, error); } } // 检查 2: 被取代的 ADR 状态是否正确 if (adr.supersedes && adr.supersedes.length > 0) { for (const supersededId of adr.supersedes) { issues.push({ type: 'superseded', description: `此 ADR 声称取代 ADR-${supersededId},请确认该引用仍然有效`, severity: 'warning' }); } } // 检查 3: 日期过期检测 const adrDate = new Date(adr.date); const now = new Date(); const daysSinceCreation = (now.getTime() - adrDate.getTime()) / (1000 * 60 * 60 * 24); if (daysSinceCreation > 365) { issues.push({ type: 'deprecated_usage', description: `ADR 创建于 ${Math.floor(daysSinceCreation)} 天前,建议审查是否仍然适用`, severity: 'warning' }); } // 检查 4: 状态完整性 if (adr.status === ADRStatus.DEPRECATED && !adr.supersededBy?.length) { issues.push({ type: 'pattern_violation', description: 'ADR 已废弃但未标注被哪位 ADR 取代', severity: 'error' }); } return { adrId: adr.id, isConsistent: issues.length === 0, issues, lastVerified: new Date().toISOString() }; } /** * 批量检查所有 ADR */ async checkAll(adrs: ADR[], codebaseRoot: string): Promise<ConsistencyResult[]> { const results: ConsistencyResult[] = []; for (const adr of adrs) { const result = await this.checkConsistency(adr, codebaseRoot); results.push(result); } // 输出汇总 const consistentCount = results.filter(r => r.isConsistent).length; const issuesCount = results.reduce((sum, r) => sum + r.issues.length, 0); console.log( `[ADR 一致性检查] ${consistentCount}/${adrs.length} 条 ADR 一致, ` + `共 ${issuesCount} 个问题` ); return results; } }3.5 ADR 模板管理
提供标准 ADR 模板,并支持 AI 扩展:
// adr/template-engine.ts class ADRTemplateEngine { /** * 基于决策类型推荐模板 */ getTemplate(decisionType: string): string { const templates: Record<string, string> = { 'technology_selection': this.technologySelectionTemplate(), 'architecture_pattern': this.architecturePatternTemplate(), 'api_design': this.apiDesignTemplate(), 'deprecation': this.deprecationTemplate(), 'default': this.defaultTemplate() }; return templates[decisionType] || templates.default; } private defaultTemplate(): string { return `# ADR-{id}: {title} - **状态**: {status} - **日期**: {date} - **标签**: {tags} ## 背景 {context} ## 决策 {decision} ## 备选方案 {alternatives} ## 后果 ### 正面影响 {positive_consequences} ### 负面影响 {negative_consequences} ### 风险 {risks} ## 关联决策 - 取代: {supersedes} - 被取代: {supersededBy} - 相关: {relatedDecisions} ## 受影响组件 {affectedComponents} `; } private technologySelectionTemplate(): string { return this.defaultTemplate() + ` ## 选型评估矩阵 | 方案 | 性能 | 社区活跃度 | 学习曲线 | 许可协议 | 结论 | |------|------|-----------|---------|---------|------| | | | | | | | `; } private architecturePatternTemplate(): string { return this.defaultTemplate() + ` ## 架构变更影响分析 - **变更范围**: {scope} - **迁移路径**: {migrationPath} - **向后兼容性**: {backwardCompatibility} `; } private apiDesignTemplate(): string { return this.defaultTemplate() + ` ## API 契约变更 \`\`\` // 旧接口(如适用) {old_api} // 新接口 {new_api} \`\`\` `; } private deprecationTemplate(): string { return this.defaultTemplate() + ` ## 废弃计划 - **废弃时间**: {deprecationDate} - **移除时间**: {removalDate} - **迁移指南**: {migrationGuide} `; } }四、AI + ADR 的边界与陷阱
4.1 AI 的"假决策"风险
AI 可能会从讨论内容中提取出并不存在的"决策"。团队成员可能只是随口讨论了一个想法,AI 却将其生成为正式的 ADR。这要求生成流程中必须有"人工确认"环节,不可全自动归档。
4.2 过期检测的粒度限制
基于文件存在性和创建日期的过期检测是粗粒度的。真正的"ADR 与代码不一致"需要在架构模式层面进行检查(如"ADR 说使用分层架构,但代码中出现了跨层调用"),这需要更深入的架构合规工具配合。
4.3 语义搜索的冷启动
向量嵌入的质量依赖充足的 ADR 数据。在 ADR 数量 < 20 时,语义搜索的准确率可能低于关键词搜索。建议在初期同时提供两种搜索方式。
4.4 ADR 的文化建设比工具更重要
AI 辅助可以让 ADR 的编写更容易,但团队是否愿意写 ADR、是否会在做决策时查阅 ADR,取决于团队的工程文化。工具是辅助,文化是根基。
五、总结
智能架构决策记录通过 AI 的能力,降低了 ADR 全生命周期的摩擦:辅助编写降低了"写"的门槛,语义检索提升了"找"的效率,过期检测避免了"用错"的风险。
落地建议:
- 即日开始:为当前最重要的 3-5 个架构决策补写 ADR,建立团队写作惯性
- AI 辅助:将讨论记录和设计文档输入 AI 生成草稿,开发者只需审核修正
- 语义搜索:当 ADR 累积到 20+ 条时,引入 Embedding + 向量搜索
- 持续检测:每季度运行一次一致性检查,将过期的 ADR 标记或更新
ADR 不是多写的负担,而是少犯错的保障。当团队在决策时能快速找到"上次我们为什么选了 X 而非 Y"时,当初花在写 ADR 上的时间就获得了百倍回报。
架构决策的真正价值不在做出决定的瞬间,而在未来某天,有人问"为什么是这样设计的"时,你能指向一条 ADR 说:这是我们的思考过程。