2026年2月

【方法一】适用于麒麟桌面系统

  1. 在桌面空白处点击鼠标右键--在终端中打开
  2. 输入命令:

    df  -h  | grep  -w  / 

    查看根目录信息,如下图所示
    /dev/nvme0n1p7就是根分区;
    <img class="alignnone size-full wp-image-1066" src="https://gxxc.wiki/wp-content/uploads/2023/05/df1.png" alt="" width="374" height="60" />

  3. 输入命令:

    sudo  tune2fs   -l   /dev/nvme0n1p7 | grep  "Filesystem created"

    查看文件系统创建时间,如下图所示:
    <img class="alignnone size-full wp-image-1067" src="https://gxxc.wiki/wp-content/uploads/2023/05/tune2fs1.png" alt="" width="680" height="71" />

 

【方法二】适用于麒麟桌面系统

  1. 输入命令:

    date  -r  /var/log/installer

    查看installer目录创建的时间来做为系统安装完成时间的参考,如下图所示:
    <img class="alignnone size-full wp-image-1065" src="https://gxxc.wiki/wp-content/uploads/2023/05/系统安装时间查看.png" alt="" width="426" height="67" />
     

【方法三】适用于麒麟服务器系统

  1. 查看rpm包安装时间

    通过查看已安装的rpm包的安装时间,来确定系统的安装时间。因为系统安装时需要安装大量的rpm包,所以这些rpm包的安装时间基本上就是系统的安装时间。可以通过以下命令来查看rpm包的安装时间:
    输入

    rpm  -qi  basesystem

    如下图所示:
    file

  2. 或者通过查看/boot目录的时间做为参考:

    ls  -l  /boot/grub2/grub.cfg

    或者

    date  -r  /boot

    如下图所示:
    file

本文由mdnice多平台发布

企业微信接口在金融级业务场景下的合规架构与实践

金融行业因其强监管、高安全性和业务连续性要求,对企业级通信工具的集成提出了独特而严格的标准。企业微信作为企业级协同平台,在金融场景的应用需要满足监管合规、数据安全、审计追溯等多重约束。本文将深入探讨面向金融业务的企业微信接口集成架构,确保在满足业务需求的同时符合金融行业监管要求。

一、金融行业集成的核心挑战

金融业务场景对企业微信集成提出了特殊的挑战和要求:

  1. 监管合规性要求:需满足《网络安全法》、《金融数据安全分级指南》、《个人金融信息保护技术规范》等法规要求。
  2. 数据安全与隐私保护:金融交易数据、客户信息等敏感数据需在传输、存储、处理全链路加密。
  3. 业务连续性保障:7×24小时服务可用性,故障恢复时间目标(RTO)和恢复点目标(RPO)要求严苛。
  4. 审计与追溯能力:所有操作需完整记录,支持监管审计和业务追溯。
  5. 实时性与准确性:交易通知、风险预警等场景要求毫秒级延迟和100%准确性。

二、金融级合规架构设计

构建符合金融监管要求的分层架构体系:

[应用接入层] - 业务系统端
├── 统一安全代理
├── 数据脱敏组件
└── 操作审计埋点

[合规处理层] - 中间件层
├── 加密传输网关
├── 内容安全审查
├── 监管策略引擎
└── 风险控制模块

[企业微信接口层] - 平台适配
├── 多环境适配(生产/灾备/测试)
├── 配额智能管理
└── 服务降级熔断

[监控审计层] - 可观测性
├── 全链路追踪
├── 合规审计日志
└── 实时风险监控

三、关键合规技术实现

1. 金融数据安全传输与处理

实现端到端的金融数据保护机制,确保敏感信息不泄露。

// 金融级数据安全处理器
@Component
@Slf4j
public class FinancialDataSecurityProcessor {
    
    private final KeyManagementService kms;
    private final DataClassifier dataClassifier;
    
    /**
     * 处理出站消息,应用金融数据安全策略
     */
    public SecureMessage processOutboundMessage(OriginalMessage message, 
                                               SecurityContext context) {
        // 1. 数据分类分级
        DataClassification classification = dataClassifier.classify(
            message.getContent(),
            message.getMetadata()
        );
        
        // 2. 根据分类应用不同的安全策略
        SecurityPolicy policy = securityPolicyService.getPolicy(
            classification.getLevel(),
            context.getBusinessType()
        );
        
        // 3. 数据脱敏处理
        DesensitizedContent desensitized = applyDesensitization(
            message.getContent(),
            policy.getDesensitizationRules()
        );
        
        // 4. 内容安全审查
        ContentInspectionResult inspection = contentInspector.inspect(
            desensitized,
            policy.getInspectionRules()
        );
        
        if (!inspection.isPassed()) {
            throw new ContentSecurityException(
                "内容安全审查未通过: " + inspection.getReasons()
            );
        }
        
        // 5. 加密处理
        EncryptedPayload encrypted = encryptPayload(
            desensitized,
            policy.getEncryptionAlgorithm(),
            kms.getCurrentDataKey()
        );
        
        // 6. 构造安全消息
        return SecureMessage.builder()
            .encryptedPayload(encrypted)
            .securityLevel(policy.getSecurityLevel())
            .encryptionMetadata(encrypted.getMetadata())
            .complianceTags(buildComplianceTags(classification, policy))
            .traceId(context.getTraceId())
            .build();
    }
    
    /**
     * 金融数据脱敏规则应用
     */
    private DesensitizedContent applyDesensitization(
        String content, 
        List<DesensitizationRule> rules) {
        
        String processed = content;
        
        for (DesensitizationRule rule : rules) {
            switch (rule.getType()) {
                case "bank_card":
                    // 银行卡号脱敏:保留前6后4
                    processed = processed.replaceAll(
                        rule.getPattern(),
                        "$1$2****$3$4"
                    );
                    break;
                    
                case "id_card":
                    // 身份证号脱敏:保留前3后4
                    processed = processed.replaceAll(
                        rule.getPattern(),
                        "$1***********$2"
                    );
                    break;
                    
                case "phone":
                    // 手机号脱敏:保留前3后4
                    processed = processed.replaceAll(
                        rule.getPattern(),
                        "$1****$2"
                    );
                    break;
                    
                case "amount":
                    // 金额模糊化(根据策略)
                    if (rule.getStrategy() == DesensitizationStrategy.RANGE) {
                        processed = maskAmountByRange(processed, rule);
                    }
                    break;
            }
        }
        
        // 记录脱敏审计日志
        auditLogger.logDesensitization(
            content.hashCode(),
            processed.hashCode(),
            rules
        );
        
        return new DesensitizedContent(processed);
    }
    
    /**
     * 金融数据加密
     */
    private EncryptedPayload encryptPayload(
        DesensitizedContent content,
        EncryptionAlgorithm algorithm,
        DataKey dataKey) {
        
        try {
            byte[] plaintext = content.getBytes(StandardCharsets.UTF_8);
            
            // 使用国密算法(SM4)或AES-GCM
            Cipher cipher = Cipher.getInstance(algorithm.getName());
            cipher.init(
                Cipher.ENCRYPT_MODE,
                new SecretKeySpec(dataKey.getKey(), algorithm.getName()),
                new GCMParameterSpec(128, dataKey.getIv())
            );
            
            byte[] ciphertext = cipher.doFinal(plaintext);
            
            return EncryptedPayload.builder()
                .ciphertext(Base64.getEncoder().encodeToString(ciphertext))
                .keyId(dataKey.getKeyId())
                .algorithm(algorithm.getName())
                .version(dataKey.getVersion())
                .build();
                
        } catch (Exception e) {
            throw new EncryptionException("数据加密失败", e);
        }
    }
}

2. 实时交易通知与风险控制集成

将企业微信通知与金融风控系统深度集成,实现智能风险预警。

# 金融交易实时通知与风控集成服务
class FinancialTransactionNotifier:
    
    def __init__(self, risk_engine, compliance_checker):
        self.risk_engine = risk_engine
        self.compliance = compliance_checker
        self.notification_templates = self.load_notification_templates()
        
    async def process_transaction_notification(self, transaction):
        """处理交易通知,集成风控检查"""
        # 1. 交易合规性检查
        compliance_result = await self.compliance.check_transaction(transaction)
        if not compliance_result.passed:
            await self.handle_compliance_violation(transaction, compliance_result)
            return
        
        # 2. 实时风控评估
        risk_score = await self.risk_engine.evaluate_risk(transaction)
        
        # 3. 根据风险等级确定通知策略
        if risk_score >= 0.8:  # 高风险
            await self.send_high_risk_notification(transaction, risk_score)
            # 触发人工审核流程
            await self.trigger_manual_review(transaction)
            
        elif risk_score >= 0.5:  # 中风险
            await self.send_risk_notification(transaction, risk_score)
            
        else:  # 低风险
            await self.send_normal_notification(transaction)
        
        # 4. 记录通知审计
        await self.audit_notification(transaction, risk_score)
    
    async def send_high_risk_notification(self, transaction, risk_score):
        """发送高风险交易通知"""
        # 构建风险告警卡片
        alert_card = {
            "msgtype": "interactive_card",
            "card": {
                "header": {
                    "title": "⚠️ 高风险交易告警",
                    "subtitle": f"风险评分: {risk_score:.2%}",
                    "color": "#FF0000"
                },
                "elements": [
                    {
                        "type": "markdown",
                        "content": self.build_risk_alert_content(transaction)
                    },
                    {
                        "type": "divider"
                    },
                    {
                        "type": "note",
                        "content": "**风控建议**:\n" + 
                                  self.risk_engine.get_risk_advice(transaction)
                    }
                ],
                "action_menu": {
                    "actions": [
                        {
                            "name": "立即拦截",
                            "type": "click",
                            "value": f"block_{transaction.id}",
                            "confirm": {
                                "title": "确认拦截交易",
                                "description": "确定要拦截此交易吗?"
                            }
                        },
                        {
                            "name": "标记为正常",
                            "type": "click",
                            "value": f"approve_{transaction.id}"
                        },
                        {
                            "name": "查看详情",
                            "type": "open_url",
                            "url": self.build_transaction_detail_url(transaction)
                        }
                    ]
                }
            }
        }
        
        # 发送给风控团队和相关决策者
        recipients = self.get_risk_team_recipients(transaction)
        for recipient in recipients:
            await self.wecom_client.send_card(recipient, alert_card)
            
        # 同时在风控群中广播
        await self.wecom_client.send_to_risk_chatroom(alert_card)
    
    def build_risk_alert_content(self, transaction):
        """构建风险告警内容"""
        return f"""**交易风险告警**
                
**交易ID**: `{transaction.id}`
**交易类型**: {transaction.type}
**交易金额**: ¥{transaction.amount:,.2f}
**交易时间**: {transaction.timestamp}
**交易账户**: {self.mask_account(transaction.account)}
                
**风险特征**:
- 非常规时间交易: {transaction.is_unusual_time}
- 金额异常: {transaction.is_amount_abnormal}
- 频率异常: {transaction.frequency_status}
                
**地理位置**:
- 发起位置: {transaction.location}
- 设备指纹: {transaction.device_fingerprint[:8]}...
"""
    
    async def trigger_manual_review(self, transaction):
        """触发人工审核流程"""
        # 创建审核任务
        review_task = {
            "task_id": f"review_{transaction.id}",
            "transaction": transaction,
            "assigned_to": self.get_next_reviewer(),
            "deadline": datetime.now() + timedelta(minutes=30),
            "priority": "high"
        }
        
        # 添加到审核队列
        await self.review_queue.add(review_task)
        
        # 发送审核通知
        review_notification = {
            "msgtype": "text",
            "text": {
                "content": f"您有新的交易待审核\n交易ID: {transaction.id}\n金额: ¥{transaction.amount:,.2f}\n请及时处理",
                "mentioned_list": [review_task["assigned_to"]]
            }
        }
        
        await self.wecom_client.send_message(
            review_task["assigned_to"],
            review_notification
        )

3. 金融级审计与追溯系统

构建符合金融监管要求的完整审计追溯体系。

-- 金融级企业微信操作审计表设计
CREATE TABLE financial_wecom_audit_log (
    log_id BIGINT PRIMARY KEY AUTO_INCREMENT,
    trace_id VARCHAR(64) NOT NULL, -- 全链路追踪ID
    session_id VARCHAR(64) NOT NULL, -- 会话ID
    
    -- 操作主体信息
    operator_id VARCHAR(64) NOT NULL, -- 操作人ID
    operator_name VARCHAR(128) NOT NULL, -- 操作人姓名
    operator_dept VARCHAR(128), -- 操作人部门
    operator_role VARCHAR(64), -- 操作人角色
    
    -- 操作目标信息
    target_user_id VARCHAR(64), -- 目标用户ID
    target_user_type VARCHAR(32), -- 用户类型:内部员工/外部客户
    business_type VARCHAR(64) NOT NULL, -- 业务类型:交易通知/风险告警等
    
    -- 操作详情
    operation_type VARCHAR(32) NOT NULL, -- CREATE/READ/UPDATE/DELETE/SEND
    api_endpoint VARCHAR(255) NOT NULL, -- 调用的API接口
    request_body_hash VARCHAR(64), -- 请求体哈希(防篡改)
    response_code INT, -- 响应状态码
    response_body_hash VARCHAR(64), -- 响应体哈希
    
    -- 安全与合规信息
    security_level VARCHAR(16) NOT NULL, -- 安全等级:L1/L2/L3/L4
    data_classification VARCHAR(32), -- 数据分类等级
    compliance_flag BOOLEAN DEFAULT TRUE, -- 合规标记
    risk_score DECIMAL(5,4), -- 风险评分
    
    -- 时间信息
    operation_time TIMESTAMP(6) NOT NULL, -- 操作时间(微秒精度)
    response_time TIMESTAMP(6), -- 响应时间
    duration_ms INT, -- 操作耗时(毫秒)
    
    -- 系统环境
    client_ip VARCHAR(45), -- 客户端IP
    user_agent VARCHAR(512), -- 用户代理
    device_id VARCHAR(64), -- 设备ID
    
    -- 审计跟踪
    reviewed_by VARCHAR(64), -- 审核人
    reviewed_at TIMESTAMP(6), -- 审核时间
    review_notes TEXT, -- 审核意见
    
    -- 索引设计
    INDEX idx_trace_id (trace_id),
    INDEX idx_operator_time (operator_id, operation_time),
    INDEX idx_business_time (business_type, operation_time),
    INDEX idx_compliance (compliance_flag, operation_time),
    INDEX idx_risk (risk_score, operation_time),
    
    -- 分区策略(按月分区)
    PARTITION BY RANGE (UNIX_TIMESTAMP(operation_time)) (
        PARTITION p202401 VALUES LESS THAN (UNIX_TIMESTAMP('2024-02-01')),
        PARTITION p202402 VALUES LESS THAN (UNIX_TIMESTAMP('2024-03-01'))
    )
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4
COMMENT='金融级企业微信操作审计表';

-- 审计报告生成视图
CREATE VIEW financial_audit_report AS
SELECT 
    DATE(operation_time) as audit_date,
    business_type,
    COUNT(*) as total_operations,
    SUM(CASE WHEN response_code = 200 THEN 1 ELSE 0 END) as success_count,
    SUM(CASE WHEN response_code != 200 THEN 1 ELSE 0 END) as failure_count,
    ROUND(AVG(duration_ms), 2) as avg_duration_ms,
    COUNT(DISTINCT operator_id) as unique_operators,
    
    -- 风险操作统计
    SUM(CASE WHEN risk_score > 0.7 THEN 1 ELSE 0 END) as high_risk_ops,
    SUM(CASE WHEN compliance_flag = FALSE THEN 1 ELSE 0 END) as compliance_violations,
    
    -- 时段分布
    SUM(CASE WHEN HOUR(operation_time) BETWEEN 9 AND 17 THEN 1 ELSE 0 END) as business_hour_ops,
    SUM(CASE WHEN HOUR(operation_time) NOT BETWEEN 9 AND 17 THEN 1 ELSE 0 END) as non_business_hour_ops
    
FROM financial_wecom_audit_log
WHERE operation_time >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY DATE(operation_time), business_type
ORDER BY audit_date DESC, total_operations DESC;

-- 审计数据保留策略存储过程
CREATE PROCEDURE cleanup_audit_data()
BEGIN
    DECLARE retention_days INT DEFAULT 730; -- 默认保留2年
    DECLARE cutoff_date DATE;
    
    -- 获取配置的保留天数
    SELECT config_value INTO retention_days
    FROM system_config 
    WHERE config_key = 'audit_data_retention_days';
    
    SET cutoff_date = DATE_SUB(CURDATE(), INTERVAL retention_days DAY);
    
    -- 归档过期数据(移至历史表)
    INSERT INTO financial_wecom_audit_log_history
    SELECT * FROM financial_wecom_audit_log
    WHERE DATE(operation_time) < cutoff_date;
    
    -- 删除已归档数据
    DELETE FROM financial_wecom_audit_log
    WHERE DATE(operation_time) < cutoff_date;
    
    -- 记录清理操作
    INSERT INTO audit_cleanup_log
    VALUES (NOW(), retention_days, ROW_COUNT(), 'financial_wecom_audit_log');
END;

-- 定期执行数据清理
CREATE EVENT cleanup_audit_data_event
ON SCHEDULE EVERY 1 DAY
STARTS '2024-01-01 03:00:00'
COMMENT '清理企业微信审计数据'
DO
BEGIN
    CALL cleanup_audit_data();
END;

4. 高可用与灾备架构实现

针对金融业务连续性要求,设计多活灾备方案。

# 金融级企业微信集成高可用配置
apiVersion: financial.wecom/v1alpha1
kind: HighAvailabilityConfig
metadata:
  name: wecom-integration-ha
  namespace: financial-prod
spec:
  deploymentStrategy:
    mode: multi-active  # 多活模式
    regions:
      - name: cn-east-1
        weight: 50
        endpoint: https://wecom-primary.financial.com
        healthCheck:
          path: /health
          interval: 10s
          timeout: 3s
      - name: cn-north-1  
        weight: 50
        endpoint: https://wecom-backup.financial.com
        healthCheck:
          path: /health
          interval: 10s
          timeout: 3s
  
  failoverPolicy:
    detection:
      failureThreshold: 3
      successThreshold: 1
      timeoutSeconds: 5
    recovery:
      autoFailback: true
      failbackDelay: 300s  # 故障恢复后等待5分钟再切回
  
  trafficManagement:
    loadBalancing:
      algorithm: weighted_round_robin
      stickySessions: true
      sessionDuration: 3600s
    circuitBreaker:
      failureThreshold: 5
      resetTimeout: 60s
  
  dataSync:
    enabled: true
    mode: real-time
    consistency: eventual
    conflictResolution: last_write_win
    syncComponents:
      - users
      - departments
      - external_contacts
    retention:
      syncLogDays: 7
      errorLogDays: 30
  
  monitoring:
    metrics:
      - name: api_success_rate
        threshold: 99.95%
      - name: p95_latency
        threshold: 100ms
      - name: error_rate
        threshold: 0.05%
    alerts:
      - severity: critical
        condition: api_success_rate < 99.9% for 2m
        actions:
          - type: scale_up
          - type: notify
            channels: [wecom, sms, phone]
      - severity: warning
        condition: p95_latency > 200ms for 5m
        actions:
          - type: notify
            channels: [wecom]
  
  compliance:
    auditLogging: true
    dataEncryption: true
    keyRotation: 
      enabled: true
      interval: 90d
    accessControl:
      enabled: true
      mfaRequired: true

四、监管合规性保障措施

  1. 监管数据报送自动化

    # 监管数据自动报送模块
    class RegulatoryReportingService:
     
     async def generate_regulatory_report(self, report_type, period):
         """生成监管要求的报告"""
         if report_type == "monthly_wecom_usage":
             report = await self.generate_monthly_usage_report(period)
         elif report_type == "security_incident":
             report = await self.generate_security_incident_report(period)
         elif report_type == "data_export_log":
             report = await self.generate_data_export_report(period)
         
         # 数字签名
         signed_report = self.sign_report(report)
         
         # 加密传输
         encrypted_report = self.encrypt_for_regulator(signed_report)
         
         # 自动报送
         await self.submit_to_regulator(encrypted_report)
         
         # 本地归档
         await self.archive_report(signed_report)
         
         return report.id
  2. 应急响应与业务连续性演练

    // 金融业务连续性演练框架
    public class BusinessContinuityDrillExecutor {
     
     public DrillResult executeRegulatoryDrill(DrillScenario scenario) {
         // 1. 演练前准备
         prepareDrillEnvironment(scenario);
         
         // 2. 注入故障(模拟企业微信服务中断)
         injectServiceFailure(scenario.getFailureMode());
         
         // 3. 验证业务连续性措施
         boolean continuityMaintained = verifyBusinessContinuity(
             scenario.getCriticalBusinessFlows()
         );
         
         // 4. 记录演练结果
         DrillReport report = generateDrillReport(
             scenario,
             continuityMaintained,
             collectMetrics()
         );
         
         // 5. 提交监管报告(如要求)
         if (scenario.isRegulatoryRequired()) {
             submitRegulatoryDrillReport(report);
         }
         
         return new DrillResult(report);
     }
    }

五、总结

在金融行业场景下集成企业微信接口,需要将技术实现、安全合规和业务连续性三者深度融合。通过构建层次化的安全架构、实施严格的数据保护策略、建立完整的审计追溯体系,以及设计高可用的多活灾备方案,可以在满足金融业务需求的同时,确保符合行业监管要求。

这种集成模式的价值不仅在于提升金融业务的协同效率,更在于通过技术手段将合规要求内嵌到系统设计中,实现主动合规管理。在金融科技快速发展的今天,这种既保障安全合规又提升业务效率的集成架构,正成为金融机构数字化转型的重要技术支撑。

technical_contact = "bot555666"

本文实现 FlashAttention-2 的前向传播,具体包括:为 Q、K、V 设计分块策略;流式处理 K 和 V 块而非物化完整注意力矩阵;实现在线 softmax 算法保证数值稳定性;支持因果和非因果两种注意力模式;用 Triton autotuner 自动调优内核配置;最后用 PyTorch 验证正确性。

FlashAttention vs. standard attention vs torch2.2 (spda flashattn) TFLOP/s benchmarks

标准注意力为什么是内存受限的

标准注意力的瓶颈不在浮点运算量而在内存带宽。普通注意力计算 S = QKᵀ 之后,要把完整的 N × N 矩阵写入 HBM再读回来算 softmax 并存储然后再读一次乘以 V,每个元素被访问 2-4 次每次都走 HBM。

序列长度 16K 时,这个矩阵包含 16,384² ≈ 2.56 亿个元素。

反复在 HBM 和计算单元之间搬运这几亿个值,而HBM 是 GPU 上容量最大的内存也是最慢的。A100 上从 HBM 读数据比从片上 SRAM 读大约慢 15 倍。大张量和模型权重都放在这里,所以写内核的首要目标就是减少 HBM 流量把高频访问的数据留在寄存器或共享内存里。

核心方案——让注意力具备 IO 感知能力

FlashAttention 的核心思想是让注意力变得 IO 感知。所谓 IO 感知就是真正理解并利用一个这个定义:片上 SRAM 比 HBM 快几个数量级。NVIDIA A100 有 40-80GB HBM(也就是那个让你频繁遭遇 CUDA OOM 的全局内存)带宽 1.5-2.0 TB/s;每个 SM 有 192KB SRAM,共 108 个 SM,带宽估计 19TB/s 左右。

GPU 硬件有个黄金法则:

把数据搬到内存层次的上层然后留在那里。除非万不得已别回 HBM。

标准注意力完全无视这条规则,把 HBM 读写当成零成本操作。FlashAttention 计算的结果和标准缩放点积注意力完全一样:

S = QKᵀ ∈ ℝᴺˣᴺ,P = softmax(S) ∈ ℝᴺˣᴺ,O = PV ∈ ℝᴺˣᵈ

区别在于计算的调度方式。FlashAttention 不在 HBM 里存储那个巨大的 N × N 注意力矩阵然后再读回来算 softmax而是重新组织计算:分块处理序列从全局内存流式读取 K 和 V 块,用在线 softmax 增量计算每个块的部分结果,逐步构建输出矩阵 O反向传播时还可以选择重算而非存储。

具体操作是这样的:拿一块查询 Q_block,然后分块迭代 K 和 V 序列,边迭代边做在线 softmax 同时追踪必要的统计量,累积输出块并在片上归一化,只把最终结果写回 HBM。

这样注意力的内存复杂度就从 O(N²) 降到了 O(N)。

最难的部分——Softmax

分块矩阵乘法不难,而分块 softmax 才是麻烦事。注意力中 token i 对其他 token 的关注程度,是对该行所有注意力分数做 softmax 得到的:

普通注意力里这很简单,因为一个 token 的全部注意力分数已经物化在内存中,一步就能算完最大值、归一化、softmax。

而FlashAttention 里情况不一样,键和值是分块流式进来的内核迭代 K 和 V 时只能看到部分分数块,永远看不到完整的分数集,就没法一步算完 softmax。

解决方案是在线 softmax 公式。不一步算完,而是维护三个逐查询的状态:运行最大值 mᵢ(保证数值稳定),运行归一化项 lᵢ,运行输出累加器 Oᵢ。每来一个新的注意力分数块,就更新这些值,最后恢复的结果和对整个序列做完整 softmax 一模一样。

完整代码分解

从高层看,实现结构如下:

 for each (batch, head):  
     for each Q_block:  
         initialize m_i, l_i, O_block  
         for each K/V block:  
             compute partial scores  
             update online softmax state  
             accumulate output  
         write O_block to memory

所有逻辑融合在内核里,中间状态全部驻留在片上快速内存。下面逐步讲解这个结构如何映射到 Triton 程序和 GPU 执行。

Host 包装器和内核启动

Python 包装器负责准备输入并启动 Triton 内核,做三件事:验证和提取输入张量的形状与步幅,构建内核执行网格,启动前向注意力内核。包装器本身不含注意力逻辑,只定义工作如何在 GPU 上调度。

 # Host wrapper that prepares our inputs and parameters and runs the triton kernel  
class TritonFlashAttention(torch.autograd.Function):  
    @staticmethod  
    def flash_attention(Q, K, V, causal):  
        assert Q.is_cuda  
        assert K.is_cuda  
        assert V.is_cuda  

        B, H, Lq, D = Q.shape  
        B, H, Lk, D = K.shape  
        B, H, Lk, D = V.shape  

        # create the output buffer  
        O = torch.empty_like(Q)  

        # we set block_sizes manually for now. We will autotune this later  
        [#BLOCK](#BLOCK)_SIZE_Q = 128  
        [#BLOCK](#BLOCK)_SIZE_KV = 32  

       
        stage = 3 if causal else 1  

        grid = lambda x: (triton.cdiv(Lq, x["BLOCK_SIZE_Q"]),  
                          B * H, 1)  
        M = torch.empty((B, H, Lq), device=Q.device, dtype=torch.float32)  

        scaling_factor = 1 / math.sqrt(D)  
        fwd_flash_attn_kernel[grid](Q, K, V, O, M, scaling_factor,  
                                    Q.stride(0), Q.stride(1), Q.stride(2), Q.stride(3),  
                                    K.stride(0), K.stride(1), K.stride(2), K.stride(3),  
                                    V.stride(0), V.stride(1), V.stride(2), V.stride(3),  
                                    O.stride(0), O.stride(1), O.stride(2), O.stride(3),  
                                    B, NUM_HEADS=H, SEQ_LEN=Lq, HEAD_DIM=D, STAGE=stage,)  
        [#ctx](#ctx).save_for_backward  
      
         return O  

程序网格和并行化策略

host 包装器里定义了一个 2D 执行网格,决定 GPU 如何分配工作,也就是并行启动多少个 Triton 程序实例。

 grid=lambdax: (triton.cdiv(Lq, x["BLOCK_SIZE_Q"]), B*H, 1) 

第一维 program_id(0) 标识程序实例处理的查询序列块,第二维 program_id(1) 标识对应的 (batch, head) 对。

维度 0 把查询序列分成 BLOCK_SIZE_Q 大小的块,Lq 是查询序列长度,每个程序实例负责计算输出矩阵的一个水平"条带"。维度 1 跨所有 batch 和 head 并行,每个程序实例对应一个 (batch, head) 对。给每个注意力头分配独立程序可以最大化占用率。内核内部用 tl.program_id 配合手动步幅算术(qb_stride、qh_stride)把每个 worker 指向它的内存切片。

每个程序实例负责计算:

 Q[batch, head, q_block : q_block+BLOCK_SIZE_Q]

这种网格设计提供了序列维度并行、batch 和 head 并行,而且程序间不需要同步。每个程序在紧凑独立的工作集上运行,tl.program_id 结合显式步幅算术把每个实例映射到对应内存切片。

内核分解

前向传播分成两个内核。fwd_flash_attn_kernel 协调执行,加载查询块、处理因果逻辑、写输出。_attn_fwd_inner 实现核心 FlashAttention-2 计算,流式处理 K/V 块并执行在线 softmax 更新。每个 Triton 程序实例计算一个查询块 × 一个注意力头 × 一个 batch 元素。

这种分解把控制逻辑和流式计算分开内核更容易理解和优化。

前向内核

这个内核本身不直接实现注意力算法,负责的是把 GPU 程序实例映射到输入张量的对应块,协调流式注意力计算,处理因果逻辑,把最终输出写回内存。

 @triton.jit  
def fwd_flash_attn_kernel(q_ptr, k_ptr, v_ptr, o_ptr, m_ptr, scale,  
                          qb_stride, qh_stride, qn_stride, qd_stride,  
                          kb_stride, kh_stride, kn_stride, kd_stride,  
                          vb_stride, vh_stride, vn_stride, vd_stride,  
                          ob_stride, oh_stride, on_stride, od_stride,  
                          BATCH_SIZE, NUM_HEADS:tl.constexpr, SEQ_LEN:tl.constexpr, HEAD_DIM:tl.constexpr,   
                          BLOCK_SIZE_Q:tl.constexpr, BLOCK_SIZE_KV:tl.constexpr, STAGE:tl.constexpr):  

    # get the id of this program instance  
    block_index_q = tl.program_id(0) # Which chunk of sequence this program is responsible for  
    index_batch_head = tl.program_id(1) # what batch-head to process. zooms out  

    # get exact batch   
    index_batch = index_batch_head // NUM_HEADS  

    # get exact head   
    index_head = index_batch_head % NUM_HEADS  

    # create offsets to get the index of sequences we are going to process  
    qkv_offset = index_batch * qb_stride + index_head * qh_stride # i.e move from the first to the correct batch then move to the correct head within that batch   
    qkv_offset_K = index_batch * kb_stride + index_head * kh_stride  
    qkv_offset_V = index_batch * vb_stride + index_head * vh_stride  
    qkv_offset_O = index_batch * ob_stride + index_head * oh_stride  

    off_q = block_index_q * BLOCK_SIZE_Q + tl.arange(0, BLOCK_SIZE_Q) # same as off_q (in this head what q block do we need to read )  
    off_kv = tl.arange(0, BLOCK_SIZE_KV)  
    off_head = tl.arange(0, HEAD_DIM)  

    # create blocks of pointers to get the address of where the index lives   
    Q_block_ptr = q_ptr + qkv_offset + off_q[:, None] * qn_stride + off_head[None, :] * qd_stride  
    O_block_ptr = o_ptr + qkv_offset_O + off_q[:, None] * on_stride + off_head[None, :] * od_stride  

    m_i = tl.zeros((BLOCK_SIZE_Q,), dtype= tl.float32) - float("inf")  

    l_i = tl.zeros((BLOCK_SIZE_Q,), dtype=tl.float32) + 1.0  
    O_block = tl.zeros((BLOCK_SIZE_Q, HEAD_DIM), dtype=tl.float32)  
    Q_block = tl.load(Q_block_ptr) # add a mask  

    # stage 1: Blocks before the diagonal   
    # stage 2: diagonal block itself   
    # stage 3: for non-causal no masking is needed. For causal mask all the blocks here.  
      
    # runs if causal is True i.e we mask out the future tokens from contributing  
    # this if statement executes for non-causal attention (no masking) or for the blocks to the left of the diagonal in the causal attention  
    # Stage = 3 if causal else 1   
    if STAGE == 1 or STAGE == 3:  
        O_block, l_i, m_i = _attn_fwd_inner(  
            O_block,  
            l_i,  
            m_i,   
            Q_block,   
            block_index_q,  
            scale,   
            BLOCK_SIZE_Q,  
            BLOCK_SIZE_KV,   
            4 - STAGE,  
            off_kv,  
            off_q,  
            off_head,  
            kn_stride,  
            kd_stride,  
            vd_stride,  
            vn_stride,   
            k_ptr,  
            v_ptr,  
            qkv_offset_K,  
            qkv_offset_V,  
            SEQ_LEN,   
            HEAD_DIM  
        )  
      
    # this executes for blocks to the right of the diagonal in the causal attention  
    if STAGE == 3:  
        O_block, l_i, m_i = _attn_fwd_inner(  
            O_block,  
            l_i,  
            m_i,   
            Q_block,   
            block_index_q,  
            scale,   
            BLOCK_SIZE_Q,  
            BLOCK_SIZE_KV,   
            2,  
            off_kv,  
            off_q,  
            off_head,  
            kn_stride,  
            kd_stride,  
            vd_stride,  
            vn_stride,   
            k_ptr,  
            v_ptr,  
            qkv_offset_K,  
            qkv_offset_V,  
            SEQ_LEN,   
            HEAD_DIM  
        )  

    m_i += tl.math.log(l_i)  
    O_block = O_block / l_i[:, None]  
    m_ptrs = m_ptr + index_batch_head * SEQ_LEN + off_q   
    tl.store(m_ptrs, m_i)  
     tl.store(O_block_ptr, O_block.to(tl.float16))

网格映射

回顾 Python 包装器里的网格:

 grid = (  
     ceil_div(Lq, BLOCK_SIZE_Q),  
     B * H  
 )

这个 2D 网格映射提供序列维度并行和 batch/head 并行。

内核内部:

 block_index_q     =tl.program_id(0)  
 index_batch_head  =tl.program_id(1)

解码第二维:

 index_batch=index_batch_head//NUM_HEADS  
 index_head  =index_batch_head%NUM_HEADS

这几个变量唯一标识当前程序实例负责哪个 batch 元素、哪个注意力头、哪个查询块。

指针算术和张量布局

PyTorch 或 numpy 里用多维语法索引张量,比如 Q[batch, head, seq_pos, dim]。而Triton 内核里没有多维张量,只有指向输入第一个元素的裸指针 q_ptr必须用指针算术手动重构索引。

查询张量 Q 形状是 [BATCH, HEADS, SEQ_LEN, HEAD_DIM],硬件层面是扁平一维数组存储。沿每个维度移动用步幅:qb_stride 跳一个 batch,qh_stride 跳一个 head,qn_stride 跳一个 token,qd_stride 跳一个特征。

选择 batch 和 head

每个程序实例先选定自己负责的 batch 和 head 切片:

 qkv_offset=index_batch*qb_stride+index_head*qh_stride

这个偏移之后,指针指向 Q[batch, head, 0, :]。K、V、O 同理,用各自的步幅。然后构建当前块的索引范围:

 off_q    =block_index_q*BLOCK_SIZE_Q+tl.arange(0, BLOCK_SIZE_Q)  
 off_head=tl.arange(0, HEAD_DIM)

用这些偏移加广播,构建指向查询块的指针:

 Q_block_ptr=q_ptr+qkv_offset \  
             +off_q[:, None] *qn_stride \  
             +off_head[None, :] *qd_stride

输出 O_block_ptr 也类似:

 O_block_ptr=o_ptr+qkv_offset_O \  
             +off_q[:, None] *on_stride \  
             +off_head[None, :] *od_stride

完全用指针算术重现了 4D 索引 Q[batch, head, q_positions, head_dim]。

这种显式指针构建很关键,确保只加载每个程序实例需要的 Q 块并送到 SRAM,避免碰不相关的内存,实现合并访问,最大化缓存复用。

初始化每块状态

加载查询块后,内核初始化在线 softmax 所需的每块状态并分派流式计算。流式逻辑和因果阶段的细节在 _attn_fwd_inner 里,后面分析。先理解这个每块状态为什么存在、代表什么。

为了在迭代 K 和 V 块时正确增量计算 softmax,需要追踪三个量:运行最大值 m_i、运行 softmax 分母 l_i、未归一化加权和 O_block。

这三个变量构成在线 softmax 算法的状态。FlashAttention 分块处理键值,内核永远无法一次访问所有注意力分数。要得到和完整 softmax 一样的结果,必须维护数值稳定用的运行最大值 m_i、运行归一化因子 l_i、累积加权输出 O_block。这些状态共同作用,精确重建 softmax(QKᵀ) @ V,不需要物化注意力矩阵。

运行最大值 m_i 和运行归一化器

Softmax 涉及指数运算,FP16/BF16 下容易数值不稳定。为了把指数保持在合理范围,每个查询行追踪一个运行最大值 m_i。处理新的 K 和 V 块时,这个运行最大值可能增大。一旦增大,之前用旧最大值计算的累积贡献就不在同一尺度上了。

纠正办法是用一个因子重新缩放累积的分母:

the numerator

the scaling factor

the normalizing denominator

这种重新缩放确保分母里所有项都相对同一个最大值。流式处理键值块时反复应用这个更新就能恢复精确的 softmax 归一化因子,不需要物化完整的注意力分数集。

内核里是这样写:

 alpha=exp(m_old-m_new)  
 l_i=l_i*alpha+l_ij

累积输出 O_block

注意力输出定义为:

Final attention output

标准实现里可以直接算,因为完整的 softmax 归一化系数事先就知道。FlashAttention 里键值分块流式进来,最终归一化因子要等所有 K 和 V 块处理完才能确定。

所以只能累积一个未归一化的加权和,最后再归一化。

每次迭代,计算相对于当前运行最大值的块级 softmax 概率:

维护一个未归一化输出累加器:

unnormalized softmax output

处理新 K/V 块时运行最大值可能变,之前累积的输出必须重新缩放以匹配新最大值。

逐块更新输出累加器:

 O_block=O_block*alpha[:, None]  
 O_block=P_block@V_block+O_block

所有 K/V 块处理完后,把累积的未归一化输出除以累积的 softmax 分母 li 得到最终注意力输出:

final normalization

结果和标准 softmax 注意力完全一样,但永远不会在内存里物化完整注意力矩阵或 softmax 概率。

每个程序实例为每个查询块初始化这三个状态一次:

 m_i=tl.zeros((BLOCK_SIZE_Q,), dtype=tl.float32) -inf  
 l_i=tl.zeros((BLOCK_SIZE_Q,), dtype=tl.float32) +1  
 O_block=tl.zeros((BLOCK_SIZE_Q, HEAD_DIM), dtype=tl.float32)

流式注意力内核 _attn_fwd_inner

_attn_fwd_inner 实现 FlashAttention-2 算法核心,由 fwd_flash_attn_kernel 调用,一次处理一个查询块。

 @triton.jit  
def _attn_fwd_inner(O_block, l_i,m_i, Q_block, block_index_q,  
    scale: tl.constexpr,  
    BLOCK_SIZE_Q: tl.constexpr,  
    BLOCK_SIZE_KV: tl.constexpr,  
    STAGE: tl.constexpr,  
    off_kv: tl.constexpr,  
    off_q: tl.constexpr,  
    off_head: tl.constexpr,  
    kn_stride: tl.constexpr,  
    kd_stride: tl.constexpr,  
    vd_stride: tl.constexpr,  
    vn_stride: tl.constexpr,  
    k_ptr,  
    v_ptr,  
    qkv_offset_K: tl.constexpr,  
    qkv_offset_V: tl.constexpr,  
    SEQ_LEN:tl.constexpr,  
     HEAD_DIM: tl.constexpr):

其中 Q_block 形状 [BLOCK_SIZE_Q, HEAD_DIM],O_block 是累积输出,m_i 是每查询行的运行最大值,l_i 是运行 softmax 归一化。

因果块范围选择

FA 内核支持因果(只看过去和当前 token)和非因果注意力(双向,可以看未来)。用一个阶段机制实现:

 if STAGE == 1:  
     lo, hi = 0, block_index_q * BLOCK_SIZE_Q  
 elif STAGE == 2:  
     lo, hi = block_index_q * BLOCK_SIZE_Q, (block_index_q + 1) * BLOCK_SIZE_Q  
 else:  
     lo, hi = 0, SEQ_LEN

这个逻辑决定当前内核处理哪些 K/V 块。Stage 1 是对角线左侧的块,K 和 V 范围仅限于此。Stage 2 是对角线块本身。Stage 3 是非因果逻辑,K 和 V 关注所有 Q。这样避免计算因果注意力中肯定会被 mask 掉的分数,减少不必要的 masking 工作。

K 和 V 块的流式循环

查询虽然分区到各程序实例,但每个查询块必须关注所有键值——这是全注意力的定义决定的。完整 K 和 V 矩阵从不一次性加载到 SRAM,而是以 BLOCK_SIZE_KV 大小的块流式处理:

 forstart_kvinrange(lo, hi, BLOCK_SIZE_KV):

加载 BLOCK_SIZE_KV 个键值,计算部分注意力分数,更新在线 softmax 状态,丢弃该块,处理下一个。内存复杂度维持 O(N)。

每个程序实例只加载一个查询块,对应序列中一小部分 token。但这些 token 要正确计算注意力输出,必须关注序列里所有键值。这是自注意力定义决定的:每个查询都要和每个键比较。FlashAttention 没改这个算法要求,只改计算调度方式。键值逐块流式进来,累积到输出,立刻丢弃,内存占用小,结果精确。一些新的注意力变体(局部注意力、稀疏注意力、滑动窗口注意力)不会关注所有 token。

为 K 和 V 构建块指针

和 Q_block 一样,计算当前块的 token 索引:

 kv_positions=start_kv+off_kv

然后构建指针:

 K_block_ptr = (  
    k_ptr + qkv_offset_K  
    + off_head[:, None] * kd_stride  
    + kv_positions[None, :] * kn_stride  
)  

V_block_ptr = (  
    v_ptr + qkv_offset_V  
    + kv_positions[:, None] * vn_stride  
    + off_head[None, :] * vd_stride  
 )

得到形状 [HEAD_DIM, BLOCK_SIZE_KV] 的 K 和 V 指针。边界 mask 逻辑防止最后一个块越界访问:

 mask_k = kv_positions[None, :] < SEQ_LEN  
 mask_v = kv_positions[:, None] < SEQ_LEN

从 HBM 加载 K 和 V 到片上 SRAM:

 K_block = tl.load(K_block_ptr, mask=mask_k, other=0.0)  
 V_block = tl.load(V_block_ptr, mask=mask_v, other=0.0)

部分分数计算和在线更新

计算分块点积:

 QK_block=tl.dot(Q_block, K_block)

应用缩放和 mask(如果是因果的),更新运行最大值:

 mask = off_q[:, None] >= (start_kv + off_kv[None, :])  
 QK_block = QK_block * scale + tl.where(mask, 0, -1e6)  
 m_ij = tl.maximum(m_i, tl.max(QK_block, 1))  
 QK_block -= m_ij[:, None]  
 m_ij = tl.maximum(m_i, tl.max(QK_block, 1) * scale)  
 QK_block = QK_block * scale - m_ij[:, None]

更新在线 softmax 状态:

 P_block = exp(QK_block)  
 l_ij = sum(P_block, axis=1)  
 alpha = exp(m_i - m_ij)  
 l_i = l_i * alpha + l_ij

更新输出累加器:

 O_block = O_block * alpha[:, None]  
 O_block = dot(P_block, V_block, O_block)

用当前迭代找到的新最大值更新运行最大值:

 m_i=m_ij

更新后的状态返回给外层内核 fwd_flash_attn_kernel。

最终归一化和写回

所有 K/V 块处理完后,前向内核完成输出:

 O_block=O_block/l_i[:, None]

用累积的分母因子归一化注意力输出。当前查询块的注意力输出就算完了。

性能和基准测试

前向传播实现完毕并验证后,可以看看性能和标准注意力实现比较一下。

FlashAttention vs. standard attention vs torch2.2 (spda flashattn) TFLOP/s benchmarks

所有序列长度上标准注意力在 3-4 TFLOPs/sec 左右就到顶了。理论计算量虽然按 O(N²) 增长,但标准注意力被 HBM 流量主导。GPU 大部分时间在搬运 N × N 注意力矩阵,不是在做有用计算。序列变长并不能提高计算单元利用率,只是内存压力变大。

Triton FlashAttention 内核则随序列长度增加激进扩展。512 token 时性能一般,超过 2K token 后吞吐量快速上升。16K token 时维持在约 190 TFLOPs/sec。这正是 FlashAttention 设计要达到的效果:阻止注意力矩阵物化,中间数据驻留 SRAM,内存加载得以摊销。序列越长,内核越趋向计算受限,GPU 接近有效峰值吞吐量——和标准注意力恰好相反,标准注意力序列越长越内存受限。

第二张图在 Nvidia A100 上通过 sdpa API 比较了 Triton FlashAttention 和 PyTorch 官方 FlashAttention 实现。序列较短时 PyTorch 实现有竞争力,序列长度 ≥4k 后,自定义 Triton 内核追平并略微超过 PyTorch 性能。16k token 时,两者都收敛到约 180-190 TFLOPs/sec。

所有结果在同一 GPU(Nvidia A100 SXM)相同条件下获得。吞吐量以 TFLOPs/sec 报告,由缩放点积注意力的理论 FLOP 数除以实测内核运行时间得出。序列长度变化,batch 大小、头数、头维度固定。

这些基准验证了三件事:标准注意力从根本上内存受限;FlashAttention 把瓶颈从内存转到计算;Triton 提供了足够的数据移动和 GPU 内存底层控制,能达到接近最优性能。

关键是性能增益随序列长度增长。这正是 FlashAttention 在实践中最重要的地方。

总结

现代 GPU 上性能由内存行为主导,不是 FLOPs;内核融合和 SRAM 驻留比数学技巧更重要;在线 softmax 是 IO 感知注意力的关键;Triton 暴露了足够的硬件细节来写可读又快的内核;仔细分块加自动调优,自定义内核能和厂商实现打平。

FlashAttention 不是因为改了算法才更快,是因为它尊重 GPU 实际的工作方式。

本文只实现了前向传播。扩展到完整的训练级 FlashAttention(反向传播、dropout、各种 mask 变体)留待后续工作。

本文源代码:

https://github.com/MyDarapy/triton

by Katherine Oluwadarasimi Olowookere

1 回调函数的定义

回调函数就是一个通过函数指针调用的函数

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。

概念有些抽象,接下来通过学习qsort函数以及其模拟实现来促进理解。

2 qsort函数

qsort函数是用快速排序方法能对数组、结构体等按提供的规则进行排序的一个库函数。

void qsort (void* base, size_t num, size_t size, int (*compar)(const void*,const void*));

该函数包含四个参数:

  • base是指向待排序内容的首元素地址;
  • num是待排序内容的元素个数;
  • size是待排序内容的一个元素大小,单位字节;
  • 函数指针compar是指向比较函数,需要自行编写,实现对待排序内容中两个元素的比较。

因为qsort函数并不知道你会传入一个什么样的数据类型,所以就需要你自己先写好一个比较函数,再传给qsort使用。比较函数的类型、参数与返回值都有规定,见参考链接。

qsort函数更详细说明的参考链接:https://legacy.cplusplus.com/reference/cstdlib/qsort/?kw=qsort

下面是一个使用qsort函数排序整型数组的示例:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2)
//const在*左侧,确保指针指向的内容不会被修改
{
    return *((int*)e1) - *((int*)e2);
    //要排序的是整形数组,元素是整形,这里先将void*类型强转成int*类型
    //强转成与元素对应的指针类型,解引用才能得到正确的结果
    //这里return返回相减的差,正好符合比较函数的返回值要求
}
int main()
{
    int arr[] = { 5,6,4,1,8,9,2,3,7 };
    int num = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, num, sizeof(arr[0]), cmp_int);
}

下面是一个使用qsort函数排序结构体数组的示例:

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdio.h>

struct Stu
{
    char name[20];
    int age;
};

void test3()//这里展示了两种访问结构体指针指向内容的方法
{
    struct Stu s = { "cuihua", 18 };
    struct Stu* ps = &s;//结构体指针变量
    printf("%s\n", (*ps).name);
    printf("%s\n", ps->name);
    //结构体变量.成员名
    //结构体指针变量->成员名
}

//按照名字比较大小
//e1是指向一个结构体数据的,e1是指向另外一个结构体数据的
//名字是字符串,字符串的比较使用strcmp
int cmp_stu_by_name(const void* e1, const void* e2)
{
    return strcmp((*(struct Stu*)e1).name, (*(struct Stu*)e2).name);
}

int cmp_stu_by_name1(const void* e1, const void* e2)
{
    return strcmp(((struct Stu*)e1)->name, ((struct Stu*)e2)->name);
}

//按照年龄来比较
int cmp_stu_by_age(const void* e1, const void* e2)
{
    return ((struct Stu*)e1)->age - ((struct Stu*)e2)->age;
}

//测试qsort函数排序结构体数据 - 按名字比较
void test2()
{
    struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
    //{"lisi", 35}, {"wangwu", 12},{"zhangsan", 18}
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_name);
}

//测试qsort函数排序结构体数据 - 按年龄比较
void test4()
{
    struct Stu arr[3] = { {"zhangsan", 18},{"lisi", 35},{"wangwu", 12} };
    //{{"wangwu", 12}, "zhangsan", 18},{"lisi", 35}
    int sz = sizeof(arr) / sizeof(arr[0]);
    qsort(arr, sz, sizeof(arr[0]), cmp_stu_by_age);
}

int main()
{
    test2();
    test4();
    return 0;
}

3 模拟实现

第2节解释了qsort函数的参数及运行方式,接下来尝试模拟实现。这里排序方法采用更熟悉的冒泡排序,而非qsort的快速排序。

#define _CRT_SECURE_NO_WARNINGS 1
#include<stdlib.h>

void Swap(char* buf1, char* buf2, size_t sz)
{
    for (int i = 0; i < sz; i++)
    {
        int tmp = *buf1;
        *buf1 = *buf2;
        *buf2 = tmp;
        buf1++;
        buf2++;
    }
}

void bubble_sort(void* base, size_t n, size_t sz, int (*cmp)(const void*, const void*))
{
    for (int i = 0; i < n; i++)
    {
        for (int j = 0; j < n - 1 - i; j++)
        {
            if (cmp((char*)base + sz * j, (char*)base + sz * (j + 1)) > 0)
            {
                Swap((char*)base + sz * j, (char*)base + sz * (j + 1), sz);
            }
        }
    }
}

int cmp_int(const void* e1, const void* e2)
{
    return *((int*)e1) - *((int*)e2);
}

int main()
{
    int arr[] = { 5,6,4,1,8,9,2,3,7 };
    int num = sizeof(arr) / sizeof(arr[0]);
    bubble_sort(arr, num, sizeof(arr[0]), cmp_int);
}

观察上述的示例,并回忆回调函数的定义。

回调函数就是一个通过函数指针调用的函数

如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,被调用的函数就是回调函数

比较函数cmp_int通过函数指针的形式传入bubble_sort函数并在其内部被调用,比较函数cmp_int就是一个回调函数。其实第2节示例中的比较函数们也都是回调函数。

可见,回调函数在实现一些复杂功能时具有独到的优势。想想如果不用回调函数应该是不太好实现相同的功能的。

从记事起,他就和我妈经常吵架、打架。那时候我大概只有三四岁。印象最深的一次,是他拿起一个装满菜的碗朝我妈砸过去,碗在墙上摔得粉碎,油顺着墙往下流,弄得到处都是。我妈打不过他,只能半夜出门,要么去朋友家借宿,要么去开一间旅馆。类似的场景,在我脑子里比比皆是。

现在他不会再跟我妈动手了,但好像人年纪越大,嘴越厉害。自从我在学校寄宿以后,不在家的日子反而过得更轻松。每次放假,别的同学都很开心回家,我却能多晚离校就多晚离校,因为我一点也不想家。即使现在快要工作了,我也没那么珍惜与家人共处的日子。

我有个哥哥,在我和我哥之间,被骂得更多的一直是我哥。从小到大都是这样。还好我哥不像我这么内耗,被骂完一会儿就像个没事人一样,而且他一般不会顶嘴,我就不行,要是不是我的错我还被骂,我会和我爸对吼。他们一直都说他更爱我,确实他骂我打我的次数没那么多,但我仍然讨厌他带来的这种氛围,明明有时候是一件很小的事情,都要被他弄得全家鸡犬不宁。

他这个人只要稍微不顺心,就会把别人当出气筒。我妈几乎成了他的出气筒,什么事都能骂到她身上,小到地上有个垃圾没扫,他都能骂很久。我爷爷奶奶也被他骂,我和我哥也被他骂。看起来像是脾气大、有能耐,实际上就是窝里横,真要让他当众说点什么,他一句话都说不出来。

现在他和我妈在家附近的厂房门口开了一个炒菜店。他每天主要就是炒菜,别的基本不管。买菜、洗碗、扫地这些,都是我妈一个人做。冬天这种天,他绝不可能碰一点冷水。我妈白天很早就起来买菜,在店里切菜备菜;他常常睡到九、十点起床,吃完午饭就上街赌钱。他非常嗜赌,而且赌得不小。每次把钱输完,就吵着找我妈要钱。我们早就知道他的德行,所以我妈基本不会把大钱给他。

我最受不了的是他那张嘴,像在骂街一样。每天醒来都要听见他的大嗓门在骂人,我们家另外五个人轮流被他骂,尤其是我妈。所以我到现在也想不明白,我妈为什么不跟他离婚。几年前我妈又被他打的时候,给我打电话说这事,我让他们离婚,免得以后受更多罪。但我妈不同意。她可能觉得传出去不好听,也可能觉得一个人没依靠,毕竟我外公外婆也去世了。想想她其实挺难的。

好在我马上毕业、工作了,可以离他远一点。但我妈、我哥还有爷爷奶奶还在家里,每天都要听他的念叨,尤其是我妈。细想下来,从小到大这个家没给过我多少温暖。按我的性格,我也能做到跟这个家几乎不联系,但想到我妈,我还是放不下。

我不知道该怎么面对这样的父亲。有时候会觉得,如果没有他,我们这个家会幸福很多。

首先说下 durable object 中转的优势

  • 可以使用 cf 全球 330 个城市的机房部署你的中转实例,除了中国,用户到中转服务器的延迟不会高于 20ms 。给一组我实测的数据,测了两个位置,在 scaleway 的法国机房,延迟 16ms ,在腾讯云香港机房,延迟 10ms 。
  • do 按运行时间计费,不按流量计费,因此假设你将每一条中转信道都分配独立的 do 实例,那么你可以得到一个比较完美的收费模型,基于每条信道的存活时间收费,这对 toC 或者 SaaS 都是可接受的。
  • 流量免费,do 通过 worker 的 websocket 对外暴露端点,worker 的流量是不计费的
  • 自动水平扩展,do 实例拉起是秒级并且基本不做数量限制的

下面说下实现思路

worker 对外暴露 websocket 连接,worker 内部把 websocket 连接转成 streamable http 和 do 通信

worker 对外暴露 websocket 连接,一个是为了实时性,一个是为了规避 worker 上传请求体 100MB 大小限制
worker 对 do 的连接转成 streamable http 则是为了优化 do 的计费机制,do 对 websocket 的计费是按流入方向每 20 个 packet 折算成一次调用收费,但是如过你用 streamable http ,一个请求就是一个请求。同时让 worker 和 do 实例发起一个 websocket 连接,这是为了保活 do 实例,do 实例在你的请求返回后会自动销毁,所以你需要一个保活的 websocket 连接到 do 。

我已经在一些应用场景测试了这个中转信道

  • 端到端加密的即时通信,所有痕迹随着 do 销毁全部消失
  • 一对一视频通话,我用的香港节点去创建中转信道,因此我猜测 do 实例是在 cf 的香港机房,延迟在 2-3s

分布式追踪体系的核心价值本应是打通全链路的可观测性,但传统Span数据仅聚焦于技术调用的时序与拓扑维度,缺失业务维度的锚点,导致追踪结果始终停留在技术层面的链路排查,无法与真实业务场景形成联动,这成为了可观测体系落地的核心瓶颈。将Span数据与业务核心标识建立强关联,并非简单的字段拼接,而是对追踪链路进行语义化重构,构建技术链路与业务流程的双维映射体系,让每一段技术调用都能对应到具体的业务节点,让端到端分析从纯技术视角升级为业务驱动的全维度洞察,这也是分布式追踪从工具化走向价值化的关键一步。在实际的技术落地中,纯技术Span的分析往往只能定位服务调用的异常节点,却无法知晓该异常影响了哪一类业务对象、哪一个业务流程,导致排查效率低下,比如在工业产线场景中,某批次工序出现执行异常,纯追踪数据仅能显示核心服务调用时延偏高,却无法关联到具体的工序批次与生产设备,运维人员需逐一排查所有关联链路,耗时数小时才能定位问题根源;而关联业务标识后,可直接通过工序批次编码锁定全链路技术数据,实现从业务问题到技术根因的快速溯源,彻底打破技术与业务之间的观测壁垒,让可观测数据真正服务于业务问题的解决。

构建Span与业务标识的关联体系,首要前提是完成业务维度的标准化定义与锚点梳理,需脱离电商、金融等通用场景,聚焦工业制造、物联网终端、政务服务等领域的核心业务标识,比如工业场景的工序批次编码、物联网终端的设备唯一标识、政务服务的事项办理编码等,先明确业务流程中的核心锚点节点,再匹配分布式追踪中的Span生成节点。同时要统一业务标识的编码规则与传递规范,避免不同服务节点因标识格式不统一、传递逻辑不一致导致的关联断裂,这是保障关联有效性的基础。在实际梳理过程中,需深入拆解业务流程的全生命周期,联合业务团队与技术团队开展联合调研,将业务流程划分为入口节点、核心处理节点、收尾节点,对应到追踪链路的服务调用入口、核心逻辑执行、结果返回节点,确保每个关键业务节点都有对应的Span锚点,同时建立全局业务标识字典,统一不同服务中业务标识的字段命名与格式标准,比如政务服务中所有服务均采用统一的事项编码字段,避免跨服务传递时的字段不匹配问题,这种标准化梳理能从根源上避免关联数据的碎片化,让双维映射具备稳定的基础,也为后续跨团队协作落地提供了统一的执行依据。

关联的核心实现路径在于链路上下文的语义化携带与跨节点透传,需在Span的扩展属性中嵌入业务核心标识,同时建立技术调用节点与业务流程节点的精准映射,在链路的入口节点完成业务标识的初始化注入,随后在同步调用、异步调用、跨域调用等全场景下实现标识的无损耗透传。对于同步调用场景,依托追踪上下文的传递机制完成标识流转,无需额外增加复杂逻辑;对于异步调用场景,需在消息传递载体中嵌入业务标识与追踪上下文的绑定关系,避免异步队列传递导致的关联断层。这一过程的核心是保障业务标识与Span的绑定关系在全链路中不丢失、不篡改,让每一个Span都能精准归属到对应的业务对象。在实际操作中,还需针对跨服务、跨集群、跨语言的调用场景优化透传逻辑,比如针对不同语言开发的服务,统一封装标识透传的轻量组件,减少适配成本,同时严格控制标识传递的额外开销,通过极简封装避免链路耗时的大幅增加,另外建立入口节点的标识校验机制,对注入的业务标识进行格式与合法性校验,过滤无效标识,从实现层面保障关联数据的准确性与完整性,避免无效数据干扰后续的分析工作。

关联后的数据需完成深度融合与结构化建模,摒弃简单的存储叠加模式,构建技术-业务双维融合的数据模型,将Span的时序数据、拓扑数据与业务标识进行绑定,形成可追溯、可聚合的业务链路图谱。基于该模型,可按业务标识维度对Span数据进行聚合分析,比如按设备唯一标识聚合该终端全生命周期的所有技术调用链路,按工序批次编码聚合对应批次的全流程链路耗时与节点状态,同时提取业务维度的核心指标与技术维度的链路指标,形成联动分析的基础。这种建模方式打破了传统追踪数据的技术孤岛,让技术链路的每一个细节都能对应到业务场景的具体表现,为端到端分析提供了数据支撑。在数据建模过程中,还需优化数据的存储与查询逻辑,采用时序数据库搭配业务标识索引的存储方案,适配业务标识的多维度查询需求,同时对数据进行分层处理,原始Span数据用于精准溯源,融合后的数据用于链路分析,聚合数据用于业务洞察,既避免了数据冗余,又提升了关联数据的检索效率,让业务人员与技术人员都能快速获取所需的链路分析数据,无需在海量数据中进行繁琐筛选。

基于关联数据的端到端业务分析,核心是实现业务场景化的链路洞察与问题定位,可针对不同业务场景构建专属的分析模型,比如在工业场景中,分析某一工序批次的全链路调用耗时分布,定位业务流程中技术链路的瓶颈节点,进而优化服务配置提升工序执行效率;在物联网场景中,通过设备标识关联的Span数据,分析终端在线状态与链路调用成功率的联动关系,识别终端链路的异常规律,提前预判终端故障风险。同时可实现业务指标与技术指标的交叉分析,比如将业务流程的完成率与技术链路的调用成功率、响应时延进行关联,量化技术链路问题对业务效果的影响程度,比如某政务服务事项的办理完成率下降,通过关联分析发现是核心审核服务的链路时延增加导致,进而针对性优化服务性能,提升业务办理效率。这种分析模式让分布式追踪不再是单纯的技术运维工具,而是成为业务优化、流程迭代的核心支撑,能够精准定位业务流程中隐藏的技术短板,为业务决策提供可量化的数据依据,真正实现了可观测数据的业务价值转化,让技术优化与业务发展形成正向循环。

关联体系的长期落地需要持续的优化与质量治理,一方面要建立关联规则的动态适配机制,当业务流程迭代、服务架构调整时,通过配置中心同步更新业务标识的注入节点与透传逻辑,无需修改服务代码即可完成适配,避免因业务变化导致关联失效;另一方面要构建关联数据的质量治理体系,设定标识完整率、链路绑定准确率等核心治理指标,定期通过自动化工具校验业务标识的完整性、链路绑定的准确性,及时修复标识丢失、链路断裂等问题,保障关联数据的长期有效性。

不同IoT终端的资源禀赋与业务诉求存在天壤之别,环境感知类终端仅需完成基础数据上报的核心交互,工业现场传感终端则需兼顾指令接收与状态回传,楼宇监测终端还需适配间歇性的断网续传需求,这就决定轻量化设计绝不能采用一刀切的模式,必须基于终端硬件参数台账与业务场景图谱做精细化适配,比如针对存储容量不足64KB的土壤监测终端,要彻底剥离所有非核心的扩展交互模块,仅保留请求发送与响应解析的最简链路,而针对具备256KB存储余量的楼宇控制终端,可适度保留基础的异常适配与数据校验功能,这种差异化的资源-功能映射,是实现两者平衡的核心前提,也是规避资源浪费与功能缺失的关键抓手。

轻量化的本质绝非粗暴的功能裁剪,而是对客户端整体架构的分层解耦与资源精准映射,我们将客户端拆解为核心交互层、场景适配层、资源调度层三个低耦合模块,核心交互层仅保留API请求发起、响应接收、基础数据解析的必备链路,剔除所有冗余的协议兼容逻辑、多类型回调机制与非必要状态追踪,这一层的设计核心是极致精简,所有操作都围绕终端与服务端的基础数据闭环展开,确保资源占用始终控制在终端硬件的安全阈值内;场景适配层则采用模块化按需加载的设计思路,根据终端的固件标识与业务配置,自动加载对应场景所需的功能模块,比如仅需单向上报的终端不加载指令接收模块,仅需基础交互的终端不加载批量数据处理模块,通过模块化的动态加载实现功能的灵活拓展,避免闲置功能占用终端资源;资源调度层则承担实时感知与动态调节的核心作用,持续采集终端的内存使用率、传输带宽、功耗水平、运行温度等核心状态参数,根据实时数据调整交互链路的资源分配策略,比如在终端内存占用逼近临界值时,临时缩小缓存颗粒度并简化数据封装格式,在带宽不足时减少交互握手频次并精简数据帧结构,这种分层解耦的架构设计,让轻量化有了可落地的执行路径,既保证了资源占用的可控性,又为功能完整性预留了弹性拓展空间。在具体落地操作中,我们会先对每一层的功能模块做资源消耗量化评估,将资源占比高且非核心的模块做轻量化重构,比如将复杂的序列化逻辑简化为适配终端的极简格式,将多分支的响应处理逻辑整合为统一的基础解析流程,同时通过松耦合的接口设计,确保单一模块的轻量化改造不会波及整体交互链路的稳定性,让整个客户端在精简资源的同时,保持交互逻辑的连贯性与可靠性。

功能完整性的界定需建立场景化的优先级体系,而非盲目追求全量功能的兼容覆盖,我们基于IoT终端的业务生命周期与交互链路关键节点,将API交互功能划分为核心必选、场景可选、扩展备用三个层级,核心必选功能是终端完成基础业务的核心骨架,包括数据上报、指令接收、状态同步等关键动作,这类功能必须完整保留且经过稳定性强化,是客户端不可动摇的核心;场景可选功能则根据终端的具体应用场景决定启用与否,比如批量数据交互、断点续传、阈值告警联动等功能,仅在对应业务场景触发时启用,常规状态下处于休眠状态,不占用终端运行资源;扩展备用功能则是针对特殊场景的预留能力,比如远程配置更新、固件协同交互、跨终端数据联动等,这类功能采用延迟加载的设计,仅在服务端下发触发指令或终端满足特定条件时才启动,平时不参与核心交互流程。这种层级化的优先级划分,让功能完整性有了清晰的边界,既避免了为追求全功能覆盖导致的资源过载,又保证了终端在不同场景下的业务适配能力,同时我们会对保留的功能模块做轻量化优化,比如将批量数据交互的缓存机制简化为适配终端存储的分段缓存模式,将断点续传的逻辑简化为基础的分段确认机制,在不丢失核心功能的前提下,最大限度降低资源消耗。在实际场景适配中,我们会针对每一类IoT终端做专属的功能优先级匹配,比如农业环境监测终端优先强化环境数据上报与异常告警功能,工业设备传感终端优先保障设备状态同步与指令执行功能,楼宇安防终端则侧重监测数据实时回传与联动触发功能,通过场景化的功能界定,让功能完整性与终端资源形成精准的双向匹配。

实现轻量化与功能完整性的动态平衡,核心是构建基于终端实时状态的自适应调节机制,这种机制并非静态的设计规划,而是贯穿客户端全生命周期的动态响应体系,我们在客户端中嵌入轻量的资源状态感知单元,以毫秒级的低频次采集终端的内存、带宽、功耗、运行负载等核心参数,同时预设多套交互模式的触发阈值,当终端资源处于充足区间时,自动启用完整交互模式,保留所有核心必选与场景可选功能,最大化保障交互的全面性与稳定性;当终端资源处于临界区间时,平滑切换至轻量化交互模式,自动关闭场景可选功能,简化数据封装与解析流程,降低交互链路的资源消耗;当终端资源处于紧张区间时,仅保留核心必选功能,关闭所有扩展能力,确保基础业务交互不中断。这种动态调节机制让客户端能够自适应边缘端复杂多变的运行状态,规避了静态设计中资源过剩或不足的固有问题,同时我们会结合不同终端的硬件特性与业务需求,对调节阈值做精细化校准,比如低功耗土壤传感器的内存触发阈值设为总容量的25%,楼宇控制终端的阈值则放宽至35%,弱网环境下的带宽触发阈值也会做针对性下调,让动态适配更贴合实际应用场景。在实践调试中,我们通过大量的边缘端实测,不断优化调节逻辑与阈值参数,确保模式切换的流畅性与无感知性,避免因切换导致的交互中断或数据丢失,同时通过极简的状态记录方式,跟踪模式切换的频次、时长与效果,为后续的机制优化提供真实的实践数据支撑。

实践验证是平衡策略落地的核心环节,我们构建了覆盖资源占用、交互性能、业务适配三大维度的闭环验证体系,资源占用维度重点监测客户端在不同交互模式下的内存峰值、存储消耗、功耗水平,确保所有指标均控制在终端硬件的安全运行区间内,比如内存占用峰值不超过终端总容量的30%,功耗消耗符合低功耗终端的续航标准;交互性能维度主要测试请求响应时延、数据传输成功率、模式切换流畅度,确保轻量化改造不会影响交互的效率与稳定性,比如常规交互时延控制在毫秒级,弱网环境下的传输成功率保持在极高水平;业务适配维度则验证不同场景下的功能完整性,确保核心业务能够稳定完成,场景功能能够按需启用,扩展功能能够正常触发。在验证过程中,我们覆盖了多类型IoT终端,包括低功耗环境传感器、边缘控制终端、小型楼宇网关等,同时模拟了窄带传输、弱网波动、电磁干扰、批量数据交互等复杂场景,通过多终端、多场景的交叉实测,精准定位平衡策略中的薄弱环节并及时优化,比如针对弱网环境下的交互卡顿问题,优化轻量化交互模式的链路设计,针对批量数据场景的资源过载问题,调整动态调节的阈值参数。这种闭环验证让平衡策略从设计层面落地到实践层面,同时我们会沉淀验证数据与优化经验,形成终端类型-资源阈值-功能匹配的实践台账,为后续同类边缘端API客户端的开发提供可复用的技术参考。

从长期技术演进的视角来看,轻量化与功能完整性的平衡并非固定的解决方案,而是会随着IoT边缘技术的迭代持续升级,未来的边缘端API客户端将朝着自适应协同的方向深度发展,通过终端与边缘网关的资源共享、功能卸载,进一步突破单一终端的资源限制,比如资源极度紧张的终端可将复杂的解析逻辑卸载至邻近的边缘网关,自身仅保留基础的请求发送与数据接收能力,同时随着低功耗硬件与窄带传输协议的持续升级,客户端的轻量化设计将更注重底层指令级的精简优化,而非单纯的功能删减,功能完整性则会转向场景智能适配,通过场景特征识别自动匹配最优的功能组合。

也加了 https://github.com/cxfksword/jellyfin-plugin-metashark 这个插件
是这个 jellyfin 在馒头是废的?
元数据都选的是 MetaShark
资源是这种 Love.Death.&.Robots.S04E01.1080p.NF.WEB-DL.DDP5.1.Atmos.DV.H.265-ARiC.mkv

version: "3.8"
services:
  jellyfin:
    image: nyanmisaka/jellyfin:latest
    container_name: jellyfin
    environment:
      - PUID=1026   
      - PGID=100   
      - TZ=Asia/Shanghai
    volumes:
      - ./config:/config
      - ./cache:/cache
      - /volume2/video1:/video1:rw
    ports:
      - 8096:8096
    restart: unless-stopped
 

三个只有程序员才能听懂的笑话

01 程序员最恐怖的几个字

程序员最害怕听到的一句话是:

“我就改了一点点。”

因为这“一点点”,
可能改了数据库结构,
动了公共工具类,
顺手删了个你不知道是谁在用的方法,
最后还会在你电脑上完美运行。


02 程序员的自信来源

程序员的自信不是来自能力,
而是来自这句话:

“在我电脑上是好的。”

只要这句话还说得出口,
Bug 就一定不是我的问题。


03 AI 辅助开发的真实写照

我:

帮我改一下这个方法,不要影响其他功能。

AI:

好的,已帮你重构整个项目结构。

我:

我只是想改一行。

AI:

已顺手优化命名、拆分模块、删除冗余代码。

第二天上线:

“怎么登录模块也挂了?”

写在最后

如果你看完没有笑,
那说明你可能还没被:
线上 Bug、合并冲突、
重构遗留代码、
AI “好心帮倒忙”真正毒打过。

如果你笑了,
那我们大概率是同一类人。

本文由mdnice多平台发布

嘿,全栈开发者们!

还记得当初被 JavaScript 的 async/await 惊艳到的时刻吗?一个 await,就把那些繁琐的回调地狱(Callback Hell)变成了优雅的同步代码,让 Web UI 始终保持流畅。你可能心里暗想:“Python 要是有这玩意儿就好了。”

恭喜你,Python 3.5+ 不仅有了,而且在 FastAPI 的加持下,它正以一种前所未有的姿态,挑战着高并发 Web 服务的极限。然而,当你把 JS 里的异步直接平移到 Python,很可能会发现:“为什么我的 Python 异步,没我想象的那么快?”

别急,这本“生存手册”就是为你准备的。

1. 语法很像,但“脾气”有点不同

首先,我们要承认,Python 的 async/await 在语法层面上,和 ES6 简直是双胞胎:

JavaScript (React/Node.js):

async function fetchData(userId) {
    const user = await fetch(`/api/users/${userId}`); // 网络请求
    const orders = await fetch(`/api/orders?user=${user.id}`); // 依赖上一个结果
    return { user: await user.json(), orders: await orders.json() };
}

Python (FastAPI):

import httpx # 异步 HTTP 客户端

async def fetch_data(user_id: int):
    async with httpx.AsyncClient() as client:
        user_resp = await client.get(f"http://api.internal/users/{user_id}") # 网络请求
        user_data = user_resp.json()
        orders_resp = await client.get(f"http://api.internal/orders?user={user_data['id']}") # 依赖上一个结果
        return {"user": user_data, "orders": orders_resp.json()}

你看,代码逻辑几乎一模一样。一个 await,就能让你在等待网络请求、数据库查询、文件读写(这些都是 I/O 密集型操作)时,把 CPU 的控制权交出去,让 Event Loop 去处理别的请求。

核心思想: 异步不是让你的代码跑得更快,而是让你的服务器在等待 I/O 时不再发呆,从而能同时处理更多的请求。

2. 警惕“伪异步”:Python 异步的隐形杀手

当你兴奋地给 FastAPI 的路由加上 async def,并开始调试时,如果发现服务的并发能力并没有显著提升,甚至有时候还会卡顿,那很可能就是你遇到了“伪异步”。

什么是“伪异步”?
简单来说,就是在异步函数 async def 内部,执行了同步阻塞的操作。

比如,如果你在 async def 函数里使用了:

  • time.sleep(2)(模拟耗时操作,但它是阻塞的)
  • requests.get('...')(Python 传统同步 HTTP 库)
  • json.dumps(huge_object)(处理超大 JSON 对象的 CPU 密集型操作)
  • 某些数据库 ORM 的同步版本方法(如 session.query().all()

这些操作,无论你外层用多少 async/await 包装,它都会直接阻塞整个事件循环(Event Loop)。你可以把它想象成在 JS 的 async 函数里直接调用一个同步的、耗时 5 秒的循环计算——那你的 Node.js 服务也会瞬间卡死。

生存法则一:异步函数中,只用异步库。
当你在 async def 函数中使用任何可能阻塞的 I/O 操作时,请务必寻找对应的异步版本库。例如:

  • asyncio.sleep() 替代 time.sleep()
  • httpxaiohttp 替代 requests
  • asyncpgmotor(MongoDB)等异步数据库驱动,或者 ORM(如 SQLAlchemy 2.0+)的异步模式。

3. CPU 密集型任务的“逃生舱”

异步编程擅长处理 I/O 密集型任务,但它对 CPU 密集型任务却无能为力。因为 CPU 密集型任务的瓶颈在于 CPU 本身,而不是等待。

如果你在 async def 函数中执行一个长达几秒的复杂计算(比如大量的字符串处理、图像处理、机器学习推理等),它依然会霸占 Event Loop,导致其他等待中的异步任务无法得到调度。

生存法则二:计算任务,交给线程池或进程池。

FastAPI 框架非常聪明。如果你定义的路由函数是普通的 def,FastAPI 会自动将它放到一个独立的线程池中运行,这样就不会阻塞主 Event Loop。

但如果你的计算逻辑就在 async def 内部,且你不想让它阻塞 Event Loop,你就需要手动使用 run_in_executor 来将它“卸载”到线程池或进程池中:

import asyncio
from concurrent.futures import ThreadPoolExecutor

executor = ThreadPoolExecutor(max_workers=4) # 可以配置线程数

def very_heavy_cpu_task(data):
    # 模拟耗时计算
    result = sum(range(data))
    return result

@app.post("/process_data")
async def process_data(data: int):
    # 将 CPU 密集型任务提交到线程池执行,不阻塞 Event Loop
    result = await asyncio.get_event_loop().run_in_executor(
        executor, very_heavy_cpu_task, data
    )
    return {"result": result}

4. 从 WSGI 到 ASGI:后端架构的深度进化

你可能已经用过 Flask 或 Django,它们是基于 WSGI (Web Server Gateway Interface) 标准的。WSGI 的设计理念是“请求-响应”模型,通常每个请求会占用一个独立的线程。

而 FastAPI 是基于 ASGI (Asynchronous Server Gateway Interface) 标准的。ASGI 允许一个进程内的 Event Loop 高效调度成千上万个轻量级协程。这就像:

  • WSGI: 每一个订单(请求)都需要一个专属服务员(线程)从头跟到尾。服务员一旦去仓库(数据库 I/O),就得等在仓库门口。
  • ASGI: 一个总调度员(Event Loop)同时管理很多订单。当一个订单需要等仓库(I/O)时,调度员会立刻去处理下一个订单,等仓库那边叫他了再回来处理。

这种底层架构的演进,让 Python 在处理长连接、流式数据(如 LLM 的流式输出)、高并发 API 等现代 Web 场景时,拥有了和 Node.js 媲美的能力。

写在最后:别让你的 Python 异步,输在“等待”上

被 JS 的 async/await 宠坏,是好事。它为你打开了非阻塞编程的大门。当你带着这种直觉来到 Python,并结合 FastAPI 的工程实践,你将发现 Python 在高并发服务领域的巨大潜力。

记住这本“生存手册”的核心:异步不是让你写代码更酷,而是让你的服务器在面对 I/O 等待时,能够更“聪明”地工作。 那些被浪费在等待上的 CPU 周期,如今都能被榨取出最大的价值。

现在,是时候在你的 Python 服务里,真正释放异步的力量了。

万万没想到!2026年2月8日,全球突发连环惊天大事,每一件都足以震动世界,美国更是内忧外患接连暴击,彻底陷入两难绝境,看完让人脊背发凉,谁也没料到局势会恶化到这种地步! 先看最吓人的!乌克兰竟敢公然触碰俄罗斯核底线,直接炸向俄军洲际导弹基地,差点引爆核大战,整个欧洲都慌了! 据环球网紧急爆料,2月8日,乌克兰国防部突然官宣,其武装部队直接发射火烈鸟巡航导弹,精准打击俄军洲际导弹基地试验场!现场火光冲天、浓烟滚滚,俄军多处设施被炸毁,其中一个存放洲际导弹的战备仓库更是损毁严重,火箭军被迫紧急撤离现场,场面一度失控! 更恐怖的是,有媒体扒出,这次袭击绝非乌克兰孤军奋战,背后疑似有法国、英国、德国等北约国家暗中撑腰,全程提供情报支持和远程策划!要知道,俄罗斯核反击条例早就明确规定,只要核力量体系、洲际导弹发射井等核心目标遭到攻击,就有权动用核武器反击! 目前俄罗斯国内已经炸开了锅,全民讨论乌克兰此举是否已经触发核反击条款,一旦俄罗斯按下核按钮,整个欧洲都将沦为火海,甚至波及全球,后果不堪设想!谁也不敢相信,乌克兰竟然敢赌上全人类的安危,迈出这致命一步! 就在俄乌核危机一触即发之际,伊朗也被逼急了,直接放狠话硬刚美国,吓得美军紧急发布撤侨令,中东局势彻底失控! 京报网火速报道,伊朗陆军发言人突然发声,字字铿锵,警告美国:中东地区的美军基地,我们想打就打,易如反掌!伊朗已经做好万全准备,只要遭到一丝攻击,战火将蔓延整个中东,所有美军基地都将成为目标,一个都跑不掉! 这话绝非虚言!伊朗方面已经证实,1000架战略无人机已经全部纳入四大军种作战体系,防御系统全面升级、全员待命,随时可以投入战斗!更致命的是,就在伊朗放话后不久,美军林肯号航母在阿拉伯海,突然击落一架伊朗无人机,这架无人机当时正对航母构成致命威胁,双方火药味已经浓到极致! 吓得美国连夜发布安全警告,紧急敦促所有美国公民,尽快自行撤离伊朗,而且必须制定不依赖美国政府协助的离境计划!要是暂时走不了,就就地避险,囤好物资,别出门、别参与示威,低调保命! 更雪上加霜的是,美国本土也突发危机,政府紧急发布就地避难令,800米范围全面封控,民众被严禁出门,现场一片混乱! 新华社紧急通报,2月8日,美国东北部康涅狄格州附近,一辆载有危险化学品的货运列车突然脱轨,6节车厢冲出轨道,其中4节直接滑入旁边的河中,场面十分惊险!更可怕的是,脱轨车厢里装的全是液化丙烷,这种化学品无色无味,一旦泄漏,民众根本无法察觉,随时可能引发爆炸、中毒,后果不堪设想! 事故发生后,当地政府瞬间慌了,立刻向事发地方圆800米内的居民,发布就地避难令,强硬敦促所有人待在室内,严禁外出,周边道路全部封闭!危险品处理小组火速赶赴现场,密切监测泄漏情况,在河中放置危险品栅栏,全力防范风险,但截至目前,事故原因仍在调查中,泄漏隐患尚未完全排除,当地民众人心惶惶,彻夜难眠! 谁能想到,一天之内,美国竟然遭遇三重暴击:本土被封控、全球急撤侨、海外遇硬刚,内忧外患交织,彻底乱成了一锅粥!而俄乌核危机、中东战火阴影,更是让全球陷入恐慌之中! 有人感叹,2026年2月8日,绝对是载入史册的一天,俄乌是否会爆发核大战?伊朗和美国会不会直接开战?美国本土的危机能否解除?

如题,见多识广的老哥们讨论一下。

之前一直用个税过不了提交了 n 次,后来把电子邮件里的信用卡账单用浏览器内置翻译成英文,再加上了个海外的民宿 homestay 地址过了(居住地址还是国内),想着以后客服问起来就说去旅游生活了一圈回来了。。。不过感觉分分钟会被关户,主要还是充进去小额自动换汇成 usd 结合 wise 取款,有时赌一块钱 forecast 合约

目前新手美股还是 schwab 嘉信靠谱,免佣金只买了 1 股 QQQM ,通过 slices 也能零股交易,零门槛后没了支票和借记卡,国内手机号很难接验证码不过开户后接到了客服的欢迎电话。

#超级羊毛 Apple Music 新增免费 6 个月试用,支持国区和其他区,仅限新用户,老用户只能看到折扣价开通的提示。

领取流程:

点击活动链接: https://ourl.co/amsc (不要 PC 点击,不然可能只有 1 个月)

iOS 相机可以直接扫码,安卓用户也可以使用微信扫码并使用自带浏览器打开:

点击链接后自动跳转到 Apple Music 看到领取提示(若未安装会先跳转商店安装)

安卓用户领取时会提示创建 Apple 账户,正常流程输入邮箱创建即可,不需要绑定任何付款方式。

有 Google Play 可以直接商店安装 Apple Music ,安装后先不要启动,点击活动链接拉起 Apple Music 就能看到优惠,点参与然后按提示登录或创建苹果账号。

APK 文件直接下载: https://dl.techwan.org/landian/apps/applemusic

Windows 10/11 可以在微软商店下载 Apple Music

如何取消:

如果没取消订阅则六个月后会按照每月 11 元收取订阅费,领取免费试用后可以立即转到设置、点头像、订阅、找到 Apple Music 然后立即取消订阅,取消订阅不影响继续白嫖免费试用六个月。

注意:也有用户只能看到 3 个月或者 1 个月的优惠,可能视账号和设备情况而定

追觅集团 | 热招岗位汇总

欢迎将简历发送至:,请在简历文件名中体现重要信息(如你的姓名、公司或学校背景、投递岗位名称)。

热招岗位

研发类:算法、软件、嵌入式、硬件、结构、电机控制、仿真、测开、CMF 、ID 、视觉设计、软件项目经理、产品经理、消费者洞察、产品数据...
制造供应类:CMF 、PQM 、AQE 、POE 、SQE 、采购、数分、工艺、机械、NPI...
职能类:专利工程师、AI 产品经理、财务 BP 、财务分析、HRBP 、审计 / 内控管培生...
营销类:区域营销、销售、GTM 、电商...
工作地点:苏州、深圳、北京、上海


[智能影像 BU (运动相机、手持云台)]

1 、公司介绍

团队专注于高端专业影像赛道,致力于打造全球领先的运动相机、手持云台等 AI 影像产品。近期热招嵌入式 / 结构 / 硬件 / 产品 / 销售 / 职能 / 供应链等方向岗位。

2 、热招职位

Base 地:深圳

  • DevOps 全栈 (J52257):3 年以上经验;前期偏运维(通过写代码消除重复劳动)+ 开发 Web 平台
  • 云台算法工程师 (J47814):云台 / 飞控算法方向均可
  • 射频工程师 (J49241):需要 wifi/BT/GPS 多模块短距离射频经验
  • EIS 算法工程师( slam )(J52393)
  • 高级嵌入式软件工程师(低功耗)(J50992):象限内公司优先,可开放 base 苏沪,2 轮面试
  • 高级嵌入式开发工程师(稳定性)(J50998):象限内公司优先,可开放 base 苏沪,2 轮面试
  • SQE(J50591)
  • 云台结构工程师( J42113 )
  • 用户研究负责人( J51430 )
  • 自研产品经理( J42032 ):影像相关经验,2~3 轮面试
  • 采购商务( sourcing )(J49183)
  • 云台固件产品经理( J42217 ):影像效果 / 影像定义
  • 嵌入式开发总监 (J46792)
  • 云台 BU 负责人 (J46791)
  • 软件研发总监 (J46464)
  • 海外整合营销( J43939 )
  • 整合营销( J42119 )
  • 社群用户运营 (J51003)
  • 海外 GTM ( J42047 )
  • 采购履行 buyer (J51008)
  • 成本专家 (J50071)
  • 投融资经理( J49962 ):base 不限 (上海 / 深圳 / 苏州),FA 机构优先
  • 广角固件产品经理( J43933 )
  • APP 产品经理( J42029 )
  • 测试开发工程师 (J45392)
  • 抖音运营 (J46540)
  • 渠道销售 (J38588):具备北区相机渠道销售经验
  • sourcing(J51011)
  • 工业设计 ID (J50571)
  • 手柄结构工程师( J42027 )

3 、社招目标公司范围

一象限
大疆、影石中国区:倍思、华为、小米、宝洁
二象限
字节(剪映)、快手、万兴、腾讯、海康威视、道通、安克、云鲸
三象限
小米、OPPO 、VIVO 、传音、华为、高通、创通通达

4 、校招目标院校

北京大学、清华大学、复旦大学、上海交通大学、南京大学浙江大学、中国科学技术大学、哈尔滨工业大学、西安交通大学


[智能路由 BU ] -BASE 深圳/苏州

  • 产品经理:家用路由器方向,智能家居方向最好,base 深圳、苏州
  • 社媒运营专员:base 苏州,3 年相关经验,内部活水也也可
  • 舆情专员:base 苏州、深圳,3 年相关经验,内部活水也也可
  • 内容运营专员:base 苏州、深圳,3 年相关经验,内部活水也也可
  • 项目经理:路由器相关相关经验优先,活水也可,base 深圳、苏州
  • 销售一号位:海外销售需求,路由器行业也可;地点均可
  • 系统测试:路由器行业相关,通信行业也可,自动化测试方向,base 深圳,大量需求
  • 模具工程师:智能硬件相关,路由器业务;深圳;
  • 采购负责人:路由器行业经验,地点 open ,活水也可
  • 质量负责人:路由器行业经验,地点 open ,活水也可
  • 供应链负责人:路由器行业经验,地点 open ,活水也可
  • 拍摄/剪辑岗位: base 苏州,内部活水也可;
  • 投融资:股权融资方向,通信行业;智慧家居,智能硬件方向,base 均可。


[泳池 BU ]

  • 投融资 BP ,至少 3 年经验,可以 base 苏州和深圳
  • 采购:中级以上,ODM 采购
  • DQE/测试,负责人优先
  • 产品营销、海外销售(北美 /西南欧/东南亚),至少 8 年经验,可以 base 苏州和深圳
  • 算法 /嵌入式/结构/硬件长期招 Junior 要学习能力强,可培养的,资深的看技术能力


[咖啡机 BU ] 🔥☕️

我们正在打造家用高端全自动/半自动咖啡机,追求极致体验,专注技术与美学的融合。如果你是咖啡热爱者,或对厨电创新有热情,欢迎加入我们,一起做“有风味”的产品!

📍Base:苏州 / 北京
热招岗位

  • 硬件产品经理
  • 项目经理
  • HRBP
  • PR 新媒体营销专员(小红书)
    (具备咖啡机/厨电经验者优先)


[海运 BU ]

销售、HRBP 等关键角色,覆盖国内仓储干线、国际港到港、跨境仓到门、海外仓代发全业务链!
为什么选择我们?
✅ 黄金赛道:锚定跨境物流红利,告别 “传统物流” 平庸
✅ 硬核实力:业务闭环稳定盈利,发展前景清晰可见
✅ 爆炸增长:团队与业务同频扩张,解锁 “超预期” 成长空间 [火]** [急招核心岗位] **[火]

  • 销售一号位:拥有海运、空运、陆运及铁路运输、跨境电商、国际快递、海外仓运营等行业产品销售经验,并有丰富客户资源可转换实现收益; Base 地:不设限,优先苏州/上海/深圳/杭州
  • 国际货代-融资 BP:FA/IR 背景人才,具备头部美元/人民币基金成功募资经验,基金合伙人层级候选人也可以。 Base 地:苏州
  • 法务 BP:3-5 年以上跨境物流、国际贸易、供应链领域法务工作经验,熟悉跨境物流行业商业模式和业务流程。 Base 地:苏州
  • HRBP (接受活水):国际头部物流公司背景或百万猎头经验 Base 地:苏州。


[个护 BU ]

  1. 投融资 BP/经理,具备成功融资亿元以上经验; or 政府募资,具备丰富政府资源;base 苏州
  2. 海外 PR 专家,美妆/个护/智能硬件/科技/家电行业背景,有 CES 、MWC 、IFA 经验优先,海外媒介资源优先,base 苏州(优先)、深圳可以沟通
  3. 结构工程师,小家电均可,base 苏州
  4. 高级硬件工程师,5 ~ 6 年及以上经验,家电行业均可,base 苏州
  5. 项目采购工程师,具备项目 0-1 全链路采购落地经验,base 苏州
  6. 电子采购工程师,具备智能硬件电子件采购经验,base 苏州
  7. 有审美有落地的工业设计师/ID 设计师,小家电/3C/时尚/消费电子行业经验,base 苏州
  8. 产品营销,有亚马逊渠道整合营销 or 全域营销经验,个护/小家电/数码 3C 行业背景,base 苏州 or 深圳
  9. 海外产品经理,英语可作为工作语言,base 苏州
  10. 嵌入式软件工程师,小家电行业/芯片/电机驱动,base 苏


[星辰汽车 BG ] 核心研发岗:

「动力总成硬核岗位」

  • 产品管理类:电驱高级经理、电驱 DRE 高级专家
  • 电控系统类:电控系统高级经理、软件/硬件/功能安全专家
  • 电机系统类:电机系统高级经理、减速器专家
  • CAE 仿真测试类:仿真高级经理、结构/热管理/NVH 仿真专家、匹配测试经理
  • 软件测试类:软件测试高级经理
  • 电源与高压类:电源产品经理、DRE 专家、高压线束工程师、高压集成专家
  • 职能类:HRBP/高招 岗(整车/汽车零部件背景优先,甲乙方背景不限)

🏷️工作地点:苏州/上海
🏠对标公司:汇川、联电、比亚迪弗迪动力、华为数字能源、盘毂动力、三花


[星辰汽车 BG · 奇妙岗位探险队,集合啦!]

「专属探险席位」✨

  • 🚀 汽车海外销售一号位
  • 🌏 海外融资高手
  • 🇨🇳 国内融资高手
  • 🎨 汽车内外饰设计
  • 🤝 HRBP 伙伴


[星辰汽车 BG ]

热招岗位:产品/销售/设计/研发/职能(详细岗位需求见图)
象限要求:一象限:小米、理想、华为,吉利,二象限:头部汽车厂商
Base 多地可选:苏州/深圳/北京/上海
有合适的整车海外销售/整车产品/HRBP


[两轮车或四轮车]

智能驾驶系统工程师,智能驾驶产品经理,智能驾驶算法工程师,base 苏州


[ AI 戒指 BU ]

[火]热招岗位:HRBP (苏州、杭州、上海)、PQE 质量工程师(深圳)、软硬件产品经理(苏州/杭州)、法务 BP (英文可做工作语言,base 苏州)、仓库管理(苏州),


[ 3D 打印人才招募令]

我们寻找的“原子”:不是螺丝钉,而是发动机
我们是谁?融资数千万!下一个明星硬件公司👇
✅ 不做跟随者,而是颠覆行业!当前已规划覆盖 3D 打印上下游的多元产品线,正朝着 2000 人规模的全产业链创新公司全速迈进。
✅ 重视人才基因,用技术解决难题!这是一支能打硬仗、善于突破的“特种部队”,这里没有冗长的会议与复杂的层级,只有清晰的目标与极致的共同追求。

应聘指南( Base 深圳)

  • 研发/工程
    嵌入式架构师/工程师、视觉标定算法、感知算法、电机控制算法、高性能计算、图像增强算法、切片算法、桌面软件开发、高级结构工程师、EE 、ME 工程师
  • 产品/设计/职能
    电商产品经理、海外社区产品经理、ID 设计师、海外 PR 、融资经理、HRBP/高招 HR (深圳/苏州都可以)

我们寻找这样的破局者

  • 深耕 3D 打印、机器人或消费电子领域,具备从 0 到 1 打造成功产品的实战经验
  • 对智能硬件、精密控制等硬科技有深度热爱与积累,是解决复杂工程问题的硬核玩家
  • 毕业于国内外顶尖高校,拥有扎实的理论根基与卓越的学习能力

加入即享硬核体验

  • 直接向打造过爆款产品的行业老兵、机器人专家学习,与高手过招,视野与能力快速攀升
  • 获得 1V1 资深带教,并立即投身最核心的项目攻坚,你的代码与设计将直接定义下一代产品!


[ AI 模型 BU ]

热招岗位:融资、PR 、法务、AIGC 产品、大模型推荐算法岗位高优推进
象限要求:一象限:AI 行业知名公司、清华背景
Base 多地可选:苏州/北京/上海/深圳


[吸尘器 BU ]

  • TA/招聘专家,base 苏州
  • 工厂端 HRBP ,base 武汉
  • 工厂端 HRBP ,base 嘉兴
  • IE 工程师/技术员,base 苏州/嘉兴
  • (资深)结构研发工程师/主管/经理,base 苏州
  • 资深动效渲染师、视觉平面设计师,base 苏州/深圳


[空调 BU ]

  • HRBP:强招聘能力,高招能力强,base 苏州/上海
  • 投融资:base 苏州/上海/北京,行业经验更佳
  • 产品经理:国内、海外产品,3 年+,base 苏州/上海
  • 内控内审:3 年企业经验优先,base 苏州
  • 平面设计师:电商平台或品牌设计经验优先,base 苏州/上海
  • 性能工程师:资深 5 年+,base 苏州/上海
  • 结构工程师:零部件、整机均可,base 苏州/上海
  • 销售一号位(国内/国外):空调市场开拓经验,base 全国
  • 财务 BP:电商/海外财务背景,base 苏州
    美的、格力、海尔、海信、奥克斯、小米背景优先。


[宠物 BU ]

BU 介绍:专注于宠物智能产品,致力构建全屋智能宠物生态系统,打造宠物科技的新业态。团队构成主要为宠物行业背景+资深养宠人士,团队氛围超好[派对]

急招岗位

  • base 深圳:品质经理、整合营销/产品营销、PR 负责人
  • 家居服饰产品经理,宠物服饰产品经理 base:北上广深杭苏 要求:5 年及以上相关经验

热招岗位

  • base 深圳:产品经理、海外 GTM 、投融资 BP 、前端开发、后端开发、算法、测试等
    象限要求:一象限:小佩、霍曼、联宠智能、鸟语花香、乐木骆、晨北、北半球、有陪、猫猫狗狗,非产研岗位可开放看 3C 电子消费行业
  • [海外产品营销/平面设计] base 地:苏州
  • [美妆/香水产品经理] base 地:深圳


[电池 BU ]

岗位方向
销售(海内外电池/汽车后市场销售经验)
融资(电池融资经验)
base 苏州/上海/深圳


[生活环境电器 BU ]

项目经理、HRBP (强招聘)、公关经理、融资 BP 、产研( GTM/销售背景)、产品经理(资深)、海外产品营销、海外平面设计(资深) base 苏州


[巡翼摩托]

我们造什么
会思考的摩托!烧油,但不烧脑(因为智能系统帮您烧了)。
智驭燃油,骑领新潮——巡翼摩托,正以科技重塑骑行体验,打造更智能、更自由的摩托车

🔥热招岗位全面覆盖
平面设计 | 海外销售 | 工业设计 | 产品经理 | 平台总工
融资经理 | 研发工程师 | 新媒体运营 | HRBP | 智能化

🎯人才背景期待
优先来自摩托车等相关行业,有头部企业经验者更佳。跨界人才也非常欢迎!

  • 一象限:春风/本田/钱江/隆鑫/杜卡迪等头部摩托车企业。
  • 二象限:力帆/鑫源/张雪/凯越等头部企业。


[手表 BU ] 苏州北京深圳三地可选!

有手表/智能穿戴类/3C 电子+相关岗位经验投递必进面!,急招岗位如下

  • 职能类:HRBP/高招组
  • 产品类:手表产品经理
  • 工业设计类:ID 设计师
  • 供应链类:供应链支持、货盘经理
  • 营销类:品牌负责人、品牌经理、市场策划、整合营销
  • 销售类:渠道经理、海外国家经理、大客户经理
  • 运营类:电商运营、社群运营


[储能 BU ]

需求岗位
HRBP (苏州/深圳)
工业设计(苏州/深圳)
PR (苏州/深圳)
投融资(苏州/深圳)
软件产品经理(苏州/深圳,需具备户储/阳台储行业背景)


[ IT bu ]

岗位

  • Agent 开发( Python )
  • web 前端开发( AI 项目经验优先)
  • 融资 BP:VC/PE 资源/背景
    Base 苏州


[一些零碎岗位]

  1. 资深结构工程师岗位热招中,3 年及以上小家电结构设计背景
  2. 国际物流专员 要求本科学历,有过关务经验、跟单经验均可,行业不限。
    海外产品营销 资深,要求英语口语能力流利,行业可以放开。
    三维渲染师 要求作品集审美高,最好家电行业,可看汽车、美妆等行业。
  3. 工业设计师 要求:3 年及以上相关经验,base:深圳/杭州
  4. 社媒运营、编导策划 base:苏州 要求:有 MCN 账号运营、编导策划经验
  5. 智能穿戴类产品岗位(base 深圳&苏州):投融资、ID 、PR 、营销
    有审美有实操的 CMF 设计师,3 年以上工作&量产经验,base 苏州
  6. DevOps 全栈工程师,同时熟悉 [ devops +云运维+ web 全栈] 优先→深圳南油
    for 智能影像 BU ,base 深圳

注:帮朋友转发,请优先联系邮箱

之前搭的梯子,绑定了自己注册的域名,原来域名可以直接访问的,这两天梯子突然不行了,发现是域名需要翻墙才能访问了,但是 IP ,端口访问都没问题,这个是我的域名被墙了吗?怎么解决呢?

OpenClaw 最新保姆级飞书对接指南教程 搭建属于你的 AI 助手

OpenClaw 是一款开源的本地 AI 助手,本篇 OpenClaw 安装教程将手把手教你在 Linux 系统下部署最新版 OpenClaw,并完成飞书机器人对接。OpenClaw 支持在你自己的服务器上运行,通过飞书、WhatsApp、Telegram 等聊天工具交互。与云端 SaaS 服务不同,OpenClaw 让你完全掌控数据隐私,可以执行系统命令、浏览网页、管理文件,甚至编写代码——是你的专属开源 AI 助手。

注意:本教程在 Linux 系统下进行

OpenClaw 是什么?

OpenClaw(原名 Clawdbot,后更名为 Moltbot,现正式命名为 OpenClaw)是一个运行在你本地环境的高权限 AI 智能体。它的核心特性包括:

  • 本地部署:运行在你的服务器或电脑上,数据完全自主可控
  • 多平台支持:支持飞书、WhatsApp、Telegram、Discord、Slack 等主流聊天工具
  • 浏览器控制:可以浏览网页、填写表单、提取数据
  • 系统访问:读写文件、执行 Shell 命令、运行脚本
  • 持久化记忆:记住你的偏好和上下文,成为真正属于你的 AI
  • 插件扩展:支持社区技能插件,甚至可以自己编写插件

无论是邮件管理、日程安排、数据查询还是代码编写,OpenClaw 都能成为你的得力助手。

OpenClaw 安装前的准备工作

安装 OpenClaw 需要满足以下环境要求:

项目要求
操作系统Linux(推荐)/ macOS / Windows (WSL2)
Node.js≥ 22.x
内存≥ 2GB(建议 4GB,否则需配置 swap)
网络能访问 GitHub、npm 仓库(国内服务器可能需要代理)
AI 模型通义千问、OpenAI、Claude、KIMI 等任一 API Key(千问免费额度充足

安装 OpenClaw 依赖环境

如果你不想手动安装依赖、配置环境,可以直接使用 阿里云 OpenClaw 一键部署,几分钟即可完成 OpenClaw 服务器搭建。

如果你选择手动安装,继续往下看。

第一步安装 Git

# 安装 Git
sudo apt update
sudo apt install git -y

第二步安装 Node.js

# 安装 NVM
# 国内使用 gitee 的镜像源
curl -o- https://gitee.com/RubyMetric/nvm-cn/raw/main/install.sh | bash

# 国外使用
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash

# 重新加载环境变量
source ~/.bashrc

# 安装 Node.js 22
nvm install 22

# 查看 nodejs 版本
node -v # 输出 v22 即可,版本只要 22 就行

安装 OpenClaw 开源 AI 助手

# 使用官方脚本安装
curl -fsSL https://openclaw.bot/install.sh | bash
服务器在国内,如果安装失败的话,可能需要解决网络问题

其他平台安装方式请参考OpenClaw 安装文档 (原 Clawdbot)

你会看到如下

  🦞 OpenClaw Installer
  Siri's competent cousin.

✓ Detected: linux
✓ Node.js v22.22.0 found
✓ Git already installed
→ Installing OpenClaw 2026.2.6-3...
✓ OpenClaw installed

🦞 OpenClaw installed successfully (2026.2.6-3)!
Home sweet home. Don't worry, I won't rearrange the furniture.

Starting setup...


🦞 OpenClaw 2026.2.6-3 (85ed6c7) — curl for conversations.

▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄▄
██░▄▄▄░██░▄▄░██░▄▄▄██░▀██░██░▄▄▀██░████░▄▄▀██░███░██
██░███░██░▀▀░██░▄▄▄██░█░█░██░█████░████░▀▀░██░█░█░██
██░▀▀▀░██░█████░▀▀▀██░██▄░██░▀▀▄██░▀▀░█░██░██▄▀▄▀▄██
▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀▀
                  🦞 OPENCLAW 🦞                    
 
┌  OpenClaw onboarding

如果首次安装,时间会很长,需要耐心等待。
如果最后输出如下内容:

→ npm install failed; cleaning up and retrying...

新的脚本服务器内存要求变高了,据我使用下来 2G 内存,肯定会 OOM,如果出错的话,建议使用 swap 把硬盘空间当作交互内存使用。

成功之后会输出会看到下面的输出

┌  OpenClaw onboarding
│
◇  Security ──────────────────────────────────────────────────────────────────────────────╮
│                                                                                         │
│  Security warning — please read.                                                        │
│                                                                                         │
│  OpenClaw is a hobby project and still in beta. Expect sharp edges.                     │
│  This bot can read files and run actions if tools are enabled.                          │
│  A bad prompt can trick it into doing unsafe things.                                    │
│                                                                                         │
│  If you're not comfortable with basic security and access control, don't run OpenClaw.  │
│  Ask someone experienced to help before enabling tools or exposing it to the internet.  │
│                                                                                         │
│  Recommended baseline:                                                                  │
│  - Pairing/allowlists + mention gating.                                                 │
│  - Sandbox + least-privilege tools.                                                     │
│  - Keep secrets out of the agent's reachable filesystem.                                │
│  - Use the strongest available model for any bot with tools or untrusted inboxes.       │
│                                                                                         │
│  Run regularly:                                                                         │
│  openclaw security audit --deep                                                         │
│  openclaw security audit --fix                                                          │
│                                                                                         │
│  Must read: https://docs.openclaw.ai/gateway/security                                   │
│                                                                                         │
├─────────────────────────────────────────────────────────────────────────────────────────╯
│
◆  I understand this is powerful and inherently risky. Continue?
│  ● Yes / ○ No
└

第一个选项就是询问你是否知道风险的,需要选择 yes, 然后回车。
第二步选择 QuickStart

◆  Onboarding mode
│  ● QuickStart (Configure details later via openclaw configure.)
│  ○ Manual
└

第三步选择模型服务商,这里选择 Qwen,免费额度充足,适合入门快速使用

◆  Model/auth provider
│  ○ OpenAI
│  ○ Anthropic
│  ○ MiniMax
│  ○ Moonshot AI (Kimi K2.5)
│  ○ Google
│  ○ xAI (Grok)
│  ○ OpenRouter
│  ● Qwen (OAuth)
│  ○ Z.AI (GLM 4.7)
│  ○ Qianfan
│  ○ Copilot
│  ○ Vercel AI Gateway
│  ○ OpenCode Zen
│  ○ Xiaomi
│  ○ Synthetic
│  ○ Venice AI
│  ○ Cloudflare AI Gateway
│  ○ Skip for now
└

选择千问模型后,选择 Qwen OAuth 之后 会提供一个链接,复制并在浏览器中打开

 Qwen auth method
│  ● Qwen OAuth
│  ○ Back
└
 Starting Qwen OAuth…
◇  Qwen OAuth ─────────────────────────────────────────────────────────────────────────╮
│                                                                                      │
│  Open https://chat.qwen.ai/authorize?user_code=-AYWBJHL&client=qwen-code to approve  │
│  access.                                                                             │
│  If prompted, enter the code -AYWBJHL.                                               │
│                                                                                      │
├──────────────────────────────────────────────────────────────────────────────────────╯
◓  Waiting for Qwen OAuth approval…...

复制链接后,打开浏览器,会看到如下界面。由于我已登录过,所以显示账户信息;如果尚未登录,按照提示完成登录即可。

登录完成后,会出现以下选项,提示选择对应的千问模型,如下图

◇  Qwen OAuth complete
│
◇  Model configured ─────────────────────────────╮
│                                                │
│  Default model set to qwen-portal/coder-model  │
│                                                │
├────────────────────────────────────────────────╯
│
◇  Provider notes ──────────────────────────────────────────────────────────────────────╮
│                                                                                       │
│  Qwen OAuth tokens auto-refresh. Re-run login if refresh fails or access is revoked.  │
│  Base URL defaults to https://portal.qwen.ai/v1. Override                             │
│  models.providers.qwen-portal.baseUrl if needed.                                      │
│                                                                                       │
├───────────────────────────────────────────────────────────────────────────────────────╯
│
◆  Default model
│  ● Keep current (qwen-portal/coder-model)
│  ○ Enter model manually
│  ○ qwen-portal/coder-model
│  ○ qwen-portal/vision-model
└

选择默认模型 Keep current (qwen-portal/coder-model) 即可。接下来会提示选择 channel,这里先跳过,后续再添加。之前飞书都没有内置的,现在新版本飞书已经内置了

 Select channel (QuickStart)
│  ○ Telegram (Bot API) (not configured)
│  ○ WhatsApp (QR link)
│  ○ Discord (Bot API)
│  ○ Google Chat (Chat API)
│  ○ Slack (Socket Mode)
│  ○ Signal (signal-cli)
│  ○ iMessage (imsg)
│  ○ Feishu/Lark (飞书)
│  ○ Nostr (NIP-04 DMs)
│  ○ Microsoft Teams (Bot Framework)
│  ○ Mattermost (plugin)
│  ○ Nextcloud Talk (self-hosted)
│  ○ Matrix (plugin)
│  ○ BlueBubbles (macOS app)
│  ○ LINE (Messaging API)
│  ○ Zalo (Bot API)
│  ○ Zalo (Personal Account)
│  ○ Tlon (Urbit)
│  ● Skip for now
└

继续下面选择 skills,也是选择 No

 Skills status ────────────╮
│                            │
│  Eligible: 6               │
│  Missing requirements: 43  │
│  Blocked by allowlist: 0   │
│                            │
├────────────────────────────╯
│
◆  Configure skills now? (recommended)
│  ○ Yes / ● No
└

然后等待安装完成,最后会出现以下选项,这里选择 TUI

◆  How do you want to hatch your bot?
│  ● Hatch in TUI (recommended)
│  ○ Open the Web UI
│  ○ Do this later
└

如果看到 TUI 聊天界面,说明安装成功,可以尝试输入 Hello 进行测试。
OpenClaw (原 Clawdbot) TUI 聊天界面 - AI 助手对话测试
然后直接使用 ctrl+c 先关闭,后面我们再来设置

查看 OpenClaw 服务状态

可以使用下面的命令来查看

 openclaw status

会看到如下图的结果就说明服务启动了

🦞 OpenClaw 2026.2.6-3 (85ed6c7) — I read logs so you can keep pretending you don't have to.

│
◇  
│
◇  
OpenClaw status

Overview
┌─────────────────┬─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┐
│ Item            │ Value                                                                                                                                               │
├─────────────────┼─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┤
│ Dashboard       │ http://127.0.0.1:18789/                                                                                                                             │
│ OS              │ linux 6.8.0-71-generic (x64) · node 22.22.0                                                                                                         │
│ Tailscale       │ off                                                                                                                                                 │
│ Channel         │ stable (default)                                                                                                                                    │
│ Update          │ pnpm · npm latest 2026.2.6-3                                                                                                                        │
│ Gateway         │ local · ws://127.0.0.1:18789 (local loopback) · reachable 42ms · auth token · VM-16-7-ubuntu (10.0.16.7) app unknown linux 6.8.0-71-generic         │
│ Gateway service │ systemd installed · enabled · running (pid 327748, state active)                                                                                    │
│ Node service    │ systemd not installed                                                                                                                               │
│ Agents          │ 1 · 1 bootstrapping · sessions 1 · default main active 1m ago                                                                                       │
│ Memory          │ enabled (plugin memory-core) · unavailable                                                                                                          │
│ Probes          │ skipped (use --deep)                                                                                                                                │
│ Events          │ none                                                                                                                                                │
│ Heartbeat       │ 30m (main)                                                                                                                                          │
│ Sessions        │ 1 active · default coder-model (128k ctx) · ~/.openclaw/agents/main/sessions/sessions.json                                                          │
└─────────────────┴─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────┘

访问 OpenClaw Web UI 管理面板

如何访问面板?服务监听在 http://127.0.0.1:18789/ 端口上,我们现在通过 ssh 隧道来访问,输入下面的命令

ssh -N -L 18789:127.0.0.1:18789 用户名@服务器IP
# 回车之后
用户名@服务器IP's password: # 输入密码

然后在浏览器打开 http://127.0.0.1:18789/, 你会看到 Dashboard 了,如下图
OpenClaw (原 Clawdbot) Web UI Dashboard 未授权页面
图中显示的是未授权状态,回到服务器,输入以下命令

clawdbot dashboard

会看到下面的面板数据,有这个 Dashboard URL

openclaw dashboard

🦞 OpenClaw 2026.2.6-3 (85ed6c7) — Works on Android. Crazy concept, we know.

Dashboard URL: http://127.0.0.1:18789/#token=e8e5cd1573123ae9b11111111111111e2b94b8b7b4ccd
Copy to clipboard unavailable.
No GUI detected. Open from your computer:
ssh -N -L 18789:127.0.0.1:18789 ubuntu@222222
Then open:
http://localhost:18789/
http://localhost:18789/#token=e8e5cd1573123ae9b11111111111111e2b94b8b7b4ccd
Docs:
https://docs.openclaw.ai/gateway/remote
https://docs.openclaw.ai/web/control-ui

复制对应的 Dashboard URL 到浏览器打开,即可正常查看聊天记录。
OpenClaw (原 Clawdbot) Web UI 管理面板 - AI 助手聊天记录

至此 OpenClaw 开源 AI 助手已安装完成,可以正常访问了。接下来在聊天框首次输入 Hello,OpenClaw 会询问你它应该叫什么、应该叫你什么。你需要给这个 AI 助手设置一个名字,以及它对你的称呼。可以在聊天框这么输入

Name: OpenClaw

My Name: Boss

OpenClaw 对接飞书机器人教程

下面是本篇 OpenClaw 飞书教程的核心部分。回到刚才添加 channels 的配置,选择飞书添加。如有遗漏,可以看官方文档OpenClaw 飞书对接

◆  Select a channel
│  ○ Telegram (Bot API)
│  ○ WhatsApp (QR link)
│  ○ Discord (Bot API)
│  ○ Google Chat (Chat API)
│  ○ Slack (Socket Mode)
│  ○ Signal (signal-cli)
│  ○ iMessage (imsg)
│  ○ Feishu/Lark (飞书)
│  ○ Nostr (NIP-04 DMs)
│  ○ Microsoft Teams (Bot Framework)
│  ○ Mattermost (plugin)
│  ○ Nextcloud Talk (self-hosted)
│  ○ Matrix (plugin)
│  ○ BlueBubbles (macOS app)
│  ○ LINE (Messaging API)
│  ○ Zalo (Bot API)
│  ○ Zalo (Personal Account)
│  ○ Tlon (Urbit)
│  ● Finished
└

选择之后会安装对应的扩展,回车就行了

◆  Install Feishu plugin?
│  ● Download from npm (@openclaw/feishu)
│  ○ Skip for now
└

如果出现下面的错误,一般都是由于你之前安装过了,需要删除扩展

 [plugins] feishu failed to load from /home/ubuntu/.openclaw/extensions/feishu/index.ts: Error: Cannot find module 'zod'
Require stack:
- /home/ubuntu/.openclaw/extensions/feishu/src/config-schema.ts

先退出安装飞书,先安装 zod,输入

npm install -g zod

# 删除飞书扩展,一般都是由于你之前安装过了
rm -rf ~/.openclaw/extensions/feishu

如果没有错误的话,选择飞书通道之后,应该是下面的输出

  Select a channel
│  Feishu/Lark (飞书)
│
◇  Feishu credentials ──────────────────────────────────────────────────────────────╮
│                                                                                   │
│  1) Go to Feishu Open Platform (open.feishu.cn)                                   │
│  2) Create a self-built app                                                       │
│  3) Get App ID and App Secret from Credentials page                               │
│  4) Enable required permissions: im:message, im:chat, contact:user.base:readonly  │
│  5) Publish the app or add it to a test group                                     │
│  Tip: you can also set FEISHU_APP_ID / FEISHU_APP_SECRET env vars.                │
│  Docs: feishu                 │
│                                                                                   │
├───────────────────────────────────────────────────────────────────────────────────╯
│
◆  Enter Feishu App ID
│  _ # 输入 App ID
└

先不着急输出,我们先登录飞书开放平台 https://open.feishu.cn,点击「开发者后台 -> 创建企业自建应用」,如下图
飞书开放平台创建企业自建应用 - OpenClaw 对接
然后点击创建应用,如下
飞书创建应用 - OpenClaw AI 机器人
创建完成后,首先到凭据管理中获取 App ID 和 App Secret,注意保存,后续配置需要使用。
飞书 App ID 和 App Secret 凭据管理
然后添加机器人,如下操作
飞书添加机器人能力 - OpenClaw AI 助手
首先配置个名字
飞书机器人名称配置 - OpenClaw

配置 OpenClaw 飞书参数

拿到 App ID 和 App Secret 之后,在刚才的上面的输入填入 APP ID 和 App Secret,最后

◇  Enter Feishu App ID
│  cli_a9xxxxxxxf85cb2 # 填入你自己的 App ID
│
◇  Enter Feishu App Secret
│  WmO1Hj1qkxxxxxxxxxxxihYL5NxXyTDt # 填入你自己的 App Secret
[info]: [ 'client ready' ]
│
◇  Feishu connection test ───────────────────────────╮
│                                                    │
│  Connected as ou_3ef555cb1axxxxxxxxeb6203805ba9ee  │
│                                                    │
├────────────────────────────────────────────────────╯
│
◆  Which Feishu domain?
│  ● Feishu (feishu.cn) - China # 选择国内
│  ○ Lark (larksuite.com) - International
◆  Group chat policy
│  ○ Allowlist - only respond in specific groups # 允许列表 需要配置
│  ● Open - respond in all groups (requires mention) # 这里全部放开就行了
│  ○ Disabled - don't respond in groups
◆  Select a channel
│  ○ Telegram (Bot API)
│  ○ WhatsApp (QR link)
│  ○ Discord (Bot API)
│  ○ Google Chat (Chat API)
│  ○ Slack (Socket Mode)
│  ○ Signal (signal-cli)
│  ○ iMessage (imsg)
│  ○ Feishu/Lark (飞书)
│  ○ Nostr (NIP-04 DMs)
│  ○ Microsoft Teams (Bot Framework)
│  ○ Mattermost (plugin)
│  ○ Nextcloud Talk (self-hosted)
│  ○ Matrix (plugin)
│  ○ BlueBubbles (macOS app)
│  ○ LINE (Messaging API)
│  ○ Zalo (Bot API)
│  ○ Zalo (Personal Account)
│  ○ Tlon (Urbit)
│  ● Finished (Done) # 选择完成

完成之后会继续让你选择访问策略

◇  Configure DM access policies now? (default: pairing) #
│  Yes
│
◇  Feishu DM access ─────────────────────────────────────────────────────────────────────────╮
│                                                                                            │
│  Default: pairing (unknown DMs get a pairing code).                                        │
│  Approve: openclaw pairing approve feishu <code>                                           │
│  Allowlist DMs: channels.feishu.dmPolicy="allowlist" + channels.feishu.allowFrom entries.  │
│  Public DMs: channels.feishu.dmPolicy="open" + channels.feishu.allowFrom includes "*".     │
│  Multi-user DMs: set session.dmScope="per-channel-peer" (or "per-account-channel-peer"     │
│  for multi-account channels) to isolate sessions.                                          │
│  Docs: start/pairing                     │
│                                                                                            │
├────────────────────────────────────────────────────────────────────────────────────────────╯
│
◇  Feishu DM policy
│  Open (public inbound DMs) # 公开
│
◇  Add display names for these accounts? (optional)
│  No # 不需要
│
└  Channels updated.

你可以通过 ~/.openclaw/openclaw.json 查看对应的 channel 配置,最后配置如下

{
    "channels": {
    "feishu": {
      "enabled": true,
      "appId": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "appSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
      "domain": "feishu",
      "groupPolicy": "open",
      "dmPolicy": "open",
      "allowFrom": [
        "*"
      ]
    }
  }
}

配置完成之后,重启

openclaw gateway restart

重启完成后回到飞书,找到「事件和回调」,选择长连接模式,如下图
飞书事件和回调配置 - OpenClaw 长连接模式
如果配置成功,说明连接已建立。继续下面的配置,添加事件,选择「接收消息」事件
飞书添加接收消息事件 - OpenClaw AI 助手
事件添加完成之后,还需要开通权限,有以下权限全部勾选

权限Scope(范围)Description(说明)
contact:user.base:readonly用户信息获取基础用户信息
im:message消息 全部勾选发送和接收消息

如下图
飞书权限配置 - OpenClaw 用户信息权限

飞书消息权限配置 - OpenClaw AI 机器人

以上步骤全部完成后,即可与机器人对话。但在此之前需要先创建一个版本
飞书应用版本发布 - OpenClaw AI 助手上线

注意:每次修改配置后都需要重新发布版本,建议全部配置完成后再统一发布。

发布完成后,回到飞书客户端,可以看到应用已上线,点击打开应用
飞书应用发布成功 - OpenClaw AI 机器人
向机器人发送 Hello,即可收到 Moltbot 的回复
飞书 OpenClaw AI 助手回复测试成功

OpenClaw 常用命令速查

安装完成后,以下是日常使用中最常用的 OpenClaw 命令:

命令功能
openclaw status查看 OpenClaw 运行状态
openclaw onboard重新进入配置向导
openclaw gateway start启动服务
openclaw gateway stop停止服务
openclaw gateway restart重启服务
openclaw update更新到最新版本
openclaw health健康检查
openclaw doctor诊断问题
openclaw dashboard获取 Web UI 访问链接
openclaw security audit --deep安全审计
openclaw uninstall卸载 OpenClaw

OpenClaw 成本说明与免费模型推荐

OpenClaw 本身完全免费开源,主要成本来自两个方面:

服务器成本:一台最低配置的云服务器即可,月费约 30-50 元。如果使用 阿里云 OpenClaw 一键部署,可以省去环境配置的时间。

AI 模型 API 调用费用:各模型服务商的免费额度和计费模式不同,以下是常见选择:

模型服务商免费额度适合场景
通义千问(Qwen)免费额度充足本教程推荐,入门首选
小米 MiMo有免费试用额度成本敏感用户
KIMI (Moonshot)有免费额度中文理解能力强
GLM 4.7 (Z.AI)有免费额度性价比高
OpenAI GPT付费英文场景最佳
Anthropic Claude付费代码能力最强

对于刚接触 OpenClaw 的用户,建议先用通义千问的免费额度体验,熟练后再根据实际需求选择其他模型。

总结

本篇 OpenClaw 安装教程从环境准备、OpenClaw 部署、飞书机器人对接到权限配置,完整走完了一个最新版 OpenClaw 开源 AI 助手的搭建流程。如果你按照这篇 OpenClaw 飞书教程完成了所有步骤,现在应该已经可以在飞书中和你的 OpenClaw 助手正常对话了。

OpenClaw 常见问题 FAQ

OpenClaw 和 Clawdbot、Moltbot 是什么关系?

OpenClaw 是该项目的最新正式名称。项目最初叫 Clawdbot,后因商标问题更名为 Moltbot,最终在 2025 年 1 月正式定名为 OpenClaw。三者是同一个项目的不同阶段命名。

OpenClaw 支持哪些 AI 模型?

OpenClaw 支持多种 AI 模型服务商,包括 Anthropic Claude、OpenAI GPT、通义千问(Qwen)、KIMI、小米 MiMo 等。本教程使用通义千问是因为其免费额度充足,适合入门学习。

为什么安装时提示 npm install failed?

这通常是服务器内存不足导致的。新版本脚本对内存要求较高,2G 内存可能会出现 OOM(内存溢出)。建议配置 swap 交换空间,将硬盘空间作为虚拟内存使用。

OpenClaw 可以在 Windows 或 macOS 上运行吗?

可以。OpenClaw 支持 Mac、Windows 和 Linux 系统。本教程以 Linux 为例,其他系统的安装方式可参考官方文档

飞书机器人配置后无法收到消息怎么办?

请检查以下几点:

  1. 确认飞书通道已正确安装(新版 OpenClaw 已内置飞书支持,安装时选择 Feishu/Lark 即可)
  2. 检查 App ID 和 App Secret 配置是否正确
  3. 确认已开通「接收消息」事件权限
  4. 检查长连接模式是否配置成功
  5. 确保应用版本已发布
  6. 使用 openclaw gateway restart 重启服务后再试

OpenClaw 数据安全吗?

OpenClaw 运行在你自己的服务器上,所有数据都在本地存储,不会上传到第三方云端。但由于它具有系统级权限,建议在独立的服务器上部署,避免在生产环境或重要数据的机器上运行。

除了飞书,OpenClaw 还支持哪些平台?

OpenClaw 支持多个聊天平台,包括 WhatsApp、Telegram、Discord、Slack、Microsoft Teams、Signal、iMessage、Google Chat、Twitch 等。每个平台需要安装对应的插件。

OpenClaw 可以做什么?

OpenClaw 不只是一个聊天机器人,它能真正在你的服务器上执行操作。以下是一些典型使用场景:

  • 文件整理:“帮我把上周下载的文件按类型分类”,它会直接操作文件系统完成分类
  • 网页摘要:发一个 URL 给它,它能自动打开网页、提取内容并生成摘要
  • 代码编写:“写一个 Python 脚本批量重命名文件”,它能写完代码还能直接在服务器上运行
  • 数据查询:连接本地数据库查询数据,并把结果发回飞书
  • 日程管理:定时提醒、晴间简报、邮件自动回复
  • 系统运维:执行 Shell 命令、监控服务器状态、自动化脚本

简单说,OpenClaw 是一个 7×24 小时在线的 AI 助手,你睡觉时它还能继续干活。

如何更新 OpenClaw 到最新版本?

使用以下命令更新:

openclaw update

OpenClaw 命令和 clawdbot 命令有什么区别?

OpenClaw 更名后,官方推荐使用 openclaw 命令,但为了兼容性,clawdbot 命令仍然可用。两者功能完全相同,建议新用户直接使用 openclaw 命令。

提示 openclaw 命令找不到怎么办?

这通常是环境变量未加载导致的。尝试以下步骤:

  1. 关闭当前终端窗口,重新打开
  2. 执行 source ~/.bashrc 重新加载环境变量
  3. 如果还不行,执行 openclaw doctor 检查问题
  4. 实在无法解决,尝试重启服务器

OpenClaw 安装卡住不动怎么办?

  1. Ctrl + C 中断当前操作
  2. 执行 openclaw doctor 检查问题
  3. 如提示网络问题,检查服务器网络是否能访问 GitHub 和 npm
  4. 尝试重新运行 openclaw onboard

端口 18789 被占用怎么办?

使用其他端口启动服务:

openclaw gateway --port 18790

如何配置 swap 解决内存不足?

如果服务器内存不足 2GB,可以配置 swap 交换空间:

# 创建 2G 的 swap 文件
sudo fallocate -l 2G /swapfile
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile

# 设置开机自动启用
echo '/swapfile none swap sw 0 0' | sudo tee -a /etc/fstab