
Agno 指令生成实战Self-Instruct、Evol-Instruct 与 Topic-Tree 三条合成数据流水线【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno本文基于 agno 仓库cookbook/data_labeling/_20_instruction_generation/目录下的三个脚本及其测试日志TEST_LOG.md系统讲解如何用少量手写种子指令生成大规模合成训练数据Self-Instruct 扩池去重、Evol-Instruct 复杂度进化、Topic-Tree 主题树产出 SFT 就绪对话数据。读完本文你将掌握三条流水线的设计动机、配置参数、运行方式与输出格式并理解它们如何为下游数据集策展_22_dataset_curation与拒绝采样_21_rejection_sampling提供带溯源provenance的输入。一、背景定义性合成数据工作负载在模型训练数据生产中指令生成Instruction Generation是典型的定义性合成数据工作负载不依赖大规模外部语料而是从一小部分手工编写的输入出发让 LLM 生成更多样、更复杂、更有结构的训练指令。agno 的cookbook/data_labeling/_20_instruction_generation/目录给出了三套经典配方Self-Instructbasic.py——从种子指令池横向扩增追求广度Evol-Instructevol_instruct.py——用类型化进化算子提升难度追求深度Topic-Treetopic_tree.py——主题树逐级展开为 SFT 就绪的对话行追求结构化的领域覆盖。三者的共同点是每一行输出都携带溯源信息种子 ID、父指令、或树分支下游策展可以据此追踪和剪枝。测试日志TEST_LOG.md记录了 2026-07-18 针对gemini-3.5-flash模型、agno 2.7.4 版本的真实运行结果本文将结合该日志与源码逐条剖析。二、Self-Instruct从 8 条种子指令扩出指令池2.1 核心思路basic.py实现了 Self-Instruct 的核心循环一个生成器 Agent运行 2 轮每轮从 8 条手写种子中取确定的 3 条切片作为少样本示例few-shot examples并要求模型产出 5 条全新指令。关键设计点是第 1 轮使用种子 1–3第 2 轮使用种子 4–6源码中的切片逻辑SEEDS[round_idx * SEEDS_PER_ROUND : (round_idx 1) * SEEDS_PER_ROUND]种子 7–8 不参与少样本演示只参与去重比对避免模型直接复写未展示的种子每轮请求 5 条候选候选在写入data/generated/instructions.jsonl之前需通过词集合 Jaccard ≥ 0.7的去重过滤与种子和已接受指令逐一比对。2.2 关键配置参数源码basic.py顶部定义了全部可调参数参数默认值含义SEEDS_PER_ROUND3每轮作为少样本示例的种子数ROUNDS2生成轮数CANDIDATES_PER_ROUND5每轮要求生成的候选指令数JACCARD_THRESHOLD0.7词集合 Jaccard 去重阈值≥ 该值视为近重复理论上输出上限为2 轮 × 5 条 10 行实际行数取决于过滤后保留的数量。2.3 结构化输出与提示设计生成器 Agent 使用 agno 的结构化输出能力通过 Pydantic 模型约束返回格式class NewInstructions(BaseModel): instructions: list[str] Field( ..., descriptionNovel, self-contained task instructions, each on a different task type and domain, )Agent 的output_schemaNewInstructions让每次run()返回的对象可直接以属性访问run.content.instructions这正是RunOutput的content字段承载 Pydantic 结构化结果的方式见 run/agent.py 中RunOutput数据类定义。系统提示要求每条指令自包含、不依赖外部文件或链接、可变起始动词以最大化指令多样性。2.4 纯标准库去重过滤器去重不依赖任何第三方库仅用 Python 标准库实现def word_set(text: str) - set: cleaned .join(c if c.isalnum() or c.isspace() else for c in text.lower()) return set(cleaned.split()) def jaccard(a: set, b: set) - float: if not a or not b: return 0.0 return len(a b) / len(a | b) def is_near_duplicate(candidate: str, existing: list) - bool: candidate_words word_set(candidate) return any( jaccard(candidate_words, word_set(text)) JACCARD_THRESHOLD for text in existing )处理流程为小写化 → 非字母数字替换为空格 → 分词取集合 → 计算 Jaccard 相似度。这种实现简单透明适合作为小规模基线大规模场景可替换为_22_dataset_curation中的纯标准库 MinHash 近似去重。2.5 输出行格式与测试结果每行 JSON 记录包含{instruction: ..., seed_ids: [seed-01, seed-02, seed-03], round: 1}instruction生成的指令文本seed_ids本轮少样本示例的种子 ID用于溯源round生成轮次1 或 2。测试日志显示本次运行摘要行为wrote 10 rows ... kept 10, dropped 0——10 条候选2 轮 × 5 条全部通过 Jaccard 过滤。日志给出的示例主题包括虚构植物学、逻辑谜题、生鸡肉食品安全、合同责任、引力透镜等说明模型可靠地生成了与种子在词集合层面差异显著的新指令。需要说明的是kept/dropped 计数随运行而变化在 0.7 阈值下 dropped 0 是常见情况而非固定结果。三、Evol-Instruct用类型化算子把简单指令进化得更难3.1 核心思路evol_instruct.py不再追求广度而是在链式深度上增加指令复杂度5 条种子 × 2 步链式进化seed → depth 1 → depth 2每步由进化算子改写当前指令改写的产物再作为下一步的输入。演化算子通过确定性轮询round-robin分配保证 10 次调用中五个算子全部出现operator OPERATORS[call_idx % len(OPERATORS)] call_idx 13.2 五个类型化进化算子源码中定义了 5 个算子及其提示evol_instruct.py算子作用add_constraints增加 1–2 条具体约束长度限制、格式要求、禁止做法、特定输入保留原任务可识别性deepen加深任务深度要求更多细节、更多边界情况或更彻底的同一任务处理concretize将抽象/泛化术语替换为具体、特定内容具名场景、真实数量、特定受众或数据集increase_reasoning改写为必须显式多步推理才能回答的指令并要求展示步骤in_breadth在同领域写一条全新指令但落在不同且更冷门的主题上难度相当不复用原任务这类算子 提示模板的设计与 Evol-Instruct 论文中加深/加宽的进化思路一致在 agno 中落成可配置的类型化实现。3.3 标准库消除器eliminator每条进化产物写入前要经过纯标准库的消除器evol_instruct.pydef eliminate(evolved: str, parent: str) - str: if len(evolved.split()) MIN_WORDS: # MIN_WORDS 4 return degenerate if jaccard(word_set(evolved), word_set(parent)) NOOP_JACCARD: # NOOP_JACCARD 0.85 return no-op return no-op与父指令的词集合 Jaccard 0.85视为没真正变换degenerate少于 4 个词视为退化输出。被消除的行计入dropped不写入结果。这个消除器平时很少触发它的价值在于兜底拦截偶发的空转或退化返回。3.4 输出行格式与测试结果每行 JSON 记录{instruction: ..., parent: Explain how a hash table works., operator: concretize, depth: 1}parent父指令上一深度operator本次应用的算子depth进化深度1 或 2。测试日志显示摘要行为wrote 10 rows ... from 10 evolution calls, kept 10, dropped 0。日志给出了一个完整的 2 层进化案例种子指令Write a short story about a lighthouse keeper.在深度 1 被加上字数、场景与禁用词约束在深度 2 又被追加第二人称视角、感官描写与结构收尾要求——两轮都是真实变换消除器未触发。四、Topic-Tree主题树生成 SFT 就绪对话数据4.1 核心思路topic_tree.py用三个模块级 Agent 串联流水线把根主题逐步展开为问答对subtopic expander根主题database indexing→ 3 个子主题question writer每个子主题 → 2 个问题answerer为每个问题生成 1–2 段的实质性回答。输出直接是微调就绪的对话格式SFT-ready chat rows{messages: [{role: user, content: ...}, {role: assistant, content: ...}], provenance: {topic: database indexing, subtopic: ..., depth: 3}}其中provenance.depth: 3标记该行来自根主题 → 子主题 → 问答的第三层树分支下游过滤器可以一次性剪掉整棵子主题。4.2 三个 Agent 的分工与提示expandertopic_tree.pyoutput_schemaSubtopics要求子主题互不重叠、各自有足够内容支撑多个问题question_writertopic_tree.pyoutput_schemaQuestions要求问题具体、自包含、可独立回答且彼此不重复answerertopic_tree.py无结构化 schema直接用run.content取文本要求1–2 个短段落、无开场白无结束语。三个 Agent 通过RunOutput的content字段串联expander 的输出切片后喂给 question_writerquestion_writer 的输出逐个喂给 answerer。4.3 计数上限由切片决定与前面两个脚本的过滤机制不同topic-tree 用切片slicing封顶计数subtopics expand_run.content.subtopics[:NUM_SUBTOPICS] questions question_run.content.questions[:QUESTIONS_PER_SUBTOPIC]即使模型多返回了子主题或问题也只保留前 3 个/前 2 个。因此3 子主题 × 2 问题 6 行是上限模型若少返回行数相应减少。测试日志显示本次运行产出wrote 6 rows ... (3 subtopics x up to 2 questions each)子主题包括 Index Data Structures and Algorithms 等问题涉及 B Tree 与 LSM 的写放大对比、PostgreSQL Bitmap Index Scan 的选择条件等每个问题都配有 1–2 段实质性回答。日志同时提示子主题与问题的措辞随运行变化属于预期内的不确定性。五、运行方式与依赖在 agno 仓库根目录执行python cookbook/data_labeling/_20_instruction_generation/basic.py python cookbook/data_labeling/_20_instruction_generation/evol_instruct.py python cookbook/data_labeling/_20_instruction_generation/topic_tree.py三个脚本均需要环境变量GOOGLE_API_KEY默认使用google:gemini-3.5-flash模型。若要搭建完整演示环境可参考 data_labeling/README.md先运行./scripts/demo_setup.sh创建演示虚拟环境再source .venvs/demo/bin/activate后执行脚本。测试日志注明本次验证环境为agno 2.7.4 gemini-3.5-flash2026-07-18。输出写入cookbook/data_labeling/_20_instruction_generation/data/generated/该目录被 gitignore运行脚本即可重新生成脚本输出文件行结构basic.pyinstructions.jsonlinstructionseed_idsroundevol_instruct.pyevolved_instructions.jsonlinstructionparentoperatordepthtopic_tree.pytopic_tree.jsonlmessages[]provenance{topic, subtopic, depth}每个脚本运行结束后都会用rich.pretty.pprint预览前几行并打印类似wrote N rows ... kept N, dropped N的摘要行。六、三条流水线的选型建议与下游衔接6.1 何时用哪条Self-Instruct种子池很小、需要快速扩出覆盖面时选它产出以广度见长Evol-Instruct已有偏简单的指令、需要更难的变体时选它产出以深度见长Topic-Tree想系统覆盖某个领域、并希望数据结构化可溯源时选它直接产出 SFT 对话行。6.2 生成只是前半程README.md明确指出生成只是流水线的一半。把输出交给_22_dataset_curation/做规模化过滤与去重judge 质量门控、MinHash 近重复去除、13-gram 基准污染清洗若能对回答做验证测试、检查器、judge则用_21_rejection_sampling/只保留通过验证的生成结果。三条流水线统一输出带provenance的 JSONL这一设计保证了数据从生成、策展到训练的全程可追踪、可剪枝——这也是本目录在整个 data_labeling 合成数据工作流_20–_25见 data_labeling/README.md中的定位它是源头而非终点。七、小结通过测试日志与源码对照可以总结三条流水线的关键经验溯源先行从生成第一行起就记录seed_ids/parent/operator/depth/provenance为下游策展和剪枝留好抓手过滤与消除尽量纯标准库Jaccard 词集合去重、no-op/degenerate 消除器均不引入第三方依赖逻辑透明、易调阈值结构化输出是骨架output_schema Pydantic 模型让每轮 Agent 输出立即可编程访问三条流水线因此可以串成清晰的多 Agent 管线计数上限 vs 过滤计数topic-tree 用切片封顶self-instruct / evol-instruct 用过滤器决定 kept/dropped两类机制适用不同场景需要按数据需求选择。从cookbook/data_labeling/目录的整体布局看README_20_instruction_generation属于合成数据生成族其输出格式与_21拒绝采样、_22数据集策展一脉相承。若要从零搭建指令生成 → 策展 → 微调的数据管线本目录的三条流水线就是最直接的起点。【免费下载链接】agnoBuild, run, and manage agent platforms.项目地址: https://gitcode.com/GitHub_Trending/ag/agno创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考