1. 背景与核心概念:当 LLM Agent 有了“成长”需求
如果你最近在关注大模型应用开发,一定对 LLM Agent(大语言模型智能体)不陌生。简单来说,Agent 是能够感知环境、做出决策并执行动作的 AI 程序,而 LLM 在其中扮演“大脑”的角色,负责理解任务、拆解步骤、调用工具和生成结果。从自动写代码到操作浏览器,从数据分析到客服对话,LLM Agent 正在成为 AI 落地的重要形态。
但这里有一个非常关键的问题:大多数 Agent 是“一次性”的。
什么意思?比如你让 Agent 帮你完成一个数据分析任务,它通过调用 Python 代码、读取 CSV、画图,最终交付了结果。整个过程看起来不错,但下次你再给它一个类似任务,它还会重复一遍完全相同的探索过程,既不会记住上次的处理经验,也不会调用已经沉淀下来的分析模板。它没有“越用越顺手”的成长性。
这就引出了本文的核心主题:ContinualSkillBench——一个用来评估 LLM Agent 是否能持续获取技能、真正实现能力演进的基准框架。
从标题来看,这个项目聚焦于一个非常尖锐的问题:LLM Agent 真的能演化自身能力吗?“演化”这个词很重。它不是指模型参数在训练阶段被更新,而是指 Agent 在部署使用过程中,能否像人类一样,通过完成任务积累经验,把经验转化为可复用的技能,并在未来任务中主动使用这些技能。
换句话说,我们要评估的不是“这个 Agent 在某个任务上表现多好”,而是“这个 Agent 在经历一系列任务之后,是否变得更强了”。
这个方向的价值在于:当前主流的 Agent 评估基准,比如 HotpotQA、ALFWorld、WebArena 等,多数是静态的、单次任务的。它们能告诉我们 Agent “当前能做什么”,但无法回答“Agent 能否持续成长”。而如果 Agent 无法从经验中学习,那么它在真实业务中的价值就会大打折扣——因为真实业务中,同一类型的任务会反复出现,每一次都应该比上一次做得更快、更好。
ContinualSkillBench 正是冲着这个缺口来的。它试图构建一套可复现、可量化的评估方法,用来回答三个层次的问题:
- Agent 能否从已完成的任务中提取出可复用的技能?
- Agent 能否在后续任务中主动应用已学到的技能?
- 这种“技能获取-技能应用”的过程能否持续闭环,形成能力跃迁?
下面,我会从概念出发,逐步拆解 ContinualSkillBench 的设计思路、核心模块、评估指标,以及对 Agent 开发实践的启示。即使你没有读过原论文,跟着这篇文章走一遍,也能理解这个基准的骨架和背后逻辑。
2. 环境准备与版本说明:如何搭建一个可运行的 Agent 评估环境
虽然 ContinualSkillBench 是一个研究型基准,但理解它最好的方式,还是亲手跑一个 Agent 任务流,观察它如何在“任务执行—经验沉淀—技能复用”的循环中运行。下面我们先搭建一个最小可运行的 LLM Agent 环境。
2.1 环境依赖
本文示例以常见环境为例,版本需要根据你的实际项目调整,重点是演示配置思路。
- 操作系统:Ubuntu 20.04 / macOS 12+ / Windows 10+(建议 Linux/macOS)
- Python:3.9 或 3.10
- 大模型访问方式:OpenAI API 或兼容接口(如本地部署的 vLLM、Ollama)
- 依赖库:openai、langchain(可选)、pandas、numpy、rich
建议使用虚拟环境隔离项目依赖:
python -m venv agent_env source agent_env/bin/activate # Windows 下使用 agent_env\Scripts\activate pip install openai langchain pandas numpy rich2.2 项目结构
我们按下面结构组织代码,方便后续扩展成完整的持续学习评估流程:
continual_skill_demo/ ├── agent.py # Agent 核心逻辑 ├── experience_pool.py # 经验池,用来存储和检索历史技能 ├── task_router.py # 任务路由,判断当前任务是否需要调用已有技能 ├── data/ │ └── tasks.json # 测试任务流 └── main.py # 主流程,执行多轮任务2.3 基础 Agent 实现
我们先用一个最简单的 Agent 原型,来演示“执行任务”的部分。它接收一个用户指令,调用 LLM 生成答案:
# 文件路径:agent.py import os from openai import OpenAI client = OpenAI( api_key=os.getenv("OPENAI_API_KEY"), base_url=os.getenv("OPENAI_BASE_URL") # 兼容本地模型服务 ) SYSTEM_PROMPT = "你是一个善于总结和解决问题的智能助手。" def run_agent(task: str, context: str = "") -> str: """执行单个任务,返回 Agent 的回答。""" messages = [ {"role": "system", "content": SYSTEM_PROMPT}, ] if context: messages.append({"role": "system", "content": f"历史技能提示:\n{context}"}) messages.append({"role": "user", "content": task}) resp = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=messages, temperature=0.2 ) return resp.choices[0].message.content这里的关键是context参数。当 Agent 从经验池中检索到相关技能时,它会以系统提示的形式注入到上下文里,从而影响当前任务的执行方式。这也对应了 ContinualSkillBench 中“技能复用”的基本机制。
3. 核心设计拆解:ContinualSkillBench 在评估什么
现在我们已经有了一个可运行的 Agent 雏形。但 ContinualSkillBench 关心的不是“Agent 能不能做任务”,而是“Agent 能不能越做越好”。要做到这一点,我们需要把评估框架拆开来看。
3.1 三个核心评估维度
基于项目标题所传递的信息,ContinualSkillBench 的设计至少应该覆盖三个维度:
第一个维度是技能获取能力。Agent 在完成一个任务后,能否从执行过程中提炼出可复用的方法论?比如,Agent 完成了一个“从网页中批量提取商品标题”的任务,它能否总结出“使用 BeautifulSoup 定位 HTML 标签时,应该先分析目标网站的 DOM 结构”这样的技能?这个维度衡量的是“经验的转化率”。
第二个维度是技能调用能力。当 Agent 遇到一个新任务时,能否主动检索并应用之前学到的技能?这远比“是否学会了”更难,因为它涉及到任务匹配、技能检索和上下文融合。很多 Agent 的问题在于:经验池里存了很多技能卡片,但遇到新任务时,它不知道“这个问题和之前的那个问题是同一类”。
第三个维度是持续累积能力。Agent 经历的每一个任务,都应该成为后续任务的垫脚石。如果一个 Agent 在 10 个任务后表现得和 1 个任务后一样,那它没有真正的演化。这个维度关注的是“能力曲线的斜率”。
3.2 任务流:从单任务到多任务序列
传统评估中,每个任务都是独立采样的。ContinualSkillBench 则会设计一系列存在内在关联的任务序列。举个例子:
- 任务 1:写一个 Python 函数,读取 CSV 文件并统计每列的缺失值。
- 任务 2:写一个 Python 函数,读取 JSON 文件并统计嵌套字段的缺失值。
- 任务 3:写一个 Python 函数,读取 SQLite 数据库中的表,并统计每列的缺失值。
- 任务 4:写一个 Python 函数,针对一个包含 CSV、JSON、SQLite 三种数据源的数据集,输出统一格式的缺失值报告。
可以看到,任务 4 是一个综合性任务,它要求 Agent 把前三项任务中积累的经验融合成一个更通用的解决方案。如果 Agent 没有持续学习的机制,它会在任务 4 中从零开始思考;如果 Agent 具备技能沉淀与复用能力,它在任务 4 中应该能直接调用“数据源探测”“缺失值统计”“统一格式化输出”等技能,效率和质量都会有明显提升。
这种设计思路的核心是:用任务序列的递进关系,来模拟真实业务中的经验积累场景。
3.3 技能表达形式:自然语言 vs 代码模板
在 ContinualSkillBench 这类框架中,技能的表达方式决定了后续检索和复用的效果。目前主流做法有两种。
第一种是自然语言技能描述。Agent 在完成任务后,会生成一段结构化的文本,比如:
技能名称:CSV 缺失值统计 适用场景:读取本地 CSV 文件并统计缺失值 核心步骤: 1. 使用 pandas.read_csv 读取文件 2. 使用 df.isnull().sum() 统计每列缺失值 3. 用 df.dtypes 确认列类型,注意空字符串会被视为非缺失 注意事项: - 文件编码建议使用 utf-8-sig,避免中文乱码 - 大文件建议使用 dtype 参数减少内存占用这种表达方式的好处是语义丰富,易于被 LLM 理解和改写,坏处是检索时依赖 embedding 匹配的精度。
第二种是代码模板技能。Agent 保存的是一个带占位符的代码片段,比如:
def load_data(file_path: str, file_type: str) -> pd.DataFrame: if file_type == "csv": return pd.read_csv(file_path, encoding="utf-8-sig") elif file_type == "json": return pd.read_json(file_path) else: raise ValueError(f"Unsupported file type: {file_type}")这种方式的好处是复用起来直接、执行可靠,坏处是灵活性不足,难以应对任务变化较大的场景。
ContinualSkillBench 的价值在于:它提供了一个标准化的评估机制,让我们可以量化比较“用自然语言技能”和“用代码模板技能”哪种方式对 Agent 的能力演化贡献更大。这一点对 Agent 工程实践非常有指导意义。
3.4 评估指标:不只是任务准确率
如果只看“任务完成率”,我们无法判断 Agent 是否发生了能力演化。ContinualSkillBench 这类基准通常会引入更精细的指标。
一个常用指标是技能获取率,即 Agent 在完成 N 个任务后,成功沉淀出的高质量技能数量占可提取技能总量的比例。一个只会执行任务、不会总结经验的 Agent,这项指标会很低。
另一个指标是技能复用率,即在后续任务中,Agent 主动调用了之前学到的技能的比例。这个指标可以直接反映 Agent 是否具备“记忆”和“联想”能力。
还有一个指标是性能增益,即随着任务序列的推进,Agent 在同类任务上的完成时间、调用步数、最终质量是否有显著提升。如果 Agent 学了 10 个技能但第 11 个任务的表现和第 1 个任务没有任何差异,那说明它没有学会“举一反三”。
更严格的设计还会加一个遗忘测试:在 Agent 完成前 10 个任务并学习了相关技能后,过一段时间再给出一个与任务 3 类似但略有变化的任务,考察 Agent 是否还记得、能否正确应用。这与人类的“记忆衰减”和“知识巩固”概念非常相似。
4. 实战案例:实现一个带经验池的持续学习 Agent
理论讲完了,下面我们动手实现一个简化版的持续学习 Agent。这个案例虽然没有完全复刻 ContinualSkillBench 的完整实验设计,但能让你直观感受到“技能沉淀—检索—复用”的完整闭环。
4.1 经验池模型
我们用字典存储技能,用 embedding 做语义检索:
# 文件路径:experience_pool.py import numpy as np from openai import OpenAI client = OpenAI() class ExperiencePool: def __init__(self): self.skills = [] # 技能文本列表 self.embeddings = [] # 技能向量列表 def add_skill(self, skill_text: str): """新增一条技能,并计算 embedding 存入经验池。""" vec = client.embeddings.create( model="text-embedding-3-small", input=skill_text ).data[0].embedding self.skills.append(skill_text) self.embeddings.append(vec) def retrieve(self, task: str, top_k: int = 2) -> str: """根据当前任务检索最相关的技能描述。""" if not self.skills: return "" task_vec = client.embeddings.create( model="text-embedding-3-small", input=task ).data[0].embedding scores = [] for skill_vec in self.embeddings: score = np.dot(task_vec, skill_vec) / ( np.linalg.norm(task_vec) * np.linalg.norm(skill_vec) ) scores.append(score) top_idx = np.argsort(scores)[-top_k:][::-1] context_blocks = [] for i in top_idx: context_blocks.append(f"【已有技能 {i+1}】\n{self.skills[i]}") return "\n\n".join(context_blocks)4.2 技能提取函数
Agent 完成任务后,我们会调用 LLM 生成一份结构化的技能描述,并过滤掉无效内容:
# 文件路径:agent.py(追加) def extract_skill(task: str, experience: str) -> str | None: """从执行经验中提取可复用技能,无效时返回 None。""" prompt = f""" 根据以下任务和 Agent 执行经验,总结一条可复用的技能描述。 - 技能名称 - 适用场景 - 核心步骤 - 注意事项 只输出技能内容本身,不要多余解释。 如果经验中没有值得复用的内容,请输出:NULL 任务:{task} 执行经验:{experience} """ resp = client.chat.completions.create( model=os.getenv("LLM_MODEL", "gpt-4o-mini"), messages=[{"role": "user", "content": prompt}], temperature=0.1 ) content = resp.choices[0].message.content.strip() return None if content.upper().startswith("NULL") else content4.3 主流程
主流程模拟了“连续执行 5 个任务,并在第 3、第 4、第 5 个任务时注入历史技能”的完整过程:
# 文件路径:main.py from agent import run_agent, extract_skill from experience_pool import ExperiencePool TASKS = [ "写一个 Python 函数,读取 CSV 文件,并返回每列的缺失值数量。", "写一个 Python 函数,读取 JSON 文件,并统计嵌套字典中每个键的缺失情况。", "写一个 Python 函数,读取 SQLite 数据库中的表,并统计每列的空值数量。", "写一个 Python 函数,自动识别数据文件类型(csv/json/sqlite),并统一输出缺失值报告。", ] def main(): pool = ExperiencePool() for i, task in enumerate(TASKS, 1): print(f"\n===== 执行第 {i} 个任务 =====") # 1. 检索历史技能 context = pool.retrieve(task) if context: print(f"检索到已有技能,注入上下文。") else: print("未检索到相关技能,从零开始执行。") # 2. 执行任务 result = run_agent(task, context=context) print(f"Agent 输出片段:{result[:100]}...") # 3. 提取技能并存入经验池 skill = extract_skill(task, result) if skill: pool.add_skill(skill) print(f"已沉淀技能:{skill[:50]}...") else: print("本次任务未提取到有效技能。") if __name__ == "__main__": main()4.4 运行与验证
配置好OPENAI_API_KEY后,直接运行:
python main.py预期输出中可以看到,前两个任务因为经验池为空,Agent 从零开始推理;到第 3 个任务时,经验池中已经存在“CSV 缺失值统计”和“JSON 缺失值统计”两条技能,Agent 会得到相似任务的上下文提示;第 4 个任务是一个综合性任务,它会融合多个技能片段,输出更高效的实现。
这里需要特别说明一点:由于我们没有定义自动化的答案质量评估器,所以这个演示只能看到“技能是否被检索和注入”,无法量化“技能注入后效果提升了多少”。ContinualSkillBench 的完整设计中,会加入人工评估或 LLM 评测器,为每个任务的输出质量打分,从而计算出能力演化曲线。
5. 常见问题与排查思路:持续学习 Agent 的工程陷阱
在构建带经验池的 Agent 过程中,你可能会遇到几个高频问题。下面整理成表格,方便快速定位。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 检索到的技能与当前任务无关 | embedding 模型区分度不足,或技能描述过于泛化 | 在技能文本中增加“适用场景”和“排除场景”;使用更强的 embedding 模型;调整 top_k 值 |
| Agent 忽略了注入的技能提示 | 技能上下文太长,挤占了模型注意力窗口 | 精简技能文本,只保留步骤和注意事项;把技能放在用户消息末尾,而不是 system prompt 中 |
| 技能越存越多,检索越来越慢 | 经验池无上限,且每次任务都全量计算相似度 | 增加技能数量上限,或引入周期性压缩;使用向量数据库(如 Milvus、Chroma)替代内存数组 |
| 提取的技能质量参差不齐 | 温度参数过高,或 LLM 模型本身总结能力不足 | 调低 temperature 到 0.1 以下;使用更强模型做技能提取;加入格式约束和人工抽检 |
| 同一类型技能重复存储 | 缺少去重机制 | 存入前先与已有技能计算相似度,超过阈值则合并或跳过 |
| 早期错误经验被不断复用 | 没有负反馈机制 | 在技能描述中保留“已知局限”;建立失败案例池,标记不可用技能 |
这些问题的根源往往不是某一个环节,而是“提取—存储—检索—注入”这条链路缺乏闭环反馈。在工程实践中,除了做好每个环节,还需要记录每次技能注入后的效果,形成数据驱动的技能淘汰机制。
6. 最佳实践与工程建议:如何打造真正会成长的 Agent
ContinualSkillBench 不仅仅是学术界的评估基准,它的设计思想可以直接借鉴到生产环境的 Agent 架构中。下面我从工程落地的角度,给出几条具体建议。
第一条建议是技能分层管理。不要把所有内容都塞进一个经验池。建议拆成三层:第一层是领域常识,比如“CSV 文件默认使用 utf-8-sig 编码读取”,这类知识稳定且通用;第二层是任务模板,比如“数据缺失值报告的输出格式”,这类模板适用于一类任务;第三层是项目专属经验,比如“某个内部系统的 API 调用方式”,这类经验只在特定场景下有效。不同层级的技能,要有不同的索引策略和生命周期管理策略。
第二条建议是技能存储结构要结构化。不要只存一段自然语言文本。建议使用统一的技能 Schema,至少包含技能名称、触发条件、核心步骤、预期输出、已知局限、适用范围、创建时间、最后使用时间等字段。结构化存储让检索和过滤变得更容易,也让未来的技能去重、合并算法有了操作基础。
第三条建议是主动引入负面经验。大多数 Agent 只会总结“怎么做成功”,却不知道“什么不能做”。ContinualSkillBench 如果只是衡量正向技能的增长,会忽略一个关键问题:Agent 可能记住了失败的方法,导致同样的错误反复出现。工程上,我们应该在经验池中加入负例标签,比如“使用 str.split() 处理嵌套 JSON 时会产生 KeyError,应改用递归遍历”。当一个技能被多次标记为“使用后出错”,系统应该自动降低它的检索权重。
第四条建议是让技能注入具有可追溯性。在实际项目中,你一定遇到过“Agent 突然输出变了,但不知道为什么”的情况。这就是因为技能注入是黑盒的。推荐在每次 Agent 运行前,打印检索到的技能编号和相关性分数,运行后记录最终效果,这样既方便调优,也方便事故定位。
第五条建议是评估闭环要制度化。如果只是上线了一个带经验池的 Agent,却不持续评估它的能力变化,那这个经验池很快会变成垃圾堆。建议借鉴 ContinualSkillBench 的任务流设计:每周准备一组新的测试任务集,一部分与历史任务相似,一部分与历史任务存在潜在关联,一部分是完全新颖的任务。分别记录 Agent 在三种任务上的表现,就能动态刻画它的“能力演化曲线”——技能是否在被复用、是否在发生迁移、是否存在灾难性遗忘。
7. 总结与下一步学习方向
回到标题中的问题:Can LLM Agents Truly Evolve Their Capabilities?
从 ContinualSkillBench 的设计思路来看,答案不是简单的“能”或“不能”,而是“取决于我们如何设计 Agent 的学习闭环”。如果我们只把 Agent 当做一个单次调用的大模型接口,那它当然不会演化;但如果我们给它引入经验池、技能提取、语义检索、任务路由、负反馈机制,它就有可能在一次次任务中形成能力积累,表现出持续进化的特征。
这篇文章从评估框架的角度拆解了 ContinualSkillBench,再带你实现了一个简化版的经验池 Agent。如果你对这个方向感兴趣,下一步可以从这几个方面深入:
- 读一下持续学习(Continual Learning)的相关论文,理解灾难性遗忘和知识巩固的数学原理。
- 尝试用更强的开源模型替代 GPT 系列,配合本地向量数据库搭建一套完整的离线持续学习 Agent。
- 关注 Agent 评估方法学,特别是如何设计任务序列来避免数据泄漏和技能互污染。
- 在自己业务场景中,选取一个高频重复的任务类型,设计一套“执行—总结—沉淀—复用”的闭环,测量技能复用前后的效率差异。
如果你正在做 LLM Agent 开发,建议把“持续技能积累”作为系统设计的一等公民,而不是事后补丁。真正让 Agent 在业务中产生价值的地方,不是它第一次完成任务的惊艳表现,而是它在第 100 次任务时,已经比第 1 次更熟练、更稳定、更聪明。