最佳實踐

指南、疑難排解與環境設定。

最佳實踐

1. 保持 API 金鑰在伺服器端

始終在伺服器端使用 createAgentHandler。絕對不要將 LENS_API_KEYOPENAI_API_KEY 暴露給客戶端。

.env
# 不要使用 NEXT_PUBLIC_ 前綴
LENS_API_KEY=lens-xxx
OPENAI_API_KEY=sk-xxx

2. 提供豐富的工具 Metadata

你提供的 metadata 越多,LLM 就越知道何時以及如何使用工具。

好的做法 vs 不好的做法
// 好的做法
{
  description: '依關鍵字、分類或作者搜尋商品',
  whenToUse: '當用戶想尋找、比較或瀏覽商品時',
  schema: {
    query: { type: 'string', required: true, description: '搜尋關鍵字(1-3 個詞)' },
    topK: { type: 'number', required: false, default: 10 },
  },
  output: '包含 title、price、imageUrl 的商品陣列',
  execute: async (params) => { ... },
}

// 不好的做法 — LLM 不知道何時使用這個工具
{
  execute: async (params) => { ... },
}

3. 始終回傳結構化的 ToolResult

ToolResult
// 成功
{ success: true, result: { message: '找到 5 個商品', products: [...] } }

// 錯誤
{ success: false, error: '資料庫連線失敗' }

4. 優雅地處理錯誤

錯誤處理
execute: async (params) => {
  try {
    const res = await fetch(url, { ... });
    if (!res.ok) {
      const err = await res.json();
      return { success: false, error: err.error || '請求失敗' };
    }
    return await res.json();
  } catch (err) {
    return {
      success: false,
      error: err instanceof Error ? err.message : '未知錯誤',
    };
  }
}

5. 使用 context.userId 處理用戶相關工具

用戶 context
user_orders: {
  execute: async (params, context) => {
    // context.userId 由 SDK 自動填入
    const res = await fetch('/api/orders', {
      method: 'POST',
      body: JSON.stringify({ ...params, userId: context?.userId }),
    });
    return await res.json();
  },
}

環境變數

.env
# 必填(僅限伺服器端 — 絕對不要使用 NEXT_PUBLIC_ 前綴)
LENS_API_KEY=lens-xxx              # Lens OS API 金鑰
OPENAI_API_KEY=sk-xxx              # OpenAI API 金鑰

# 可選
OPENAI_MODEL=gpt-4o               # LLM 模型(預設:gpt-4o)
NEXT_PUBLIC_BASE_URL=http://localhost:3000  # 內部 API 呼叫的 Base URL

疑難排解

Module not found: @lens-os/sdk/server

確認你的 @lens-os/sdk 版本包含 ./server 匯出。 檢查 node_modules/@lens-os/sdk/package.json

package.json exports
{
  "exports": {
    ".": { "import": "./dist/index.mjs", "types": "./dist/index.d.mts" },
    "./react": { "import": "./dist/react/index.mjs", "types": "./dist/react/index.d.mts" },
    "./server": { "import": "./dist/server/index.mjs", "types": "./dist/server/index.d.mts" }
  }
}

SSE 連線中斷

如果 SSE 串流提前斷線,確認你的伺服器/代理沒有進行緩衝:

SSE Headers
// createAgentHandler 已自動設定這些標頭:
{
  'Content-Type': 'text/event-stream',
  'Cache-Control': 'no-cache, no-transform',
  'Connection': 'keep-alive',
  'X-Accel-Buffering': 'no',  // 用於 nginx
}

工具沒有被呼叫

  • 確認 ToolExecutorConfig 中有提供 description whenToUse
  • 確認工具已傳入 createAgentHandler({ toolExecutors: ... })
  • 啟用事件日誌:
除錯日誌
onEvent: (event) => {
  console.log('[SSE]', event.type, event);
}

Action Request 逾時

如果 DOM 動作逾時(預設 30 秒),可增加逾時時間:

增加逾時
createAgentHandler({
  ...config,
  actionTimeout: 60000, // 60 秒
});

OpenAI 錯誤:Image URLs only allowed for role 'user'

如果使用包含螢幕截圖的頁面狀態,請確認你的 SDK 版本 >= 0.1.3, 該版本修正了 prompt builder,將包含圖片的頁面狀態訊息使用 role: "user"

Warning

絕對不要將 API 金鑰提交到版本控制。使用 .env 檔案並將它們加入 .gitignore