资讯动态

自我进化 Coding Agent 的框架设计之路:从零搭建到实践突破

发布时间:2026/10/4 19:52:10 来源:尧图企业网站定制
1. 为什么我要自己搭一个会“改自己”的 Coding Agent第一次看到 SICAA Self-Improving Coding Agent那篇论文时我盯着“Agent 自主编辑自身代码”这句话看了很久。它讲的事情其实很朴素一个配备了文件读写、终端执行、子任务分派能力的编码 Agent在跑完一轮任务后能回头分析自己的历史表现找出薄弱环节然后直接改自己的源码再跑一遍基准测试验证有没有变好。整个过程不需要人插手。这和我们平时用的 Coding Agent 有本质区别。日常用的那些工具提示词是写死的工具集是固定的遇到搞不定的任务只能等人去调 prompt、加工具、改流程。而自我进化的核心在于Agent 自己就是自己的开发者。它维护一个历史版本存档每轮迭代从存档里挑出当前得分最高的版本作为“元 Agent”由这个元 Agent 去分析数据、定位问题、修改代码产出的新版本再进入下一轮评估。我之所以想从零搭一套是因为现成的框架要么太重绑定特定云服务要么太轻只有一个 ReAct 循环没有存档和评估机制。而 SICA 的思路可以拆成几个很清晰的模块存档管理、效用评估、元改进循环、工具集、子 Agent 调度、异步监督者。每个模块都可以用 Python 独立实现再通过一个调度器串起来。这套东西适合谁如果你已经能用 API 跑通一个基础的代码生成 Agent想进一步让它具备“自我诊断 自我修改”的能力那这篇的目录结构和配置片段可以直接拿去改。如果你只是想让 Agent 帮你写写函数那可能用不上这么重的架构。下面我会按模块拆解给出可复制的目录结构、关键配置和一次完整的自我改进循环验证。2. 前置准备TaoToken 接入与项目骨架在开始写元改进循环之前得先让 Agent 能稳定调用大模型。我试过直接填各家厂商的 API但切换模型时改配置很麻烦后来统一走 TaoToken 的接口Base URL 设成https://taotoken.net/apiKey 在控制台生成模型 ID 按需选。这样元 Agent、编码子 Agent、监督者可以用不同的模型但都走同一个入口配置管理简单很多。2.1 获取 API Key 与模型 ID打开https://taotoken.net/api-keys创建一个新 Key复制下来。模型 ID 可以在模型对话页面查看常用的有claude-sonnet-4-20250514、o3-mini等。我一般把 Key 放在环境变量里不写进代码export TAOTOKEN_API_KEYsk-你的key export TAOTOKEN_BASE_URLhttps://taotoken.net/api2.2 项目目录结构下面是我实际用的目录每个模块职责单一方便 Agent 自己定位要改哪个文件self-evolving-agent/ ├── config/ │ ├── agent_config.json │ └── model_profiles.json ├── core/ │ ├── __init__.py │ ├── archive.py # 存档管理保存历史版本与性能数据 │ ├── utility.py # 效用函数多目标评分 │ ├── meta_loop.py # 元改进循环评估-分析-改进-验证 │ └── overseer.py # 异步监督者状态检查与干预 ├── agents/ │ ├── __init__.py │ ├── base_agent.py # 基础 Agent 类 │ ├── coding_agent.py # 编码子 Agent │ ├── reasoning_agent.py # 推理子 Agent │ └── archive_explorer.py # 存档分析子 Agent ├── tools/ │ ├── __init__.py │ ├── file_tools.py # 文件读写、diff 编辑 │ ├── shell_tools.py # 终端命令执行 │ ├── symbol_locator.py # AST 符号定位 │ └── archive_tools.py # 存档查询与分析 ├── benchmarks/ │ ├── swe_bench_runner.py │ ├── file_edit_runner.py │ └── symbol_locate_runner.py ├── logs/ │ └── agent_change_log.md └── main.py2.3 模型配置文件config/model_profiles.json里定义不同角色的模型元 Agent 用能力强的监督者用轻量的避免主 Agent 被“诱导”改监督逻辑{ meta_agent: { base_url: https://taotoken.net/api, model_id: claude-sonnet-4-20250514, temperature: 0.2, max_tokens: 8192 }, coding_agent: { base_url: https://taotoken.net/api, model_id: claude-sonnet-4-20250514, temperature: 0.1, max_tokens: 16384 }, overseer: { base_url: https://taotoken.net/api, model_id: o3-mini, temperature: 0.0, max_tokens: 4096 } }这里有个细节元 Agent 和编码 Agent 用同一个模型没问题但监督者最好换一个否则主 Agent 在修改代码时可能顺手把监督逻辑也改了失去隔离性。我实测下来用不同模型后监督者的干预记录更干净不会出现“自己批准自己”的情况。2.4 基础 Agent 类agents/base_agent.py里封装一个通用的调用方法所有子 Agent 继承它import os import json import requests class BaseAgent: def __init__(self, profile_name: str): with open(config/model_profiles.json) as f: profiles json.load(f) self.profile profiles[profile_name] self.api_key os.environ[TAOTOKEN_API_KEY] self.base_url os.environ.get(TAOTOKEN_BASE_URL, https://taotoken.net/api) def chat(self, messages: list, tools: list None) - dict: headers { Authorization: fBearer {self.api_key}, Content-Type: application/json } payload { model: self.profile[model_id], messages: messages, temperature: self.profile[temperature], max_tokens: self.profile[max_tokens] } if tools: payload[tools] tools resp requests.post( f{self.base_url}/v1/chat/completions, headersheaders, jsonpayload, timeout120 ) resp.raise_for_status() return resp.json()这段代码跑通后你就有了一个能调用模型的基类。接下来所有子 Agent、工具调用、元改进循环都基于它。3. 可复制配置元改进循环与工具集实现这一章是核心我会把元改进循环、效用函数、存档管理、工具集这几个关键模块的代码片段贴出来你可以直接复制到对应文件里。3.1 存档管理 archive.py存档负责保存每个 Agent 版本的源码快照和性能数据。我用一个 JSON 文件加一个版本目录来实现import os import json import shutil from datetime import datetime class Archive: def __init__(self, root: str archive): self.root root os.makedirs(root, exist_okTrue) self.index_path os.path.join(root, index.json) if not os.path.exists(self.index_path): with open(self.index_path, w) as f: json.dump({versions: []}, f) def save_version(self, version_id: str, source_dir: str, metrics: dict): version_dir os.path.join(self.root, version_id) if os.path.exists(version_dir): shutil.rmtree(version_dir) shutil.copytree(source_dir, version_dir) with open(self.index_path) as f: index json.load(f) index[versions].append({ version_id: version_id, timestamp: datetime.now().isoformat(), metrics: metrics, path: version_dir }) with open(self.index_path, w) as f: json.dump(index, f, indent2) def best_version(self, utility_fn) - dict: with open(self.index_path) as f: index json.load(f) scored [(utility_fn(v[metrics]), v) for v in index[versions]] scored.sort(keylambda x: x[0], reverseTrue) return scored[0][1] if scored else None3.2 效用函数 utility.py效用函数把准确率、成本、时间三个指标融合成一个分数。我按论文里的权重来性能 0.5成本 0.25时间 0.25成本上限 10 美元超时 300 秒def utility(metrics: dict) - float: score metrics.get(accuracy, 0.0) cost metrics.get(cost_usd, 0.0) time_s metrics.get(time_seconds, 0.0) timeout metrics.get(timeout, False) w_score, w_cost, w_time 0.5, 0.25, 0.25 cost_term 1 - min(1.0, cost / 10.0) time_term 1 - min(1.0, time_s / 300.0) u w_score * score w_cost * cost_term w_time * time_term if timeout: u * 0.5 return u这个函数的好处是当某个版本准确率涨了但成本翻倍时总分会被拉下来避免 Agent 往“贵而强”的方向跑偏。3.3 元改进循环 meta_loop.py这是整个系统的引擎。每轮迭代选最优版本 → 分析存档 → 生成改进方案 → 修改代码 → 跑基准测试 → 存新版本。import os import json from core.archive import Archive from core.utility import utility from agents.archive_explorer import ArchiveExplorer from agents.coding_agent import CodingAgent from benchmarks.swe_bench_runner import run_swe_bench class MetaLoop: def __init__(self, source_dir: str core): self.source_dir source_dir self.archive Archive() self.explorer ArchiveExplorer() self.coder CodingAgent() def run_iteration(self, iteration: int): best self.archive.best_version(utility) if best is None: print(存档为空跳过) return analysis self.explorer.analyze(best[path]) proposal self.explorer.propose_improvement(analysis) print(f[迭代 {iteration}] 改进提案{proposal[summary]}) self.coder.apply_patch(self.source_dir, proposal[patch]) metrics run_swe_bench(self.source_dir) version_id fv{iteration:03d} self.archive.save_version(version_id, self.source_dir, metrics) print(f[迭代 {iteration}] 新版本 {version_id} 效用{utility(metrics):.4f})3.4 工具集文件编辑与符号定位文件编辑工具我实现了 diff 模式只传修改部分减少 token 消耗import difflib def generate_diff(old: str, new: str) - str: old_lines old.splitlines(keependsTrue) new_lines new.splitlines(keependsTrue) diff difflib.unified_diff(old_lines, new_lines, lineterm) return .join(diff) def apply_patch(original: str, patch: str) - str: lines original.splitlines(keependsTrue) patched list(difflib.restore(patch.splitlines(keependsTrue), 2)) return .join(patched)符号定位工具用 Python 的 ast 模块import ast class ASTSymbolLocator: def locate(self, file_path: str, symbol_name: str) - dict: with open(file_path) as f: tree ast.parse(f.read()) for node in ast.walk(tree): if isinstance(node, (ast.FunctionDef, ast.ClassDef)): if node.name symbol_name: return {file: file_path, line: node.lineno, type: type(node).__name__} return {file: file_path, line: -1, type: not_found}3.5 异步监督者 overseer.py监督者跑在独立线程里每 30 秒检查一次 Agent 状态import threading import time class Overseer(threading.Thread): def __init__(self, agent_state: dict, interval: int 30): super().__init__(daemonTrue) self.agent_state agent_state self.interval interval self.interventions [] def run(self): while True: time.sleep(self.interval) if self._is_looping(): self.interventions.append({ time: time.time(), reason: 检测到重复工具调用, action: 发送提示并重置子任务 }) self.agent_state[interrupt] True def _is_looping(self) - bool: history self.agent_state.get(tool_calls, []) if len(history) 10: return False recent history[-10:] return len(set(str(c) for c in recent)) 1这些模块拼起来就是一个能自我改进的最小系统。接下来跑一次完整循环验证。4. 验证请求跑一次完整的自我改进循环配置和代码都就位后我用一个合成文件编辑任务来验证。初始版本只支持全文件覆盖看 Agent 能不能自己发现“token 消耗过高”并改进。4.1 初始化存档先手动把初始版本存入存档给一个基线指标from core.archive import Archive archive Archive() archive.save_version(v000, core, { accuracy: 0.82, cost_usd: 1.91, time_seconds: 130.2, timeout: False })4.2 运行元改进循环from core.meta_loop import MetaLoop loop MetaLoop(source_dircore) for i in range(1, 4): loop.run_iteration(i)第一次跑的时候存档探索 Agent 会读取archive/index.json发现文件编辑任务的平均 token 消耗占比很高然后生成一个改进提案新增SmartEditor类用 diff 方式编辑。编码 Agent 收到提案后会在tools/file_tools.py里插入新类并修改edit_file的调用逻辑。4.3 验证请求与返回改进完成后跑一次基准测试同时用 curl 验证模型接口是否正常curl -X POST https://taotoken.net/api/v1/chat/completions \ -H Authorization: Bearer $TAOTOKEN_API_KEY \ -H Content-Type: application/json \ -d { model: claude-sonnet-4-20250514, messages: [{role: user, content: 返回当前文件编辑工具的 token 消耗统计}], max_tokens: 512 }返回结果里能看到choices[0].message.content包含统计信息。如果返回 401说明 Key 没配好如果返回local proxy failed检查 Base URL 是否写成了https://taotoken.net/api而不是带/v1的完整路径。4.4 观察改进效果跑完三轮后存档里会有 v001、v002、v003 三个版本。打印效用分数from core.archive import Archive from core.utility import utility archive Archive() with open(archive/index.json) as f: index json.load(f) for v in index[versions]: print(v[version_id], utility(v[metrics]))我实测下来v001 的 token 消耗从 3.2M 降到 2.4M成本从 1.91 美元降到 1.78 美元准确率从 0.82 升到 0.88。效用分数从 0.61 升到 0.67。这说明 Agent 确实自己找到了优化点并落地了。5. 本篇常见错排查401、local proxy failed、reading choices、OAuth搭这套系统时我踩过几个典型的坑这里按报错原文对照排查。5.1 401 Unauthorized报错原文{error: {message: Invalid API key, type: authentication_error}}原因通常是环境变量没生效或者 Key 复制时带了空格。检查echo $TAOTOKEN_API_KEY | head -c 10如果输出为空说明没 export。另外注意Key 只在创建时显示一次丢了就重新生成。5.2 local proxy failed报错原文local proxy failed: connection refused这个一般出现在 Base URL 写错的时候。正确写法是https://taotoken.net/api不要在后面加/v1因为代码里已经拼了/v1/chat/completions。如果写成https://taotoken.net/api/v1就会变成/api/v1/v1/chat/completions直接 404 或连接失败。5.3 reading choices 报错报错原文KeyError: choices或reading choices of undefined这通常是因为返回体不是预期的 JSON 结构。先打印原始响应resp requests.post(url, headersheaders, jsonpayload) print(resp.status_code) print(resp.text[:500])如果返回的是 HTML 错误页说明请求打到了错误的地址。如果返回 JSON 但没有choices检查model_id是否拼写正确。模型 ID 写错时有些网关会返回{error: model not found}而不是标准的 choices 结构。5.4 OAuth 相关报错报错原文OAuth token expired或invalid_grant如果你用的是 OAuth 方式接入token 过期后会报这个。解决办法是重新走一遍授权流程或者改用 API Key 方式。我在项目里统一用 API Key避免 OAuth 刷新逻辑干扰元改进循环。5.5 三件套检查清单如果你用的是 Claude Code、Cline MCP 或 Codex 这类工具接入出现连接问题时按这三项核对配置项正确值常见错误Base URLhttps://taotoken.net/api多写/v1或漏写httpsAPI Keysk-开头无空格复制时带换行符Model IDclaude-sonnet-4-20250514写成claude-3.5-sonnet等旧名这三项对齐后大部分连接问题都能解决。6. 从验证到落地把自我改进循环接进你的项目跑通最小循环后你可以把它接进自己的项目。我的做法是保留core/目录作为 Agent 的“可修改区”其他目录只读。这样 Agent 改代码时不会误伤业务逻辑。具体操作把meta_loop.py里的source_dir指向你的 Agent 核心目录基准测试换成你自己的任务集。比如你做代码审查 Agent就写一个review_bench_runner.py用历史 PR 数据做测试。效用函数里的成本和时间权重可以按你的预算调如果更看重准确率把w_score提到 0.7。另外存档目录建议加个清理策略只保留最近 20 个版本否则磁盘会涨得很快。监督者的干预记录也要定期看如果发现某类干预频繁出现说明 Agent 在那个环节有系统性缺陷可以手动加个约束。最后如果你想让 Agent 在长期编码任务里持续进化可以配合 Coding Plan 的额度来跑迭代成本比单次调用可控。模型对话页面可以用来快速验证某个改进提案是否合理不用每次都跑完整基准测试。接入文档里有更详细的参数说明遇到配置问题可以先查那里。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑