iT邦幫忙

2026 iThome 鐵人賽

DAY 9
0

昨天介紹了 Deep Agents 和 harness engineering。

今天再往下一層,看 Agent 裡面真正和 LLM 溝通的 component:ChatModel

不管上層是 Deep Agents、LangGraph,還是其他 Agent framework,最後都還是需要一個 model 來完成 reasoning、tool calling,以及產生 response。

這一篇主要看幾個建立 Agent 時最常用到的能力:

  • model initialization
  • streaming
  • tool calling
  • structured output
  • model configuration
  • prompt caching

What Is ChatModel?

ChatModel 可以先理解成:

Agent
  ↓
ChatModel
  ↓
LLM Provider

LangChain 提供一個比較統一的 interface,讓我們可以用類似的方式呼叫不同 provider 的 models。

例如:

  • OpenAI
  • Anthropic
  • Google
  • Mistral
  • 其他 providers

source

最基本的 usage:

from langchain.chat_models import init_chat_model

model = init_chat_model(
    "anthropic:claude-sonnet-4-6",
    temperature=0,
)

response = model.invoke("What's your name?")

到這裡,它還只是一個普通的 LLM call。

真正讓它適合拿來建立 Agent 的,是下面這些能力。

Streaming

如果沒有 streaming,使用者通常要等 model 完整產生 response 之後,才能看到結果。

有 streaming 之後,可以一邊產生、一邊把 output 傳回去。

例如:

for chunk in model.stream("Analyze this dataset"):
    print(chunk.text, end="")

概念上從:

User
  ↓
wait...
wait...
wait...
  ↓
Complete Response

變成:

User
  ↓
Partial Output
  ↓
More Output
  ↓
Complete Response

這對 Agent 特別重要,因為一個 task 可能包含多個 steps,整體 execution time 會比單純聊天長很多。

Pros

  • 使用者可以更早看到 response
  • long-running task 的體驗比較好
  • 適合 real-time UI

Cons

  • frontend / backend handling 會更複雜
  • partial output 不一定完整
  • streaming 開始之後,error handling 會比較麻煩

Tool Calling

Tool calling 是 Agent system 最重要的能力之一。

如果沒有 tools,model 基本上只能 reasoning 和產生文字。

有了 tools 之後,model 才能真正和外部 system 互動。

例如前幾天做的 datasource tools:

tools = [
    read_file,
    query_database,
]

Model 可以決定:

Need data
   ↓
Call read_file()
   ↓
Receive result
   ↓
Continue reasoning

這樣 Agent 就不只是說:

我應該讀這份資料。

而是真的可以執行:

read_file(...)

Tool design 本身也會直接影響 Agent 的表現。

例如:

  • tool name
  • description
  • input schema
  • return value
  • permission boundary

都會影響 model 是否能正確選擇和使用 tool。

Pros

  • 讓 LLM 可以執行真實 action
  • 可以連接 database、API、filesystem 等外部 system
  • implementation details 可以留在 tool 裡
  • 可以建立 multi-step Agent workflow

Cons

  • tool 太多可能讓 model 更容易選錯
  • tool result 可能快速增加 context size
  • dangerous tools 需要額外 guardrails
  • tool failure 需要額外處理

Structured Output

因為 LLM 的原理是機率模型,如果用一串文字當作決定下一個步驟的 routing 會非常不穩定,所以我們會希望模型輸出一個固定格式讓後續的步驟好執行。

例如:

I think we should ask the user for more information.

對流程來說,更好處理的是:

{
  "action": "clarify",
  "reason": "The metric is ambiguous."
}

這就是 structured output。

我們可以先用 Pydantic 或 TypeDict 來定義 schema,我習慣用 Pydantic 因為可以檢查型別:

from typing import Literal

from pydantic import BaseModel


class AgentDecision(BaseModel):
    action: Literal["continue", "clarify"]
    reason: str

然後要求 model 的 output 符合這個結構。

例如:

User Request
    ↓
LLM
    ↓
Structured Decision
    ↓
┌──────────────┬──────────────┐
│ continue     │ clarify      │
└──────────────┴──────────────┘

這樣 downstream code 不需要再從自然語言中猜 model 到底想做什麼。

但有一點很重要:

structured output 只能保證格式比較 predictable,不代表內容一定正確。

此時剛好有一個新模型 Jev 在這類 classification workload 上表現很驚人:相較於一般 LLM,inference speed 最快可以快到 200 倍,成本則可以低到 1/400,參考:What is Jev, TypeSafe AI's System One model?

Pros

  • output format 比較穩定
  • downstream 比較容易處理
  • 可以做 schema validation
  • 很適合 routing 或 deterministic flow

Cons

  • schema valid 不代表 answer correct
  • schema 太複雜可能增加失敗機率
  • 有時需要 retry
  • 過度限制 output 可能降低 model flexibility

Tool Calling vs Structured Output

這兩個能力很容易混在一起,但用途不太一樣。

可以先簡單理解成:

Tool Calling
→ Model decides to DO something

Structured Output
→ Model returns a structured DECISION

例如:

Need to inspect data
        ↓
Tool Calling
        ↓
read_file()

而:

Enough information?
        ↓
Structured Output
        ↓
continue / clarify

兩個通常會一起出現在 Agent system 裡,但解決的是不同問題。

Model Configuration

除了基本 capabilities 之外,不同 model provider 通常還會提供很多 configuration。

例如:

  • temperature
  • max tokens
  • reasoning settings
  • timeout
  • retry
  • caching

這些設定會直接影響:

  • quality
  • latency
  • cost
  • reliability

這個系列不會把每個 configuration 都講一遍。

目前只挑一個 Agent application 裡很實用的例子:prompt caching

Prompt Caching

Agent 很常會反覆帶著相同的 context 呼叫 model。

例如:

System Prompt
Tool Definitions
Project Instructions
Conversation Context
New User Message

其中很多內容其實每次都一樣。

如果每次都重新處理:

Large Static Context + User Message 1
Large Static Context + User Message 2
Large Static Context + User Message 3

就會增加 token cost 和 latency。

如果 model provider 支援 prompt caching,就可以 reuse 一部分重複 context。

例如 Anthropic:

from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage, SystemMessage

llm = init_chat_model("anthropic:claude-sonnet-4-6")

messages = [
    SystemMessage(
        content=[
            {
                "type": "text",
                "text": "You are a data analysis agent..."
            },
            {
                "cache_control": {
                    "type": "ephemeral"
                }
            }
        ]
    ),
    HumanMessage(
        content="Analyze the first dataset."
    ),
]

response = llm.invoke(messages)

對 Agent system 來說,prompt caching 特別有價值,因為:

  • system prompt 可能很長
  • tool definitions 可能很多
  • project context 可能固定
  • model calls 可能非常頻繁

所以即使只是 model configuration,也可能直接影響整個 system 的成本和 latency。

Why This Matters for Agents

到這裡,可以把今天的內容串起來:

ChatModel
   ↓
Streaming
Tool Calling
Structured Output
Configuration
Caching

這些能力不是獨立存在的 feature。

它們最後會一起支撐 Agent 的 behavior。

例如一個 Agent request 可能是:

User Request
    ↓
Model decides what to do
    ↓
Tool Calling
    ↓
read_file()
    ↓
Model receives result
    ↓
Structured Output
    ↓
continue / clarify
    ↓
Streaming response to user

而 Deep Agents 做的事情,就是把這些 capabilities 放進更完整的 Agent harness 裡。


上一篇
[Day 8]:介紹 Deep Agents
系列文
AaaS from Scratch: 從一次性定義,到規模化分析9
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言