课程LubanAgent 实战课:8 课从零到部署你的业务 Agent / 第一章 · 起步:认识并跑起你的第一个 Agent / 第 2 课 第一个自己的 Agent:写定义、配人设
— 9 min read

第 2 课 第一个自己的 Agent:写定义、配人设

复制内置客服写出图书馆助手「小图」的定义,亲手验证 YAML 投喂口与数据库存储的分工、init 幂等的版本化行为,学会用 export 导出定义,建立「Agent 是配置不是代码」的心智。

第 2 课:第一个自己的 Agent——从 YAML 到对话

学完本课你能:独立写出一份 Agent 定义并让它出现在工作台;解释「定义落库 + 版本化」比「Prompt 写在代码里」好在哪;亲手验证改 YAML → 重跑 init → 版本号 +1 的完整链路 | 预计耗时 30 分钟 | 前置:第 1 课(环境就绪、服务能起) | 难度 ★☆☆

这一课你要造自己的第一个 Agent:图书馆借阅助手「小图」。它会陪我们走完剩下的所有课——第 4 课给它写查借阅和挂失工具,第 5 课给它喂馆藏规章,第 6 课给它配评测,第 7 课给它上流程和团队,第 8 课把它部署上线。

先说实话:这一课结束时小图还不会真的用你的人设说话——它的大脑还是第 1 课那个只会一句台词的 FakeLLM。这一课造的是身体:定义、版本、导入链路。第 3 课换上真大脑的那一刻,你会回来问它"你是谁",那时候人设才真正活过来。身体和大脑分开造,恰好让你看清这个框架最重要的一条设计:Agent 是配置,不是代码

当然,图书馆只是我们的主线。你完全可以换成奶茶店点单助手、社团招新答疑——所有步骤同构,教程末尾 reference/ 目录只维护图书馆版,保证教程可验证。

1. 学习目标

  • 写出一份最小可用的 Agent YAML 并导入生效
  • 解释 YAML(投喂口)和数据库(存储)的分工,以及为什么改 YAML 必须重跑 luban init
  • 验证版本化行为:内容变化 → 版本 +1;内容不变 → 不追加
  • luban export 导出定义并理解往返无损

2. 概念讲解:Agent 是配置,不是代码

回想一下传统做法:一个 AI 助手的人设(系统 Prompt)、它能用什么工具、守什么规矩,都写死在代码里。改一句人设 = 改代码 = 走一遍提交、评审、构建、发版。人设这种一天要调三次的东西,凭什么要走发版流程?

LubanAgent 的回答:Agent 的一切要素是一份定义(definition),存在数据库里,带版本号。改人设 = 改一条库记录 = 立即生效,不发版。每次修改产生一个不可变的历史版本——出了问题可以回滚,评测时可以钉住某个版本对比(第 6 课)。

那 YAML 是干什么的?投喂口。仓库自带的 app/agents/*.yaml 是定义的"源文件草稿",luban init 把它们灌进数据库。之后系统运行读的是库,不是 YAML——所以改了 YAML 必须重跑 luban init 才生效。这个分工你上一课已经听过一遍,这一课亲手验证它。

一份最小定义只有两个必填项:name(即 Agent 的 key)和 instruction(系统 Prompt)。其余字段都是渐进增强——本课只用 greeting(进会话的问候语)、brain(逻辑槽位,下一课细讲)、memory(记忆策略)、limits(治理限额:步数/Token 预算/超时)。工具、知识、技能这些以后再挂。完整字段表别背,用户手册 §1 是字典,用到再查。

3. 动手:主线步骤

步骤 1:从内置客服复制一份起点

cp app/agents/customer_service.yaml app/agents/my_lib_assistant.yaml

然后打开 app/agents/my_lib_assistant.yaml整个文件替换为下面这份(不是改几行——把客服的工具、知识、技能段全删掉,那些第 4、5 课再加;留下的字段都是你现在认识的最小集):

name: my-lib-assistant
description: 图书馆借阅助手:查借阅记录、校园卡挂失、馆藏规章咨询
greeting: 你好,我是图书馆助手「小图」~借阅查询、挂失补办、规章咨询都可以找我。
instruction: |
  你是「小图」,学校图书馆的借阅服务助手。
  服务范围:借阅记录查询、校园卡挂失、馆藏规章咨询。
  边界:不处理图书采购建议、不提供文献代查,超出范围如实说明并建议到馆咨询前台。
brain: smart
memory: session
limits: { max_steps: 12, budget_tokens: 50000, timeout_sec: 120 }
io_contract: { input: text, output: text }

✅ 检查点:文件保存后,grep name app/agents/my_lib_assistant.yaml 输出 name: my-lib-assistant——注意 name 就是 Agent 的 key,横线命名。

步骤 2:导入落库

.venv/bin/luban init

✅ 你应该看到:输出列表多了一行 my-lib-assistant@1(首次导入,版本 1),其余 9 个内置 Agent 都是 (unchanged)

步骤 3:工作台确认

刷新工作台 Agents 页

✅ 你应该看到:my-lib-assistant 出现在列表里,版本 1。点进去能看到你写的人设全文——注意你现在看到的内容来自数据库,不是 YAML 文件。做个实验:把 YAML 文件里的 description 随便改一个字,刷新工作台——没有任何变化。数据库里存的还是老版本。这就是"投喂口 vs 存储"。

步骤 4:验证版本化——改一次,重导一次

把 YAML 里 instruction 的第一句改成:

你是「小图」,学校图书馆的借阅服务助手,语气亲切像个耐心的学姐。

然后:

.venv/bin/luban init
.venv/bin/luban init   # 紧接着再跑一遍

✅ 你应该看到:第一次输出 my-lib-assistant@2(内容变了 → 追加新版本);第二次输出 my-lib-assistant@2 (unchanged)(内容没变 → 不追加)。工作台刷新后小图的版本变成 2,点版本历史能看到 v1 和 v2 两个快照。

这个行为比你想象的更重要:部署脚本反复跑 init 是安全的——不会无限膨胀版本号,也不会把你在线上调过的东西改回 YAML 的状态(那个更深的坑第 3 课会专门验证)。

步骤 5:导出——定义随时能带走

.venv/bin/luban export my-lib-assistant

✅ 你应该看到:stdout 打出一份 YAML,字段与你最后保存的版本一致(export 导出的是数据库里的当前版,不是你手上的 YAML 文件——如果两者不一致,以这个输出为准,说明你忘了 init)。加 --output x.yaml --version 1 可以导出历史版本快照。

定义存库、随时导出成文件、文件又能 init 回库——这条往返链路就是你将来迁移环境(开发库 → 生产库)的通道。

步骤 6:跟小图说句话(诚实的部分)

调试台选 my-lib-assistant,说"你是谁"。

✅ 你应该看到:回复还是那句 你好,我是 LubanAgent。——FakeLLM 不读人设,它的剧本只有一句台词。小图的身体(定义、版本、导入链路)已经就位,大脑还没换。这不是失败,是这一课和下一课的分界线。

4. 完成自检清单

  • luban init 后输出 my-lib-assistant@1,工作台可见
  • 改 YAML 不 init → 工作台无变化(投喂口实验)
  • 改 YAML + init → 版本 +1;再跑 init → (unchanged)
  • luban export my-lib-assistant 输出与库中当前版一致
  • 理解了为什么调试台里小图还是固定台词(身体 vs 大脑)

5. 常见坑

改了 YAML,工作台没变化。 最高频问题。YAML 是投喂口不是存储——重跑 luban init。判断你看到的是不是库里的内容:luban export <key> 导出的永远是库。

init 报 未知键 一类校验错误。 YAML 里出现了不认识的字段(多半是从客服 YAML 复制时带了没删干净的段,或者手滑打错字段名)。错误信息会指出是哪个键,删掉或改对即可——字段白名单在 lubanagent/cli/yaml_io.py,字典在用户手册 §1

name 里有下划线或大写。 key 的惯用命名是横线小写(my-lib-assistant)。下划线能过校验,但和仓库内置 Agent 的风格不一致,工作台里看着也别扭。

版本号比我改的次数多。 检查是不是 YAML 里有多余的空格/换行变化——init 的对比是内容深度相等,格式变化也算内容变化。用编辑器的"去掉行尾空格"功能对齐一下。

6. 练习

练习 A(模仿):给小图换一个你自己写的 greeting,走完 init → 工作台确认 → export 验证的完整链路。

练习 B(变式,重要):把 instruction 整段删到只剩一句"你是图书馆助手",init 后导出看一眼。然后想:第 3 课接上真模型后,这个人设会产出什么样的回复?先写下你的预判,第 3 课结束回来对照——这个练习是让你提前体感"人设的详细程度直接决定回复质量",比任何解释都管用。

练习 C(综合):用你自己的场景(奶茶店/社团/实验室)从零写一份新定义(不复制客服,从空文件开始),只准看字段名不许抄内容。写完 init,确认导入成功。从下一课起你可以两条线并行:跟着教程走图书馆主线,同时用自己的 Agent 做每课练习的变体。

7. 小结与下一课预告

小图有了身体:一份入库的、版本化的定义,以及一条你已经亲手验证过的"改 YAML → init → 版本 +1"链路。你也有了"投喂口 vs 存储"的实感——这个心智模型后面每一课都会用到。

下一课换大脑:注册 DeepSeek(或任一 OpenAI 兼容模型),绑到 smart 槽位。然后你会回来问小图"你是谁"——它终于开口说出"我是小图"的那一刻,前面两课的铺垫全部兑现。顺便你会看清"换模型不改代码"不是口号:改一条库里的映射,同一个 Agent 立刻换脑。


延伸阅读:用户手册 §1(Agent 定义与版本化——全部字段字典)、§9(工程基座) | 参考产出:reference/02-lib-assistant.yaml | 涉及源码:lubanagent/cli/yaml_io.py(YAML 解析与字段白名单)

目录