CodeGym /Các khóa học /ChatGPT Apps /Production và bảo mật: quyền, sandbox, secrets, monitorin...

Production và bảo mật: quyền, sandbox, secrets, monitoring

ChatGPT Apps
Mức độ , Bài học
Có sẵn

1. Vì sao agent cần “tư duy production”

Khi bạn viết backend thông thường, chỉ riêng ý nghĩ “đưa lên prod” đã tự bật chế độ cảnh giác cao độ: uỷ quyền/kiểm soát truy cập, logging, xử lý lỗi, giới hạn, secrets trong .env chứ không nằm trong code.

Với agent, bạn cần cùng chế độ đó, nhưng còn nghiêm ngặt hơn. Lý do đơn giản: backend thường thực thi đúng những gì bạn viết, còn agent — những gì mô hình tự quyết định làm trong khuôn khổ công cụ và hướng dẫn đã cấp. Ảo giác kiểm soát ở đây còn mạnh hơn code cổ điển: có vẻ như prompt mô tả hết, nhưng thực tế bạn chỉ kiểm soát môi trường và các hành động khả dụng, chứ không phải mọi “ý nghĩ” của mô hình.

Vì vậy trong bài giảng này, chúng ta sẽ dần bọc agent bằng “các lớp bảo vệ”:

  • trước hết giới hạn chính xác những gì agent được làm (quyền công cụ và tách agent),
  • sau đó cô lập môi trường thực thi (sandbox và các giới hạn),
  • tiếp theo sắp xếp lại chuyện secrets và PII,
  • và cuối cùng bật observability: log, metrics và tracing cơ bản.

Để cụ thể, ta tiếp tục với GiftGenius: agent giúp chọn quà và có chạm nhẹ vào thế giới commerce (qua đặt hàng và checkout, nhưng chưa có chi tiết về ACP — phần đó để sau).

2. Quyền: agent không cần “mọi nút trên đời”

Nguyên tắc đặc quyền tối thiểu (Least Privilege)

Quy tắc đầu tiên: agent không cần biết làm mọi thứ. Càng nhiều công cụ, xác suất gọi “nhầm” hàm vào “nhầm” thời điểm càng cao. Thay vì một manageEverything() “quái vật” đọc và ghi đủ thứ, ta thiết kế các hàm nhỏ, rõ ràng, tối thiểu tách riêng đọc và ghi.

Với GiftGenius điều này càng rõ: một chuyện là đọc danh sách quà và sở thích người dùng, chuyện khác là tạo hoặc xác nhận đơn (dính đến tiền). Vì vậy thường ta làm:

  • tập công cụ “read‑only” an toàn (tìm quà, xem chi tiết),
  • các công cụ “write” riêng (tạo bản nháp đơn hàng, huỷ đơn),
  • và nếu cần, thêm tầng nữa cho thao tác đặc biệt nguy hiểm (xác nhận thanh toán, thay đổi hàng loạt).

Tách agent theo nhiệm vụ

Một kỹ thuật mạnh khác — tách agent theo vùng trách nhiệm. Một agent cho “chọn quà”, một agent cho “quản lý đơn”. Như vậy kể cả khi mô hình trong gift‑agent “lệch pha” đôi chút, nó về mặt vật lý cũng không thể gọi công cụ thanh toán, vì công cụ đó không có trong cấu hình của nó.

Hãy hình dung một kiểu cấu hình tối giản cho agent và công cụ:


// Các kiểu tối giản để minh hoạ ý tưởng
type ToolName = 'suggest_gifts' | 'get_gift_details' |
  'create_order_draft' | 'confirm_order';

type AgentConfig = {
  id: string;
  allowedTools: ToolName[];
  maxSteps: number;
};

Giờ mô tả hai agent của GiftGenius:

export const giftPlannerAgent: AgentConfig = {
  id: 'gift-planner',
  allowedTools: ['suggest_gifts', 'get_gift_details'],
  maxSteps: 6,
};

export const orderAgent: AgentConfig = {
  id: 'order-manager',
  allowedTools: ['create_order_draft', 'confirm_order'],
  maxSteps: 4,
};

Đúng là đây chỉ là một lớp trừu tượng, nhưng ý chính đơn giản: dù trong code có đủ cả bốn công cụ, từng agent chỉ nhận tập con cần thiết.

Ràng buộc quyền với người dùng và vai trò

Cần nhớ có hai thực thể khác nhau:

  • người dùng và quyền của họ (liệu user_id này có thể mua, huỷ, xem lịch sử hay không),
  • agent và các công cụ được phép của nó.

Lý tưởng là mỗi lần gọi công cụ phải qua hai lớp kiểm tra: “agent có được phép không?” và “người dùng cũng được phép không?”.

Ví dụ giả định:

type UserRole = 'guest' | 'customer' | 'admin';

function canUserCallTool(role: UserRole, tool: ToolName): boolean {
  if (tool === 'confirm_order') {
    return role === 'customer' || role === 'admin';
  }
  if (tool === 'create_order_draft') {
    return role !== 'guest';
  }
  return true; // cho phép đọc cho tất cả
}

Ở phía MCP/backend khi xử lý tool‑call ta có thể kiểm tra kép:

function assertToolAllowed(
  agent: AgentConfig,
  userRole: UserRole,
  tool: ToolName,
) {
  if (!agent.allowedTools.includes(tool)) {
    throw new Error(`Tool ${tool} không được phép cho agent ${agent.id}`);
  }
  if (!canUserCallTool(userRole, tool)) {
    throw new Error(`Người dùng với vai trò ${userRole} không thể gọi ${tool}`);
  }
}

Kết quả là kể cả khi mô hình bỗng quyết định gọi confirm_order từ agent không đúng chức năng hoặc với tư cách khách — lời gọi sẽ vấp phải kiểm tra này và biến thành một lỗi có kiểm soát, chứ không phải một thanh toán ngoài ý muốn.

Cấu hình khác nhau theo môi trường

Ở môi trường dev và staging bạn thường muốn cho agent nhiều tự do hơn: công cụ thử nghiệm, dịch vụ thanh toán giả, tính năng thử nghiệm. Trong production, ngược lại, cấu hình phải chặt nhất: một phần công cụ bị tắt, endpoints là bản chạy thật, token là dữ liệu thực.

Sơ đồ tối giản:

type Env = 'dev' | 'staging' | 'production';

const env = (process.env.APP_ENV as Env) ?? 'dev';

const orderAgentByEnv: Record<Env, AgentConfig> = {
  dev: {
    id: 'order-manager-dev',
    allowedTools: ['create_order_draft', 'confirm_order'],
    maxSteps: 8,
  },
  staging: {
    id: 'order-manager-staging',
    allowedTools: ['create_order_draft', 'confirm_order'],
    maxSteps: 6,
  },
  production: {
    id: 'order-manager-prod',
    allowedTools: ['create_order_draft'], // confirm chỉ qua một luồng riêng
    maxSteps: 4,
  },
};

export const currentOrderAgent = orderAgentByEnv[env];

Trong prod, confirm_order thậm chí có thể được tách sang một agent “nguy hiểm” riêng, chỉ được gọi sau cú nhấp “Xác nhận đơn hàng” rõ ràng trên widget và các kiểm tra bổ sung.

3. Sandbox: agent không cần quyền root vào vũ trụ của bạn

Các mức độ cô lập

Sau khi đặt quyền cho agent và người dùng, chuyển sang lớp bảo vệ tiếp theo — sandbox và cô lập môi trường thực thi.

Sandbox cho agent và công cụ có thể chia đại khái thành vài mức:

  1. Mức code của công cụ. Ta giới hạn truy cập hệ thống tệp, mạng và tài nguyên tiến trình: không cho phép ghi bừa bãi, truy cập domain tuỳ ý, quay vòng CPU vô hạn hoặc ngốn hàng GB bộ nhớ.
  2. Mức Agents SDK. Ta đặt giới hạn số bước của vòng chạy, số lần gọi tool và kích thước ngữ cảnh (token limit). Mô hình không thể “suy nghĩ” vô hạn và sinh tool‑calls không ngừng — đến một lúc, run sẽ kết thúc với lỗi “quá giới hạn bước” hoặc “hết thời gian”.

Tất cả hợp lại thành “kiến trúc phòng thủ” cổ điển, có thể hình dung bằng sơ đồ.

graph TD
    A[Prompt / hướng dẫn hệ thống] --> B[JSON Schema của tools]
    B --> C[Permissions của agent và người dùng]
    C --> D[Sandbox của hạ tầng]
    D --> E[Dịch vụ bên ngoài / DB]

    subgraph Agent
      A
      B
      C
    end

    subgraph Hạ tầng
      D
    end

Prompt là lớp bảo vệ yếu nhất; sức mạnh thực sự bắt đầu ở nơi bạn giới hạn vật lý những gì code có thể làm và API nào được phép truy cập.

Giới hạn cho vòng chạy: số bước, thời gian, tool‑calls

Một phần sandbox có thể thể hiện ngay trong cấu hình của agent: số bước tối đa, tổng thời gian thực thi, giới hạn số lần gọi tool. Đây không chỉ là bảo vệ khỏi runaway‑loop mà còn là kiểm soát chi phí.

Ví dụ cấu hình trừu tượng cho tuỳ chọn run:

type RunLimits = {
  maxSteps: number;
  maxToolCalls: number;
  timeoutMs: number;
};

const defaultLimits: RunLimits = {
  maxSteps: 8,
  maxToolCalls: 10,
  timeoutMs: 30_000,
};

Bạn sẽ truyền các giới hạn này vào wrapper khởi chạy agent. Nếu mô hình quyết định gọi công cụ lần thứ 11 — bạn dừng run của agent và nói thẳng với người dùng rằng tác vụ quá phức tạp, thay vì để agent “đốt” ngân sách ngoài kiểm soát.

Cô lập code và mạng

Ở mức container/tiến trình, các thực hành thường gặp gồm:

Code của MCP‑server và/hoặc service của agent chạy trong container có hệ thống tệp chỉ‑đọc (trừ thư mục làm việc dành riêng) và bị giới hạn tài nguyên (CPU, RAM). Mạng được cấu hình theo allow‑list: chỉ có thể gọi tới các dịch vụ ngoài cần thiết (commerce‑backend của bạn, cổng thanh toán, một vài API ngoài), chứ không phải Internet tuỳ ý.

Với kịch bản agent, điều này càng quan trọng: mô hình có thể cố đi tới một API “lạ” nào đó hoặc đọc những tệp không ngờ, và tốt nhất là kể cả khi thử, nó về mặt quyền hạn cũng không thể chạm tới tài nguyên thừa.

Trong code, điều này không phải “một dòng TypeScript thần kỳ”, mà thường là cài đặt của orchestrator (Docker Compose, Kubernetes, Vercel, Fly.io, v.v.). Tuy nhiên, về lâu dài, nghĩ về điều này ngay từ giai đoạn thiết kế là có ích:

  • công cụ chạy code bên ngoài (ví dụ sinh báo cáo với lệnh shell) phải hoạt động trong môi trường riêng được cô lập rất chặt;
  • các công cụ không được phép đọc tệp của nhau, secrets, config;
  • nên giới hạn truy cập mạng rõ ràng theo domain hoặc IP.

4. Secrets và dữ liệu nhạy cảm: những gì agent không cần biết

Secrets nên ở đâu và không nên ở đâu

Quy tắc cơ bản: không secret nào — API key, mật khẩu, access token — được đưa vào prompt của mô hình, widget, log hay repository. Chúng sống:

  • trong biến môi trường (process.env.SOMETHING),
  • trong secret manager (AWS Secrets Manager, GCP Secret Manager, Vault, v.v.),
  • trong các kho riêng được mã hoá, quyền truy cập được kiểm soát chặt chẽ.

Trong GiftGenius, ví dụ có khoá cho commerce‑API của cửa hàng. Ta cần agent có thể tạo bản nháp đơn qua MCP‑tool, nhưng bản thân mô hình không được thấy khoá.

// mcp/tools/createOrderDraft.ts
const COMMERCE_API_KEY = process.env.COMMERCE_API_KEY!;

export async function createOrderDraft(args: {
  userId: string;
  giftId: string;
  quantity: number;
}) {
  // Mô hình sẽ không bao giờ thấy COMMERCE_API_KEY — nó chỉ tồn tại ở đây, phía server
  const res = await fetch(`${process.env.COMMERCE_API_URL}/orders/draft`, {
    method: 'POST',
    headers: {
      'Authorization': `Bearer ${COMMERCE_API_KEY}`,
      'Content-Type': 'application/json',
    },
    body: JSON.stringify(args),
  });

  if (!res.ok) {
    throw new Error(`Commerce API returned ${res.status}`);
  }

  return res.json(); // Phản hồi trả về cho agent là một đối tượng đã được làm an toàn
}

Quan trọng: trong phản hồi của công cụ bạn không được “kéo theo” khoá hay chi tiết nhạy cảm khác. Agent chỉ cần biết draftOrderId, danh sách mặt hàng và có thể là trạng thái.

PII và tối thiểu hoá dữ liệu trong ngữ cảnh

Ngoài secrets còn có PII (dữ liệu định danh cá nhân): tên, điện thoại, địa chỉ giao hàng, email, v.v. Agent thường không cần toàn bộ “văn bản thô” này. Chỉ cần một hồ sơ đã cấu trúc: “thích board game”, “tuổi 30–35”, “ngân sách khoảng 50–70$”.

Thay vì nhét toàn bộ lịch sử đơn hàng của người dùng vào prompt, có thể làm một tool get_user_profile_summary, trả về hồ sơ đã được tổng hợp và khử định danh.

type ProfileSummary = {
  ageRange: '18-25' | '26-35' | '36-50' | '50+';
  interests: string[];
  preferredBudget: { min: number; max: number };
};

export async function getUserProfileSummary(userId: string): Promise<ProfileSummary> {
  // Ở đây bạn truy vấn DB, nhưng chỉ trả ra thông tin đã được tổng hợp
  return {
    ageRange: '26-35',
    interests: ['board games', 'gadgets'],
    preferredBudget: { min: 30, max: 80 },
  };
}

Mô hình chỉ thấy vừa đủ để gợi ý quà — và không hơn.

Scrubbing logs

Log là nơi tự nhiên các secrets và PII có thể vô tình lộ ra. Nhất là khi viết một logger “tiện” kiểu console.log(...) và in “mọi thứ”.

Cách tốt là có một logger trung tâm, trước khi in sẽ duyệt payload và che (mask) các trường nhạy cảm.

type LogPayload = Record<string, unknown>;

const SENSITIVE_KEYS = ['email', 'phone', 'cardNumber', 'token'];

function scrub(payload: LogPayload): LogPayload {
  const result: LogPayload = {};
  for (const [key, value] of Object.entries(payload)) {
    if (SENSITIVE_KEYS.includes(key)) {
      result[key] = '***redacted***';
    } else {
      result[key] = value;
    }
  }
  return result;
}

export function logEvent(event: string, payload: LogPayload) {
  const safe = scrub(payload);
  console.log(JSON.stringify({ event, ...safe }));
}

Như vậy thay vì một ngày nào đó dính sự cố production kiểu “chúng ta đã log điện thoại và token khách hàng suốt nửa năm”, bạn thiết kế hệ thống ngay từ đầu để điều đó không thể xảy ra. Đây không chỉ là sự cẩn thận, mà còn giúp đáp ứng quy định (GDPR và luật địa phương): càng ít PII trong log, cuộc sống sản phẩm càng dễ thở.

5. Monitoring và observability cho agent

Cần nhìn thấy điều gì

Ta đã giới hạn những gì agent có thể làm, dữ liệu nào nó thấy và thứ gì vào log. Câu hỏi tiếp theo — làm sao biết agent ở prod đang hành xử đúng như ta kỳ vọng?

Kiểu monitoring “service sống/chết” gần như vô dụng cho agent. Ta không chỉ cần biết tiến trình sống, mà còn phải hiểu hành vi của nó: nó làm những bước nào, gọi công cụ nào, lỗi ở đâu, vòng lặp ở đâu.

Bộ dữ liệu tối thiểu cho mỗi lần run:

  • agent_run_id — định danh duy nhất cho lần chạy;
  • user_id ẩn danh hoặc session ID;
  • tên agent và môi trường;
  • danh sách tool đã gọi: tên, số lượng, tổng thời gian;
  • các bước workflow và dừng ở bước nào;
  • trạng thái cuối cùng: success, partial_success, failed, canceled, timeout, limits_exceeded.

Có thể mô hình hoá như cấu trúc:

type RunStatus =
  | 'success'
  | 'partial_success'
  | 'failed'
  | 'canceled'
  | 'timeout'
  | 'limits_exceeded';

type ToolCallLog = {
  name: ToolName;
  durationMs: number;
  success: boolean;
};

type AgentRunLog = {
  runId: string;
  agentId: string;
  userId: string;
  env: Env;
  startedAt: string;
  finishedAt: string;
  status: RunStatus;
  toolCalls: ToolCallLog[];
  errorMessage?: string;
};

Ví dụ “wrapper” quanh việc khởi chạy agent

Giả sử bạn có hàm runAgent bao đóng lời gọi Agents SDK thực sự. Ta sẽ bọc nó bằng monitoring:

async function runAgentWithLogging(
  agent: AgentConfig,
  input: string,
  userId: string,
): Promise<string> {
  const runId = crypto.randomUUID();
  const startedAt = new Date();

  const toolCalls: ToolCallLog[] = [];

  try {
    const result = await runAgent(agent, input, {
      userId,
      limits: defaultLimits,
      onToolCall: (name, durationMs, success) => {
        toolCalls.push({ name, durationMs, success });
      },
    });

    const finishedAt = new Date();

    const log: AgentRunLog = {
      runId,
      agentId: agent.id,
      userId,
      env,
      startedAt: startedAt.toISOString(),
      finishedAt: finishedAt.toISOString(),
      status: 'success',
      toolCalls,
    };

    logEvent('agent_run', log);
    return result;
  } catch (err) {
    const finishedAt = new Date();
    const log: AgentRunLog = {
      runId,
      agentId: agent.id,
      userId,
      env,
      startedAt: startedAt.toISOString(),
      finishedAt: finishedAt.toISOString(),
      status: 'failed',
      toolCalls,
      errorMessage: (err as Error).message,
    };
    logEvent('agent_run', log);
    throw err;
  }
}

Ở đây runAgent là một “hộp đen” có thể được hiện thực bằng Agents SDK thực tế; ta chỉ minh hoạ cách thêm observability mà không phụ thuộc API cụ thể.

Logs vs metrics vs tracing

Tiện nhất là phân biệt ba tầng observability:

Tầng Đó là gì Ví dụ cho agent GiftGenius
Logs “Câu chuyện” về từng lần run cụ thể AgentRunLog chi tiết với các bước và công cụ
Metrics Các chỉ số số liệu đã được tổng hợp p95 thời lượng run, số tool‑calls trung bình, error‑rate
Tracing Cây/đồ thị các request và subrequest Run → bước → tool‑calls → gọi ra API ngoài (commerce, DB, v.v.)

Metrics giúp trả lời “nhìn chung có ổn không?” (ví dụ, error‑rate trong giờ qua). Logs và tracing giúp trả lời “vì sao chỗ này lại lỗi?” và tái hiện run gặp vấn đề.

Bạn có thể bắt đầu metrics từ logs: một tác vụ định kỳ tổng hợp sự kiện agent_run và tính p95 thời lượng, số lỗi, v.v.

6. Bức tranh tổng thể trong GiftGenius

Để bớt trừu tượng, ta ghép các mảnh lại cho ứng dụng học tập của chúng ta.

Agent gift-planner ở môi trường production chỉ có công cụ an toàn: chọn quà và lấy chi tiết. Nó không thấy thanh toán hay quản lý đơn. System‑instructions của nó nói rõ rằng nó không được hứa “tôi sẽ trả thay bạn”, tối đa là chuẩn bị gợi ý và có thể là bản nháp danh sách quà.

Agent order-manager tồn tại riêng và chỉ biết làm việc với đơn. Trong production nó chỉ có thể tạo bản nháp đơn (create_order_draft), còn xác nhận đơn (confirm_order) hoặc do người dùng thực hiện qua trigger UI rõ ràng trong widget, hoặc chỉ mở ở dev/staging. Công cụ của nó dùng secrets (API key cửa hàng) hoàn toàn ở phía backend, và chỉ proxy các trường cần thiết trong phản hồi.

Cả hai agent được chạy qua wrapper runAgentWithLogging, wrapper này áp giới hạn và ghi log với agent_run_id, userId, môi trường và danh sách công cụ. Log không có email hay điện thoại; các trường này được scrub trước. Hồ sơ người dùng dùng ở dạng khử định danh: khoảng tuổi, sở thích, ngân sách, chứ không phải toàn văn lịch sử mua sắm.

Hạ tầng nơi MCP‑server và dịch vụ agent sống được cô lập: container với hệ thống tệp chỉ‑đọc (trừ /tmp hoặc thư mục chuyên dụng), giới hạn CPU/RAM, mạng theo allow‑list domain. Nếu agent bỗng cố gọi “thứ gì lạ”, nó đơn giản không thể chạm tới về mặt vật lý.

Nếu tại một thời điểm bạn thấy metric “tỷ lệ run có trạng thái limits_exceeded” tăng vọt hoặc “số tool‑calls trung bình > 10”, bạn hiểu rằng hoặc prompt trở nên quá “lắm lời”, hoặc một công cụ nào đó lỗi khiến agent lặp lại bước.

Đó là hành vi của một dịch vụ “trưởng thành”, chứ không phải agent thử nghiệm kiểu “làm tạm cho xong”.

7. Các lỗi thường gặp khi đưa agent lên production

Những gì ta bàn ở trên là “bức tranh đúng đắn” của production‑agent. Trên thực tế hay gặp những “cái bẫy” điển hình. Tập hợp lại thành một danh sách: chỉ cần tránh được các lỗi này, việc lên prod sẽ yên ả hơn nhiều.

Lỗi #1: agent được “cho phép mọi thứ”.
Kịch bản phổ biến: bạn mô tả cả đống MCP‑tool (tìm, sửa, xoá, thanh toán), và khi tạo agent thì nạp toàn bộ danh sách. Kết quả là mô hình có thể vô tình gọi xoá hoặc thanh toán ở nơi chỉ muốn đọc. Chữa bằng cách tách công cụ theo vai trò và tạo nhiều agent hẹp hơn, mỗi agent có allowedTools riêng.

Lỗi #2: kiểm tra quyền chỉ trong prompt.
Đôi khi developer viết trong system‑instructions: “không bao giờ mua thứ gì nếu chưa có xác nhận của người dùng” và yên tâm. Nhưng prompt là lớp bảo vệ yếu, jailbreak và lỗi không ai đảm bảo. Cần có kiểm tra thực sự ở backend: “agent được phép dùng tool này” và “người dùng được phép dùng tool này”, nếu không một lần sinh thiếu cẩn trọng có thể dẫn tới hành động ngoài mong muốn.

Lỗi #3: secrets trong prompt và log.
Có lúc muốn “đẩy nhanh tích hợp” nên đặt API key vào system‑prompt hoặc truyền trong tham số tool để agent tự gọi API ngoài. Kết cục key có trong log của mô hình, và tiềm ẩn trong hệ thống bên thứ ba. Đây là con đường thẳng tới rò rỉ và bị cấm trên Store. Secrets phải chỉ sống ở server, trong biến môi trường hoặc secret manager, và không bao giờ vào ngữ cảnh của mô hình.

Lỗi #4: log “thô” không scrub.
Khi debug, tiện tay viết console.log(...) rồi quên. Vài tháng sau phát hiện log có địa chỉ người dùng, điện thoại, mã đơn với PII. Đặc biệt phiền trong thế giới GDPR và các quy định khác. Tốt hơn là thiết lập logger trung tâm và che tự động các trường nhạy cảm ngay từ đầu, kể cả khi “chỉ log ở dev”.

Lỗi #5: không có giới hạn hành vi của agent.
Không có giới hạn về bước, thời gian và số lần gọi tool, agent có thể rơi vào vòng lặp: gọi đi gọi lại cùng một công cụ, cố sửa cùng một lỗi vô hạn, đốt nhiều token và làm nặng các API ngoài. Tốt nhất bạn nhận hoá đơn khổng lồ cho mô hình, tệ nhất là làm sập backend và chọc giận người dùng. Giới hạn vòng chạy và các timeout hợp lý là phần bắt buộc của cấu hình.

Lỗi #6: trộn read và write trong cùng một công cụ.
Đôi khi tạo các phương thức “tiện” như getOrCreateOrder, nếu không có đơn thì tạo mới. Với backend cổ điển đây có thể là pattern chấp nhận được, nhưng với agent có thể gây tác dụng phụ: mô hình chỉ muốn biết trạng thái, còn công cụ lại tạo thứ gì đó. An toàn hơn là tách get_order_detailscreate_order_draft, như vậy kể cả gọi lặp thì hậu quả vẫn trong kiểm soát hơn.

Lỗi #7: bỏ qua observability.
Nhiều người bắt đầu bằng “sau này gắn log và metrics, giờ miễn chạy được”. Agent không có monitoring là một hộp đen: bạn không biết nó gọi công cụ nào, làm bao nhiêu bước, lỗi ở đâu. Mọi phàn nàn của người dùng biến thành điều tra trong phòng tối. Dễ hơn nhiều nếu đặt cấu trúc log (agent_run_id, tools, trạng thái) và metrics cơ bản ngay từ đầu, hơn là cố chắp vá lên một codebase hỗn loạn.

1
Khảo sát/đố vui
, cấp độ , bài học
Không có sẵn
Điều phối tác nhân
Điều phối tác nhân với Agents SDK
Bình luận
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION