一、核心问题:系统提示「代币浪费+信息过载」

第四季的父-子 Agent 解决了上下文膨胀问题:
https://fairy-study.blog.csdn.net/article/details/159253478?spm=1001.2014.3001.5502
但新痛点出现:
如果把所有领域技能(如git规范、代码审查清单、测试流程)都提前写进系统提示,会导致:

  1. 代币浪费10个技能 × 2000 代币 = 20000 代币,而用户任务可能只用到 1 个技能,90% 的代币花在无用信息上;
  2. 信息过载:系统提示过长,模型抓不住核心,甚至忽略关键规则(比如 git 提交规范被淹没在大量文本里)。

二、核心方案:两层技能注入(按需加载)

核心逻辑是「轻量摘要常驻,详细技能按需加载」,也就是俗称的 懒加载 ,把技能知识拆成两层,只在需要时加载完整内容,彻底解决代币浪费问题:

┌─────────────────────────┐  第一层(Layer 1):系统提示(始终存在,低成本)
│ 系统提示:               │  - 仅包含技能名称 + 极简描述(如「git: Git workflow helpers」)
│ You are a coding agent.  │  - 代币消耗极少(每个技能~100代币)
│ Skills available:        │  - 让模型知道「有哪些技能可用」,但不包含具体规则
│   - git: Git规范助手     │
│   - test: 测试最佳实践   │
└─────────────────────────┘
        ↓ 模型判断需要用 git 技能,调用load_skill("git")工具
┌─────────────────────────┐  第二层(Layer 2):tool_result(按需加载,高成本)
│ tool_result:            │  - 仅在模型调用load_skill工具后,注入完整技能文档
│ <skill name="git">       │  - 包含git提交的完整步骤、规范、示例(~2000代币)
│   1. 提交前必须lint代码  │  - 用完即融入上下文,未使用的技能永远不加载
│   2. 提交信息格式:...   │
│ </skill>                 │
└─────────────────────────┘

三、核心代码逻辑(技能加载核心)

1. SkillLoader:技能文件管理器(加载/解析技能文件)

SkillLoader 包含第一层技能的简单加载(用于 System Prompt),和技能详细内容的提取(get_content 提取技能的详细内容,用于第二层的懒加载)

class SkillLoader:
    def __init__(self, skills_dir: Path):
        self.skills = {}  # 存储所有技能的元信息+完整内容
        # 扫描skills目录下所有SKILL.md文件(每个技能一个文件)
        for f in sorted(skills_dir.rglob("SKILL.md")):
            text = f.read_text()
            # 解析文件:分离前置元信息(name/description)和正文(完整技能规则)
            meta, body = self._parse_frontmatter(text)
            # 技能名称优先用元信息,没有则用目录名
            name = meta.get("name", f.parent.name)
            # 存储技能:meta=极简描述,body=完整规则
            self.skills[name] = {"meta": meta, "body": body}

    # 生成第一层内容:所有技能的极简描述(用于系统提示)
    def get_descriptions(self) -> str:
        lines = []
        for name, skill in self.skills.items():
            desc = skill["meta"].get("description", "")
            lines.append(f"  - {name}: {desc}")
        return "\n".join(lines)

    # 生成第二层内容:指定技能的完整规则(用于tool_result注入)
    def get_content(self, name: str) -> str:
        skill = self.skills.get(name)
        if not skill:
            return f"Error: Unknown skill '{name}'."
        # 用固定格式包裹完整技能内容,方便模型识别
        return f"<skill name=\"{name}\">\n{skill['body']}\n</skill>"
  • SKILL.md 文件格式示例(git技能):
    ---
    name: git
    description: 按照仓库规范创建git提交
    ---
    # Git提交规范
    1. 提交前必须运行 `lint` 检查代码格式
    2. 提交信息格式:[类型]: 简短描述(如 feat: 新增登录功能)
    3. 类型可选:feat/fix/docs/style/refactor/test/chore
    
  • _parse_frontmatter 方法(核心):拆分 --- 分隔的元信息和正文,实现「摘要」和「完整内容」的分离。
SkillLoader 代码解释

1. Skills 的目录:
在这里插入图片描述

			  self.skills = {}  # 存储所有技能的元信息+完整内容
        # 扫描skills目录下所有SKILL.md文件(每个技能一个文件)
        for f in sorted(skills_dir.rglob("SKILL.md")):
            text = f.read_text()
  • sorted():把找到的文件按路径排序,保证每次加载顺序一致;
  • rglob:通过迭代的方式,循环里会逐个读取每个 SKILL.md 的文本内容,为后续解析做准备

2. 解析 [Skill内容] 拆分为 [元信息] 和 [正文]

					 # 解析文件:分离前置元信息(name/description)和正文(完整技能规则)
            meta, body = self._parse_frontmatter(text)
            # 技能名称优先用元信息,没有则用目录名
            name = meta.get("name", f.parent.name)
            # 存储技能:meta=极简描述,body=完整规则
            self.skills[name] = {"meta": meta, "body": body}

  • self._parse_frontmatter(text):用 --- 分隔「元数据」和「正文」;
  • name = meta.get("name", f.parent.name):将 skill.md 的父目录名称作为 name,然后存储技能;

所以 self.skills的形状如下所示:

self.skills = {
    "git": {
        "meta": {"name": "git", "description": "Git规范助手"},
        "body": "# Git提交规范\n1. 提交前lint代码...\n2. 提交信息格式..."  # 详细内容
    },
    "test": {
        "meta": {"name": "test", "description": "测试最佳实践"},
        "body": "# 测试规范\n1. 单元测试覆盖率≥80%..."  # 详细内容
    }
}
2. 系统提示 + 工具注册(两层注入的落地)
# 1 初始化技能加载器,扫描 skills 目录,拆分每个技能的 meta(极简描述)和 body(完整内容)
SKILL_LOADER = SkillLoader(Path("skills"))

# 2 系统提示(第一层):包含所有技能的极简描述,始终存在
SYSTEM = f"""You are a coding agent at {WORKDIR}.
Skills available:
{SKILL_LOADER.get_descriptions()}  # 注入「git: Git规范助手」等极简描述
Use load_skill to get full instructions for a skill when needed."""

# 3 工具映射(新增load_skill工具,触发第二层注入)
TOOL_HANDLERS = {
    "bash":       lambda **kw: run_bash(kw["command"]),
    "read_file":  lambda **kw: run_read(kw["path"], kw.get("limit")),
    "write_file": lambda **kw: run_write(kw["path"], kw["content"]),
    "edit_file":  lambda **kw: run_edit(kw["path"], kw["old_text"], kw["new_text"]),
    "load_skill": lambda **kw: SKILL_LOADER.get_content(kw["name"]),
}

# load_skill工具定义:告诉模型如何调用
TOOLS.append({
    "name": "load_skill",
    "description": "Load full instructions for a specific skill (use when needed).",
    "input_schema": {
        "type": "object",
        "properties": {"name": {"type": "string"}},  # 传入技能名称(如git)
        "required": ["name"],
    }
})

def agent_loop(messages: list):
    while True:
        response = client.messages.create(
            model=MODEL, system=SYSTEM, messages=messages,
            tools=TOOLS, max_tokens=8000,
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return
        results = []
        for block in response.content:
        		 # 第二层的核心:调用工具的时候,才通过 get_content 获取工具的详细内容
            if block.type == "tool_use":
                handler = TOOL_HANDLERS.get(block.name)
                try:
                    output = handler(**block.input) if handler else f"Unknown tool: {block.name}"
                except Exception as e:
                    output = f"Error: {e}"
                print(f"> {block.name}: {str(output)[:200]}")
                results.append({"type": "tool_result", "tool_use_id": block.id, "content": str(output)})
        messages.append({"role": "user", "content": results})

四、从第四季到第五季的核心变化(面试重点)

组成部分 第四季(父-子Agent) 第五季(按需技能加载)
工具集 5个(基础+task) 5个(基础+load_skill)
系统提示 静态固定字符串 动态注入技能极简描述(第一层)
知识存储 无专门技能文件 拆分到skills/*/SKILL.md文件
知识注入方式 无(子Agent靠自身上下文执行) 两层注入:系统提示(轻量)+ tool_result(按需)
核心目标 解决上下文膨胀(隔离+销毁) 解决代币浪费(按需加载)

五、执行流程示例(面试可直接说)

用户指令:「按照git规范提交当前代码修改」

  1. 系统提示里只有「git: Git规范助手」的极简描述,模型知道有这个技能,但不知道具体规范;
  2. 模型判断需要git技能的完整规则,调用 load_skill("git") 工具;
  3. SkillLoader 读取skills/git/SKILL.md的完整内容,以 <skill name="git">...</skill> 格式返回;
  4. 这个完整规则作为tool_result注入上下文,模型基于这些规则调用bash工具执行git提交(如 git commit -m "feat: 新增登录功能");
  5. 整个过程中,只有git技能的完整内容被加载,其他技能(如code-review、deploy)的完整内容从未进入上下文,节省90%代币。

六、核心优势(面试答法)

  1. 代币高效:仅加载任务需要的技能完整内容,避免未使用技能占用代币;
  2. 系统提示轻量化:始终只保留技能名称+极简描述,模型更容易聚焦核心;
  3. 技能可扩展:新增技能只需在skills目录下创建SKILL.md,无需修改系统提示,维护成本低;
  4. 上下文清晰:技能知识按需注入,不会让上下文充斥无关规则,模型执行更精准。

总结(核心关键点)

第五季的核心是「技能分层+按需加载」:

  1. 第一层(系统提示):低成本常驻,让模型知道「有什么技能」;
  2. 第二层(load_skill工具):高成本按需加载,让模型获取「技能的完整规则」;
  3. 对比第四季:第四季解决「上下文膨胀」(横向隔离),第五季解决「系统提示冗余」(纵向按需),两者结合能支撑更复杂的Agent系统。

面试时重点说清「两层注入」的逻辑:为什么要分层(代币浪费)、怎么分层(摘要 + 完整内容)、怎么落地(SkillLoader + load_skill工具),就能精准回应面试官的核心关注点。

一、为什么需要向量数据库?第五季方案的痛点

第五季的「load_skill」是被动加载:模型需要明确知道「技能名称」(比如调用 load_skill("git")),但实际场景中存在问题:

  1. 模型可能「词不达意」:用户说「帮我规范代码提交」,模型可能想不到要调用「git」技能(名称和需求的语义不匹配);
  2. 技能多了之后,系统提示里的「技能列表」会变长:100个技能的极简描述也会占用代币,且模型难以快速匹配;
  3. 无法模糊匹配:比如用户说「审核PR漏洞」,模型如果不知道对应技能叫「code-review」,就会错过加载。

而向量数据库的核心价值是:把「技能描述/工具说明」向量化,通过「语义相似度」主动匹配用户需求,自动找到该加载的技能,无需模型记住技能名称。

二、核心原理:向量数据库+语义匹配的技能加载

1. 核心流程(对比第五季)
# 第五季(被动加载)
用户需求 → 模型识别技能名称 → 调用load_skill(名称) → 加载完整技能

# 向量数据库版(主动匹配)
1. 预处理:把所有技能的「名称+描述」向量化,存入向量数据库(比如FAISS/Pinecone);
2. 运行时:
   用户需求 → 向量化用户需求 → 向量库检索「语义最相似的技能」 → 自动加载对应技能的完整内容 → 模型基于技能执行任务
2. 核心落地代码(关键片段)
import faiss
import numpy as np
from sentence_transformers import SentenceTransformer

# 1. 初始化向量模型(用于文本向量化)和向量数据库
embed_model = SentenceTransformer("all-MiniLM-L6-v2")  # 轻量高效的语义模型
index = faiss.IndexFlatL2(384)  # 向量维度和模型匹配(384维)

# 2. 技能数据预处理(离线完成)
class VectorSkillLoader(SkillLoader):
    def __init__(self, skills_dir: Path):
        super().__init__(skills_dir)
        # 提取所有技能的「名称+描述」作为匹配文本
        self.skill_texts = []
        self.skill_names = []
        for name, skill in self.skills.items():
            match_text = f"{name}: {skill['meta']['description']}"
            self.skill_texts.append(match_text)
            self.skill_names.append(name)
        # 向量化技能文本,存入向量库
        skill_embeddings = embed_model.encode(self.skill_texts)
        index.add(np.array(skill_embeddings).astype("float32"))

    # 核心:根据用户需求语义匹配技能
    def match_skill(self, user_query: str) -> str:
        # 向量化用户需求
        query_embedding = embed_model.encode([user_query])
        # 向量库检索最相似的技能(Top1)
        distances, indices = index.search(np.array(query_embedding).astype("float32"), k=1)
        if distances[0][0] > 0.5:  # 相似度阈值,避免匹配无关技能
            return "Error: No matching skill found."
        # 返回匹配到的技能名称
        matched_name = self.skill_names[indices[0][0]]
        # 直接返回该技能的完整内容(无需模型手动调用load_skill)
        return self.get_content(matched_name)

# 3. Agent循环中自动匹配技能
def agent_loop(messages: list):
    # 提取用户最新需求
    user_query = messages[-1]["content"]
    # 自动匹配并加载技能
    matched_skill_content = SKILL_LOADER.match_skill(user_query)
    if "Error" not in matched_skill_content:
        # 把匹配到的技能内容注入上下文(替代手动load_skill)
        messages.append({
            "role": "user",
            "content": [{"type": "text", "text": matched_skill_content}]
        })
    # 后续逻辑和第五季一致(调用模型、执行工具等)
    ...

三、核心优势(面试重点)

  1. 语义级匹配:用户说「规范代码提交」能匹配到「git」技能,说「审核PR漏洞」能匹配到「code-review」,无需模型记住技能名称;
  2. 更极致的代币节省:系统提示里甚至可以去掉「技能列表」,完全靠向量匹配,进一步降低代币消耗;
  3. 可扩展:技能数量从10个增加到1000个,向量匹配的效率远高于模型手动遍历列表,且不会增加上下文负担;
  4. 自动化:从「模型手动调用load_skill」升级为「代码自动匹配加载」,减少模型的决策负担,降低出错概率。

四、和第五季方案的对比(面试答法)

维度 第五季(按需加载) 向量数据库版(语义匹配)
加载触发方式 模型主动调用load_skill工具 代码自动语义匹配,无需模型干预
匹配粒度 精确匹配技能名称 模糊匹配语义(更贴近自然语言需求)
技能数量上限 几十个(多了系统提示会变长) 上千个(向量检索效率不受数量影响)
核心依赖 模型识别技能名称的能力 向量模型+向量数据库的语义匹配能力
适用场景 技能少、名称易记忆 技能多、用户需求表述不固定

五、面试高频追问回应

1. 为什么用向量数据库,而不是直接让模型匹配?
  • 模型匹配需要把所有技能描述放进上下文,占用代币且效率低;
  • 向量数据库的语义匹配是「数值计算」,速度远快于模型推理,且能处理海量技能;
  • 向量匹配的阈值可控制,避免模型主观判断的误差(比如模型可能漏匹配)。
2. 向量数据库选哪个?怎么权衡?
  • 轻量场景:用FAISS(本地部署,无需联网,适合单机Agent);
  • 分布式场景:用Pinecone/Weaviate(云服务,支持大规模向量数据,适合多Agent协作);
  • 核心权衡:本地库(快、免费、数据隐私)vs 云库(可扩展、维护成本低)。

总结(核心关键点)

向量数据库赋能Agent技能加载的核心是:

  1. 预处理:把技能描述向量化存入数据库,完成「文本→数值」的转换;
  2. 运行时:用户需求向量化后,通过「数值相似度」找到最匹配的技能;
  3. 自动化:无需模型手动调用工具,代码自动加载匹配到的技能内容;
  4. 核心价值:解决「技能名称和用户需求语义不匹配」的问题,同时支持海量技能的高效匹配,是第五季「按需加载」的进阶形态。

这也是工业级Agent系统的常见做法——面试时能说清「语义匹配」「向量预处理」「自动加载」这三个关键词,就能体现你对Agent技能管理的深度理解。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐