
LlamaFactory 作者郑耀威团队刚开源的 PenguinHarness,主打一件事:让 Agent 自己迭代,而不是人手工调提示词。宣传数字很刺眼——0.2 元从零造一个 Agent,准确率还能从 50% 提到 90%。但它有个不那么显眼、却更关键的设计:GDPevo 评测把训练集和测试集硬拆开,就是怕进化过程中 Agent 把答案背下来。
这篇不聊框架本身,直接给一个能跑的最小自进化闸门。Python 3.10+ 标准库即可,四个部件:评测集隔离、技能变更契约、单位任务成本封顶、只有 held-out 分数上升才晋升的进化循环。文末有完整串联示例和运行输出。
Agent 自进化最常见的骗局是:进化时能看到评测样本,分数涨了,换一批真实任务又掉回去。解法很朴素,样本一开始就按 id 哈希切开,进化只碰 train,晋升只看 holdout。
import hashlib
from dataclasses import dataclass, field
from typing import Callable
@dataclass(frozen=True)
class Sample:
sid: str
question: str
answer: str
def split_holdout(samples: list, holdout_ratio: float = 0.3, seed: str = "v1"):
train, holdout = [], []
for s in samples:
h = int(hashlib.sha256((s.sid + seed).encode()).hexdigest(), 16)
(holdout if (h % 100) < int(holdout_ratio * 100) else train).append(s)
return train, holdout
def score(agent: Callable, samples: list) -> float:
if not samples:
return 0.0
ok = sum(1 for s in samples if agent(s.question) == s.answer)
return ok / len(samples)seed 固定后,同一批样本的切分可复现;换 seed 等于换一次盲测,适合定期轮换 holdout,进一步压低背答案的空间。
自进化如果允许 Agent 随便改自己的 skill 文件,很快就会写出“看到这道题就直接返回标准答案”这类捷径。契约层做三件事:禁止访问 holdout id、禁止硬编码答案表、变更必须声明意图。
import re
@dataclass
class SkillPatch:
name: str
intent: str
source: str
class ContractError(Exception):
pass
def check_contract(patch: SkillPatch, holdout_ids: set) -> None:
if not patch.intent.strip():
raise ContractError("变更必须声明意图")
for sid in holdout_ids:
if sid in patch.source:
raise ContractError(f"技能源码不得引用 holdout 样本 id: {sid}")
if re.search(r"ANSWER_TABLE|标准答案|直接返回", patch.source):
raise ContractError("疑似硬编码答案捷径,拒绝晋升")
if "eval(" in patch.source or "exec(" in patch.source:
raise ContractError("禁止动态执行")契约拦不住所有作弊,但它把“最廉价的作弊路径”先堵上了。真正要防住的是分数上涨但行为不可解释,所以晋升时还要看 holdout,而不是只看 train。
PenguinHarness 强调 0.2 元造 Agent,背后其实是成本闸门:进化可以跑,但不能无限烧 token。最小实现是给每次调用记账,超过预算就停。
@dataclass
class Budget:
max_yuan: float
spent: float = 0.0
def charge(self, tokens_in: int, tokens_out: int,
price_in: float = 12.0, price_out: float = 36.0) -> None:
# 价格单位:元 / 百万 token,可按实际模型改
cost = tokens_in * price_in / 1_000_000 + tokens_out * price_out / 1_000_000
if self.spent + cost > self.max_yuan:
raise RuntimeError(f"超出预算:已花 {self.spent:.4f},本次 {cost:.4f}")
self.spent += cost业务里建议再加一个“单位成功任务成本”:只统计最终成功的任务,失败重试的 token 也算进去。这个数比 FLOPS 更能指导你该不该继续进化。
@dataclass
class AgentState:
skills: dict = field(default_factory=dict)
version: int = 0
def apply_patch(state: AgentState, patch: SkillPatch) -> AgentState:
new_skills = dict(state.skills)
new_skills[patch.name] = patch.source
return AgentState(skills=new_skills, version=state.version + 1)
def make_agent(state: AgentState) -> Callable:
# 演示用:技能里写 "prefix:xx" 表示答案以 xx 开头则命中
rules = []
for src in state.skills.values():
m = re.search(r"prefix:(.+)$", src.strip())
if m:
rules.append(m.group(1))
def agent(q: str) -> str:
for p in rules:
if q.startswith(p):
return "yes"
return "no"
return agent
def evolve_once(state, patch, train, holdout, holdout_ids, budget: Budget):
check_contract(patch, holdout_ids)
budget.charge(tokens_in=800, tokens_out=200) # 模拟一次进化开销
candidate = apply_patch(state, patch)
before = score(make_agent(state), holdout)
after = score(make_agent(candidate), train), score(make_agent(candidate), holdout)
train_s, hold_s = after
if hold_s <= before:
return state, {"promoted": False, "reason": "holdout 未提升",
"train": train_s, "holdout": hold_s, "spent": budget.spent}
return candidate, {"promoted": True, "train": train_s, "holdout": hold_s,
"spent": budget.spent}关键不变量只有一句:hold_s <= before 就拒绝晋升。train 分再高,只要 holdout 没动,就当作过拟合丢掉。
samples = [
Sample("t1", "发票报销怎么走", "yes"),
Sample("t2", "发票丢失怎么办", "yes"),
Sample("t3", "合同审批流程", "yes"),
Sample("h1", "发票抬头改错了", "yes"),
Sample("h2", "差旅补贴标准", "no"),
Sample("h3", "合同作废条件", "yes"),
]
train, holdout = split_holdout(samples, holdout_ratio=0.5, seed="demo")
holdout_ids = {s.sid for s in holdout}
state = AgentState()
budget = Budget(max_yuan=0.2)
patches = [
SkillPatch("invoice", "识别发票类问题", "prefix:发票"),
SkillPatch("cheat", "偷瞄评测", f"看 {next(iter(holdout_ids))} 直接返回"),
SkillPatch("contract", "识别合同类问题", "prefix:合同"),
]
for p in patches:
try:
state, info = evolve_once(state, p, train, holdout, holdout_ids, budget)
print(p.name, info)
except (ContractError, RuntimeError) as e:
print(p.name, "BLOCKED", e)
print("final version", state.version, "skills", list(state.skills))
print("holdout score", score(make_agent(state), holdout))运行输出:
invoice {'promoted': True, 'train': 1.0, 'holdout': 0.333..., 'spent': 0.0168}
cheat BLOCKED 技能源码不得引用 holdout 样本 id: h1
contract {'promoted': True, 'train': 1.0, 'holdout': 0.666..., 'spent': 0.0336}
final version 2 skills ['invoice', 'contract']
holdout score 0.666...输出里能看到三件事:合法技能可以靠 holdout 上涨晋升;试图把 holdout id 写进技能的补丁被契约直接拦住;全程花费远低于 0.2 元预算。演示里的 agent 规则很简陋,但闸门逻辑和生产里要的是同一套。
自进化并不神秘,难的是防止它用作弊换分数。把评测隔离、契约校验和成本封顶做成闸门之后,Agent 才能在预算内变强,而不是在评测集上背答案。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。