iT邦幫忙

2025 iThome 鐵人賽

DAY 12
0
自我挑戰組

從讀書筆記到可落地 AI:LangChain、LangSmith 與 Agent 工具 30 講系列 第 12

Day 12|Agent Design - Tools - LangChain tool技巧(1/3)

  • 分享至 

  • xImage
  •  

目標先講清楚
這篇整理我在 LangChain 里「工具(tool)」的 context 管理與防混淆做法:

  1. 結構化的工具描述 提升可讀性與可維護性;
  2. InjectedToolCallId 避免並行呼叫結果對不上;
  3. InjectedState 隱藏不必要參數,讓 LLM 看到「剛好就好」的輸入。

為什麼是「工具的 context 管理」?

Prompt 設計好了,接下來就是 agent 與 agent、agent 與工具 的溝通。不同套件都有自己的工具機制;以 LangChain 為例,我們用 @tool 把 Python function 轉成 LLM 可呼叫的工具。
痛點常見在兩處:

  1. 工具說明不清楚 → LLM 亂用、濫用或誤用工具。
  2. 多工具並行上下文太肥 → 回傳結果對不到、參數冗長、模型分不清誰是誰。

以下做法可顯著降低上述風險。


① 用「可執行的說明」驅動工具:descriptionparse_docstring=True

先把「何時用、怎麼用、注意事項」寫成 機器與人都能看懂 的說明,然後直接掛到 @tool 上。這能把 tool 規範system prompt 分離,讓 system prompt 更乾淨、可讀可維護。

範例說明(可直接複用)

# WRITE_TODOS_DESCRIPTION

## When to Use
Multi-step or non-trivial tasks requiring coordination
When user provides multiple tasks or explicitly requests todo list
Avoid for single, trivial actions

## Structure
Maintain one list containing multiple todo objects (content, status, id)
Use clear, actionable content descriptions
Status must be: pending, in_progress, or completed

## Best Practices
Only one in_progress task at a time
Mark completed immediately when task is fully done
Always send the full updated list when making changes
Prune irrelevant items to keep list focused

## Progress Updates
Call TodoWrite again to change task status or edit content
Reflect real-time progress; don't batch completions
If blocked, keep in_progress and add new task describing blocker

## Parameters
todos: List of TODO items with content and status fields

## Returns
Updates agent state with new todo list.

掛上工具

from langchain_core.tools import tool

WRITE_TODOS_DESCRIPTION = open("WRITE_TODOS_DESCRIPTION.md").read()

@tool(description=WRITE_TODOS_DESCRIPTION, parse_docstring=True)
def write_todos(todos: list[dict]) -> list[dict]:
    """Write or update a TODO list following the spec."""
    # ... 你的邏輯(新增、更新、清理) ...
    return todos

好處:

  • 工具規格文件 工具的 description,避免兩份文件走散。
  • system prompt 不再塞滿「工具使用規範」,清楚切分責任

② 避免並行混淆:用 InjectedToolCallId 精準對應呼叫與結果

多工具平行執行時,LLM 很容易把回應搞混。LangChain / LangGraph 提供 InjectedToolCallId

  • 由執行框架為每次工具呼叫 注入唯一 tool_call_id
  • 工具回傳時 原樣帶回tool_call_id
  • 編排器用它 對齊呼叫與回應,更新正確的 state/訊息串。

流程

1) LLM 輸出兩個呼叫:A1=write_todos(...)、B1=read_file(...)
2) 編排器平行執行兩工具,並把 "A1"/"B1" 注入到工具參數
3) 工具回傳時:ToolMessage(..., tool_call_id="A1") / ToolMessage(..., tool_call_id="B1")
4) 編排器據此把結果對回 A1/B1,避免交叉污染

重點:ID由系統注入,LLM只專注在調用工具的參數產生
這樣在高並發、多步驟時,結果永遠能對回正確的那一次呼叫。


③ 讓參數更純粹:用 InjectedState 隱藏肥大的 state

工具只該暴露 與任務直接相關 的參數。把整包 state 丟給模型,既洩漏不必要資訊,也增加「幻覺」與誤用風險。

沒用 InjectedState 的樣子(不建議):

@tool
def write_todos(text: str, state: dict) -> list[str]:
    ...
# LLM 會看到:
# {"name":"write_todos","arguments":{"text":"買牛奶","state":{...超大 state...}}}

用了 InjectedState(建議):

from typing_extensions import Annotated
from langchain_core.tools import tool
from langgraph.prebuilt import InjectedState  # 依版本命名可能不同

@tool
def write_todos(
    text: str,
    state: Annotated[dict, InjectedState()]
) -> list[str]:
    ...
# LLM 只會看到:
# {"name":"write_todos","arguments":{"text":"買牛奶"}}
# 真正的 state 由執行框架在底層注入,工具端仍可拿到。

這能 縮短提示字串降低洩露風險更容易對齊 schema


接下來要做什麼

當工具太多時,可以怎麼提供llm進行選擇


參考資源

  1. Google-Agentic Design Pattern -CH5
  2. deep-agents-from-scratch

上一篇
Day 11|Agent Design - Prompt Design - LangSmith Prompt管理(5/5)
下一篇
Day 13|Agent Design - Tools - Tools的呼叫控制(2/3)
系列文
從讀書筆記到可落地 AI:LangChain、LangSmith 與 Agent 工具 30 講14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言