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
相关推荐
doiito1 天前
左脚踩右脚:让 LLM 自进化的 Agent 轨迹训练法——为什么它能补上主流范式的最后一块拼图
ai·系统设计
带刺的坐椅1 天前
从 Claude Code 隐私争议,看 SolonCode 的设计选择
ai·llm·agent·claudecode·soloncode·codingplan
冬奇Lab1 天前
Workflow 系列(03):状态管理——持久化、幂等性与版本绑定
人工智能·工作流引擎
冬奇Lab1 天前
每日一个开源项目(第146篇):openpilot - 开源自动驾驶辅助系统,曾在 Consumer Reports 评测中超过特斯拉 Autopilot
人工智能·开源·自动驾驶
吴佳浩2 天前
AI 工程师知识地图:模型格式、框架、部署工具一次讲明白
人工智能·aigc·ai编程
IT_陈寒2 天前
Java的Date类又坑了我一次,改用时间戳真香
前端·人工智能·后端
码农胖大海2 天前
AI额度不够用的解决方案
人工智能
lincats2 天前
Claude Code项目越写越乱?这套清理流程能救你
ai·ai agent·claude code
后端小肥肠2 天前
小红书虚拟商品怎么做?我先用 Skill 跑通了壁纸品类
人工智能·aigc·agent