nanobot 原始碼解讀:用 3,400 行程式碼重建一個完整 AI Agent 框架

nanobot 是香港大學 HKUDS 團隊開源的超輕量級個人 AI 助手,宣稱只用約 3,400 行核心程式碼,就實現了 Claude Code(Clawdbot)430k+ 行程式碼中 99% 的核心功能。這個專案的價值不只在於「小」,更在於它用極簡的設計揭示了一個 AI Agent 框架的本質結構:Message Bus 解耦通訊、Tool Registry 動態擴展、Subagent 背景任務、Skills 漸進式載入,以及 Heartbeat 主動喚醒機制。讀完這份原始碼,你能學到如何從零設計一個可擴展的 Agent 架構,而不被複雜性淹沒。

nanobot GitHub Repository — 專案原始碼,包含本文解讀的所有設計實作

專案概覽

nanobot 是一個 Python 3.11+ 的 AI Agent 框架,核心定位是「個人 AI 助手」。它支援多家 LLM Provider(OpenRouter、Anthropic、OpenAI、DeepSeek、Gemini、vLLM 等),可透過 Telegram、Discord、WhatsApp、飛書等通訊渠道與使用者互動,內建檔案操作、Shell 執行、網頁搜尋、排程任務、記憶系統等工具。技術棧:Python + asyncio + Pydantic + LiteLLM + Typer CLI,使用 hatchling 打包。

架構總覽

nanobot/
├── agent/              # 核心 Agent 邏輯
│   ├── loop.py         #   Agent 主迴圈(LLM ↔ Tool 執行)
│   ├── context.py      #   Prompt 組裝器(System Prompt Builder)
│   ├── memory.py       #   持久化記憶系統
│   ├── skills.py       #   Skills 載入與漸進式披露
│   ├── subagent.py     #   背景子代理管理
│   └── tools/          #   工具系統
│       ├── base.py     #     Tool 抽象基類(含參數驗證)
│       ├── registry.py #     Tool 註冊表
│       ├── filesystem.py #   檔案工具
│       ├── shell.py    #     Shell 執行(含安全防護)
│       ├── web.py      #     網頁搜尋與抓取
│       ├── spawn.py    #     Subagent 啟動工具
│       └── message.py  #     訊息發送工具
├── bus/                # 訊息匯流排
│   ├── events.py       #   InboundMessage / OutboundMessage
│   └── queue.py        #   Async Queue + Pub/Sub
├── channels/           # 通訊渠道
│   ├── base.py         #   Channel 抽象基類
│   ├── manager.py      #   Channel 管理器
│   ├── telegram.py     #   Telegram 實作
│   ├── discord.py      #   Discord 實作
│   ├── whatsapp.py     #   WhatsApp 實作
│   └── feishu.py       #   飛書實作
├── providers/          # LLM Provider 層
│   ├── base.py         #   LLMProvider 抽象介面
│   └── litellm_provider.py # LiteLLM 統一適配
├── config/             # 設定系統
│   ├── schema.py       #   Pydantic Schema
│   └── loader.py       #   JSON 載入 + camelCase 轉換
├── session/            # Session 管理
│   └── manager.py      #   JSONL 持久化對話歷史
├── cron/               # 排程任務
│   └── service.py      #   Cron/Interval/One-shot 排程引擎
├── heartbeat/          # 心跳喚醒
│   └── service.py      #   定期喚醒 Agent 檢查任務
├── skills/             # 內建 Skills
│   ├── github/SKILL.md #   GitHub CLI 操作指引
│   ├── skill-creator/  #   Skill 建立指南
│   └── ...
├── cli/                # CLI 入口
│   └── commands.py     #   Typer CLI 命令定義
└── workspace/          # Workspace 模板
    ├── AGENTS.md       #   Agent 行為指引
    ├── SOUL.md         #   Agent 人格設定
    ├── USER.md         #   使用者資訊模板
    ├── TOOLS.md        #   工具使用說明
    └── HEARTBEAT.md    #   心跳任務清單
Code language: PHP (php)

這個架構的核心設計哲學是「分層解耦」:Channel 層負責收訊息,Bus 層負責路由,Agent 層負責思考和執行,Provider 層負責呼叫 LLM。每一層都可以獨立替換。

核心設計解析

設計一:Message Bus — 用 asyncio Queue 實現 Channel 與 Agent 的完全解耦

nanobot 最核心的架構決策之一,是在 Channel(通訊渠道)和 Agent(核心邏輯)之間放了一個 Message Bus。這個設計讓 Agent 完全不需要知道訊息來自 Telegram 還是 WhatsApp,Channel 也不需要知道 Agent 怎麼處理訊息。

nanobot/bus/queue.py — Message Bus 實作,用兩個 asyncio.Queue 實現雙向通訊

class MessageBus:
    def __init__(self):
        self.inbound: asyncio.Queue[InboundMessage] = asyncio.Queue()
        self.outbound: asyncio.Queue[OutboundMessage] = asyncio.Queue()
        self._outbound_subscribers: dict[str, list[Callable[[OutboundMessage], Awaitable[None]]]] = {}

Bus 的設計極其精簡:一個 inbound Queue 負責接收所有 Channel 的訊息,一個 outbound Queue 負責發送回覆。出站訊息支援 Pub/Sub 模式 —— Channel 透過 subscribe_outbound 訂閱自己 channel 名稱的訊息,dispatch_outbound 方法會根據 msg.channel 欄位自動路由到正確的 Channel。

nanobot/bus/events.py — 訊息事件定義,使用 dataclass 保持極簡

@dataclass
class InboundMessage:
    channel: str      # telegram, discord, whatsapp
    sender_id: str
    chat_id: str
    content: str
    timestamp: datetime = field(default_factory=datetime.now)
    media: list[str] = field(default_factory=list)

    @property
    def session_key(self) -> str:
        return f"{self.channel}:{self.chat_id}"
Code language: CSS (css)

注意 session_key 的設計 —— 用 channel:chat_id 組合作為 Session 的唯一識別。這意味著同一個使用者在 Telegram 和 WhatsApp 會有不同的 Session,這是有意為之的設計選擇:每個通訊渠道維護獨立的對話上下文。

設計二:Agent Loop — 經典的 LLM ↔ Tool 迴圈

Agent 的核心是一個 while 迴圈:呼叫 LLM → 如果有 tool call 就執行 → 把結果餵回 LLM → 重複直到 LLM 純文字回覆。這是 ReAct 模式的直接實作。

nanobot/agent/loop.py — Agent 主迴圈,整個框架的心臟

while iteration < self.max_iterations:
    iteration += 1
    response = await self.provider.chat(
        messages=messages,
        tools=self.tools.get_definitions(),
        model=self.model
    )
    if response.has_tool_calls:
        messages = self.context.add_assistant_message(
            messages, response.content, tool_call_dicts
        )
        for tool_call in response.tool_calls:
            result = await self.tools.execute(tool_call.name, tool_call.arguments)
            messages = self.context.add_tool_result(
                messages, tool_call.id, tool_call.name, result
            )
    else:
        final_content = response.content
        break
Code language: PHP (php)

關鍵設計決策:

  1. max_iterations 限制:預設 20 次迭代上限,防止 Agent 無限迴圈。這是一個簡單但必要的安全閥。
  2. Session 持久化只保存最終結果:只有迴圈結束後的 user input 和 final response 才會寫入 Session,中間的 tool call 過程不保存。這大幅節省了 Session 檔案的大小。
  3. System message 路由機制:當 msg.channel == "system" 時,走不同的處理邏輯。Subagent 完成後的回報就是透過這個管道,chat_id 格式為 "original_channel:original_chat_id",實現了跨 channel 的結果回傳。

設計三:Tool 系統 — 抽象基類 + Registry 動態註冊

Tool 系統的設計遵循了「定義與執行分離」的原則。每個 Tool 需要實作四個元素:namedescriptionparameters(JSON Schema)、execute 方法。Registry 負責統一管理和呼叫。

nanobot/agent/tools/base.py — Tool 抽象基類,包含內建的 JSON Schema 參數驗證

class Tool(ABC):
    _TYPE_MAP = {
        "string": str, "integer": int, "number": (int, float),
        "boolean": bool, "array": list, "object": dict,
    }

    @property
    @abstractmethod
    def name(self) -> str: ...

    @property
    @abstractmethod
    def parameters(self) -> dict[str, Any]: ...

    @abstractmethod
    async def execute(self, **kwargs: Any) -> str: ...

    def validate_params(self, params: dict[str, Any]) -> list[str]:
        """Validate tool parameters against JSON schema."""
        ...

    def to_schema(self) -> dict[str, Any]:
        return {
            "type": "function",
            "function": {
                "name": self.name,
                "description": self.description,
                "parameters": self.parameters,
            }
        }

亮點在於 validate_params 方法 —— Tool 基類自帶了一個遞歸的 JSON Schema 驗證器,在執行前先檢查 LLM 回傳的參數是否合法。這避免了在每個 Tool 實作中重複寫驗證邏輯。而 to_schema 方法直接輸出 OpenAI function calling 格式,讓 Tool 的定義和 LLM API 規格無縫對接。

nanobot/agent/tools/registry.py — Tool Registry,用 dict 管理所有已註冊的工具

class ToolRegistry:
    def __init__(self):
        self._tools: dict[str, Tool] = {}

    def register(self, tool: Tool) -> None:
        self._tools[tool.name] = tool

    async def execute(self, name: str, params: dict[str, Any]) -> str:
        tool = self._tools.get(name)
        if not tool:
            return f"Error: Tool '{name}' not found"
        errors = tool.validate_params(params)
        if errors:
            return f"Error: Invalid parameters: " + "; ".join(errors)
        return await tool.execute(**params)

Registry 的 execute 方法把「查找 → 驗證 → 執行 → 錯誤處理」統一封裝,Agent Loop 只需要呼叫 self.tools.execute(name, args) 一行程式碼。錯誤不拋異常而是回傳錯誤字串,讓 LLM 看到錯誤訊息後可以自我修正。

設計四:Shell 安全防護 — Deny Pattern 黑名單機制

賦予 LLM 執行 Shell 命令的能力是一把雙面刃。nanobot 用 regex deny pattern 實現了一層安全防護。

nanobot/agent/tools/shell.py — Shell 執行工具,包含危險指令攔截

class ExecTool(Tool):
    def __init__(self, ...):
        self.deny_patterns = deny_patterns or [
            r"\brm\s+-[rf]{1,2}\b",           # rm -r, rm -rf
            r"\b(format|mkfs|diskpart)\b",     # disk operations
            r"\bdd\s+if=",                     # dd
            r"\b(shutdown|reboot|poweroff)\b", # system power
            r":\(\)\s*\{.*\};\s*:",            # fork bomb
        ]

    def _guard_command(self, command: str, cwd: str) -> str | None:
        for pattern in self.deny_patterns:
            if re.search(pattern, lower):
                return "Error: Command blocked by safety guard"

設計特點:

  1. 黑名單而非白名單:預設允許所有命令,只攔截已知危險操作。這符合「個人助手」的定位 —— 使用者需要靈活性。
  2. 可選的 workspace 路徑限制restrict_to_workspace 選項開啟後會攔截路徑穿越(../)和 workspace 外的路徑。
  3. 輸出截斷:最長 10,000 字元,防止大量輸出塞滿 LLM context window。
  4. Timeout 保護:預設 60 秒超時,避免 hang 住整個系統。

設計五:Context Builder — 模組化的 System Prompt 組裝

nanobot 不是把 System Prompt 寫死在程式碼裡,而是從多個來源動態組裝。

nanobot/agent/context.py — Prompt 組裝器,從 workspace 檔案動態建構 System Prompt

class ContextBuilder:
    BOOTSTRAP_FILES = ["AGENTS.md", "SOUL.md", "USER.md", "TOOLS.md", "IDENTITY.md"]

    def build_system_prompt(self, skill_names: list[str] | None = None) -> str:
        parts = []
        parts.append(self._get_identity())      # 核心身份
        bootstrap = self._load_bootstrap_files() # Workspace 設定檔
        if bootstrap:
            parts.append(bootstrap)
        memory = self.memory.get_memory_context() # 記憶
        if memory:
            parts.append(f"# Memory\n\n{memory}")
        # Skills...
        return "\n\n---\n\n".join(parts)

System Prompt 由五層組裝而成:

  1. Identity:硬編碼的核心身份(包含時間、系統資訊、workspace 路徑)
  2. Bootstrap Files:從 workspace 目錄讀取 AGENTS.mdSOUL.mdUSER.md 等設定檔
  3. Memory:長期記憶(MEMORY.md)和當日筆記
  4. Always-on Skills:標記為 always=true 的 Skill 全文載入
  5. Skills Summary:其他可用 Skills 只載入摘要,需要時再用 read_file 讀取

這個設計讓使用者不需要改程式碼,只要編輯 workspace 裡的 Markdown 檔案,就能自訂 Agent 的行為、人格和知識。

設計六:Subagent — 輕量級背景任務架構

nanobot 支援主 Agent 透過 spawn 工具啟動子代理(Subagent)來處理耗時任務。Subagent 的設計巧妙地平衡了能力和安全性。

nanobot/agent/subagent.py — Subagent 管理器,用 asyncio.Task 實現背景執行

class SubagentManager:
    async def spawn(self, task: str, label: str | None = None, ...) -> str:
        task_id = str(uuid.uuid4())[:8]
        bg_task = asyncio.create_task(
            self._run_subagent(task_id, task, display_label, origin)
        )
        self._running_tasks[task_id] = bg_task
        bg_task.add_done_callback(lambda _: self._running_tasks.pop(task_id, None))
        return f"Subagent [{display_label}] started (id: {task_id})."

Subagent 的權限被刻意限縮:

  • 可以:讀寫檔案、執行 Shell、搜尋網頁
  • 不可以:發送訊息給使用者(沒有 message tool)、再 spawn 子代理(沒有 spawn tool)

完成後的結果透過 Message Bus 的 system channel 回報,chat_id 編碼為 "origin_channel:origin_chat_id" 格式,讓主 Agent 知道要把結果發送到哪裡:

async def _announce_result(self, ...):
    msg = InboundMessage(
        channel="system",
        sender_id="subagent",
        chat_id=f"{origin['channel']}:{origin['chat_id']}",
        content=announce_content,
    )
    await self.bus.publish_inbound(msg)
Code language: JavaScript (javascript)

設計七:Skills 漸進式載入 — 三層 Progressive Disclosure

Skills 系統的設計解決了一個核心矛盾:Agent 需要知道很多能力,但 LLM 的 context window 有限。

nanobot/agent/skills.py — Skills 載入器,實現三層漸進式載入

class SkillsLoader:
    def build_skills_summary(self) -> str:
        lines = ["<skills>"]
        for s in all_skills:
            lines.append(f"  <skill available=\"{str(available).lower()}\">")
            lines.append(f"    <name>{name}</name>")
            lines.append(f"    <description>{desc}</description>")
            lines.append(f"    <location>{path}</location>")
            lines.append(f"  </skill>")
        lines.append("</skills>")
        return "\n".join(lines)
Code language: HTML, XML (xml)

三層載入策略:

層級 何時載入 Token 成本 內容
L1: Metadata 永遠載入 ~100 tokens/skill 名稱 + 描述
L2: SKILL.md Agent 判斷需要時 ~500-2000 tokens 完整操作指引
L3: Resources 具體操作時 不限 腳本、參考文件、資源

Skills 還有一個精巧的設計 —— 需求檢查:每個 Skill 可以在 metadata 中宣告需要的 binary(如 gh CLI)和環境變數。載入器會在啟動時檢查這些依賴是否存在,不符合的 Skill 會標記為 available="false" 並提示缺少什麼。

def _check_requirements(self, skill_meta: dict) -> bool:
    requires = skill_meta.get("requires", {})
    for b in requires.get("bins", []):
        if not shutil.which(b):
            return False
    for env in requires.get("env", []):
        if not os.environ.get(env):
            return False
    return True
Code language: PHP (php)

設計八:LLM Provider 抽象 — 用 LiteLLM 實現多 Provider 統一介面

nanobot 沒有自己寫各家 LLM 的 API 對接,而是站在 LiteLLM 肩膀上,加了一層薄包裝。

nanobot/providers/base.py — LLM Provider 抽象介面,定義統一的 chat 方法

@dataclass
class LLMResponse:
    content: str | None
    tool_calls: list[ToolCallRequest] = field(default_factory=list)
    finish_reason: str = "stop"
    usage: dict[str, int] = field(default_factory=dict)

    @property
    def has_tool_calls(self) -> bool:
        return len(self.tool_calls) > 0

class LLMProvider(ABC):
    @abstractmethod
    async def chat(self, messages, tools, model, ...) -> LLMResponse: ...
Code language: CSS (css)

nanobot/providers/litellm_provider.py — LiteLLM 實作,處理多 Provider 的 model prefix 邏輯

LiteLLM Provider 的核心工作是處理各家 Provider 的 model 名稱前綴:

if self.is_openrouter and not model.startswith("openrouter/"):
    model = f"openrouter/{model}"
if self.is_vllm:
    model = f"hosted_vllm/{model}"
if "gemini" in model.lower() and not model.startswith("gemini/"):
    model = f"gemini/{model}"
Code language: PHP (php)

以及將 LLM 回傳的錯誤包裝成 content 而不是拋異常,讓 Agent 可以 graceful 地處理 LLM 錯誤:

except Exception as e:
    return LLMResponse(
        content=f"Error calling LLM: {str(e)}",
        finish_reason="error",
    )
Code language: PHP (php)

設計九:Channel 抽象 — 統一的聊天平台整合模式

所有聊天平台都遵循同一個抽象:start(開始監聽)、stop(停止)、send(發送訊息),以及一個內建的 _handle_message 方法做權限檢查和訊息路由。

nanobot/channels/base.py — Channel 抽象基類,定義聊天平台的統一介面

class BaseChannel(ABC):
    def is_allowed(self, sender_id: str) -> bool:
        allow_list = getattr(self.config, "allow_from", [])
        if not allow_list:
            return True  # 空列表 = 允許所有人
        return str(sender_id) in allow_list

    async def _handle_message(self, sender_id, chat_id, content, ...):
        if not self.is_allowed(sender_id):
            return
        msg = InboundMessage(channel=self.name, ...)
        await self.bus.publish_inbound(msg)

is_allowed 的白名單設計是安全考量 —— 這是個「個人助手」,不應該讓陌生人使用。空列表表示「允許所有人」的設計,讓使用者在測試階段可以快速上手。

nanobot/channels/manager.py — Channel 管理器,按需初始化和路由訊息

Channel Manager 用了 lazy import 模式 —— 只在 config 中啟用某個 channel 時才 import 對應的模組。這避免了未安裝某個 SDK 時整個系統無法啟動的問題。

設計十:Heartbeat — 主動喚醒機制讓 Agent 不只被動回應

大多數 AI 助手只能被動回應使用者訊息,nanobot 的 Heartbeat 機制讓 Agent 可以主動「醒來」檢查任務。

nanobot/heartbeat/service.py — 心跳服務,定期喚醒 Agent 執行 HEARTBEAT.md 中的任務

HEARTBEAT_PROMPT = """Read HEARTBEAT.md in your workspace (if it exists).
Follow any instructions or tasks listed there.
If nothing needs attention, reply with just: HEARTBEAT_OK"""

class HeartbeatService:
    async def _tick(self) -> None:
        content = self._read_heartbeat_file()
        if _is_heartbeat_empty(content):
            return  # 沒任務就跳過
        response = await self.on_heartbeat(HEARTBEAT_PROMPT)
        if HEARTBEAT_OK_TOKEN in response.upper():
            logger.info("Heartbeat: OK (no action needed)")

設計巧思:

  1. 先檢查檔案是否有實質內容_is_heartbeat_empty 會過濾掉空行、標題、HTML 註解,只有真正有待辦事項時才喚醒 Agent,避免浪費 LLM token。
  2. HEARTBEAT_OK Token:Agent 判斷沒事做時回覆 HEARTBEAT_OK,系統據此決定是否記錄日誌。
  3. 純 Markdown 驅動:使用者只需要編輯 HEARTBEAT.md 就能新增或移除週期性任務,不需要寫任何程式碼。

Prompt 設計解讀

nanobot 的 prompt 系統採用「Workspace-as-Prompt」的理念——把 System Prompt 拆成多個 Markdown 檔案,每個檔案負責不同面向的人格和行為定義。

workspace/SOUL.md — Agent 人格設定,使用者可以自訂 AI 的個性和價值觀

# Soul
I am nanobot 🐈, a personal AI assistant.
## Personality
- Helpful and friendly
- Concise and to the point
## Values
- Accuracy over speed
- User privacy and safety
- Transparency in actions
Code language: PHP (php)

workspace/AGENTS.md — Agent 行為指引,定義工具使用規範和記憶策略

workspace/USER.md — 使用者資訊模板,Agent 會根據這些資訊個性化回應

這個設計的精妙之處在於:使用者不需要理解 prompt engineering,只需要填寫一份「個人檔案」USER.md 模板設計了時區、語言偏好、技術水準、工作背景等欄位,Agent 會自動將這些資訊融入回應中。而 SOUL.md 讓進階使用者可以完全重塑 Agent 的人格。

設計理念萃取

  • Message Bus 解耦是多 Channel 系統的根基 — 只要用一個 async Queue 就能讓 N 個 Channel 和 Agent 之間完全解耦。新增一個 Channel 不需要改動 Agent 的任何程式碼,反之亦然。這個模式適用於任何需要多輸入源的系統。
  • Tool 系統 = 抽象基類 + Registry + 自動驗證 — 把「Tool 定義」(name/description/parameters)和「Tool 執行」(execute)統一在一個抽象類別中,Registry 負責查找和錯誤處理。這套模式讓新增工具只需要建一個新 class,不用改任何已有程式碼。
  • 「錯誤當成回傳值」比拋異常更適合 Agent 系統 — nanobot 在 Tool 執行和 LLM 呼叫中都採用「錯誤回傳為字串」而非拋異常的策略。這讓 LLM 能看到錯誤訊息並嘗試自我修正,比直接 crash 更有彈性。
  • Workspace-as-Prompt 讓非工程師也能自訂 Agent 行為 — 把 System Prompt 拆成多個 Markdown 檔案放在 workspace 中,使用者編輯檔案就能改變 Agent 的行為,不需要改程式碼。這是「設定優於編碼」原則在 AI Agent 領域的極致體現。
  • 漸進式載入是 LLM 資源管理的關鍵模式 — Skills 的三層載入策略(Metadata → SKILL.md → Resources)解決了「能力多但 context window 有限」的矛盾。這個模式可以推廣到任何需要在有限 context 中管理大量知識的場景。

延伸思考

nanobot 的架構雖然只有 3,400 行,但它展示了一個「完整」AI Agent 框架需要的所有核心組件。如果你正在建構自己的 Agent 系統,可以考慮以下應用:

  1. 先建 Message Bus,再建其他一切。即使你一開始只有一個 Channel(比如 CLI),Bus 的存在也讓未來擴展變得零成本。
  2. Tool Registry 模式可以直接複製。抽象基類 + dict registry + 自動 JSON Schema 驗證,這套模式不到 100 行程式碼就能實現,卻能支撐整個 Tool 生態。
  3. Heartbeat 機制值得加入任何長駐型 Agent。讓 Agent 有能力主動檢查待辦事項、發出提醒,是從「工具」進化到「助手」的關鍵一步。
  4. 用 Markdown 檔案作為設定介面。比起 JSON/YAML 設定檔,Markdown 檔案對使用者更友好,而且可以包含富文本說明,讓「設定」本身成為一份指南。

Vibe Coding:把觀念變成程式碼

以下是你可以直接給 AI 的 prompt 指令,將本文介紹的設計觀念應用到你的專案中:

Message Bus 解耦通訊層

場景:你正在建構一個需要支援多個輸入來源的系統(例如同時支援 API、WebSocket、CLI),而且希望未來能輕鬆新增通道而不改動核心邏輯。

給 AI 的指令

請幫我在專案中實作一個 Message Bus 模式,用 Python asyncio.Queue 實現雙向通訊。需要:(1) 一個 InboundMessage dataclass 包含 channel、sender_id、content 欄位,(2) 一個 MessageBus 類別包含 inbound 和 outbound 兩個 asyncio.Queue,outbound 支援 Pub/Sub 訂閱機制讓不同 channel 只接收屬於自己的訊息,(3) Channel 抽象基類定義 start/stop/send 介面。目標是讓 Channel 層和核心處理邏輯完全解耦,新增 Channel 不需要改動任何已有程式碼。請參考 nanobot 的 Message Bus 設計。

效果:AI 會產出一組完整的 Message Bus 基礎設施程式碼,包含事件定義、Bus 實作、Channel 抽象基類,以及一個範例 Channel 實作展示如何串接。

Tool Registry 動態註冊與自動驗證

場景:你正在為 AI Agent 建構工具系統,希望新增工具時只需要建一個新 class,不用改動其他程式碼。

給 AI 的指令

請幫我實作一個 Tool Registry 系統,參考 nanobot 的「抽象基類 + Registry + 自動驗證」模式。需要:(1) Tool 抽象基類,定義 name、description、parameters(JSON Schema 格式)屬性和 async execute 方法,基類內建 validate_params 方法根據 JSON Schema 自動驗證 LLM 回傳的參數,以及 to_schema 方法輸出 OpenAI function calling 格式,(2) ToolRegistry 類別用 dict 管理工具,execute 方法統一處理「查找 → 驗證 → 執行 → 錯誤處理」,錯誤回傳字串而非拋異常讓 LLM 能自我修正,(3) 一個具體 Tool 實作作為範例。

效果:AI 會產出一套可直接使用的 Tool 系統,包含基類、Registry、範例工具。新增工具只需繼承基類並 register,完全符合開放封閉原則。

錯誤回傳字串而非拋異常

場景:你在 Code Review 一個 AI Agent 專案,發現 Tool 執行錯誤時直接拋異常導致 Agent 中斷,想改成更有彈性的錯誤處理策略。

給 AI 的指令

請重構我專案中的 Tool 執行錯誤處理機制,採用 nanobot 的「錯誤當成回傳值」策略。目前 Tool 執行失敗時會拋異常導致整個 Agent Loop 中斷。請改成:(1) Tool 的 execute 方法在內部 catch 所有異常,回傳格式化的錯誤字串如 “Error: [錯誤描述]”,(2) LLM Provider 呼叫失敗時也回傳包含錯誤訊息的 LLMResponse 物件而非拋異常,(3) 確保 Agent Loop 不會因為單一 Tool 失敗就停止,而是讓 LLM 看到錯誤訊息後能嘗試替代方案或自我修正。這個模式的核心理念是:在 Agent 系統中,LLM 本身就是最好的錯誤處理器。

效果:AI 會逐一檢查 Tool 和 Provider 的錯誤處理邏輯,將 raise Exception 改為 return error string,並確保 Agent Loop 的容錯性。

System Prompt 模組化組裝(Workspace-as-Prompt)

場景:你的 Agent 的 System Prompt 越來越長且難以維護,想把它拆成多個可管理的模組。

給 AI 的指令

請幫我重構 Agent 的 System Prompt 系統,採用 nanobot 的「Workspace-as-Prompt」模式——把 System Prompt 拆成多個 Markdown 檔案動態組裝。需要一個 ContextBuilder 類別,從指定目錄讀取以下檔案來組裝 System Prompt:(1) IDENTITY.md — 核心身份與能力描述,(2) RULES.md — 行為規範與限制,(3) USER.md — 使用者偏好與背景資訊,(4) MEMORY.md — 長期記憶。組裝時用 “—” 分隔各區塊,檔案不存在就跳過。這樣使用者編輯 Markdown 檔案就能自訂 Agent 行為,不需要改程式碼。

效果:AI 會建立一個 ContextBuilder 和對應的 workspace 目錄結構,包含各模板檔案和讀取邏輯,讓 System Prompt 變成可維護的模組化結構。

Skills 漸進式載入節省 Context Window

場景:你的 Agent 支援越來越多功能,但把所有 Skill 說明都塞進 System Prompt 會佔用太多 token,影響實際對話品質。

給 AI 的指令

請幫我實作一個 Skills 漸進式載入系統,參考 nanobot 的三層 Progressive Disclosure 設計。三層策略為:L1 Metadata(永遠載入,每個 Skill 約 100 tokens,只含名稱和描述摘要),L2 SKILL.md(Agent 判斷需要時才載入,包含完整操作指引),L3 Resources(具體操作時才載入的腳本和參考資料)。需要:(1) 每個 Skill 目錄包含 metadata.json(名稱、描述、依賴檢查)和 SKILL.md,(2) SkillsLoader 類別啟動時掃描所有 Skill 目錄,檢查依賴是否滿足(如特定 CLI 工具是否安裝),(3) build_skills_summary 方法產出 L1 摘要嵌入 System Prompt,(4) Agent 需要某個 Skill 時用 read_file 工具讀取 L2 內容。

效果:AI 會建立完整的 Skills 目錄結構、Loader 邏輯和依賴檢查機制,讓你的 Agent 在有限的 context window 中管理大量功能而不浪費 token。

Heartbeat 主動喚醒機制

場景:你有一個長駐型 AI Agent,目前只能被動回應使用者訊息,想讓它能定期主動檢查待辦事項並執行任務。

給 AI 的指令

請幫我為現有的 Agent 加入 Heartbeat 主動喚醒機制,參考 nanobot 的設計。需要:(1) HeartbeatService 類別,用 asyncio 定時器按設定間隔執行心跳,(2) 每次心跳先讀取 HEARTBEAT.md 檔案,過濾空行和純標題,只有存在實質待辦事項時才喚醒 Agent,(3) 喚醒時發送固定 prompt 請 Agent 讀取並執行 HEARTBEAT.md 中的任務,(4) Agent 判斷沒事做時回覆特定 token(如 HEARTBEAT_OK),系統據此記錄日誌。關鍵是先檢查再喚醒,避免無謂的 LLM 呼叫浪費 token。

效果:AI 會產出一個完整的 Heartbeat 服務,包含定時器、檔案檢查邏輯、Agent 喚醒流程,讓你的 Agent 從被動工具進化為主動助手。

延伸閱讀

0

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *