新智能体的开发循环
在 Agent OS 平台上创建新 AI 智能体的分步流程—— 从用例到部署和持续监控。智能体的全部逻辑 通过配置(YAML)描述,无需修改 Rust 代码。开发 遵循 test-first:先确定智能体应解决哪些问题, 再针对这些用例构建数据和工具。
↻ 监控 → 新用例 → 第 1 步:循环重复
第 1 步. 用例与基准测试场景(test-first)
在编写任何代码之前,先确定智能体应具备哪些能力:用户的 真实问题及预期回答。智能体类型各异,用例取决于 智能体类型(关于类型的更多内容——见「解决方案」栏目,Вектор Решений 网站):
| 智能体类型 | 用例示例 | 回答中检查什么 |
|---|---|---|
| 知识库支持(FAQ) | «如何报考 НГУ?»、«有哪些奖学金?» | 基于知识库的回答、来源链接、无杜撰内容 |
| 顾问 / 销售(e-commerce) | «推荐一台 8 万以内的学习用笔记本»、«比较这两款型号» | 找到的商品及链接、考虑了筛选条件、给出推荐 |
| 员工助理 | «办理 8 月 1 日起的休假»、«重置邮箱密码» | 正确调用操作、澄清缺失数据、确认 |
| 数据分析师 | «7 月各地区的订单量是多少?» | 正确的 SQL → 回答 + 图表/表格 |
| 观察者 / 监控 | «如果冷藏车温度高于正常值就提醒我» | 触发触发器、预警通知 |
再加上复杂场景(筛选条件组合、边界情况)、 多步链路(选择 → 比较 → 下单)以及多轮对话 (检查上下文保持:«详细讲讲第一个»)。
用例写在 config/agents/<агент>/bench/scenarios.yaml 中——
同一个文件之后会用于运行基准测试(第 8 步):
name: "NGU Agent"
model: "ngu-agent"
scenarios:
- name: "Поступление"
turns:
- user: "Как поступить в нгу"
assert:
contains: [поступлен]
min_links: 1
valid_urls: true
检查类型:contains(子串)、min_links/min_urls(最少
markdown 链接数)、valid_urls(所有链接均为 http/https)。建议:对于
罕见结果使用 min_links: 1;valid_urls: true 可捕获
失效链接;面向俄语智能体使用俄语查询。
第 2 步. 数据与来源:API、适配器、抓取
首先要弄清楚智能体将从哪里获取数据,以及有哪些 API 可用。为每个来源挑选现成的引擎(第 3 步),如果没有 现成的——就编写适配器。
2a. 梳理来源与 API
| 来源 | 是什么 | 引擎 |
|---|---|---|
| 网站公开页面 | 内含结构化 JSON 的 HTML <script> |
site_tool(live fetch + extract) |
| 网站内部 API | JSON 端点(搜索、目录、筛选) | 针对 API 的适配器或带 method: POST 的 site_tool |
| RSS 订阅源 | 新闻和更新 | rss_tool |
| 文档(PDF) | 说明、目录、规程 | pdf_tool |
| 现成的数据转储/导出 | 含数据的 JSON 文件(品牌、商品) | json_tool(预加载的 JSON) |
| 用于搜索的大型页面语料库 | 整个网站用于 RAG 搜索 | archive_tool(第 2c 步) |
关于每个来源需要弄清:响应格式、请求参数、
是否需要请求头/密钥(通过 env_var() 从环境注入)、
限制和分页、数据更新频率。
适配器——要么是现有引擎的 YAML 配置(对于 site_tool
只需描述 fetch + extract + output),要么,如果需要
非标准行为(复杂逻辑、非 HTTP 来源),则是新的
Rust 引擎:实现 ToolHandler 并在
crates/tools/src/registry.rs 中注册工厂。对于现成的数据转储/导出,有
通用引擎 json_tool(对 JSON 文件的声明式搜索:
search + 基于 MiniJinja 的 computed 字段)。示例——
config/agents/drom/tools/drom_find_brands.yaml:按
预加载的 data/drom_brands.json 搜索品牌。
2b. 网站抓取
cargo run --bin agent-os -- scrape https://nsu.ru -w 8 -d 3 -n 500
参数:-w 个工作线程、-d 遍历深度、-n 最大页面数、
-r 请求间隔、-o 归档路径、-m 导航地图路径。
分析收集到的归档——找出重复出现的块(页眉、页脚、 菜单),需要从文本中清除:
cargo run --bin agent-os -- scrape --analyze site.archive
2c. RAG 归档
对于全文搜索,页面被存入二进制归档
ArchiveBuilder(带重叠的分块、1–3 阶 n-gram 作为俄语的隐式
词干提取、BM25)。归档结构:
data/<источник>/
├── meta.json — 来源名称、文档数量、缩写
├── docs.bin — 文档分块
└── terms.bin — 术语词典和倒排索引
缩写会从形如 «Автоматизации Физико-Технических Исследований (АФТИ)» 的模式中自动提取——搜索 «афти» 能找到完整名称。
现成示例:data/ngu/(НГУ,启动时从 crawled pages 组装)、
data/hg/(HobbyGames,约 4300 件商品)。
第 3 步. 工具
工具 = config/agents/<агент>/tools/*.yaml 中的 YAML 文件。可用的
引擎(kind:):
| kind | 作用 |
|---|---|
site_tool |
HTTP 请求 → 字段提取(JSON 指针、数组匹配、转换)→ 格式化 |
archive_tool |
归档搜索:search、search_grouped、lookup、read_chunks、outline + 按元字段过滤 |
pdf_tool |
下载 PDF 并按页读取 |
rss_tool |
搜索 RSS 订阅源 |
json_tool |
对预加载 JSON 的声明式搜索(search.paths、computed) |
外加内核内置工具:http_fetch、fs_read、job_done。
最小 site_tool 示例:
kind: site_tool
name: my_scraper
description: Scrapes example.com product pages.
parameters:
type: object
properties:
url: { type: string, description: "Product page URL" }
required: [url]
fetch:
url: "{{ input.url }}" # MiniJinja-шаблон
timeout_secs: 10
source:
type: script_json # JSON внутри <script> на странице
contains: productData
extract:
title:
pointer: /productData/title
price:
pointer: /productData/price
transform: int
site_tool 的关键能力:
- 字段:简单字段(
pointer、fallback)、数组(array_match、collect_all)、计算字段(compute带条件when/then); - Transforms:
lookup、truncate、take、skip、len、not_null、bool、int—— 以链式方式应用; - 输出:可选的
output模板(MiniJinja);没有它时 返回提取字段的映射; - 警告:未解析的字段会进入
_warnings,引擎 从不 panic。
步骤 4. 工具本地调试
任何 YAML 工具都可以在没有服务器、没有 LLM 的情况下检查:
cargo build --bin agent-os --release
./target/release/agent-os tool config/agents/ngu/tools/ngu_search.yaml '{"query":"поступление"}'
输出显示:传入的参数、call_template(用户在调用前
看到的内容)、LLM 的响应以及 result_template。支持
所有引擎:site_tool、archive_tool、pdf_tool、rss_tool、json_tool。
步骤 5. 工具测试
测试是 config/agents/<агент>/tests/*.yaml 中的 YAML 文件,会被
自动发现。有两种类型:
Fixture 测试(kind: site_tool_tests)—— 无网络,数据直接
提供。运行:cargo test。
kind: site_tool_tests
tool: tools/my_tool.yaml
tests:
- name: "extracts correctly"
input: { url: "https://example.com/p/1" }
fixture: { productData: { title: "Widget", price: 99 } }
assert:
- { path: /title, equals: "Widget" }
- { path: /price, equals: 99 }
Live 测试(kind: site_tool_live_tests、
archive_tool_live_tests、rss_tool_live_tests)—— 真实 HTTP 请求,
标记为 #[ignore]。运行:cargo test -- --ignored。
kind: site_tool_live_tests
tool: tools/my_tool.yaml
tests:
- name: "structure smoke test"
fetch: true
input: { url: "https://example.com/p/1" }
assert:
- { path: /title, not_null: true }
检查类型:equals、not_null、len、contains。
步骤 6. 智能体配置
config/agents/<агент>/agent.yaml —— 智能体的全部行为:
name: ngu-agent
welcome: |
👋 Привет! Я ассистент НГУ...
instructions: |
You are an assistant for NGU (nsu.ru).
Use ngu_search to find ANY information...
Answer in Russian, helpful and informative. Always cite source URLs.
tools: # встроенные инструменты ядра
- http_fetch
tool_files: # YAML-инструменты (пути относительно каталога агента)
- tools/ngu_search.yaml
hook_files: # хуки (см. шаг 7)
- hooks/faq_cache.yaml
关于 instructions 的建议:
- 描述何时调用哪个工具以及调用链;
- 要求把工具响应中的
links[]复制到 用户响应中 —— 链接是必须的; - 指定响应格式和语言;
- 对于地理替换,可使用
__GEOIP__(用户所在城市)。
步骤 7. 钩子(可选)
钩子拦截管道中的消息。现成示例 —— faq_cache
(DuckDB):记住问答对,由 LLM 裁判判断相似
问题,缓存的答案要么作为提示插入
(short_circuit: false),要么直接返回而不调用 LLM
(short_circuit: true)—— 节省 token 并加快响应。
kind: faq_cache
name: faq_cache
db_path: data/faq_cache_ngu.db
max_candidates: 5
min_confidence: 0.7
short_circuit: true
store: { enabled: true, min_question_chars: 8, min_answer_chars: 8 }
llm:
base_url: https://api.deepseek.com
api_key_env: DEEPSEEK_API_KEY
model: deepseek-chat
步骤 8. 运行基准测试并迭代
运行步骤 1 中的场景:
# Продакшн-сервер
cargo run --bin agent-os --release -- bench --url=https://176.108.251.90 --agent=ngu --max=5
# Локальный сервер (нужен DEEPSEEK_API_KEY в .env)
cargo run --bin agent-os --release -- bench --port=3001 --max=3
标志:--agent= 按智能体过滤,--name= 按场景名称
(正则),--parallel=、--max= 限制,--timeout=。
运行之后:在控制台查看 ✓/✗,分析
bench_report_*.json(字段 failures、error),比较两份报告
以查找回归。失败通过修改 instructions、
工具或数据来修复 —— 必要时向
scenarios.yaml 添加新用例 —— 然后循环重复。
步骤 9. 发布与部署
网站页面
- 内容:
config/web/pages/<агент>.md; - 注册:在
cards:中添加卡片,或在extra_pages:的config/web/index.md中添加条目; - 在真实网站上的演示 —— 在
demos:中添加条目(打开/demo/overlay?model=<агент>)。
网站在服务器启动时从 config/web/ 读取 —— 内容修改
只需重启,无需重新构建。
部署到服务器
scp -i ~/.ssh/ssh-pk-025d38 -r config/ data/ realityfaker@176.108.251.90:/home/realityfaker/agent_os/
ssh -i ~/.ssh/ssh-pk-025d38 realityfaker@176.108.251.90 "sudo kill \$(pgrep agent-os); cd /home/realityfaker/agent_os && sudo bash -c 'set -a && source .env && set +a && nohup ./target/release/agent-os serve > agent_os.log 2>&1 &'"
curl -sk https://176.108.251.90/health
如果修改了 Rust 代码(新工具引擎、钩子)—— 在重启前
重新构建 cargo build --release -p agent_web_bot。
步骤 10. 监控与迭代
部署后循环并未结束 —— 智能体在生产环境中运行,这是 下一次迭代的主要数据来源。
需要关注什么
| 来源 | 位置 | 提供什么 |
|---|---|---|
| 服务器日志 | ssh ... "tail -50 agent_os.log" |
启动错误、崩溃、警告 |
| JSONL 日志 | 服务器上的 logs/*.jsonl |
内核事件:spawn、消息、工具调用、错误 |
| Event DB(DuckDB) | logs/events.db |
对事件的 SQL 查询:错误频率、热门请求 |
| FAQ 缓存 | data/faq_cache_<агент>.db |
用户的真实问题 —— 新用例的热门来源 |
| 生产环境基准测试 | agent-os bench --url=... --max=5 |
更新后的回归 |
| Health | curl -sk https://.../health |
服务是否存活 |
迭代
- 定期(或每次部署后)在生产环境运行基准测试并 比较报告 —— 捕捉回归;
- 从 FAQ 缓存和日志中收集真实问题 —— 智能体
回答得不好或未回答的问题,会成为
bench/scenarios.yaml中的新用例(步骤 1); - 修改
instructions、工具或数据 —— 然后重新走一遍 循环:测试 → 基准测试 → 部署; - 随着源网站的变化,更新知识库/归档。
这样循环就闭合了:每一轮都让智能体更准确,用例也 更贴近用户的真实请求。
检查清单
- 用例:
bench/scenarios.yaml描述了主要功能和边界情况 - 数据:来源和 API 已梳理,适配器已就绪(或已选定引擎),归档已收集,垃圾块已清理
- 工具:YAML 配置,字段可提取(
site_tool) - 测试:fixture 测试通过,live 测试已通过
-
agent.yaml:welcome、instructions、tool_files - 钩子:必要时已接入
- 基准测试:运行通过,报告无回归
- 网站:页面在
pages/中,条目在index.md中 - 部署:配置已上传,服务器已重启,
/health有响应 - 监控:日志干净,生产环境基准测试通过,真实问题 → 新用例