標籤: LangChain

  • 用 LangChain Deep Agents 搭一個自己的 Perplexity

    用 LangChain Deep Agents 搭一個自己的 Perplexity

    📌 本文重點

    • Deep Agents 讓 AI 像專職助理跑長流程
    • 可自動調研、用多工具、多模型協作
    • 完全自建自控,比封閉 SaaS 更可客製

    給它一個問題,它會自動上網查資料、調工具、寫成 Markdown 報告——LangChain Deep Agents 解決的,就是過去只有 Perplexity、Claude 這類封閉產品才有的「自動調研 + 多步推理 + 多工具協作」能力,現在你可以自己部署掌控。

    官方介紹與範例程式可見 LangChain 部落格與文件:https://python.langchain.com/docs/deep_agents/
    參考解讀(英文):Towards AI:LangChain Just Made Frontier-Style Deep Agents Open Source


    核心功能:讓 Agent 像「專職助理」一樣持續做事

    1. 多步推理:自動拆解任務,不再只回答一個問題

    一般 ChatGPT 類工具,回答完一輪就結束;Deep Agents 是會自己拆任務的「流程型助理」:

    • 你只丟一句:幫我比較今年主流開源 LLM,寫一份 Markdown 報告
    • Agent 自己決定步驟:
    • 搜索今年開源 LLM 清單
    • 進一步查每個模型規格(參數量、效能、硬體需求)
    • 彙整成表格 + 評語
    • 最後輸出成 Markdown

    💡 關鍵: Deep Agents 會自己規劃與拆解步驟,從一句任務描述完成整個多階段流程,而不是只回覆一輪答案。

    你可以做的事:

    • 寫「任務描述」而不是「一步一步指令」,例如:
      “`text
      幫我整理一份 2024 年可本地部署 LLM 的比較報告,條件:
    • 模型大小 7B–30B
    • 適合在 24GB VRAM 內運行
    • 需要列出評測來源與分數
      “`
    • 把這段描述寫死在後端程式裡,前端只接使用者問題,就能幫同事省下一堆「怎麼問」的時間。

    2. 多工具 / 多模型:像 Perplexity 一樣「邊問邊查」

    Deep Agents 的設計重點是「會自己決定要用哪個工具」,典型會接:

    • 搜索工具(如 Tavily、SerpAPI、自建搜尋 API)
    • HTTP 工具(抓 REST API:GitHub、Jira、自家服務)
    • 向量庫(如 Chroma、PGVector,查公司內部文件)
    • 多個 LLM 模型(雲端 / 本地混搭)

    你可以配置:

    • 一個「大模型」負責推理與規劃(例如雲端的 GPT-4 / Claude / Qwen API)
    • 一個「便宜模型」負責簡單摘要(例如本地 Qwen 7B / DeepSeek 7B,參考:r/LocalLLaMA 模型比較表)

    你可以做的事:

    • 先接一個搜尋工具 + 一個 HTTP 工具:
    • 搜索:Tavily 或 SerpAPI
    • HTTP:LangChain 內建 Requests 工具
    • 讓 Agent 自己選:
    • 有網路 → 先查
    • 有內部 API → 優先打 API

    3. 長任務鏈管理與錯誤恢復:不怕中斷的長流程

    Deep Agents 預設支援:

    • 任務狀態記錄:每一步做了什麼、用什麼工具
    • 中途錯誤重試:API timeout / 工具失敗能重跑某步
    • 可視化 trace(在 LangSmith 裡看完整呼叫鏈)

    這讓它適合跑「需要 10–20 步」的長任務,例如:

    💡 關鍵: 對需要 10–20 步、會呼叫多個外部 API 的長流程,Deep Agents 能維持完整狀態並在錯誤時自動重試,不會整個任務報廢。

    • 連續調 5 個外部 API
    • 多輪搜尋 + 逐段寫報告

    你可以做的事:

    • 在程式裡加入:
    • 最大步數限制(防止無限迴圈)
    • 單次任務總 token 上限(防止爆成本)
    • 把 trace 連到 LangSmith 或 logging 系統,出錯時直接看哪一步掛掉。

    適合誰用:三個具體場景

    1. 技術團隊:自動化「調研 + 報告」Agent

    使用情境:

    • 每週要寫「某技術趨勢整理」
    • 每次都要:Google → 點 10 個連結 → 摘要 → 整理成内部 wiki

    用 Deep Agents 可以:

    • 設定固定 Prompt:
    • 主題
    • 需要標準小節(背景 / 優缺點 / 成熟度 / 相關開源專案)
    • Agent 自動:
    • 上網搜尋最新資料
    • 過濾過舊或不相關文章
    • 整理成 Markdown 報告

    可立即行動:

    • Fork 官方範例(見下方實作段落),改成輸出 .md 檔,定期跑一個「技術雷達」任務。

    2. 資料與營運團隊:自動週報 / 數據分析 Agent

    使用情境:

    • 每週要從 BI、資料庫、CRM 抓數字,整理成週報

    Deep Agents 可以:

    • 用 HTTP 工具接你的 BI / 數據 API
    • 每週:
    • Agent 調 API 拿數據
    • 自動計算環比 / 年比
    • 寫成「管理者看得懂」的自然語言結論

    可立即行動:

    • 先把一個「既有的 SQL 報表 API」包成工具,讓 Agent 負責:「怎麼解讀數字」而不是「查數字」。

    3. 個人開發者:半自動運維助手(GitHub / Jira / 自家 API)

    使用情境:

    • 你一個人或小團隊維護多個專案,需要:
    • 看 issue / PR 狀態
    • 查 error log
    • 開 / 關 ticket

    Deep Agents 可以:

    • 接:
    • GitHub API(Issue / PR 摘要)
    • Jira API(任務狀態)
    • 自家監控或 log API
    • 做到:
    • 問:這週專案 A 有哪些重大 bug?
    • Agent 自己去拉 GitHub / Jira 資料,整理成列表與優先順序

    可立即行動:

    • 先做「只讀版」:只拉資料不寫入,避免一開始就讓 Agent 動到 production。

    LangChain Deep Agents vs 封閉產品

    下表用「使用方式」角度,快速比較:

    名稱 核心功能 免費方案 適合誰
    LangChain Deep Agents 開源多步 Agent,工具任意擴充 開源,需自付模型 想自建 / 部署自家 Agent 的團隊
    Perplexity 現成搜尋 + 答題 + 文件上傳 有免費層 想立刻用、不寫程式的使用者
    Claude Projects / Workflows 團隊用流程型 Agent,整合在 Claude 內 有試用 / 收費方案 已採用 Anthropic 生態的公司

    重點差異在:Deep Agents 完全自己掌控與客製,但需要你寫一點 Python 與 DevOps;Perplexity / Claude 比較像「即用即走」的 SaaS。

    💡 關鍵: Deep Agents 是開源、可客製的方案,需要自付模型成本與運維,而封閉產品則以方便與免部署為主。


    怎麼開始:從零搭一個「自動上網查 + Markdown 報告」Deep Agent

    下面以 Python 為例,做一個簡單版本:給問題 → 自動上網查 → 輸出 Markdown 報告。

    1. 安裝環境:Python + Poetry + LangChain

    前置條件:

    • 建議 Python 3.10+
    • 安裝 Poetry(包管理):
      bash
      pip install poetry

    建立專案:

    mkdir deep-agent-report && cd deep-agent-report
    poetry init  # 按提示建立 pyproject.toml
    poetry add langchain langchain-openai langchain-community
    poetry add tavily-python  # 當作搜尋工具
    

    若要用其他模型供應商,改裝對應的 LangChain integration 即可,例如 langchain-anthropic、langchain-qianfan 等。

    2. 選模型:先雲端,之後再切本地

    先用雲端(方便、快速試)

    例如用 OpenAI 或對應的相容 API:

    export OPENAI_API_KEY=你的_key
    

    在程式裡:

    from langchain_openai import ChatOpenAI
    
    llm = ChatOpenAI(model="gpt-4.1-mini", temperature=0)
    

    你可以先選便宜模型(如 -mini / -lite),控制成本。

    想切到本地 LLM 怎麼辦?

    • 可以用 Ollama 或本地伺服器跑 Qwen / DeepSeek 等模型
    • 再用 LangChain 的 ChatOllama 或 HTTP wrapper 包進來
    from langchain_community.chat_models import ChatOllama
    
    llm = ChatOllama(model="qwen:7b")
    

    建議架構:

    • 推理規劃:用雲端較強模型
    • 摘要 / 重寫:用本地 LLM(成本較低)

    3. 配置工具:搜尋 + HTTP(可選)

    以 Tavily 搜尋為例:

    export TAVILY_API_KEY=你的_tavily_key
    
    from langchain_community.tools.tavily_search import TavilySearchResults
    
    search_tool = TavilySearchResults(k=5)
    

    如果你有自己的 API(例如:公司知識庫、專案清單):

    from langchain_community.tools import RequestsGet
    
    http_tool = RequestsGet()
    

    之後你可以把兩個工具一起交給 Deep Agent,讓它自己決定何時使用。

    4. 建一個最小可用 Deep Agent:問題 → 報告

    下面是一個簡化範例(概念版),實際 API 以官方文件為準:

    from langchain_deep_agents import DeepAgent  # 假想匯入路徑,請以官方為準
    
    agent = DeepAgent(
        llm=llm,
        tools=[search_tool, http_tool],
        max_steps=12,           # 防止無限迴圈
        max_tokens=6000,        # 控制成本
    )
    
    SYSTEM_PROMPT = """
    你是一個技術研究助手,任務:
    1. 收集最新公開資訊(優先使用搜尋工具)
    2. 整理成結構化 Markdown 報告,包含:
       - 摘要
       - 主要發現
       - 資料來源(附上連結)
    3. 避免抄全文,請用自己的話重新整理。
    """
    
    question = input("請輸入想調研的主題:")
    
    result = agent.run(
        system_prompt=SYSTEM_PROMPT,
        user_query=question,
    )
    
    with open("report.md", "w", encoding="utf-8") as f:
        f.write(result)
    
    print("已輸出到 report.md")
    

    你可以立刻做的事:

    • 把 SYSTEM_PROMPT 改成你團隊的報告模板
    • 把 max_steps 調小(例如 8),避免初期測試跑太久

    5. 基本錯誤處理與成本控制

    幾個實用建議:

    1. 限制任務長度與次數:
    2. max_steps、max_tokens 都要設
    3. 外層再加一個「總執行時間」timeout
    4. 工具錯誤重試:
    5. 用 LangChain 的 retry 機制(例如 Retrying decorator)
    6. 搜尋失敗時,可以 fallback 到其他工具或直接回答「資料不足」
    7. 記錄花費:
    8. 把每次任務的 token 使用與工具呼叫次數寫 log
    9. 每週 review 哪些任務最花錢,是否可以改成「批次離線」處理

    小結:讀完可以做的三步

    1. 先用雲端模型 + Tavily,照上面的範例跑出第一個 Markdown 報告 Agent。
    2. 替換 Prompt 和工具,改成符合你團隊需求的「技術調研」「數據週報」或「運維助手」。
    3. 再考慮本地 LLM + 更完整工具鏈,把敏感資料與成本問題一起收回自己掌控。

    只要願意寫一點 Python,你就能把「Perplexity 那種會自己查、自己寫的 AI 助理」搬進自己的專案與內網。

    🚀 你現在可以做的事

    • 到 LangChain 官方文件頁面閱讀 Deep Agents 章節並 Fork 範例專案
    • 建立一個 deep-agent-report 專案,照文中步驟跑出第一個 report.md
    • 把 SYSTEM_PROMPT 與工具配置改成你的技術調研或數據週報模板,部署在內網試跑
  • 用 HeyZoku 指揮程式碼 Agent 的最簡上手法

    用 HeyZoku 指揮程式碼 Agent 的最簡上手法

    📌 本文重點

    • HeyZoku 以語音協調多個程式碼 Agent
    • 讓 AI 自動分工處理修 bug、重構、測試與文件
    • 適合個人開發者與團隊做多 Agent 開發實驗
    • 從連接 Git repo 與設定 Agent 即可開用

    用一句話說清楚:HeyZoku 是讓你用語音指令協調多個程式碼代理(coding agents),一起幫你完成修 bug、重構、寫測試、產生文件的工作台。

    相比用鍵盤一個一個丟指令給 ChatGPT、Cursor、LangChain,HeyZoku 主打的是「我只負責說清楚要做什麼,具體執行交給一群 AI 程式碼 Agent 分工」。

    官網/Product Hunt:https://www.producthunt.com/products/heyzoku


    核心功能:把你的語音變成多 Agent 的「作業清單」

    1. 語音介面:說話就能派工,而不是打字寫 Prompt

    HeyZoku 的核心是語音介面:你開口說,系統負責轉成結構化指令,再分派給不同程式碼 Agent。

    可以先從這幾種語音指令開始練習:

    • 任務型指令:
    • 「幫我檢查這個 PR 的潛在 bug,重點看錯誤處理。」
    • 「把 user-service.ts 裡的重複邏輯抽成共用函式。」
    • 範圍型指令:
    • 「只看 backend 資料夾,不要動 frontend。」
    • 限制型指令:
    • 「改動控制在 50 行以內,先給我 diff 再套用。」

    💡 關鍵: 透過語音明確描述任務與限制,HeyZoku 能自動轉成結構化多 Agent 作業清單,大幅減少手動寫 Prompt 的負擔。

    可以馬上做的事:

    1. 列一份自己常對 AI 說的開發請求,把它改寫成一句話就能說清楚的格式。
    2. 盡量包含:目標檔案/行為(修 bug、重構、寫測試…)/限制條件(行數、不要動哪裡)。

    2. 多個程式碼 Agent 協作:讓 AI 自動分工而不是一個模型包山包海

    HeyZoku 的設計是「一個工作台 + 多個角色分明的 coding agents」。典型設定會長這樣:

    • 分析 Agent:負責理解需求、拆解成具體子任務。
    • 實作 Agent:在程式碼庫裡進行實際修改、新增檔案。
    • 測試 Agent:補齊或更新單元測試、行為測試。
    • 文件 Agent:根據變更更新 README、API 文件、註解。

    當你下達語音指令時,HeyZoku 的工作台會:

    1. 把語音轉成文字並抽取「任務」、涉及檔案、限制。
    2. 交給分析 Agent 拆解成多個可執行的步驟。
    3. 由不同 Agent 順序執行,並把進度回報到同一個介面。

    可以馬上做的事:

    先替自己腦中「理想的 AI 開發小組」命名 3 個角色:

    • Agent A:只負責拆需求和寫 TODO。
    • Agent B:只負責動程式碼和開 PR。
    • Agent C:只負責寫測試和文件。

    到 HeyZoku 裡對應設定,避免「一個 Agent 什麼都做」導致上下文混亂。


    3. 支援的典型開發任務與環境整合

    HeyZoku 主打的是重複、結構明確的開發工作:

    • 修 bug:根據 log / issue / PR diff,幫你定位問題並產生修補程式碼。
    • 重構:整理長函式、去除重複邏輯、抽共用模組。
    • 寫測試:補齊缺失的單元測試、行為測試(例如 Gherkin 格式)。
    • 產生文件:同步更新 README、API docs、註解,避免程式碼改了文件沒跟上。

    典型整合環境包含:

    • Git repo(GitHub / GitLab 等,實際支援要以 HeyZoku 最新說明為準)。
    • 主流語言專案:JavaScript/TypeScript、Python 等常見 web/backend 專案結構。
    • 可能串接現有 Issue tracker 或 CI,把測試結果回報到同一工作台。

    可以馬上做的事:

    1. 選一個已存在的 Git 專案,而不是從零開始(AI agent 在既有結構裡表現更好)。
    2. 清點這個專案里最耗時的重複工作(例如每次改 API 都要改 3 個地方),列成 HeyZoku 要接手的任務清單。

    適合誰用:三種典型場景

    1. 個人開發者:把「重複體力活」外包給 AI 小組

    如果你是一人公司或自由接案者,通常會遇到:

    • 改一個小需求,要自己寫程式、寫測試、寫文件。
    • 常常在不同 repo、不同專案間切換。

    HeyZoku 的用法是:你用語音定義「什麼算完成」,讓多個 Agent 幫你跑完全部流程。

    例子:

    「新增一個 POST /users/{id}/deactivate API,要求:
    – 有基本驗證錯誤處理
    – 有單元測試覆蓋成功和失敗情境
    – 更新 API 文件與 README 的使用範例」

    可以馬上做的事:

    • 選出一個你最不想自己動手寫、但規則很清楚的小功能,交給 HeyZoku 測試整個「需求到測試與文件」的流程。

    2. 團隊:試驗「AI Pair Programming 小組」

    在團隊裡,HeyZoku 更適合作為實驗用工作台:

    • 一名人類開發者 + 一組 AI Agent 當「虛擬 Pair」,幫忙拆需求、做初版 commit。
    • Team lead 定義測試、品質規則,像 Uncle Bob 提到的那樣用約束而不是肉眼審每一行 AI code。

    可以借鏡這則 Hacker News 提到的思路:

    「我不再閱讀 agents 寫的程式碼,而是用單元測試、gherkin 測試、品質指標、變異測試等手段,確保它們產出的程式碼有足夠信心。」
    原文:https://news.ycombinator.com/item?id=49074693

    對 HeyZoku 來說,就是:

    • 團隊只審查測試與約束是否夠嚴。
    • AI Agent 在這些約束下自動跑修 bug、重構、文件更新。

    可以馬上做的事:

    1. 選一個「技術債清理」類的支線專案,讓 HeyZoku 的 Agent 專門負責重構與測試補齊。
    2. 團隊只在 PR 層面把關:測試是否完備、CI 是否全綠。

    3. 行動情境:走動中、會議中也能操作 AI 開發

    HeyZoku 的語音介面特別適合這幾種情境:

    • 走路通勤時:想到一個改動,就直接用手機說明任務。
    • 會議中:討論到某個需求,當場用語音下指令,讓 Agent 回頭補程式碼與測試草稿。

    例子:需求討論到一半,你說:

    「為現在的訂單系統新增一個『預約取消』功能,先給我:
    – 用例清單
    – 影響到的服務清單
    – 這一版不改 DB 結構,只改 service 層。」

    分析 Agent 會先幫你產出拆解與影響範圍,等你回到桌前再決定要不要進一步讓實作 Agent 開始寫程式。

    可以馬上做的事:

    • 想像自己在走路或開會時,最希望 AI 幫忙做的「前期準備」是什麼(用例整理、影響分析、TODO 列表),先在 HeyZoku 裡讓分析 Agent專門負責這一塊。

    怎麼開始:從註冊到第一次語音下指令

    Step 0:準備好要讓 Agent 動手的環境

    在開啟 HeyZoku 前,先準備:

    • 至少一個 Git repo(本機或雲端皆可,但要能被 HeyZoku 讀取)。
    • 清楚的專案結構(src/, tests/, docs/ 等最好有基本規範)。
    • 一份簡短專案說明(README 或架構圖),讓分析 Agent 有上下文可用。

    可以馬上做的事:

    • 為你的主力專案補一份「給 AI 看的 README」,寫清楚:主要模組、測試位置、常見指令(啟動、測試、lint)。

    Step 1:註冊並登入 HeyZoku

    1. 前往 Product Hunt 頁面:https://www.producthunt.com/products/heyzoku,找到官方連結進入 HeyZoku。
    2. 使用 GitHub 或 Email 進行註冊(實際支援方式以當前版本為準)。
    3. 登入後,尋找「Workspace」或「Project」入口,建立第一個工作區。

    可以馬上做的事:

    • 為第一個 Workspace 命名成你常用的專案名,例如 backend-orders-service,方便之後語音指令直接說專案名。

    Step 2:連接你的程式碼庫與設定 Agent 分工

    1. 在 HeyZoku 介面中,連接你的 Git repo(授權讀寫必要權限)。
    2. 建立 3–4 個核心 Agent:
    3. PlannerAgent:拆需求、寫 TODO。
    4. CoderAgent:寫實作程式碼。
    5. TestAgent:補測試、追求覆蓋率。
    6. DocAgent:更新文件與註解。
    7. 對每個 Agent 提供對應的說明與範圍(例如 TestAgent 只能動 tests/ 資料夾)。

    可以馬上做的事:

    • 在每個 Agent 的說明中寫明「不該動的區域」,例如:DocAgent 不得修改 production config。

    Step 3:設計適合語音的指令格式

    語音指令的關鍵在於結構清楚,建議用固定模板:

    「在【專案/資料夾】裡,對【檔案/功能】,做【動作類型】,限制是【條件】。」

    幾個範例:

    • 「在 backend-orders-service 裡,針對 OrderController,重構取消訂單的邏輯,限制是不要改資料庫 schema。」
    • 「在 user-service 專案,對 auth 模組新增登入失敗重試次數的設定,限制是要有對應單元測試與 README 更新。」

    可以馬上做的事:

    • 把這個模板貼在自己螢幕前,實際對著麥克風練習 3 種不同任務的語音說法,觀察 HeyZoku 的理解情況。

    Step 4:示範一個「小型自動化開發流水線」的 playbook

    我們來示範一條簡單流水線:

    角色配置:

    • Agent 1(需求拆解):PlannerAgent
    • Agent 2(寫程式):CoderAgent
    • Agent 3(測試與文件):TestDocAgent(合併角色)

    語音指令:

    「在 backend-orders-service 專案,新增『延遲出貨』功能:
    – PlannerAgent:先拆成具體子任務與影響模組列表
    – CoderAgent:實作出貨延遲的核心邏輯與 API
    – TestDocAgent:補上對應的單元測試與 README 使用範例
    限制是:不改既有資料庫表,只在 service 層實作。」

    預期流程:

    1. PlannerAgent 產出:子任務列表 + 影響範圍(哪些 service / controller / config)。
    2. 你確認列表沒有遺漏,再允許 CoderAgent 開始 commit。
    3. TestDocAgent 根據 diff 補測試與文件。
    4. HeyZoku 工作台顯示整個流水線的狀態,最後產出一個 PR 或變更集供你審查。

    可以馬上做的事:

    • 找一個「現有功能的小改版」來跑這條流水線,例如:新增一種訂單狀態、調整折扣計算方式,用來驗證 HeyZoku 是否能完整跑完分析→實作→測試→文件的鏈。

    跟其他多 Agent 工具的差異

    如果你已經聽過 LangChain、Composer 等多 Agent 工具,可以用下表快速對比定位:

    名稱 核心功能 免費方案 適合誰
    HeyZoku 語音指令協調多個程式碼 Agent 依官方方案,偏雲端服務 想用說話操作 AI 的開發者
    LangChain Python/JS 中組合 LLM + Tool + Agent 開源框架,免費使用 要自行搭建 Agent 系統的工程師
    Composer v3 多模型、多元件工作流程編排 社群工具,視專案而定 研究者與實驗多模型整合的人

    💡 關鍵: 若你不想寫多 Agent 框架程式碼,HeyZoku 提供即開即用的語音工作台,是從「想試試多 Agent 能做什麼」到「再用 LangChain/Composer 客製」之間的理想起點。

    可以馬上做的事:

    • 如果你不想自己寫 Agent 框架程式碼,只想快點看「多 Agent 實際能幫我做什麼」,先從 HeyZoku 開始;之後再視需要用 LangChain 或 Composer 做客製化延伸。

    🚀 你現在可以做的事

    • 前往 HeyZoku Product Hunt 頁面,註冊並建立第一個 backend-orders-service 工作區
    • 選一個既有 Git 專案,連接 Repo 並設定 PlannerAgent、CoderAgent、TestAgent、DocAgent 的職責與禁止修改區域
    • 實際用語音下達一個「小改版需求」,跑完分析→實作→測試→文件的完整流水線,觀察多 Agent 協作效果
  • 用狀態機把 13GB 小模型變成工程實習生

    用狀態機把 13GB 小模型變成工程實習生

    📌 本文重點

    • 小模型別當全能 Agent,要當被流程管控的小工
    • 用顯式狀態機拆任務,大幅提升穩定性與可回滾性
    • 每步輸出 JSON + schema 驗證,讓小模型也能穩定改碼

    只靠 prompt 堆疊,13GB 本地模型在中大型改碼任務幾乎必翻車:上下文飄掉、一次回錯一堆檔、改到一半忘記需求。把模型包進顯式狀態機,把「一次大任務」拆成可恢復的子任務,可以在不改模型的前提下,大幅提升穩定性、可觀測性與可測試性——正是那篇 13.8GB 模型從 2/10 變成 10/10 的核心做法。

    💡 關鍵: 只改調用方式與流程設計,就能把同一顆 13.8GB 小模型的表現從 2/10 拉到 10/10。


    重點說明

    1. 小模型為什麼在長對話裡特別容易翻車?

    從工程視角,有三個根本原因:

    1. token 預算太小 + 資訊密度太高
      13GB 級(多是 7B〜13B 參數)在 4k–16k context 內要同時塞:需求、專案結構、幾個檔案內容、測試結果、對話歷史,關鍵訊息會被截斷或壓縮到模型抓不到。

    2. 上下文漂移(context drift)
      多輪長對話時,你不可能每次都重貼完整需求與檔案。模型只能靠「語意回憶」之前說過什麼,多輪後任務邊界就開始模糊:忘記原本的 constraint、改到不該動的檔案、把舊 bug 當新需求。

    3. 一次性決策成本過高
      傳統「一條大 prompt + chain-of-thought」會在單輪裡要求:理解需求 → 找檔 → 設計改動 → 寫碼 → 自我檢查。這在 token 限制與小模型推理能力下,極易在中間任一步 hallucinate,之後又沒有明確的 rollback 機制。

    關鍵結論: 小模型不適合當「一次性全能 Agent」,更適合當「被嚴格流程控制的小工」,讓狀態機負責 long-term 記憶與決策邊界。

    💡 關鍵: 把 long-term 記憶與流程決策交給狀態機,小模型只做局部推理,能顯著降低翻車率。


    2. 用顯式狀態機拆解大任務:核心設計

    把「改造一個中小型專案」拆成明確的 State + Transition:

    常見狀態設計可以是:

    1. DISCOVER_PROJECT:掃描 repo、建立檔案索引
    2. PLAN_CHANGE:根據需求與索引產生修改計畫(檔案清單、步驟)
    3. EDIT_FILE:逐檔案修改(step-by-step)
    4. RUN_TESTS:執行測試、收集結果
    5. ROLLBACK_OR_FIX:測試失敗→嘗試修復或回滾
    6. DONE / FAILED:終止狀態

    每個狀態都只給模型 極簡上下文 + 明確輸入/輸出 schema,例如在 EDIT_FILE:

    • 輸入:
    • 需求摘要(短)
    • 該檔案目前內容(或片段)
    • 計畫中對此檔案的變更描述
    • 輸出:
    • 結構化 JSON:{"status": "ok|skip|abort", "patch": "...diff..."}

    轉移條件示例:

    • DISCOVER_PROJECT → PLAN_CHANGE:索引成功建立
    • PLAN_CHANGE → EDIT_FILE:生成的計畫通過 schema 檢查
    • EDIT_FILE → RUN_TESTS:所有目標檔案處理完
    • RUN_TESTS →
    • 全綠 → DONE
    • 有失敗 + 可定位 → EDIT_FILE (targeted fix)
    • 多次失敗 → ROLLBACK_OR_FIX

    失敗重試策略與超時機制:

    • 每個狀態設定 max_retries,例如 2–3 次,超過則標記為 FAILED 或轉 ROLLBACK_OR_FIX。
    • 每次 LLM 回應必經:
    • JSON schema 驗證
    • domain guard(例如禁止刪除大量無關 code)
    • 超時機制:
    • 單次呼叫 timeout(例如 60s),保障工作流不被卡死
    • 整個工作流 wall-clock timeout(例如 30 分鐘),方便在 CI 或自動化工具中運行

    💡 關鍵: 把重試、超時、回滾寫死在狀態機邏輯裡,比指望 prompt 提醒模型「要小心」可靠太多。


    3. 實作範例:13GB 本地模型改造專案(Python)

    以下是精簡版 pseudo-code,示範如何把本地模型包在狀態機裡,跑 step-by-step 編碼、測試與回滾。假設:

    • 使用 vLLM / llama.cpp server 暴露出 OpenAI-compatible API
    • GPU:3060 12GB,模型用 Q4 / Q5 量化
    import enum
    import json
    import subprocess
    from dataclasses import dataclass
    from typing import Dict, Any, List
    import requests
    
    OPENAI_BASE = "http://localhost:8000/v1"
    MODEL_NAME = "local-13b-q4"
    
    class State(enum.Enum):
        DISCOVER_PROJECT = "DISCOVER_PROJECT"
        PLAN_CHANGE = "PLAN_CHANGE"
        EDIT_FILE = "EDIT_FILE"
        RUN_TESTS = "RUN_TESTS"
        ROLLBACK_OR_FIX = "ROLLBACK_OR_FIX"
        DONE = "DONE"
        FAILED = "FAILED"
    
    @dataclass
    class Context:
        repo_path: str
        requirement: str
        file_index: Dict[str, Any] = None
        plan: List[Dict[str, Any]] = None
        current_file_idx: int = 0
        test_result: str = ""
    
    
    def call_llm(system_prompt: str, user_prompt: str, max_tokens: int = 1024) -> str:
        resp = requests.post(
            f"{OPENAI_BASE}/chat/completions",
            json={
                "model": MODEL_NAME,
                "messages": [
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt},
                ],
                "temperature": 0.2,
                "max_tokens": max_tokens,
            },
            timeout=60,
        )
        resp.raise_for_status()
        return resp.json()["choices"][0]["message"]["content"]
    
    
    def discover_project(ctx: Context) -> Context:
        # 這裡可以用 ripgrep / fd 產生檔案清單,略
        ctx.file_index = {"files": ["src/a.py", "src/b.py"], "tests": ["tests/test_a.py"]}
        return ctx
    
    
    def plan_change(ctx: Context) -> Context:
        system = """你是資深工程師,輸出 JSON,字段: steps: [{file, description}]。"""
        user = f"需求: {ctx.requirement}\n可修改檔案: {ctx.file_index['files']}\n請產生最多 10 個步驟。"
        raw = call_llm(system, user)
        try:
            plan = json.loads(raw)
        except Exception:
            raise ValueError("PLAN_CHANGE: model output not JSON")
        ctx.plan = plan["steps"]
        ctx.current_file_idx = 0
        return ctx
    
    
    def apply_patch(repo_path: str, file: str, patch: str):
        # 建議用 unified diff + `patch` 指令,這裡簡化處理
        with open(f"{repo_path}/{file}", "w", encoding="utf-8") as f:
            f.write(patch)
    
    
    def edit_file(ctx: Context) -> Context:
        step = ctx.plan[ctx.current_file_idx]
        file_path = step["file"]
        with open(f"{ctx.repo_path}/{file_path}", encoding="utf-8") as f:
            content = f.read()
    
        system = """你只負責修改單一檔案。輸出 JSON: {status, patch}。
        - status: ok | skip | abort
        - patch: 完整檔案內容,不要解釋文字。"""
    
        user = f"需求: {ctx.requirement}\n此步驟: {step['description']}\n原始內容:\n{content[:4000]}"
        raw = call_llm(system, user, max_tokens=2048)
        try:
            out = json.loads(raw)
        except Exception:
            raise ValueError("EDIT_FILE: invalid JSON")
    
        if out["status"] == "ok":
            apply_patch(ctx.repo_path, file_path, out["patch"])
        elif out["status"] == "abort":
            raise RuntimeError("Model aborted edit")
    
        ctx.current_file_idx += 1
        return ctx
    
    
    def run_tests(ctx: Context) -> Context:
        proc = subprocess.run(["pytest"], cwd=ctx.repo_path, capture_output=True, text=True)
        ctx.test_result = proc.stdout + "\n" + proc.stderr
        return ctx
    
    
    def rollback_or_fix(ctx: Context) -> Context:
        # 真實情況應該搭配 git: reset --hard HEAD~1 或建立 branch
        # 這裡示意:交給模型看測試輸出,決定要修哪個檔案
        system = "請從測試輸出中找出最可能需要修改的單一檔案,輸出 JSON: {file, reason}"
        user = ctx.test_result[:4000]
        raw = call_llm(system, user)
        try:
            out = json.loads(raw)
        except Exception:
            raise ValueError("ROLLBACK_OR_FIX: invalid JSON")
    
        # 根據 out['file'] 重新插入 plan
        ctx.plan.insert(ctx.current_file_idx, {"file": out["file"], "description": out["reason"]})
        return ctx
    
    
    def run_state_machine(ctx: Context):
        state = State.DISCOVER_PROJECT
        retries: Dict[State, int] = {s: 0 for s in State}
        MAX_RETRIES = 2
    
        while True:
            try:
                if state == State.DISCOVER_PROJECT:
                    ctx = discover_project(ctx)
                    state = State.PLAN_CHANGE
    
                elif state == State.PLAN_CHANGE:
                    ctx = plan_change(ctx)
                    state = State.EDIT_FILE
    
                elif state == State.EDIT_FILE:
                    if ctx.current_file_idx >= len(ctx.plan):
                        state = State.RUN_TESTS
                    else:
                        ctx = edit_file(ctx)
    
                elif state == State.RUN_TESTS:
                    ctx = run_tests(ctx)
                    if "failed" in ctx.test_result:
                        state = State.ROLLBACK_OR_FIX
                    else:
                        state = State.DONE
    
                elif state == State.ROLLBACK_OR_FIX:
                    ctx = rollback_or_fix(ctx)
                    state = State.EDIT_FILE
    
                elif state in (State.DONE, State.FAILED):
                    return state, ctx
    
            except Exception as e:
                print(f"State {state} error: {e}")
                retries[state] += 1
                if retries[state] > MAX_RETRIES:
                    return State.FAILED, ctx
    
    
    if __name__ == "__main__":
        ctx = Context(repo_path="/path/to/repo", requirement="把 API v1 換成 v2 並修正測試")
        final_state, final_ctx = run_state_machine(ctx)
        print("Final state:", final_state)
    

    重點:

    • 模型只做 局部、可回滾的決策(例如一次只改一檔)。
    • 工作流邏輯(狀態、重試、回滾)都在 可測試的 Python 函式 中,而不是藏在 prompt 裡。

    若用 TypeScript + LangGraph / 自行寫狀態機,模式相同:每個 Node 是一個狀態,Edge 由測試結果與 JSON 輸出決定。


    4. 與「prompt + chain-of-thought」相比的實際好處

    1. 穩定性
    2. CoT 依賴模型「自己監督自己」,小模型的推理錯誤會被往後 propagate,沒有硬性 checkpoint。
    3. 狀態機把流程切成多個 可檢查的邏輯節點,每步都能強制過 schema、判斷失敗與回滾。

    4. 成本與資源

    5. 單輪 prompt 巨大 → token 費用高,且在本地 GPU 上速度慢。
    6. 狀態機讓每輪上下文更短、更聚焦,在 3060 12GB + Q4 模型上可以穩定跑 多輪短對話,總延遲往往比一輪巨 prompt 更好控制。

    7. 觀測性(logging / trace)

    8. 把每個狀態轉移、LLM input/output、git diff 全記錄(例如存到 SQLite / OpenTelemetry trace),可以:

      • 後覽失敗案例
      • 做離線分析:哪個狀態最常出錯?哪種需求最難?
    9. 可測試性

    10. 傳統做法難以單元測試 Agent:prompt 無法 deterministic。
    11. 狀態機可以用 fake LLM 或 replay 真實輸出,對每個 state handler 寫 unit test,例如:當測試結果是某種錯誤訊息時,ROLLBACK_OR_FIX 應插入哪個 plan。

    建議與注意事項

    1. 避免狀態爆炸

    • 限制狀態數量在 5–10 個,複雜度放在狀態內部的子函式,而不是新增一堆細碎狀態。
    • 優先建立 通用狀態模板:PLAN / EXECUTE / VERIFY / RECOVER 四類,大部分工程任務都能套這個骨架。

    2. 處理 hallucination 與非法狀態

    • 所有 LLM 輸出一律要求 JSON + schema 驗證,非法就走重試邏輯。
    • 在 EDIT_FILE 等關鍵步驟設計 domain guard:
    • 檢查 patch 是否刪除超過 X% 行數;
    • 檢查是否涉及黑名單檔案(例如 config、CI YAML)。

    3. 設計「保守模式」避免改壞檔案

    建議預設開啟:

    1. 所有改動先走分支 / 工作目錄拷貝
    2. 狀態機只在 temp branch/dir 上動手,最後才由人類 review + merge。

    3. 只允許白名單檔案被修改

    4. 在 PLAN_CHANGE 事先產出可修改清單,EDIT_FILE 收到不在清單內的檔案時直接拒絕。

    5. 必備 diff 檢查

    6. 每次改檔後,log 一份 git diff。
    7. 可以加一個 HUMAN_APPROVAL 狀態,在 CI 或 IDE 裡讓人按「Approve」才繼續。

    4. 3060 12GB 本地 GPU 的實務建議

    • 模型:選 Q4_K_M / Q5 量化的 7B–13B 開源模型(如 Llama 系家族、Qwen 等),在 Agentic coding 任務上實測延遲可接受。
    • 推理引擎:
    • llama.cpp / ollama:部署簡單,適合單機開發。
    • vLLM:若你需要高併發與更細緻的 batching,可考慮,但對記憶體稍敏感。
    • 參數建議:
    • max_tokens 控制在 512–2048,依狀態不同調整。
    • temperature 低於 0.3,減少 hallucination。
    • 避免在單輪塞完整檔案,改成 片段 + 明確上下文(例如「你只看這個 function」)。

    5. 映射到現有 Agent framework 的模式

    這套思路可以直接映射到:

    • LangChain / LangGraph:
    • 每個狀態 = 一個 Node(通常是 Tool + LLM)。
    • 轉移條件透過 conditional edges 判斷 JSON output 中的 status / next_state。
    • 用 LangGraph 的 checkpointing 把 Context 存到外部 store,可做恢復與可視化。

    • LangMCP(可檢視狀態的 Agent framework)

    • 把 Context 中的 file_index / plan / test_result 全部納入 inspectable state。
    • 除錯時可以直接在 UI 裡看到「Agent 在哪一步做錯決策」,而不是只看 tokens trace。

    • Claude Code / goal-workflow 類工具

    • 把這裡的狀態機當作後端 orchestrator,前端 IDE 只負責:設定 goal → 顯示 plan → 顯示每步 diff / 測試 → 提供人類 approve。

    總結工程模式:

    LLM 做局部推理 + 生成,狀態機做長期決策 + 記憶 + 恢復。
    把「智慧」從模型本體,搬到你可控、可測、可觀測的工作流程式碼中,13GB 小模型也能在工程任務裡穩定交付 10/10 的結果。


    🚀 你現在可以做的事

    • 在本地架一個 llama.cpp 或 vLLM 的 OpenAI-compatible 服務,載入一顆 7B–13B Q4/Q5 模型試跑上文的狀態機範例
    • 把你現有的「一條大 prompt 改碼流程」改寫成 5–10 個明確狀態,並為每步定義 JSON schema 與 max_retries
    • 在 CI 或開發機中為這套狀態機加上 logging / trace(例如 SQLite 或 OpenTelemetry),實際分析哪個 state 最常出錯
  • Hippo 記憶:讓本機 AI Agent 真正記住你

    📌 本文重點

    • Hippo 讓 AI Agent 具備結構化長期記憶
    • 不只用向量搜索,而是多維度記憶召回
    • 可直接接入 LangChain / LangGraph 現有架構

    如果你受夠了每次開新對話 AI 就失憶,Hippo 是一個專門幫 AI Agent 建長期記憶結構 的開源工具,讓模型可以跨對話記住你、記住專案、記住世界觀。

    專案連結:https://github.com/kitfunso/hippo-memory


    核心功能:它跟「塞進向量資料庫」有什麼不一樣?

    1. 生物啟發的記憶結構:不是只靠 embedding 搜索

    一般「加記憶」做法:

    • 把所有對話 / 文件轉成向量,丟進向量資料庫
    • 查詢時用 embedding 相似度召回

    這樣會遇到:

    • 記憶越多越慢
    • 容易抓到語意相似但情境不對的內容
    • 很難做「時間感」與「重要程度」的區分

    Hippo 的做法更像大腦海馬迴:

    • 把記憶拆成多種型態(事件、事實、偏好、任務等)
    • 每種記憶有自己的欄位:時間戳、重要性、來源、關聯 id
    • 召回時不只看相似度,還會考慮:
    • 時間距離(最近 / 很久以前但被多次提及)
    • 重要性(例如「明天要開會」權重更高)
    • 記憶種類(對話中問的是偏好,就優先找偏好類)

    💡 關鍵: Hippo 透過「記憶類型 + 時間 + 重要性」多維度過濾與排序,比單靠 embedding 更接近人類大腦的記憶方式。

    你可以採取的行動:

    • 在設計 Agent 時,把「要記住什麼」先分類(例:使用者設定、長期專案狀態、一次性任務)
    • 對應到 Hippo 的不同記憶類型,而不是全部塞進單一向量庫

    2. 高效寫入 / 召回:不是每一句話都存

    Hippo 會讓 LLM 幫忙「過濾」哪些內容要進長期記憶:

    • 每輪對話結束時,呼叫一次 LLM:
    • 請它輸出:要存什麼?是什麼類型?重要性幾分?
    • 只有被標註為「值得記住」的內容才寫入記憶庫

    召回時則是:

    • 先根據 當前任務類型 過濾可疑記憶(例如問「專案」時,優先專案類)
    • 再用 embedding + 時間 + 重要性加權排序

    效果:

    • 記憶庫不會無限膨脹
    • 查詢時間穩定,對話變長也不會暴漲

    💡 關鍵: 透過 LLM 篩選與加權排序,Hippo 在長期對話下仍能維持穩定的查詢速度與記憶品質。

    你可以採取的行動:

    • 在對話 loop 中加入一個 summarize_and_store_memory() 步驟
    • 規劃一份提示詞,教 LLM:什麼東西需要記住(例如使用者偏好、長期任務)

    3. 可以直接當「記憶模組」接到 LangChain / LangGraph

    Hippo 把核心封裝成一個獨立記憶層:

    • 暴露簡單的 API:add_memory(), retrieve_memories(), summarize_dialogue()
    • 你可以把它包成一個自訂的 LangChain Memory 類或 LangGraph node

    這代表你不必改整個 Agent 架構,只要:

    • 把原本「向量記憶」的那一層換成 Hippo
    • Prompt 裡多餵一段:{relevant_memories}

    你可以採取的行動:

    • 先用 Hippo 做一個獨立小腳本,確定記憶寫入 / 召回正常
    • 再把它接進現有的 LangChain / LangGraph 專案

    適合誰用?三種具體場景

    1. 客服 / 助理型 Agent:跨對話記住使用者

    問題:

    • 今天跟你聊過喜好,明天又得重講一次

    用 Hippo 可以:

    • 把「使用者個人偏好」「公司規則」「歷史問題」分成不同記憶類型
    • 在每次新對話開始前,先用使用者 id 去 retrieve_memories()

    具體做法:

    • 每個 user 綁一個 user_memory_store
    • 首輪對話後,寫入:
    • 常用產品 / 方案
    • 語氣偏好(喜歡簡短回答、或喜歡詳細教學)
    • 下次對話開頭,在系統 prompt 裡加:
    • 你已知這個使用者的偏好:...

    2. 工具型 Agent:追蹤專案長期狀態

    像是:

    • 本機程式碼助手
    • 專案管理 / 自動化腳本編排 Agent

    用 Hippo 可以把:

    • 已完成的任務
    • 未完成的 TODO
    • 決策理由(為什麼選 A 而不是 B)

    長期存起來,讓 Agent 未來做決策時可以「回頭想」,而不是只看當前 prompt。

    具體做法:

    • 每次工具成功執行後,寫入:操作內容、輸入、輸出、錯誤紀錄
    • 下次 Agent 在決定要不要呼叫同個工具時,先查過去的失敗 / 成功案例

    💡 關鍵: 對工具型 Agent 來說,持續累積「決策與結果」記憶,可以大幅降低重複犯錯與無效操作。

    3. 遊戲 / 模擬:角色人格與世界觀持久化

    如果你在做:

    • 文字冒險遊戲中的 NPC
    • 模擬城市 / 社會裡的多 Agent 系統

    Hippo 可以:

    • 把 NPC 的「人生事件」「人際關係」「信念」拆成不同記憶
    • 讓同一個角色在不同局遊戲中保有成長、偏好

    具體做法:

    • 每次玩家與 NPC 互動後,將關鍵事件寫入該 NPC 的記憶庫
    • 下次載入遊戲時,先載入角色記憶,再生成回應

    怎麼開始:本機跑一個有長期記憶的 Agent

    下面示範:在本機用一個輕量開源 LLM + Hippo 跑出一個最小可用的長期記憶 Agent。

    1. 安裝依賴(Python)

    假設你有 Python 3.10+,先開一個虛擬環境:

    python -m venv venv
    source venv/bin/activate  # Windows 用 venv\Scripts\activate
    
    pip install hippo-memory
    pip install transformers accelerate sentence-transformers
    

    如果你想用 Ollama / 本機模型,也可以只要 HTTP API,把 llm_call() 改成呼叫本地端服務即可。

    2. 建一個最小記憶介面

    概念流程:

    1. 使用者說話
    2. Agent 回應(用 LLM)
    3. 用 LLM 檢查這輪對話有沒有值得記住的東西
    4. 需要記住的就丟給 Hippo
    5. 下一輪對話前,從 Hippo 取出相關記憶加進 prompt

    簡化版範例(偽代碼結構):

    from hippo_memory import HippoMemory
    
    memory = HippoMemory()
    
    def llm_call(prompt: str) -> str:
        # TODO: 換成你實際使用的本機 LLM(Ollama / vLLM / transformers)
        ...
    
    
    def summarize_for_memory(dialogue: str) -> list[dict]:
        """讓 LLM 決定要存哪些記憶,回傳 list[{"type":..., "content":..., "importance":...}]"""
        prompt = f"""
    你是記憶管理員。以下是剛剛的一段對話:
    {dialogue}
    
    請列出值得長期記住的事情(使用者偏好、長期目標、重要事件)。
    用 JSON 陣列輸出,每個元素包含 type, content, importance(1-5)。
    如果沒有,回傳 []。
    """
        import json
        res = llm_call(prompt)
        return json.loads(res)
    
    
    def add_dialogue_to_memory(history: list[tuple[str, str]]):
        # history = [(user_msg, assistant_msg), ...]
        text = "\n".join([f"User: {u}\nAssistant: {a}" for u, a in history[-3:]])
        items = summarize_for_memory(text)
        for item in items:
            memory.add_memory(
                mem_type=item["type"],
                content=item["content"],
                importance=item.get("importance", 3),
            )
    
    
    def chat_loop():
        history = []
        while True:
            user = input("你:")
            if user.strip().lower() in {"exit", "quit"}:
                break
    
            # 1) 先查記憶
            relevant = memory.retrieve_memories(query=user, top_k=5)
            memory_text = "\n".join([f"- {m.content}" for m in relevant])
    
            # 2) 組 prompt
            prompt = f"""你是一個會記住使用者的助理。
    以下是你對這位使用者的已知記憶:
    {memory_text or '(目前沒有特別記憶)'}
    
    現在的對話:
    使用者:{user}
    請根據記憶與對話回應。
    """
            assistant = llm_call(prompt)
            print("Agent:", assistant)
    
            history.append((user, assistant))
            add_dialogue_to_memory(history)
    
    
    if __name__ == "__main__":
        chat_loop()
    

    你可以採取的行動:

    • 把上面程式框架複製到自己的專案
    • 將 llm_call() 替換成你現用的 LLM(如 Ollama 的 /api/chat)
    • 用幾輪「自我介紹 → 問之前說過什麼」來測試記憶效果

    3. 接到 LangChain / LangGraph 的 memory 模組

    如果你已經在用這些框架,可以這樣思考:

    • LangChain:
    • 寫一個 HippoMemoryWrapper,實作 load_memory_variables + save_context
    • 內部直接呼叫 HippoMemory.add_memory() / retrieve_memories()
    • LangGraph:
    • 把 Hippo 抽成一個 node:「更新記憶」與「擷取記憶」
    • 在主對話 graph 中,讓每輪都經過這兩個 node

    這樣你不用推翻原有設計,只是把記憶 backend 換成更聰明的 Hippo。


    小結:什麼時候值得上 Hippo?

    你可以用一個簡單判斷:

    • 只做「單次任務、一次對話就結束」的 Agent → 一般向量資料庫就夠
    • 需要「跨天、跨專案、角色長期成長」的 Agent → 值得導入 Hippo 記憶

    先從一個很小的場景開始:例如讓你的本機助理 記住你的個人偏好。當你發現它真的記得你說過的事,再把同樣的記憶結構,複製到客服、專案管理或遊戲 NPC 上。

    專案原始碼與設計細節: https://github.com/kitfunso/hippo-memory

    🚀 你現在可以做的事

    • 打開專案頁面,閱讀 Hippo 的 README 與範例程式碼:https://github.com/kitfunso/hippo-memory
    • 在本機建立一個小腳本,實作文中 llm_call() 並跑起示範的記憶型 Agent
    • 將 Hippo 接入你現有的 LangChain / LangGraph 專案,先讓一個特定 user 或 NPC 擁有長期記憶