ReAct 迴圈的實作:思考、行動、觀察的最小骨架
不用框架也能寫出可用的 Agent 迴圈。八十行程式碼就能跑,而且每一步都看得懂。
框架很方便,但第一次寫 Agent 建議手刻一次迴圈。你會清楚知道每一輪送了什麼給模型、狀態存在哪裡、什麼時候該停。
迴圈骨架
def run_agent(user_task, tools, max_steps=8):
messages = [{'role': 'system', 'content': SYSTEM},
{'role': 'user', 'content': user_task}]
for step in range(max_steps):
resp = model.chat(messages, tools=tools)
if not resp.tool_calls: # 沒有要用工具 => 收工
return resp.content
messages.append(resp.as_message())
for call in resp.tool_calls:
try:
result = dispatch(call.name, call.arguments)
except Exception as e:
result = {'error': str(e)} # 錯誤也要回給模型
messages.append({
'role': 'tool',
'tool_call_id': call.id,
'content': json.dumps(result, ensure_ascii=False)[:4000],
})
return '已達步數上限,未能完成任務。'三個關鍵細節
- 錯誤要回傳給模型,不要直接拋出。模型看到錯誤訊息後,往往能自己換參數重試。
- 工具結果要截斷。一個回傳三萬字的工具會瞬間吃光上下文。
- 一定要有步數上限。沒有上限的 Agent 遇到迴圈型任務會無限跑下去,帳單很可觀。
為什麼要保留完整訊息串
模型靠訊息串維持狀態。你若為了省 token 把中間的工具結果刪掉,模型會忘記自己已經查過什麼,開始重複呼叫同一個工具。
把每一步的
step、工具名、參數、耗時寫成結構化日誌。Agent 的除錯幾乎完全依賴這份軌跡。