智能架构决策记录:用 AI 管理 ADR 的编写、检索与演进
智能架构决策记录用 AI 管理 ADR 的编写、检索与演进一、架构决策记录的困境写了但没人看看了但已过期ADRArchitecture 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): PromisePartialADR { 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[] ): PromisePartialADR { 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 PartialADR; } 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): Promisestring { 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): Recordstring, 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): PromiseConsistencyResult { 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): PromiseConsistencyResult[] { 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: Recordstring, 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 说这是我们的思考过程。