指南
客戶端範例
Python 與 Node.js 範例:串流接收、續傳、上載與下載。
範例都從環境變數讀取地址和 API Key,與快速開始相同:AXON_API 是 API 地址,AXON_KEY 是你的 Key。
Python(httpx)
import json
import os
import httpx
API = os.environ["AXON_API"]
HEADERS = {"Authorization": f"Bearer {os.environ['AXON_KEY']}"}
TERMINAL = {"turn.completed", "turn.failed", "turn.cancelled"}
def sse_events(response):
"""把 SSE 回應解析成 (id, event, data) 三元組。"""
frame = {}
for line in response.iter_lines():
if line == "":
if "data" in frame:
yield frame.get("id"), frame.get("event"), json.loads(frame["data"])
frame = {}
elif line.startswith(":"):
continue # 保活註解
else:
field, _, value = line.partition(":")
frame[field] = value[1:] if value.startswith(" ") else value
def ask(client, session_id, content, files=()):
"""發送一條訊息(可附帶檔案 ID),輸出回覆;返回終止事件和 Agent 交付的檔案。"""
body = {"content": content, "files": list(files)}
client.post(f"{API}/sessions/{session_id}/messages", json=body).raise_for_status()
delivered = []
last_id = None
while True:
headers = {"Last-Event-ID": last_id} if last_id else {}
try:
with client.stream("GET", f"{API}/sessions/{session_id}/events/stream",
headers=headers, timeout=None) as response:
response.raise_for_status()
for last_id, event, data in sse_events(response):
if event == "agent.message.delta":
print(data["content"], end="", flush=True)
elif event == "agent.tool_use":
print(f"\n[調用工具 {data['name']}]")
elif event == "agent.tool_result":
delivered += data["files"]
elif event in TERMINAL:
print(f"\n本輪結束:{event}", data.get("reason") or data.get("usage"))
return data, delivered
return None, delivered # 流結束但沒有終止事件:以事件歷史和會話狀態為準
except httpx.TransportError:
continue # 網絡中斷:帶 Last-Event-ID 重連
with httpx.Client(headers=HEADERS) as client:
session = client.post(f"{API}/sessions", json={"model_id": "deepseek-v4-flash"}).json()
ask(client, session["id"], "用 Python 算一下 2 的 100 次方")
ask(client, session["id"], "再除以 3,保留兩位小數")
# 上載檔案交給 Agent,再下載它交付的結果
with open("sales.csv", "rb") as f:
upload = client.post(f"{API}/files", files={"file": ("sales.csv", f)}).json()
_, delivered = ask(client, session["id"], "按 region 彙總 amount,結果保存成 summary.csv 交付給我",
files=[upload["id"]])
for file in delivered:
content = client.get(f"{API}/files/{file['id']}/content", follow_redirects=True)
content.raise_for_status()
with open(file["filename"], "wb") as out:
out.write(content.content)Node.js
Node.js 18 及以上,原生 fetch;保存為 .mjs 執行。
const API = process.env.AXON_API;
const HEADERS = { Authorization: `Bearer ${process.env.AXON_KEY}`, "Content-Type": "application/json" };
const TERMINAL = new Set(["turn.completed", "turn.failed", "turn.cancelled"]);
async function* sseEvents(response) {
const decoder = new TextDecoder();
let buffer = "";
for await (const chunk of response.body) {
buffer += decoder.decode(chunk, { stream: true });
let end;
while ((end = buffer.indexOf("\n\n")) !== -1) {
const frame = {};
for (const line of buffer.slice(0, end).split("\n")) {
if (line.startsWith(":")) continue; // 保活註解
const i = line.indexOf(":");
frame[line.slice(0, i)] = line.slice(i + 1).replace(/^ /, "");
}
buffer = buffer.slice(end + 2);
if (frame.data) yield { id: frame.id, event: frame.event, data: JSON.parse(frame.data) };
}
}
}
async function ask(sessionId, content, files = []) {
await fetch(`${API}/sessions/${sessionId}/messages`, {
method: "POST", headers: HEADERS, body: JSON.stringify({ content, files }),
});
const response = await fetch(`${API}/sessions/${sessionId}/events/stream`, { headers: HEADERS });
for await (const { event, data } of sseEvents(response)) {
if (event === "agent.message.delta") process.stdout.write(data.content);
if (TERMINAL.has(event)) return data;
}
}
const session = await (await fetch(`${API}/sessions`, {
method: "POST", headers: HEADERS, body: JSON.stringify({ model_id: "deepseek-v4-flash" }),
})).json();
await ask(session.id, "列出 /workspace 裡的檔案");上載與下載檔案(同一個 .mjs 中):
import { readFile, writeFile } from "node:fs/promises";
const AUTH = { Authorization: HEADERS.Authorization }; // multipart 由 fetch 自己設定 Content-Type
const form = new FormData();
form.append("file", new Blob([await readFile("sales.csv")]), "sales.csv");
const upload = await (await fetch(`${API}/files`, { method: "POST", headers: AUTH, body: form })).json();
// 發送時把它附上:ask(session.id, "按 region 彙總 amount", [upload.id]);Agent 交付的檔案在 agent.tool_result 的 files 中
async function download(file) { // file 為 files 中的一項
const response = await fetch(`${API}/files/${file.id}/content`, { headers: AUTH }); // 自動跟隨 307
await writeFile(file.filename, Buffer.from(await response.arrayBuffer()));
}以上範例為了簡潔,省略了錯誤處理。生產環境中請檢查每個回應的狀態碼,並參考 Python 範例,在斷線時帶 Last-Event-ID 重連。
命令行對話
把上面的各個部分串起來,就是一個命令行對話程式:建立會話,串流顯示回覆,斷線時自動續傳,可以附帶檔案,並把 Agent 交付的檔案下載到 downloads/ 目錄。下面是把上面的函數接成命令行迴圈後的執行效果示意(輸入訊息後按 Enter 發送,/file 路徑 把檔案附到下一條訊息,/quit 退出):
會話 ses_679cd58a3c8344ea8c1f7f4603177955(deepseek-v4-flash)。/file 路徑 附帶檔案,/quit 退出。
> /file sales.csv
[已上載 sales.csv,45 位元組,隨下一條訊息發送]
> 按 region 彙總 amount,結果保存成 summary.csv 交付給我
[調用 read_file]
[調用 write_file]
[調用 deliver_file]
[已下載 downloads/summary.csv]
已按 region 彙總完成。結果:華南 150,華北 80。檔案 summary.csv 已交付。
— turn.completed,用量 {'input_tokens': 34084, 'output_tokens': 304}