第2462篇:AI增强的技术决策——用LLM辅助架构决策的工程实践
2026/4/30大约 6 分钟
第2462篇:AI增强的技术决策——用LLM辅助架构决策的工程实践
适读人群:架构师、技术负责人、高级工程师 | 阅读时长:约15分钟 | 核心价值:把技术选型和架构决策的过程结构化,用LLM提供更全面的视角
有一次我们要选一个消息队列,团队里一半人说Kafka,一半人说RabbitMQ。争了两个小时,最后的决策是:用Kafka,理由是"大家比较熟悉"。
这个决策过程让我觉得不舒服。不是因为结论错了,而是因为决策的过程不严谨——没有明确决策标准,没有对比各方案的关键维度,没有记录"为什么不选RabbitMQ"。
两年后,新来的同学问"为什么用Kafka不用RabbitMQ",大家都不记得当时的原因了。
技术决策的常见问题
好的技术决策应该是:
- 标准先于方案:先定好决策标准,再对比方案,而不是先有立场再找理由
- 全面性:考虑到所有重要的权衡维度,不遗漏关键因素
- 可追溯:记录决策时的约束条件、权衡过程和最终理由
- 可挑战:当约束条件变化时,之前的决策可以被重新审视
LLM能帮助的地方主要是全面性——确保你考虑到了所有重要的维度,并且对每个维度都有具体的分析。
ADR(架构决策记录)模板化
首先,把架构决策过程标准化:
@Component
public class ADRGenerator {
private final ChatClient chatClient;
/**
* 给定技术问题描述,生成结构化的ADR(Architecture Decision Record)框架
* 以及各备选方案的多维度对比分析
*/
public ADRDraft generateDraft(TechDecisionRequest request) {
String prompt = buildADRPrompt(request);
ChatResponse response = chatClient.call(new Prompt(
List.of(
new SystemMessage(ADR_SYSTEM_PROMPT),
new UserMessage(prompt)
),
OpenAiChatOptions.builder()
.withModel("gpt-4o")
.withTemperature(0.3f)
.withResponseFormat(new ResponseFormat(ResponseFormat.Type.JSON_OBJECT))
.build()
));
return parseADRDraft(response.getResult().getOutput().getContent());
}
private String buildADRPrompt(TechDecisionRequest request) {
return """
我们面临一个技术决策:
**决策背景**: %s
**约束条件**: %s
**备选方案**: %s
**团队现状**: %s
**预期规模**: %s
请生成完整的ADR草稿,包括:
1. 决策标准(基于约束条件和目标提炼)
2. 每个备选方案的详细分析(优缺点、适用场景、风险)
3. 各方案按决策标准的评分矩阵
4. 推荐方案及理由
5. 需要人工判断的关键问题清单
""".formatted(
request.getBackground(),
request.getConstraints(),
String.join(", ", request.getAlternatives()),
request.getTeamContext(),
request.getExpectedScale()
);
}
private static final String ADR_SYSTEM_PROMPT = """
你是一个经验丰富的软件架构师,帮助团队做出有据可查的技术决策。
你的职责是:
1. 提炼客观的决策标准(不偏向任何方案)
2. 全面分析每个备选方案,包括通常被忽略的运维复杂性、学习曲线、社区活跃度等
3. 明确指出各方案的隐性成本和风险
4. 识别决策中的不确定因素,提出需要进一步调研的问题
返回JSON格式:
{
"decisionTitle": "决策标题",
"context": "决策背景整理",
"decisionCriteria": [{"criterion": "标准名", "weight": "HIGH/MEDIUM/LOW", "description": "..."}],
"alternatives": [
{
"name": "方案名",
"pros": ["..."],
"cons": ["..."],
"hiddenCosts": ["..."],
"risks": ["..."],
"scores": {"criterion1": 8, "criterion2": 6}
}
],
"recommendation": {"choice": "方案名", "reasoning": "..."},
"openQuestions": ["需要进一步调研的问题"],
"decisionConstraints": ["本决策的前提假设"]
}
""";
}决策评估矩阵
@Service
public class DecisionMatrixEvaluator {
/**
* 基于ADR草稿,计算加权评分,给出量化推荐
*/
public DecisionMatrix evaluate(ADRDraft draft) {
List<DecisionCriterion> criteria = draft.getCriteria();
List<AlternativeAnalysis> alternatives = draft.getAlternatives();
// 计算权重
Map<String, Double> weights = normalizeWeights(criteria);
// 计算每个方案的加权总分
Map<String, Double> weightedScores = new LinkedHashMap<>();
for (AlternativeAnalysis alt : alternatives) {
double totalScore = 0;
Map<String, Double> criterionScores = new LinkedHashMap<>();
for (DecisionCriterion criterion : criteria) {
double rawScore = alt.getScore(criterion.getName());
double weight = weights.get(criterion.getName());
double weightedScore = rawScore * weight;
criterionScores.put(criterion.getName(), rawScore);
totalScore += weightedScore;
}
weightedScores.put(alt.getName(), totalScore);
}
// 找出最高分方案
String recommended = weightedScores.entrySet().stream()
.max(Map.Entry.comparingByValue())
.map(Map.Entry::getKey)
.orElseThrow();
return DecisionMatrix.builder()
.criteria(criteria)
.weights(weights)
.alternatives(alternatives)
.weightedScores(weightedScores)
.quantitativeRecommendation(recommended)
.build();
}
private Map<String, Double> normalizeWeights(List<DecisionCriterion> criteria) {
Map<String, Integer> rawWeights = criteria.stream()
.collect(toMap(
DecisionCriterion::getName,
c -> switch (c.getWeight()) {
case HIGH -> 3;
case MEDIUM -> 2;
case LOW -> 1;
}
));
int total = rawWeights.values().stream().mapToInt(Integer::intValue).sum();
return rawWeights.entrySet().stream()
.collect(toMap(
Map.Entry::getKey,
e -> (double) e.getValue() / total
));
}
}实际使用示例
// 使用示例:选择消息队列
TechDecisionRequest request = TechDecisionRequest.builder()
.background("我们的订单系统需要引入消息队列,用于订单状态变更通知下游服务")
.constraints("""
- 团队规模:5人,2人有Kafka经验,1人有RabbitMQ经验
- 预算:有限,倾向于使用云服务(降低运维成本)
- 消息延迟要求:P99 < 500ms
- 消息量:峰值5000/s,日均100万
- 可靠性要求:消息不丢失,最多一次重复消费
""")
.alternatives(List.of("Apache Kafka", "RabbitMQ", "AWS SQS", "RocketMQ"))
.teamContext("Java技术栈,Spring Boot,部署在AWS上")
.expectedScale("初期100万/天,一年后可能增长到1000万/天")
.build();
ADRDraft draft = adrGenerator.generateDraft(request);
DecisionMatrix matrix = evaluator.evaluate(draft);
// LLM的分析结果会包含通常被忽略的维度,比如:
// - Kafka的运维复杂性(需要Zookeeper/KRaft,分区规划等)
// - SQS的消息顺序限制(标准队列不保序)
// - 各方案的云服务成本对比
// - 数据保留策略的差异决策记录的长期价值
技术决策最容易被忽视的问题是:决策记录的维护。
很多团队用Confluence写了ADR,但三个月后没人维护,当初的约束条件变了,原来的决策可能已经不适用了,但大家还在按老决策行事,因为"文档是这么写的"。
我们的做法是给ADR加上"Review触发条件":
public class ADR {
private String id;
private String title;
private Instant createdAt;
private DecisionStatus status; // PROPOSED/ACCEPTED/SUPERSEDED/DEPRECATED
// 关键:定义什么情况下这个决策需要被重新审视
private List<String> reviewTriggers;
// 例如:
// - "当月消息量超过1000万/天时"
// - "当团队规模增加到10人以上时"
// - "当AWS SQS增加FIFO队列的吞吐量限制提升时"
private String supersededBy; // 如果这个决策被新决策取代,链接到新ADR
}LLM做技术决策的边界
有几类决策,LLM的建议要格外谨慎:
1. 和公司内部情况强相关的决策:比如"我们是否自建监控还是用SaaS",这涉及到公司预算、现有基础设施、运维团队规模,LLM没有这些信息,只能给通用建议。
2. 有强烈政治因素的决策:比如"是否把某个功能做成平台组的中台服务还是业务团队自己做",这背后有组织结构和资源竞争,不是纯技术问题。
3. 依赖预测未来的决策:比如"我们5年后的数据量会有多大",LLM可以给出分析框架,但具体数字要业务方提供,不能靠猜。
LLM最适合做的是:帮你发现你没想到的维度,和给你提供各方案的通用利弊分析。最终的判断,还是要人来做。
