← 返回蜂巢洞察

责任链设计模式:逐步解耦复杂的业务规则,每次只处理一个逻辑环节

任何系统在某个阶段都会出现这样一种情况:存在某个没人愿意去修改的功能。 一开始,这种问题很轻微:只是一个简单的验证逻辑,这里加一个if语句,那里再加一个。但随着需求的变化,越来越多的条件被添加进去,相应的功能代码也变得越来越长。有人还会留下注释,提醒别人“在修改之前一定要先阅读整个代码”。于是,这个功能就变成了新开发者入职培训中的必讲内容。 当复杂的业务规则毫无秩序地堆积在一起时,就会发生这种情况。 责任链模式正是为了解决这类问题而存在的。它不是让一个方法包揽所有事务,而是通过一系列专门处理特定任务的组件来解决问题。每个组件只负责执行其中一条业务规则,并判断请求是否符合该规则;如果符合,则将请

任何系统在某个阶段都会出现这样一种情况:存在某个没人愿意去修改的功能。

一开始,这种问题很轻微:只是一个简单的验证逻辑,这里加一个if语句,那里再加一个。但随着需求的变化,越来越多的条件被添加进去,相应的功能代码也变得越来越长。有人还会留下注释,提醒别人“在修改之前一定要先阅读整个代码”。于是,这个功能就变成了新开发者入职培训中的必讲内容。

当复杂的业务规则毫无秩序地堆积在一起时,就会发生这种情况。

责任链模式正是为了解决这类问题而存在的。它不是让一个方法包揽所有事务,而是通过一系列专门处理特定任务的组件来解决问题。每个组件只负责执行其中一条业务规则,并判断请求是否符合该规则;如果符合,则将请求传递给下一个组件;如果不符合,流程就会在此处终止。

没有任何一个组件会知道整个责任链的长度,也不知道自己之前或之后还有哪些组件在处理请求。每个组件只需完成自己的任务,然后决定是继续处理请求还是将其转发给下一个组件。

目录

什么是责任链模式?

责任链模式是一种行为设计模式,它允许你将请求沿着一系列处理组件进行传递。链条中的每个组件都会决定是直接处理该请求并终止整个流程,还是将其转发给下一个组件。

这种模式为生产环境中的系统设计提供了三个重要的优势:

首先,它将请求的发起者与接收者分离开来。负责启动交易验证的代码并不需要知道最终会由哪个组件来处理或终止这个流程,它只需要启动责任链即可。

其次,每个组件都只负责执行一条特定的业务规则。当这条规则发生变化时,只需要修改相应的组件代码,其他部分都不会受到影响。

第三,这种方式使得处理流程具有可配置性。你可以添加、删除或重新排列处理程序,而无需修改现有的处理代码。当出现新的合规要求时,只需为处理流程添加一个新的处理程序即可,而不需要在现有方法中创建新的分支结构。

它所解决的问题

以下是在没有采用这种模式时,交易验证的过程:

void handleTransaction(Transaction transaction) {
  if (transaction.isFraud) {
    // 阻止该交易
    return;
  }

  if (!transaction.isKycVerified) {
    // 拒绝该交易
    return;
  }

  if (!transaction.isAccountActive) {
    // 拒绝该交易
    return;
  }

  if (transaction.amount < 50000) {
    // 需要初级管理人员的批准
    return;
  }

  if (transaction.amount <= 200000) {
    // 需要中级管理人员的批准
    return;
  }

  if (transaction.amount <= 1000000) {
    // 需要经理的批准
    return;
  }

  // 需要高管的最终批准
}

目前这种机制运作得很好。但明天,合规团队可能会添加信用评分检查;欺诈检测团队可能会增加交易速度审核;法律团队可能会加入制裁筛查环节;产品经理也可能设置每日交易限额限制。

每条新的规则都会被添加到这个方法中。随着规则的增多,该方法中的代码行数会逐渐增加——从五十行增加到一百行,甚至更多。这些条件之间的交互关系往往很难理解。进行测试时,需要验证所有可能的组合情况;如果其中一个条件存在错误,就可能会影响到下面的所有条件。

“责任链模式”指出:每条规则都应该有自己专门的处理程序。将这些处理程序连接起来,形成一条链条。启动这条链条的方法不需要了解所有的规则,它只需要启动链条然后退出即可。

核心组件

这种模式由三个基本组成部分构成。

1. 处理程序接口

这是链条中每一个处理程序都必须实现的契约。它规定了处理请求的方法,并提供了将各个处理程序连接起来的机制。所有的具体处理程序都会继承或实现这个接口。

2. 具体处理程序

这些才是真正的实现代码。每个具体处理程序只负责执行一条业务规则,它会检查请求是否符合这条规则。如果规则不满足要求,就会停止整个流程并处理错误;如果规则通过,则会调用下一个处理程序并将请求继续传递下去。

3. 责任链

“责任链”并不是一种类,而是指使用setNext方法将各个处理程序连接起来形成的结构。你可以在应用程序的配置文件中或通过依赖注入机制来构建这条链条。这些处理程序的连接顺序决定了它们执行的先后顺序。

第一个实际案例:交易审批流程

某家金融科技平台每天要处理成千上万的交易。在任何一笔交易获得批准之前,它都必须通过多个验证和审批环节。每个环节都是独立的,且各自负责特定的任务。

  1. 欺诈检测:这笔交易是否被标记为可疑交易?

  2. 身份验证:用户是否完成了身份识别流程?

  3. 账户状态检查:该账户是否处于活跃状态且没有逾期未付的债务?

  4. 审批权限判断:哪一级别的管理人员有权批准这笔交易?

交易模型

class Transaction {
  final num amount;
  final bool isFraud;
  final bool isKycVerified;
  final bool isAccountActive;

  const Transaction({
    required this.amount,
    required this.isFraud,
    required this.isKycVerified,
    required this.isAccountActive,
  });
}

交易模型包含了每个处理程序做出决策所需的所有数据。这些数据仅属于交易模型本身,其他任何地方都没有这些数据。其中并不包含任何验证逻辑。

处理程序接口

abstract class TransactionHandler {
  TransactionHandler? _next;

  void setNext(TransactionHandler handler) {
    _next = handler;
  }

  void handle(Transaction transaction);

  void passToNext(Transaction transaction) {
    if (_next != null) {
      _next!.handle(transaction);
    } else {
      print('链式处理流程已到达末端,没有处理程序会阻止该交易的继续执行');
    }
  }
}

TransactionHandler是所有处理程序都必须实现的接口。

_next这个属性可以被设置为null,因为链式处理流程中的最后一个处理程序并没有下一个处理程序。将其设为可空并在调用之前进行检查,可以避免在到达流程末端时出现空指针异常。

setNext用于将一个处理程序与下一个处理程序连接起来。在构建处理流程链时,需要使用这个方法。

passToNext是一个辅助方法,当某个处理程序根据自身的规则判断交易符合要求时,会调用这个方法。在调用之前,该方法会先检查是否存在下一个处理程序。如果整个处理流程链中没有任何处理程序阻止交易的继续执行,系统会记录这一情况。在真实的系统中,这种情况通常会触发警报,因为这意味着处理流程链的配置存在问题。

具体的处理程序

class FraudHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (transaction.isFraud) {
      print('交易被阻止:检测到欺诈行为');
      return;
    }
    print('欺诈检测通过');
    passToNext(transaction);
  }
}

FraudHandler是第一个处理环节。如果某笔交易被认定为欺诈交易,这个处理程序会打印拒绝消息并终止该交易的处理流程。其他任何处理程序都不会收到这条交易信息。如果欺诈检测通过,FraudHandler会调用passToNext方法,让交易进入下一个处理环节。

class KycHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (!transaction.isKycVerified) {
      print('交易被阻止:身份验证未完成');
      return;
    }
    print('身份验证通过');
    passToNext(transaction);
  }
}

KycHandler负责检查用户是否完成了身份验证。如果用户尚未完成身份验证,整个处理流程就会停止;如果已经完成身份验证,交易才会继续进行。这个处理程序对欺诈检测或账户状态一无所知,它只负责执行自己的那一条规则。

class AccountHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (!transaction.isAccountActive) {
      print('交易被阻止:账户处于非激活状态');
      return;
    }
    print('账户状态检查通过');
    passToNext(transaction);
  }
}

AccountHandler用于检查账户的状态。其处理方式遵循相同的规则:要么停止交易,要么让交易继续进入下一处理环节。

class ApprovalHandler extends TransactionHandler {
  @override
  void handle(Transaction transaction) {
    if (transaction.amount < 50000) {
      print('由初级职员批准 — 金额:${transaction.amount}`);
      return;
    }

    if (transaction.amount <= 200000) {
      print('由中级职员批准 — 金额:${transaction.amount}`);
      return;
    }

    if (transaction.amount <= 1000000) {
      print('由经理批准 — 金额:${transaction.amount}`);
      return;
    }

    print('需提交给高层管理人员审批 — 金额:${transaction.amount}`);
    passToNext(transaction);
  }
}

ApprovalHandler是整个流程中的最终环节。它会根据交易金额将交易路由到相应的审批层级:金额低于50,000元的交易由初级职员批准,金额在50,000元到200,000元之间的交易由中级职员批准,金额超过1,000,000元的交易则需提交给经理审批。

需要注意的是,即使交易金额超过了经理的审批权限,ApprovalHandler仍然可以调用passToNext方法,这样之后就可以在流程中添加高层管理人员的处理环节,而无需对ApprovalHandler进行任何修改。

构建并运行处理流程

void main() {
  // 创建各个处理类对象
  final fraud = FraudHandler();
  final kyc = KycHandler();
  final account = AccountHandler();
  final approval = ApprovalHandler();

  // 构建处理流程链
  fraud.setNext(kyc);
  kyc.setNext(account);
  account.setNext(approval);

  // 进行测试
  print('测试1:欺诈性交易');
  final fraudulentTransaction = Transaction(
    amount: 100000,
    isFraud: true,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraud.handle(fraudulentTransaction);

  print('测试2:未核实的身份信息');
  final unverifiedTransaction = Transaction(
    amount: 50000,
    isFraud: false,
    isKycVerified: false,
    isAccountActive: true,
  );
  fraud.handle(unverifiedTransaction);

  print('测试3:合法交易');
  final validTransaction = Transaction(
    amount: 150000,
    isFraud: false,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraudhandles(validTransaction);

  print('测试4:高额交易');
  final executiveTransaction = Transaction(
    amount: 2000000,
    isFraud: false,
    isKycVerified: true,
    isAccountActive: true,
  );
  fraud.handle(executiveTransaction);
}

测试结果:

测试1:欺诈交易
交易被阻止:检测到欺诈行为

测试2:身份验证未完成
欺诈检测通过
交易被阻止:身份验证尚未完成

测试3:合法交易
欺诈检测通过
身份验证通过
账户状态检查通过
得到中级管理人员批准——金额:150,000

测试4:高级管理层审批的交易
欺诈检测通过
身份验证通过
账户状态检查通过
提交给高级管理层审批——金额:2,000,000
交易流程顺利完成,没有环节阻止该交易的进行

测试1在第一个处理环节就被停止;测试2的欺诈检测通过了,但因身份验证未完成而被阻止;测试3通过了所有验证环节,被正确地提交给相应的审批层级;测试4因为金额超过了经理的审批权限,所以需要上报给高级管理层进行审批。

需要注意的是,调用代码总是从fraud.handle(transaction)开始执行的。它不知道系统中存在多少个处理环节,也不知道哪个环节会阻止整个流程的继续,更不清楚各个审批层级的具体要求。它只是将交易交给第一个处理环节,然后由整个流程来决定后续的处理方式。

如果您的合规团队下个月添加了用户责任担保验证环节,只需创建一个UserIndemnityCheck类,并将其加入处理流程中,其他部分都不需要做任何修改:

final indemnity = UserIndemnityStatus();

fraud.setNext(indemnity);
indemnity.setNext(kyc);
kyc.setNext(account);
account.setNext(approval);

只需要新增一个类并更新处理流程的配置即可,所有现有的处理环节都不会受到影响。

实际案例二:用户注册验证

当用户填写完注册表格并提交后,在账户创建之前,请求必须先通过多个验证步骤。如果有任何一步失败,系统会向用户显示具体的错误信息,说明哪里出了问题。

验证步骤的具体顺序如下:

  1. 电子邮件格式验证:电子邮件的格式是否合法?

  2. 密码强度验证:密码是否符合安全要求?

  3. 年龄验证:用户是否达到注册年龄?

  4. 重复账户检查:使用该电子邮件地址是否已经存在其他账户?

  5. 账户创建:所有验证都通过后,创建账户

注册请求模型

class RegistrationRequest {
  final String email;
  final String password;
  final int age;

  const RegistrationRequest({
    required this.email,
    required this.password,
    required this.age,
  });
}

处理环节接口

abstract class RegistrationHandler {
  RegistrationHandler? _next;

  void setNext(RegistrationHandler handler) {
    _next = handler;
  }

  void handle(RegistrationRequest request);

  void passToNext(RegistrationRequest request) {
    if (_next != null) {
      _next!.handle(request);
    }
  }
}

这种结构与之前的相同。首先是“Nullable”,接着是“SetNext”用于构建处理流程链,最后是“PassToNext”用来将请求向前传递。

具体的处理类

class EmailValidationHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    final emailRegex = RegExp(r'^[\w-\.]+@([\w-]+\.)+[\w-]{2,4}$');

    if (!emailRegex.hasMatch(request.email)) {
      print('注册失败:电子邮件格式无效 — ${request.email}`);
      return;
    }

    print('电子邮件验证通过');
    passToNext(request);
  }
}

EmailValidationHandler使用正则表达式来检查电子邮件的格式。如果格式不正确,它会立即停止处理流程并显示相应的错误信息;如果格式正确,请求就会继续被传递下去。

class PasswordStrengthHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    final password = request.password;
    final hasMinLength = password.length >= 8;
    final hasUppercase = password.contains(RegExp(r'[A-Z]'));
    final hasNumber = password.contains(RegExp(r'[0-9]'));
    final hasSpecialChar = password.contains(RegExp(r'[!@#\$%^&*]');

    if (!hasMinLength || !hasUppercase || !hasNumber || !hasSpecialChar) {
      print('注册失败:密码不符合安全要求');
      print('要求:至少8个字符,包含大写字母、数字和特殊字符');
      return;
    }

    print('密码强度检查通过');
    passToNext(request);
  }
}

PasswordStrengthHandler同时检查密码是否满足四个基本要求:长度至少为8个字符,必须包含大写字母、数字和特殊字符。如果任何一项不满足要求,系统会向用户显示详细的说明;所有条件都符合时,请求才会继续被处理。

class AgeVerificationHandler extends RegistrationHandler {
  final int minimumAge;

  AgeVerificationHandler({this.minimumAge = 18});

  @override
  void handle(RegistrationRequest request) {
    if (request.age < minimumAge) {
      print('注册失败:用户年龄必须至少为 $minimumAge 岁');
      return;
    }

    print('年龄验证通过');
    passToNext(request);
  }
}

AgeVerificationHandler会检查用户的年龄是否达到最低要求。需要注意的是,这个处理类在构造函数中接收了最低年龄参数,因此无需修改代码即可调整这一要求。例如,如果某个产品的最低年龄要求从18岁改为16岁,只需在构建处理流程链时传入不同的值即可。

class DuplicateAccountHandler extends RegistrationHandler {
  final Set existingEmails;

  DuplicateAccountHandler({required this.existingEmails});

  @override
  void handle(RegistrationRequest request) {
    if (existingEmails.contains(request.email)) {
      print('注册失败:已经存在名为 ${request.email} 的账户');
      return;
    }

    print('重复账户检查通过');
    passToNext(request);
  }
}
DuplicateAccountHandler用于检查是否已经存在使用该电子邮件地址创建的账户。在真实的系统中,这通常需要访问数据库或存储系统来查询相关信息。在这里,我们使用一个包含现有电子邮件地址的集合来简化示例,以便重点展示这种处理逻辑。
class AccountCreationHandler extends RegistrationHandler {
  @override
  void handle(RegistrationRequest request) {
    print('所有验证都通过了');
    print('正在为以下用户创建账户:${request.email')); 
    // 调用账户创建服务
    print('账户创建成功');
  }
}
AccountCreationHandler是最终的处理环节。只有当前面的所有处理步骤都成功通过后,这个处理程序才会被执行。在到达这一阶段时,请求已经经过了各个方面的验证;因此,这个处理程序的主要任务就是实际创建账户。

构建并运行处理流程链

void main() {
  final existingEmails = {'existing@seyi.com', 'taken@seyi.com'};

  // 创建各个处理程序对象
  final emailValidation = EmailValidationHandler();
  final passwordStrength = PasswordStrengthHandler();
  final ageVerification = AgeVerificationHandler(minimumAge: 18);
  final duplicateCheck = DuplicateAccountHandler(existingEmails: existingEmails);
  final accountCreation = AccountCreationHandler();

  // 构建处理流程链
  emailValidation.setNext(passwordStrength);
  passwordStrength.setNext(ageVerification);
  ageVerification.setNext(duplicateCheck);
  duplicateCheck.setNext(accountCreation);

  // 进行测试
  print('测试1:无效的电子邮件地址');
  emailValidation.handle(RegistrationRequest(
    email: 'notanemail',
    password: 'SecureP@ss1',
    age: 25,
  });

  print('测试2:密码强度不足');
  emailValidation.handle(RegistrationRequest(
    email: 'user@example.com',
    password: 'weak',
    age: 25,
 ));

  print('测试3:用户未达到法定年龄');
  emailValidation.handle(RegistrationRequest(
    email: 'young@example.com',
    password: 'SecureP@ss1',
    age: 16,
  });

  print('测试4:存在重复账户');
  emailValidation.handle(RegistrationRequest(
    email: 'existing@example.com',
    password: 'SecureP@ss1',
    age: 25,
  );

  print('测试5:合法的注册信息');
  emailValidation.handle(RegistrationRequest(
    email: 'newuser@example.com',
    password: 'SecureP@ss1',
    age: 25,
  ));
}

测试结果:

测试1:无效的电子邮件地址
注册失败:电子邮件格式不正确 —— “notanemail”不是有效的电子邮件地址。

测试2:密码强度不足
电子邮件验证通过
注册失败:密码不符合安全要求
要求:密码长度至少为8个字符,且必须包含大写字母、数字和特殊字符。

测试3:用户未达到法定年龄
电子邮件验证通过
密码强度检查通过
年龄验证也通过
注册失败:用户必须年满18岁才能注册。

测试4:存在重复账户
电子邮件验证通过
密码强度检查通过
年龄验证也通过
重复账户检测也通过
注册失败:已经存在使用“existing@example.com”这个地址创建的账户。

测试5:合法的注册信息
电子邮件验证通过
密码强度检查通过
年龄验证也通过
重复账户检测也通过
所有验证都通过
正在为以下用户创建账户:newuser@example.com
账户创建成功。

每个测试过程都会在恰好正确的处理程序处停止。每条错误信息都是具体的、明确的。合法的注册请求会依次经过全部五个处理程序,最终完成账户创建流程。

当出现新的需求时,比如在账户创建之前添加电话号码验证环节,你只需创建一个PhoneVerificationHandler,并将其插入到重复检查与账户创建之间的处理流程中即可。现有的五个处理程序则完全不会受到影响。

这两个例子为何具有共同之处

从表面上看,这两种处理流程看起来很相似,但实际上它们代表了这种模式在实际应用中的两种不同用法。

交易处理流程将验证处理程序和路由处理程序结合在一个链式中。欺诈检测、客户身份核实以及账户创建相关的处理程序都起到了“关卡”的作用,而审批处理程序则负责根据具体情况选择后续的处理路径。这种设计在支付系统和合规管理系统中非常常见——因为每笔交易都必须经过多个独立的检查步骤,才能被转发给相应的处理机构。

而用户注册流程则纯粹是一个验证链式结构。每一个处理程序都充当着一个“关卡”,只有当所有关卡都通过后,才会执行最终的账户创建操作。这种设计常用于表单处理、API请求验证以及任何需要多步骤验证的场景中。

这两种流程实际上使用的是相同的模式,配置方式也完全一致。它们的区别仅在于:在请求通过各个处理程序时,这些处理程序会执行哪些具体的操作。

何时使用责任链模式

当你需要让一个请求依次经过多个独立的检查步骤或处理流程时,就可以使用这种模式。

此外,当这些检查步骤的数量或顺序可能会随时间发生变化时,这种模式也非常适用。因为新增一个检查步骤或重新调整现有步骤的顺序,并不需要修改现有的处理程序代码。

如果每个检查步骤或处理流程都具备完全独立的逻辑,那么使用责任链模式会效果更好;但如果这些步骤之间存在密切的相互依赖关系,且需要共享大量的状态信息,那么使用单一的类来进行设计可能会更加简洁明了。

当你希望每个处理步骤都能被独立地进行测试时,责任链模式也非常适用。因为使用这种模式进行测试时,只需要创建一个FraudHandler处理程序,用一条交易数据调用它的handle方法,然后检查其输出结果即可,不需要涉及其他处理程序。

另外,在不同场景下可能需要采用不同的责任链配置方案时,这种模式也显得非常实用。例如,基层员工的系统可能只需要经过较少的处理步骤,而高管的系统则可能需要更多的验证流程——尽管使用的处理程序是一样的,但配置方式可能会有所不同。

何时不应使用它

当你只需要进行一两个简单的验证操作时,就应该避免使用责任链模式。因为为这些简单的验证任务定义抽象类和多个具体类所带来的开发开销并不值得。

另外,当处理步骤的顺序是固定的、永远不会发生变化时,这种模式也不太适用。如果整个处理流程始终都是相同的,那么使用更简单的顺序函数调用方式可能会更加直观明了。

当各个处理程序需要相互传递处理结果时,不要使用这种模式。只有当每个处理程序都能独立做出决策时,这种模式才能发挥最佳效果。如果处理程序B需要了解处理程序A的检测结果,那么就应该考虑其他解决方案。

另外,当你需要确保所有处理程序都会被执行,且其执行顺序不受前面处理结果的影响时,这种模式也不适合使用。因为一旦有某个处理程序完成了请求的处理,责任链就会终止。如果你希望所有的处理步骤都能被依次执行,那么中间件管道或装饰器模式可能更适合你。

结论

“责任链”模式能够解决所有不断发展的系统最终都会面临的问题:业务规则会越来越多,验证逻辑也会变得越来越复杂。原本只有十行代码的方法,后来可能会发展成上百行代码,各种条件之间的交互方式也变得让人难以理解。没有人愿意去修改这样的代码。

而“责任链”模式为人们提供了一条出路——每个业务规则都可以由专门的处理程序来负责处理。每个处理程序只承担一项职责,并且只做出一个决定:是停止处理还是将请求继续传递下去。整个责任链的结构在配置阶段就被确定下来,各个处理程序根本不需要了解彼此的工作流程。

以交易审批流程为例,每当增加一条新的合规性规则时,就只需要添加一个新的处理程序类;在用户注册流程中,每增加一个验证步骤,也只需要添加一个新的处理程序类。在这两种情况下,其他部分都不会发生任何变化。

这就是这种模式所带来的好处:系统的复杂性是通过新增功能来逐步增加的,而不是通过修改现有代码来增加的。业务规则可以被独立设计、进行测试,也可以随时被替换;这样的系统能够轻松应对新的需求,而不会导致复杂度不断上升。

应用这种行为模式可以帮助你的代码保持一定的组织结构与可扩展性,同时也便于根据未来可能出现的新业务规则对代码进行维护和管理。

祝编码愉快!!

相关文章

技术实践

DoorDash的Flux系统通过基于云技术的代理程序来处理多达13万项工程任务。

DoorDash已将工程代理的工作负载从开发人员的笔记本电脑转移到了其Flux云平台上。该平台在一个月内自动化处理了130,000项工程任务,每周还能支持超过25,000次自动化的代码审查流程。Flux利用隔离式的Firecracker微虚拟机、MCP网关、可重复使用的脚本以及多种调用机制来运行这些代理工作流程,同时确保这些流程具备受限的访问权限,并能实现集中式的审计功能。 作者:Leela Kumili

阅读全文
技术实践

创造了现代人工智能的那篇论文:Transformer背后的故事

每次你与现代人工智能模型进行交互时,其实都在使用一种源于2017年那篇名为《注意力才是关键》的研究论文所提出的架构。 我们在freeCodeCamp.org的YouTube频道上发布的最新视频,详细讲述了八位谷歌研究人员是如何着手改进Google Translate的,最终又如何彻底改变了整个科技领域的格局的。 在这一突破性成果出现之前,人工智能领域主要依赖循环神经网络。这类模型会按顺序逐词处理文本,但这带来了两个巨大的障碍: “上下文丢失”:当处理长句或长段落时,模型会忘记开头那些重要的信息。 “训练速度缓慢”:由于需要顺序处理数据,各个步骤无法并行执行,因此即使使用庞大的GPU集群,模型的

阅读全文
技术实践

从Mixtral到Kimi K3:专家混合模型是如何发展的

在本文中,我们将探讨混合专家模型是如何从最初仅有少数几个“专家”组件,发展到每层包含近900个“专家”组件的,以及那些使得这种稀疏架构仍能保持可训练性且成本可控的压缩机制与稳定性机制。 开放权重的混合专家模型发展速度惊人:Mixtral的总参数数量约为470亿,DeepSeek-V3达到了6710亿,而Kimi K3的参数数量更是突破了万亿大关。令人惊讶的是,并非这些模型的规模本身,而是每个模型实际上只使用极少量参数来处理每一个输入数据。 Kimi K3拥有2.8万亿个参数,但其中仅有约1040亿个参数被用于处理任何一个输入数据。在几乎每一层中,都有一个小型“路由器”从896个专门的前馈网络中

阅读全文
技术实践

如何在 Django 中构建能够识别推荐行为的拆分支付流程

当一个产品只有一种结算方式时,支付逻辑通常很简单:向用户收费,将订单标记为已支付,然后完成后续流程。 但一旦商业模式涉及到预付款、后期余额结算,以及中间环节的推荐奖励或优惠券应用,情况就会发生彻底变化。 在这种情况下,你不仅仅是在收取款项,而是在管理整个支付流程。 在本教程中,我将向您展示如何在Django中构建一个能够处理推荐机制的分期支付系统,该系统能够: 分别记录预付款和剩余余额的信息 支持优惠券和应用合作伙伴提供的优惠机制 防止重复支付的发生 安全地使用数据库事务 确保推荐奖励的发放始终一致 只有当整个支付流程完成时,才会解锁相应的功能或结果 核心思想很简单:把支付过程视作一种状态转换

阅读全文