智能体自我改进 Meta^n:从 Meta^1 到 Meta^2 的闭环实现
智能体开发做到一定阶段瓶颈几乎一样智能体刚上线时效果不错但跑了几周之后面对新的任务格式、新的错误分支、新的数据分布能力并不会自动变强。Meta^n 是一类让智能体自我改进的方法思路核心不再是让人修改提示词和流程而是在智能体的正常执行循环之外增加一层或多层“观察自身表现、生成改进方案、验证改进效果、提交改进结果”的机制。n 表示递归层级数Meta^1 是智能体改进自己的执行策略Meta^2 是让智能体改进“改进流程本身”层级越高被纳入改进范围的对象越抽象。下面用一个最小 Python 案例演示 Meta^1 从零到可运行的全过程并说明向 Meta^2 扩展时最容易踩的坑。1. 先理解 Meta^n改进对象从“任务输出”升级为“智能体自身”1.1 为什么普通智能体部署后不会自动变强普通智能体架构里系统提示词、工具列表、编排逻辑都是上线前由人写死的。运行中即使失败了失败信息只是进入日志不会反过来改变智能体自己的行为。Dify、Coze 这类平台把智能体的工作流搭建、工具注册、变量配置做得非常方便但平台本身并不会在你任务失败率升高时主动重写你的提示词或调整编排节点。也就是说平台解决的是“怎么搭智能体”不解决“智能体怎么越用越强”。如果把智能体看作一个函数f(prompt, tools, task) - output那人工调优就是人在循环外修改 prompt 和 tools。Meta^n 的做法是把“修改 prompt 和 tools”这件事本身也变成一个可执行任务交给一个上层智能体去完成。这个上层智能体就是 meta-agent它读入当前版本的执行记录、评估结果、失败摘要产出一个候选版本再通过验证决定是否替换当前版本。1.2 Meta^n 的层级含义与递归深度Meta^n 中的 n 描述递归嵌套的深度。每个层级改进的对象不同成本也完全不同。层级改进对象典型循环主要成本Meta^0无只执行任务用户任务 - 工具调用 - 返回结果单次推理Meta^1系统提示词、工具描述、任务策略执行一批任务 - 评估 - 生成新提示词 - 验证 - 提交一轮评估 多次候选推理Meta^2改进器自身的配置、评估口径、提交策略观察改进历史 - 调整改进器的生成方式或阈值 - 验证改进效果需要维护改进历史成本成倍上升Meta^n第 n-1 层改进器的工作方式递归嵌套指数级上升实践中通常 n 2实际工程里大多数有价值的场景落在 Meta^1。Meta^2 更多用于研究“如何让改进过程更稳定”而不是直接提升任务效果。1.3 为什么不能把“自我改进”做成无限循环自我改进听起来像递归实际落地时最需要警惕的是失控。如果让智能体无限制地修改自己的提示词、代码、参数可能出现三种后果过拟合智能体记住了评估集里的任务答案换一批任务就退化。漂移提示词在多次修改后越来越长、越来越绕最后不再遵守工具调用格式。失控改进流程调用改进流程请求量、token 消耗、运行时间都指数膨胀。所以 Meta^n 的方法论里“验证”和“回滚”不是可选项而是核心组件。后面的四步闭环就是围绕这一点设计的。2. 核心循环拆解评估、生成、验证、提交2.1 一次完整迭代的四步闭环Meta^1 的每一个迭代轮次都围绕四个步骤展开评估当前版本在当前版本智能体上运行一批任务得到评分和失败记录。生成失败摘要从失败记录里抽取共性原因作为改进器的输入。生成候选版本改进器根据失败摘要产出一版新的系统提示词或工具描述。验证并提交在验证集上对比候选版本和当前版本只有提升超过阈值才提交。以工具调用智能体为例第 1 步可以让智能体执行 20 个任务统计“结果是否命中标准答案关键词”第 3 步的改进器可以复用同一个大模型但使用一个独立的“改进工程师”系统提示词第 4 步必须使用当前版本没有见过的验证集否则无法判断候选版本是真的变强还是背了题。2.2 评估集与验证集必须分离这是 Meta^n 最容易出错的地方。很多初版实现只准备了一份任务集改进器用这份任务集的失败摘要生成候选然后又用同一份任务集验证候选。这样得到的提升往往是虚假的因为候选提示词可能只是把评估任务的答案模式记住了。正确做法是把数据集切成两份评估集train-like和验证集held-out。评估集用于产生失败摘要验证集用于决定是否提交。如果数据量不大还可以采用多次随机切分每次用不同的划分跑一轮避免单次划分带来的偶然性。对于真实生产场景还要再保留一份更长时间周期的“在线灰度数据”用来观察提交后的真实效果。2.3 改进候选必须版本化并可回滚每个提交的候选版本都对应一份可回溯的产物包括系统提示词全文、工具描述、使用的评估指标、评估结果、验证集结果、提交时间。无论结果是变好还是变坏都应该能一键回到上一个版本。从实现角度看版本化不一定要引入复杂系统。一个 JSONL 文件、一个带日期的目录、一个 Git 提交都够用。关键点是“当前生效版本”和“历史版本”的切换路径要非常简单否则当线上指标退化时回滚动作本身又成为一次新的事故。3. 最小可运行案例让工具调用智能体学会改自己的提示词3.1 环境准备与项目结构本节使用 Python 3.10依赖保持最小PyYAML 用于读取配置。大模型调用抽象为一个client对象示例里使用MockClient返回固定结果方便在没有 API Key 的情况下跑通循环真实项目里替换为 OpenAI 兼容客户端即可。meta_agent/ ├── config.yaml # 智能体和改进器参数 ├── agent.py # 基础智能体 ├── evaluate.py # 评估指标与批量评分 ├── improver.py # 改进器失败摘要与候选生成 ├── main.py # 主循环 └── data.jsonl # 任务与标准答案先看配置文件这里集中了本文的大部分关键参数agent: name: tool_user system_prompt: | 你是一个工具调用智能体。请根据任务选择工具并返回结果。 如果信息不足必须调用 search_tool不要编造答案。 tools: - name: search_tool description: 搜索外部资料并返回摘要 parameters: query: string max_results: 3 meta: n: 1 max_iterations: 5 eval_batch_size: 20 held_out_ratio: 0.3 commit_threshold: 0.05 max_proposals: 33.2 基础智能体与最小评估器基础智能体只需要能保存当前版本的提示词、工具列表并执行一次任务# agent.py from __future__ import annotations from dataclasses import dataclass dataclass class ToolSpec: name: str description: str parameters: dict dataclass class Agent: name: str system_prompt: str tools: list[ToolSpec] client: object def run(self, task: str) - dict: messages [ {role: system, content: self.system_prompt}, {role: user, content: task}, ] response self.client.chat( messagesmessages, tools[t.__dict__ for t in self.tools], ) return { task: task, response: response.get(content, ), tool_calls: response.get(tool_calls, []), }评估器用一个最简单可解释的指标答案是否包含标准答案要求的关键词。实际项目可以根据任务类型替换为准确率、BLEU、人工评分或规则校验。# evaluate.py def score_trace(trace: dict, golden: dict) - float: answer trace.get(response, ) must_include golden.get(must_include, []) if not must_include: return 1.0 hit sum(1 for key in must_include if key in answer) return hit / len(must_include) def score_batch(agent, dataset): total 0.0 failures [] for item in dataset: trace agent.run(item[task]) score score_trace(trace, item) total score if score 1.0: failures.append({task: item[task], trace: trace, score: score}) return total / len(dataset), failures3.3 Meta^1 改进循环改进器的职责是“读失败摘要写新提示词”。注意改进器本身也是一个 LLM 调用但使用独立的系统提示词避免和执行任务的角色混在一起# improver.py def build_failure_summary(failures, limit5) - str: lines [] for s in failures[:limit]: lines.append(f任务{s[task]}) lines.append(f输出{s[trace][response][:200]}) lines.append(f得分{s[score]:.2f}) return \n.join(lines) def propose_new_prompt(client, current_prompt, failure_summary) - str: messages [ { role: system, content: ( 你是智能体改进工程师。根据失败摘要修改系统提示词 让同一类任务下次执行时更可能成功。只输出修改后的提示词本身 不要解释不要输出多余内容。 ), }, { role: user, content: f当前提示词\n{current_prompt}\n\n失败摘要\n{failure_summary}, }, ] resp client.chat(messagesmessages) return resp[content].strip()主循环把四步串起来。这里用MockClient做演示load_dataset从data.jsonl读取任务。关键设计是当前版本和候选版本必须在同一份验证集上做 A/B评估集只负责生成失败摘要。# main.py import json import random import yaml from agent import Agent, ToolSpec from evaluate import score_batch from improver import build_failure_summary, propose_new_prompt class MockClient: 演示用客户端。真实项目替换为 OpenAI 兼容接口。 def chat(self, messages, toolsNone): content messages[-1][content] if isinstance(content, str) and 当前提示词 in content: content 你是一个更严谨的工具调用智能体。必须基于工具返回结果作答。 else: content 已根据内部知识完成回答。 return {content: content, tool_calls: []} def load_dataset(pathdata.jsonl): data [] with open(path, r, encodingutf-8) as f: for line in f: line line.strip() if line: data.append(json.loads(line)) return data def main(cfg_pathconfig.yaml): with open(cfg_path, r, encodingutf-8) as f: cfg yaml.safe_load(f) client MockClient() def make_agent(prompt): return Agent( namecfg[agent][name], system_promptprompt, tools[ToolSpec(**t) for t in cfg[agent][tools]], clientclient, ) dataset load_dataset() random.shuffle(dataset) split int(len(dataset) * (1 - cfg[meta][held_out_ratio])) eval_set dataset[:split] held_out dataset[split:] agent make_agent(cfg[agent][system_prompt]) for iteration in range(cfg[meta][max_iterations]): # 评估集只用于生成失败摘要 _, failures score_batch(agent, eval_set) if not failures: print(fiter{iteration} no failure, stop) break summary build_failure_summary(failures) candidate_prompt propose_new_prompt(client, agent.system_prompt, summary) # 验证集当前版本和候选版本做 A/B base_held_score, _ score_batch(agent, held_out) candidate make_agent(candidate_prompt) cand_held_score, _ score_batch(candidate, held_out) if cand_held_score base_held_score cfg[meta][commit_threshold]: agent.system_prompt candidate_prompt print(fiter{iteration} COMMIT cand{cand_held_score:.3f} base_held{base_held_score:.3f}) else: print(fiter{iteration} REJECT cand{cand_held_score:.3f} base_held{base_held_score:.3f}) if __name__ __main__: main()这段代码里propose_new_prompt返回的候选提示词只在验证集上跑分。因为验证集没有参与失败摘要生成候选版本即使被MockClient乱改也不会因为“见过题”而虚高。3.4 向 Meta^2 扩展的最小思路Meta^2 不直接改任务提示词而是改进“改进器”本身。比如改进器当前用max_proposals3并且每次都从失败摘要的前 5 条生成候选。Meta^2 可以观察连续多轮提交结果提出调整建议提高候选数量、降低提交阈值、换一种失败摘要组织方式。# improver_config.py IMPROVER_CONFIG { temperature: 0.8, num_candidates: 3, failure_summary_limit: 5, selection: single_best, } # meta2.py 的改进入口 def improve_improver(client, improver_config, history) - dict: messages [ { role: system, content: 你是改进器调优器。根据历史提交记录输出一版新的 IMPROVER_CONFIG。, }, { role: user, content: f当前改进器配置{improver_config}\n\n历史{history}, }, ] resp client.chat(messagesmessages) return parse_config(resp[content])要理解的是Meta^2 并不会自动比 Meta^1 更安全。它只是把“谁来改进改进器”从人变成了智能体所以验证和回滚的压力也会同步上移。没有充分的数据沉淀和历史记录不建议直接启用 Meta^2。4. 关键参数说明与选型建议4.1 参数速查表参数含义常见值调大影响调小影响n递归改进层级0-2改进能力更强

相关新闻

最新新闻

日新闻

周新闻

月新闻