Agent 框架中通常如何实现 Skill 的注册与发现机制?

面试官:“在搭建 Agent 框架时,Skill 的注册与发现机制通常是怎么实现的?”

候选人:

“Skill 的注册与发现本质上是解决一个问题:Agent 怎么知道有哪些 Skill 可用,并在需要时快速找到最合适的那个。我实现过几种方案,从简单到复杂,核心都是围绕‘元信息的管理和匹配’展开。


方案一:本地注册 + 配置驱动

这是最简单的方案,适合 Skill 数量少、单机运行的场景。

实现方式:

  • 开发者通过配置文件、注解或装饰器声明 Skill。比如 Python 里用 @register_skill(name="send_email") 装饰一个 Skill 类。

  • 系统启动时扫描指定目录或模块,收集所有带注解的 Skill 类。

  • 提取每个 Skill 的元信息(名称、描述、参数 schema、触发示例等),加载到内存的一个注册表(通常是字典或 HashMap)。

  • Agent 运行时直接查这张内存表做匹配。

# 示例:基于装饰器的注册
class SkillRegistry:
    _skills = {}

    @classmethod
    def register(cls, name, description, trigger_examples):
        def wrapper(skill_cls):
            cls._skills[name] = {
                "class": skill_cls,
                "description": description,
                "trigger_examples": trigger_examples
            }
            return skill_cls
        return wrapper

    @classmethod
    def find_best_match(cls, user_intent):
        # 用嵌入模型计算相似度,返回最匹配的 Skill
        pass

优点:零依赖,启动快,调试方便。

缺点:Skill 数量多了后内存压力大,不支持动态增减,也无法跨进程共享。

image.png


方案二:注册中心 + 服务发现

当 Skill 数量达到几十上百,或者需要跨多个 Agent 实例共享时,就需要引入独立的注册中心。

架构设计:

  • Skill Provider:每个 Skill 作为一个独立服务部署,启动时向注册中心上报自己的元信息,并定期发送心跳维持租约。

  • Registry Center:用 etcd、Consul、Nacos 或 Redis 来存储 Skill 的元信息。数据结构可以是一个 Hash,Key 是 Skill 名称,Value 是元信息 JSON。

  • Agent 侧 Discovery:Agent 启动时从注册中心拉取全量 Skill 列表缓存到本地,同时订阅变更通知,Skill 上下线时实时更新本地缓存。

# 基于 Redis 的注册与发现简化实现
class RedisSkillRegistry:
    def register(self, skill_meta):
        redis.hset("skills", skill_meta["name"], json.dumps(skill_meta))
        redis.publish("skill:update", skill_meta["name"])  # 广播变更

    def discover_all(self):
        skills = redis.hgetall("skills")
        return [json.loads(v) for v in skills.values()]

    def watch_changes(self, callback):
        pubsub = redis.pubsub()
        pubsub.subscribe("skill:update")
        for msg in pubsub.listen():
            callback(msg)  # 更新本地缓存

优点:支持动态上下线,多实例共享,高可用。

缺点:引入外部依赖,增加运维复杂度,网络延迟可能影响匹配速度。


方案三:基于向量数据库的语义发现

当 Skill 数量更大(几百上千),或者触发条件非常依赖语义理解时,简单的元信息匹配不够用,需要上向量化检索。

实现方式:

  • 每个 Skill 注册时,把它的描述和触发示例用嵌入模型(如 text-embedding-3-small)转成向量。

  • 向量存储在 Milvus、Pinecone 或 pgvector 等向量数据库中。

  • Agent 收到用户意图后,同样用嵌入模型把意图转成向量。

  • 在向量数据库中做 ANN(近似最近邻)搜索,返回 Top-K 个语义最相似的 Skill。

  • 再结合参数匹配、权限校验等规则做精排,选出最终要激活的 Skill。

# 基于向量检索的 Skill 匹配
class VectorSkillDiscovery:
    def register(self, skill_meta):
        embedding = embedder.encode(skill_meta["description"])
        vector_db.insert(
            name=skill_meta["name"],
            embedding=embedding,
            metadata=skill_meta
        )

    def discover(self, user_intent, top_k=5):
        intent_embedding = embedder.encode(user_intent)
        results = vector_db.search(intent_embedding, top_k=top_k)
        return self.rerank(results, user_intent)  # 结合规则精排

优点:语义匹配精准度高,支持大规模 Skill 库,可处理模糊意图。

缺点:依赖嵌入模型和向量数据库,增加系统复杂度和成本。


方案四:分层混合发现

实际生产环境往往是上面几种方案的组合,我画一个分层架构:

用户意图
第一层:标签/分类粗筛(内存缓存)
   └── 从几百个 Skill 快速缩小到十几个候选
第二层:语义向量匹配(向量数据库)
   └── 从十几个候选中根据语义相似度选出 Top-3
第三层:参数匹配 + 前置条件校验(规则引擎)
   └── 检查必填参数是否齐全、权限是否满足,选出最终 Skill