Nanobot 从onboard启动命令来看个人助理Agent的实现

背景

在之前的文章中Nanobot 轻量级的个人AI助手,我们提到了两个命令

一个是nanobot onboard,另一个是nanobot gateway,

从整理上来看,该 nanobot用到了Typer,Rich,Questionary,prompt_toolkit这种现代、美观且交互式命令行界面 (CLI) 的强大工具组合。

Typer 用于定义 CLI 结构和参数;Rich 负责文本样式、表格、面板和 Markdown 渲染;Questionary 用于创建交互式问答界面

其中 Rich中的Console,Markdown,Table,Text用来进行渲染,支持颜色、表格、面板、语法高亮和 Markdown ,以更好的进行个性化的展示。

nanobot onboard命令

onboardTyper 子命令,用于 首次或再次初始化 nanobot:处理配置文件路径、创建/更新 config.json、可选交互向导、合并channel插件默认配置、创建工作区并同步模板,最后打印下一步提示。

复制代码
@app.command()
def onboard(
    workspace: str | None = typer.Option(None, "--workspace", "-w", help="Workspace directory"),
    config: str | None = typer.Option(None, "--config", "-c", help="Path to config file"),
    wizard: bool = typer.Option(False, "--wizard", help="Use interactive wizard"),
):
    """Initialize nanobot configuration and workspace."""
    ...
参数 说明
-c / --config 指定配置文件路径;若给出,会调用 set_config_path,后续读写均针对该文件。
-w / --workspace 覆盖配置中的 agents.defaults.workspace(在内存中的 Config 上生效,保存时写入)。
--wizard 启用交互式向导(nanobot.cli.onboard.run_onboard),由向导决定是否保存。
  1. 第一步便是配置文件的路径,如果指定了--config,就从这里进行查找,否则默认为 ~/.nanbot/.config.json
python 复制代码
if config:
        config_path = Path(config).expanduser().resolve()
        set_config_path(config_path)
        console.print(f"[dim]Using config: {config_path}[/dim]")
else:
    config_path = get_config_path()
  1. 创建或者更新config.json文件
  • 配置文件已存在

    • 如果是wizard: 只 load_config + 工作区覆盖,不在这里做覆盖/刷新的交互;后面交给向导统一保存。
    • 非 wizard:
      提示:已存在,用typer.confirm("Overwrite?")问是否 Overwrite。
      确认覆盖:Config() 全新默认 + 工作区覆盖 → save_config,相当于重置为默认。
      不覆盖:load_config + 工作区覆盖 → save_config,保留已有字段值,同时用 save_config 把结构写回到config路径下。
  • 配置文件不存在

    Config() 默认 + 工作区覆盖。

    非 wizard:立刻 save_config

    wizard:先不保存,等向导结束再决定是否写盘。

    注意这里json配置转换为python Config配置用到了 pydantic 的 BaseSettings Field(default_factory=...) 以及 model_validate用于将数据(如字典)转换并验证为模型实例的方法。

python 复制代码
if config_path.exists():
        if wizard:
            config = _apply_workspace_override(load_config(config_path))
        else:
            console.print(f"[yellow]Config already exists at {config_path}[/yellow]")
            console.print("  [bold]y[/bold] = overwrite with defaults (existing values will be lost)")
            console.print("  [bold]N[/bold] = refresh config, keeping existing values and adding new fields")
            if typer.confirm("Overwrite?"):
                config = _apply_workspace_override(Config())
                save_config(config, config_path)
                console.print(f"[green]✓[/green] Config reset to defaults at {config_path}")
            else:
                config = _apply_workspace_override(load_config(config_path))
                save_config(config, config_path)
                console.print(f"[green]✓[/green] Config refreshed at {config_path} (existing values preserved)")
    else:
        config = _apply_workspace_override(Config())
        # In wizard mode, don't save yet - the wizard will handle saving if should_save=True
        if not wizard:
            save_config(config, config_path)
            console.print(f"[green]✓[/green] Created config at {config_path}")
  1. 进行向导式的配置
    如果配置了进行了向导式的操作,则利用 questionary包 进行引导式的操作,并且借助于prompt_toolkit 这个强大、交互式命令行界面(CLI)的纯 Python 库,
    具体见 onboard.py中的_select_with_back方法,使用KeyBindings捕获键盘的操作,用来锁定选择的配置,之后在利用python的 getattrsetattr以及_configure_pydantic_model方法 进行对应的配置
    并且会根据返回的值,判断是否进行持久化配置操作。
python 复制代码
 try:
          answer = _get_questionary().select(
              "What would you like to configure?",
              choices=[
                  "[P] LLM Provider",
                  "[C] Chat Channel",
                  "[A] Agent Settings",
                  "[G] Gateway",
                  "[T] Tools",
                  "[V] View Configuration Summary",
                  "[S] Save and Exit",
                  "[X] Exit Without Saving",
              ],
              qmark=">",
          ).ask()
      except KeyboardInterrupt:
          answer = None

      if answer is None:
          action = _prompt_main_menu_exit(_has_unsaved_changes(original_config, config))
          if action == "save":
              return OnboardResult(config=config, should_save=True)
          if action == "discard":
              return OnboardResult(config=original_config, should_save=False)
          continue

      _MENU_DISPATCH = {
          "[P] LLM Provider": lambda: _configure_providers(config),
          "[C] Chat Channel": lambda: _configure_channels(config),
          "[A] Agent Settings": lambda: _configure_general_settings(config, "Agent Settings"),
          "[G] Gateway": lambda: _configure_general_settings(config, "Gateway"),
          "[T] Tools": lambda: _configure_general_settings(config, "Tools"),
          "[V] View Configuration Summary": lambda: _show_summary(config),
      }
  1. 同步配置channel
    对于已经配置好,或者没有配置的channel,都会写入到config.json文件中,
    没有配置的会写入默认值,配置的则覆盖默认值。

  2. 同步工作区配置
    把templates目录下的 AGENTS.md,HEARTBEAT.md,SOUL.md,TOOLS.md,USER.md等文件拷贝到工作目录下.
    形成如下的目录结构:

    shell 复制代码
     ├── AGENTS.md
     ├── HEARTBEAT.md
     ├── memory
     │   ├── HISTORY.md
     │   └── MEMORY.md
     ├── skills
     ├── SOUL.md
     ├── TOOLS.md
     └── USER.md
相关推荐
GuWenyue12 分钟前
写Agent还要重复封装工具?一套MCP多服务方案,3个能力让AI自动查地图、读写文件、操控浏览器
人工智能·机器学习·开源
GuWenyue12 分钟前
90%AI新手不会调参!LangChain双模型流水线实战,一套代码兼顾严谨+创意
人工智能
光锥智能2 小时前
WAIC亮点|兼具泛化能力与作业效率的极智嘉机器人天团
人工智能·机器人
阳光是sunny2 小时前
LangGraph中的Reducer是什么
前端·人工智能·后端
甲维斯2 小时前
Kimi K3 重构10000行单文件屎山代码!
人工智能
阳光是sunny2 小时前
从链到图:LangGraph 入门基础全解析
前端·人工智能·后端
新知图书2 小时前
11.3 详细实现与核心配置(作业批改智能体开发)
人工智能·agent·ai agent·智能体·扣子
深度研习笔记3 小时前
OpenCV工业视觉实战11|多线程解耦+视频流稳流+推理加速,彻底解决卡顿阻塞,实现毫秒级工业实时检测
人工智能·opencv·计算机视觉
delishcomcn3 小时前
AI视觉识别+分切算法:电化铝缺陷检测与裁切一体化解锁
人工智能·算法
触底反弹4 小时前
深入理解大模型采样:Temperature、Top-K、Top-P 的原理与实战
人工智能·算法·面试