ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

NORP-Agent元框架系列教程第二篇——学会注册

NORP-Agent元框架系列教程第二篇——学会注册 第 2 篇注册表操作组件存在哪里怎么放进去、怎么取出来2.1 从上一讲的问题开始第 1 篇里你写了npa(modelopenai_compat)然后 Agent 就跑起来了。但你有没有想过一个问题这个openai_compat是什么它存在哪里我换成npa(modelmy_model)为什么报错答案是openai_compat是注册表里的一个名字。npa()启动时install_defaults()已经把内置组件全部注册进去了——模型、工具、会话、沙箱、调度器全都在。注册表Registry就是 Norp 的“组件仓库”。所有可被槽位引用的东西都先放进这个仓库里然后按名字取用。2.2 注册表的结构注册表是一个容器里面按“命名空间”分门别类存放组件Registry ├── models → {openai_compat: Provider, mock: Provider, ...} ├── tools → {echo: Tool, file_read: Tool, ...} ├── sessions → {memory: factory, sqlite: factory, ...} ├── sandboxes → {subprocess: factory, pooled: factory, ...} ├── schedulers → {simple: factory, persistent: factory, ...} ├── uis → {console: UIAdapter, ...} ├── plugins → {Plugin 对象} ├── presets → {standard: Preset, minimal: Preset, ...} └── components → {context_store: {fts5: factory}, ...}每个命名空间就是一张“名字 → 实现”的映射表。你往里面注册什么槽位就能引用什么。记住这个结构因为后面所有操作都围绕它展开。2.3 三种核心操作放、取、造注册表有三大类操作每一类的语义都不一样。理解它们的区别是理解 Norp 装配机制的关键。操作含义典型 API放注册把组件放进注册表关联一个名字register_tool(name, tool)取解析从注册表拿出已注册的实例resolve_model(name)造构建每次调用工厂新建一个实例build_session(name)“放”是存“取”是拿现成的“造”是每次现做。2.4 放进去注册 API注册组件的方式取决于你要注册到哪个命名空间以及这个命名空间存的是什么类型。# 注册工具实例 reg.register_tool(weather, WeatherTool()) # 注册模型实例 reg.register_model(my_model, MyModelProvider()) # 注册会话工厂函数每次新建 reg.register_session(redis, lambda: RedisSession()) # 注册沙箱工厂函数每次新建 reg.register_sandbox(docker, lambda: DockerSandbox()) # 注册调度器工厂函数每次新建 reg.register_scheduler(priority, lambda: PriorityScheduler()) # 注册 UI 渲染器实例 reg.register_ui(my_ui, MyUIAdapter()) # 注册预设 reg.register_preset(Preset(namemine, modelmy_model, ...))关键区别模型、工具、UI 存的是“实例”同一个对象全局复用会话、沙箱、调度器存的是“工厂”每次build_*新建一个实例预设存的是 Preset 对象为什么要分“实例”和“工厂”因为模型和工具通常是无状态的可以全局共享但每个任务可能需要独立的会话和沙箱所以每次新建更安全。2.5 取出来解析 APIresolve_*从注册表里直接拿出已经注册的对象# 拿出模型实例 provider reg.resolve_model(openai_compat) # 拿出工具实例 tool reg.resolve_tool(echo) # 拿出 UI 实例 ui reg.resolve_ui(console) # 拿出预设 preset reg.resolve_preset(standard)这些操作都是 O(1) 查表没有副作用不会创建新对象。2.6 造出来构建 APIbuild_*调用注册时存的那个“工厂”每次返回一个全新的实例# 每次新建一个会话 session1 reg.build_session(memory) session2 reg.build_session(memory) # session1 is not session2 # 每次新建一个沙箱 sandbox reg.build_sandbox(pooled) # 每次新建一个调度器 scheduler reg.build_scheduler(simple)这就是“会话、沙箱、调度器存工厂”的原因——build_*每次调用工厂生成独立实例。你不用担心多个任务共享同一个会话导致历史串了。2.7 通用组件命名空间开放的仓库除了上面 8 个专用命名空间注册表还有一个开放命名空间components。这个东西的存在很重要——它意味着你不需要等 Norp 给你加新命名空间你自己就能扩展。# 注册一个通用组件种类 vector_store名字 pg reg.register_component(vector_store, pg, lambda: PgVectorStore()) # 取出来用 store reg.build_component(vector_store, pg)kind种类完全开放你可以注册任何种类、任何名字的组件。然后在工具里通过ctx.component(vector_store)取用。这个机制让框架的扩展性从“作者说了算”变成了“你说了算”。想加什么能力注册一个种类就行。2.8 查看注册表里有什么调试时最常用的操作是“看看注册表里到底有什么”# 列出所有工具名 reg.list_tools() # [echo, get_time, file_read, ...] # 列出所有模型名 reg.list_models() # [mock, openai_compat, anthropic] # 列出所有预设名 reg.list_presets() # [minimal, standard, ptc, ...] # 列出某个种类的通用组件 reg.list_components(context_store) # [fts5]这些命令是你排查“为什么我的组件找不到”的第一道防线。2.9 验证预设完整性预设里引用的组件可能还没注册。validate_preset帮你提前检查missing, missing_tools reg.validate_preset(my_preset) # missing → [modelmy_model, componentvector_store:pg] # missing_tools → [weather]返回两个列表missing预设里引用的模型、会话、沙箱、调度器、UI、通用组件哪些没注册missing_tools预设里引用的工具哪些没注册如果两个列表都是空的这个预设可以安全使用。2.10 注册表的生命周期记住一件事npa()每次启动都会新建一个注册表然后install_defaults()把内置组件填进去。你传的槽位值会覆盖或追加到这个注册表上。所以如果你在npa()之前自己建了一个reg并往里注册了东西你需要把这个reg传给npa()才能生效。最简单的做法是声明式装配填槽位参数→npa()内部自动创建注册表程序式装配调用register_*→ 自己管理注册表实例两种方式可以混用但记住槽位是插口注册表是仓库。插口决定“我要什么”仓库决定“我有什么”。插口填的名字必须在仓库里找得到。2.11 你现在能做的事情读完这篇你已经能理解注册表的 9 大命名空间用register_*把组件放进注册表用resolve_*取出已注册的实例用build_*每次新建实例理解“实例”和“工厂”在注册时的不同语义用list_*查看注册表内容用validate_preset检查预设完整性用开放命名空间注册任意种类的通用组件下一篇你会学到槽位注册——如何让 Norp 认识你定义的新插口让npa()接受你自定义的参数。
返回列表