3. 智能体架构概览

感知模块,说白了就是智能体的「眼睛」和「耳朵」。

没有它,你的智能体就是个瞎子。我见过太多团队,一上来就搞决策逻辑,结果数据进来全是噪音,根本没法用。嗯,这一章咱们就把感知层拆开揉碎了讲。

3.1 传感器抽象层:统一接口,屏蔽差异

现实世界里的传感器五花八门。摄像头、激光雷达、麦克风、温度计……每个都有自己的协议、数据格式、采样频率。如果让上层逻辑直接对接这些硬件,代码会变成一团乱麻。

我的做法是:加一层抽象。

传感器抽象层,本质上就是个适配器模式。它把不同传感器的原始数据,统一成标准格式。上层模块只管调用 read(),不用管底层是 I2C 还是 SPI。

核心目标:

  • 解耦:硬件更换不影响上层逻辑
  • 统一:所有传感器输出相同结构的数据帧
  • 可扩展:新增传感器只需实现抽象接口

来看一个简化版的抽象接口定义:

// 传感器抽象接口
public interface ISensor {
    SensorType getType();           // 传感器类型
    DataFrame read();               // 读取数据
    bool calibrate();               // 校准
    SensorStatus getStatus();       // 健康状态
}

// 统一数据帧结构
public class DataFrame {
    public long timestamp;          // 时间戳
    public string sensorId;         // 传感器ID
    public byte[] rawData;          // 原始数据
    public Dictionary<string, object> metadata;  // 元数据
}

我在项目中遇到过一个问题:不同传感器的时钟不同步。摄像头是 30fps,激光雷达是 10Hz,麦克风是 16kHz。如果不做时间对齐,融合出来的数据全是错的。后来我在抽象层里加了个时间戳对齐模块,强制所有数据帧以系统时钟为基准。

我的习惯:每个传感器实例都维护一个内部缓冲区。抽象层按固定周期(比如 100ms)从缓冲区取最新数据。这样即使某个传感器暂时掉线,上层也不会立刻崩溃。

3.2 数据预处理管道:把脏数据洗干净

原始数据能直接用吗?不能。你想想看,摄像头拍到的画面可能有噪点,激光雷达的点云可能有离群点,麦克风录到的声音可能有环境杂音。不处理这些,智能体就会做出错误判断。

数据预处理管道,就是一条流水线。原始数据从一头进去,经过一道道工序,从另一头出来干净的数据。

我一般把管道分成三个阶段:

  1. 清洗阶段:去噪、去重、填补缺失值
  2. 变换阶段:归一化、降维、特征提取
  3. 格式化阶段:统一维度、对齐时间戳、打包成张量

举个例子,图像数据的预处理管道:

class ImagePreprocessingPipeline:
    def __init__(self):
        self.steps = []

    def add_step(self, step):
        self.steps.append(step)

    def process(self, image):
        for step in self.steps:
            image = step.execute(image)
        return image

# 使用示例
pipeline = ImagePreprocessingPipeline()
pipeline.add_step(DenoiseStep())       # 去噪
pipeline.add_step(ResizeStep(224,224)) # 缩放
pipeline.add_step(NormalizeStep())     # 归一化
pipeline.add_step(ToTensorStep())      # 转张量

clean_data = pipeline.process(raw_image)

我曾经踩过的坑:管道里的步骤顺序很重要。先归一化再缩放,和先缩放再归一化,结果完全不同。我建议把几何变换(缩放、裁剪)放在前面,数值变换(归一化、标准化)放在后面。顺序错了,模型精度直接掉 5 个点。

另外,管道一定要支持并行处理。多模态数据量大,单线程处理会拖慢整个系统。我习惯用生产者-消费者模式,每个步骤开独立线程,用队列传递数据。

3.3 多模态信息融合策略:1+1 > 2

单一传感器总有局限。摄像头怕黑,激光雷达怕雨,麦克风怕吵。多模态融合,就是让不同传感器互相补位。

融合策略分三个层次:

融合层次 时机 优点 缺点
数据级融合 原始数据层面 信息损失最小 数据量大,对齐困难
特征级融合 特征提取之后 计算量适中,灵活性高 特征选择依赖经验
决策级融合 各模态独立决策后 容错性强,模块解耦 信息损失较大

我个人最常用的是特征级融合。为什么呢?数据级融合太吃算力,决策级融合又太浪费信息。特征级融合刚好在中间,性价比最高。

来看一个视觉+语音的特征级融合示例:

class MultimodalFusion:
    def __init__(self):
        self.vision_encoder = VisionEncoder()
        self.audio_encoder = AudioEncoder()
        self.fusion_layer = CrossAttention(dim=512)

    def forward(self, image, audio):
        # 分别提取特征
        vis_feat = self.vision_encoder(image)   # [B, 196, 512]
        aud_feat = self.audio_encoder(audio)    # [B, 128, 512]

        # 交叉注意力融合
        fused_feat = self.fusion_layer(
            query=vis_feat,
            key=aud_feat,
            value=aud_feat
        )

        return fused_feat

这里用了交叉注意力机制。视觉特征作为 query,语音特征作为 key 和 value。说白了,就是让视觉信息去「问」语音信息,找到两者之间的关联。

避坑指南:多模态融合最怕「模态缺失」。比如摄像头被挡住了,或者麦克风坏了。我建议在融合层前面加一个模态质量评估模块。某个模态质量低于阈值,就自动降低它的权重,甚至直接跳过。否则,坏数据会污染整个融合结果。

3.4 整体架构图

下面这张图,把感知模块的完整流程串起来了。从传感器到抽象层,经过预处理管道,再到多模态融合,最后输出给决策模块。

感知模块整体架构 摄像头 激光雷达 麦克风 其他传感器 传感器抽象层 统一接口 · 时间戳对齐 · 数据帧标准化 数据预处理管道 清洗阶段 → 变换阶段 → 格式化阶段 去噪 · 归一化 · 降维 · 时间对齐 · 张量化 多模态信息融合 数据级融合 / 特征级融合 / 决策级融合 统一感知输出 → 决策模块

3.5 实战要点总结

说了这么多,最后给你几个实在的建议:

  • 抽象层一定要做单元测试。每个传感器模拟器都要写,不然硬件没到的时候你什么都测不了。
  • 预处理管道要可配置。不同场景需要的预处理步骤不一样。我习惯用 YAML 配置文件来定义管道,改起来方便。
  • 融合策略不要一开始就上复杂的。先试试简单的拼接或加权平均,跑通了再上注意力机制。我见过有人一上来就搞 Transformer 融合,结果调了两个月都没收敛。
  • 别忘了监控。每个传感器的健康状态、数据延迟、丢包率,都要实时上报。感知模块出了问题,决策模块再聪明也没用。

一句话总结:感知模块设计得好不好,直接决定了智能体的上限。抽象层管「通」,预处理管「净」,融合管「准」。这三层做好了,后面的决策和规划才有意义。