Multi-agent

การกำหนดเส้นทางแบบหลายเอเจนต์

เรียกใช้เอเจนต์แบบ_แยกอิสระ_หลายตัว โดยแต่ละตัวมีพื้นที่ทำงาน ไดเรกทอรีสถานะ (agentDir) และประวัติเซสชันของตัวเอง พร้อมกับบัญชีช่องทางหลายบัญชี (เช่น WhatsApp สองบัญชี) ใน Gateway ที่กำลังทำงานหนึ่งตัว ข้อความขาเข้าจะถูกกำหนดเส้นทางไปยังเอเจนต์ที่ถูกต้องผ่านการผูก

เอเจนต์ ในที่นี้คือขอบเขตแบบเต็มต่อหนึ่งบุคลิก: ไฟล์พื้นที่ทำงาน โปรไฟล์การยืนยันตัวตน รีจิสทรีโมเดล และที่เก็บเซสชัน agentDir คือไดเรกทอรีสถานะบนดิสก์ที่เก็บการกำหนดค่าต่อเอเจนต์นี้ไว้ที่ ~/.openclaw/agents/<agentId>/ การผูก จะจับคู่บัญชีช่องทาง (เช่น พื้นที่ทำงาน Slack หรือหมายเลข WhatsApp) กับเอเจนต์หนึ่งในเหล่านั้น

"เอเจนต์หนึ่งตัว" คืออะไร?

เอเจนต์ คือสมองที่มีขอบเขตครบถ้วนพร้อมสิ่งเหล่านี้ของตัวเอง:

  • พื้นที่ทำงาน (ไฟล์, AGENTS.md/SOUL.md/USER.md, โน้ตในเครื่อง, กฎบุคลิก)
  • ไดเรกทอรีสถานะ (agentDir) สำหรับโปรไฟล์การยืนยันตัวตน รีจิสทรีโมเดล และการกำหนดค่าต่อเอเจนต์
  • ที่เก็บเซสชัน (ประวัติแชต + สถานะการกำหนดเส้นทาง) ภายใต้ ~/.openclaw/agents/<agentId>/sessions

โปรไฟล์การยืนยันตัวตนเป็นแบบต่อเอเจนต์ แต่ละเอเจนต์อ่านจากของตัวเอง:

~/.openclaw/agents/<agentId>/agent/auth-profiles.json

Skills จะถูกโหลดจากพื้นที่ทำงานของแต่ละเอเจนต์ รวมถึงรากที่ใช้ร่วมกัน เช่น ~/.openclaw/skills จากนั้นจะถูกกรองด้วยรายการอนุญาต Skills ของเอเจนต์ที่มีผลเมื่อกำหนดค่าไว้ ใช้ agents.defaults.skills สำหรับฐานร่วม และ agents.list[].skills สำหรับการแทนที่ต่อเอเจนต์ ดู Skills: ต่อเอเจนต์เทียบกับแบบใช้ร่วมกัน และ Skills: รายการอนุญาต Skills ของเอเจนต์

Gateway สามารถโฮสต์เอเจนต์หนึ่งตัว (ค่าเริ่มต้น) หรือเอเจนต์หลายตัว เคียงข้างกันได้

พาธ (แผนที่แบบเร็ว)

  • การกำหนดค่า: ~/.openclaw/openclaw.json (หรือ OPENCLAW_CONFIG_PATH)
  • ไดเรกทอรีสถานะ: ~/.openclaw (หรือ OPENCLAW_STATE_DIR)
  • พื้นที่ทำงาน: ~/.openclaw/workspace (หรือ ~/.openclaw/workspace-<agentId>)
  • ไดเรกทอรีเอเจนต์: ~/.openclaw/agents/<agentId>/agent (หรือ agents.list[].agentDir)
  • เซสชัน: ~/.openclaw/agents/<agentId>/sessions

โหมดเอเจนต์เดียว (ค่าเริ่มต้น)

หากคุณไม่ทำอะไร OpenClaw จะเรียกใช้เอเจนต์หนึ่งตัว:

  • agentId มีค่าเริ่มต้นเป็น main
  • เซสชันจะถูก key เป็น agent:main:<mainKey>
  • พื้นที่ทำงานมีค่าเริ่มต้นเป็น ~/.openclaw/workspace (หรือ ~/.openclaw/workspace-<profile> เมื่อตั้งค่า OPENCLAW_PROFILE)
  • สถานะมีค่าเริ่มต้นเป็น ~/.openclaw/agents/main/agent

ตัวช่วยเอเจนต์

ใช้วิซาร์ดเอเจนต์เพื่อเพิ่มเอเจนต์แบบแยกอิสระใหม่:

openclaw agents add work

จากนั้นเพิ่ม bindings (หรือให้วิซาร์ดทำให้) เพื่อกำหนดเส้นทางข้อความขาเข้า

ตรวจสอบด้วย:

openclaw agents list --bindings

เริ่มต้นอย่างรวดเร็ว

  • สร้างพื้นที่ทำงานของแต่ละเอเจนต์

    ใช้วิซาร์ดหรือสร้างพื้นที่ทำงานด้วยตนเอง:

    openclaw agents add coding
    openclaw agents add social
    

    แต่ละเอเจนต์จะได้พื้นที่ทำงานของตัวเองพร้อม SOUL.md, AGENTS.md และ USER.md ที่ไม่บังคับ รวมถึง agentDir เฉพาะและที่เก็บเซสชันภายใต้ ~/.openclaw/agents/<agentId>

  • สร้างบัญชีช่องทาง

    สร้างหนึ่งบัญชีต่อเอเจนต์บนช่องทางที่คุณต้องการ:

    • Discord: หนึ่งบอตต่อเอเจนต์ เปิดใช้ Message Content Intent แล้วคัดลอกแต่ละโทเค็น
    • Telegram: หนึ่งบอตต่อเอเจนต์ผ่าน BotFather แล้วคัดลอกแต่ละโทเค็น
    • WhatsApp: เชื่อมโยงแต่ละหมายเลขโทรศัพท์ต่อบัญชี
    openclaw channels login --channel whatsapp --account work
    

    ดูคู่มือช่องทาง: Discord, Telegram, WhatsApp

  • เพิ่มเอเจนต์ บัญชี และการผูก

    เพิ่มเอเจนต์ภายใต้ agents.list บัญชีช่องทางภายใต้ channels.<channel>.accounts และเชื่อมต่อด้วย bindings (ตัวอย่างด้านล่าง)

  • รีสตาร์ทและตรวจสอบ

    openclaw gateway restart
    openclaw agents list --bindings
    openclaw channels status --probe
    
  • เอเจนต์หลายตัว = หลายคน หลายบุคลิก

    ด้วยเอเจนต์หลายตัว แต่ละ agentId จะกลายเป็นบุคลิกที่แยกอิสระเต็มรูปแบบ:

    • หมายเลขโทรศัพท์/บัญชีที่แตกต่างกัน (accountId ต่อช่องทาง)
    • บุคลิกที่แตกต่างกัน (ไฟล์พื้นที่ทำงานต่อเอเจนต์ เช่น AGENTS.md และ SOUL.md)
    • การยืนยันตัวตน + เซสชันแยกกัน (ไม่มีการคุยข้าม เว้นแต่จะเปิดใช้โดยชัดเจน)

    สิ่งนี้ทำให้หลายคนใช้เซิร์ฟเวอร์ Gateway เดียวร่วมกันได้ โดยยังแยก "สมอง" AI และข้อมูลของแต่ละคนออกจากกัน

    การค้นหาหน่วยความจำ QMD ข้ามเอเจนต์

    หากเอเจนต์หนึ่งควรค้นหาทรานสคริปต์เซสชัน QMD ของเอเจนต์อื่น ให้เพิ่มคอลเลกชันเพิ่มเติมภายใต้ agents.list[].memorySearch.qmd.extraCollections ใช้ agents.defaults.memorySearch.qmd.extraCollections เฉพาะเมื่อทุกเอเจนต์ควรสืบทอดคอลเลกชันทรานสคริปต์ที่ใช้ร่วมกันชุดเดียวกัน

    {
      agents: {
        defaults: {
          workspace: "~/workspaces/main",
          memorySearch: {
            qmd: {
              extraCollections: [{ path: "~/agents/family/sessions", name: "family-sessions" }],
            },
          },
        },
        list: [
          {
            id: "main",
            workspace: "~/workspaces/main",
            memorySearch: {
              qmd: {
                extraCollections: [{ path: "notes" }], // resolves inside workspace -> collection named "notes-main"
              },
            },
          },
          { id: "family", workspace: "~/workspaces/family" },
        ],
      },
      memory: {
        backend: "qmd",
        qmd: { includeDefaultMemory: false },
      },
    }
    

    พาธคอลเลกชันเพิ่มเติมสามารถใช้ร่วมกันข้ามเอเจนต์ได้ แต่ชื่อคอลเลกชันจะยังคงระบุชัดเจนเมื่อพาธอยู่นอกพื้นที่ทำงานของเอเจนต์ พาธภายในพื้นที่ทำงานยังคงอยู่ในขอบเขตเอเจนต์ เพื่อให้แต่ละเอเจนต์เก็บชุดการค้นหาทรานสคริปต์ของตัวเอง

    หมายเลข WhatsApp หนึ่งหมายเลข หลายคน (แยก DM)

    คุณสามารถกำหนดเส้นทางข้อความส่วนตัว WhatsApp ที่ต่างกันไปยังเอเจนต์ต่างกันได้ ขณะยังคงใช้บัญชี WhatsApp เดียว จับคู่ตามผู้ส่ง E.164 (เช่น +15551234567) ด้วย peer.kind: "direct" การตอบกลับยังมาจากหมายเลข WhatsApp เดิม (ไม่มีตัวตนผู้ส่งแยกตามเอเจนต์)

    ตัวอย่าง:

    {
      agents: {
        list: [
          { id: "alex", workspace: "~/.openclaw/workspace-alex" },
          { id: "mia", workspace: "~/.openclaw/workspace-mia" },
        ],
      },
      bindings: [
        {
          agentId: "alex",
          match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230001" } },
        },
        {
          agentId: "mia",
          match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551230002" } },
        },
      ],
      channels: {
        whatsapp: {
          dmPolicy: "allowlist",
          allowFrom: ["+15551230001", "+15551230002"],
        },
      },
    }
    

    หมายเหตุ:

    • การควบคุมการเข้าถึง DM เป็นแบบทั่วทั้งบัญชี WhatsApp (การจับคู่/รายการอนุญาต) ไม่ใช่ต่อเอเจนต์
    • สำหรับกลุ่มที่ใช้ร่วมกัน ให้ผูกกลุ่มกับเอเจนต์หนึ่งตัว หรือใช้ กลุ่มกระจายข้อความ

    กฎการกำหนดเส้นทาง (ข้อความเลือกเอเจนต์อย่างไร)

    การผูกเป็นแบบกำหนดแน่นอน และรายการที่เฉพาะเจาะจงที่สุดชนะ:

  • จับคู่ peer

    ID ของ DM/กลุ่ม/ช่องทางแบบตรงกันเป๊ะ

  • จับคู่ parentPeer

    การสืบทอดเธรด

  • guildId + roles

    การกำหนดเส้นทางตามบทบาท Discord

  • guildId

    Discord

  • teamId

    Slack

  • จับคู่ accountId สำหรับช่องทาง

    fallback ต่อบัญชี

  • จับคู่ระดับช่องทาง

    accountId: "*"

  • เอเจนต์เริ่มต้น

    fallback ไปยัง agents.list[].default มิฉะนั้นใช้ รายการแรก ค่าเริ่มต้น: main

  • การตัดสินกรณีเสมอและความหมายแบบ AND
    • หากมีหลายการผูกที่ตรงกันในลำดับชั้นเดียวกัน รายการแรกตามลำดับใน config จะชนะ
    • หากการผูกตั้งค่าฟิลด์การจับคู่หลายฟิลด์ (เช่น peer + guildId) ฟิลด์ที่ระบุทั้งหมดจำเป็นต้องตรงกัน (ความหมายแบบ AND)
    รายละเอียดขอบเขตบัญชี
    • การผูกที่ละ accountId จะจับคู่เฉพาะบัญชีเริ่มต้นเท่านั้น
    • ใช้ accountId: "*" สำหรับ fallback ครอบคลุมทั้งช่องทางในทุกบัญชี
    • หากภายหลังคุณเพิ่มการผูกเดียวกันสำหรับเอเจนต์เดียวกันพร้อม ID บัญชีแบบชัดเจน OpenClaw จะอัปเกรดการผูกเฉพาะช่องทางที่มีอยู่ให้เป็นแบบมีขอบเขตบัญชีแทนที่จะทำซ้ำ

    หลายบัญชี / หมายเลขโทรศัพท์

    ช่องทางที่รองรับหลายบัญชี (เช่น WhatsApp) ใช้ accountId เพื่อระบุการเข้าสู่ระบบแต่ละครั้ง แต่ละ accountId สามารถกำหนดเส้นทางไปยังเอเจนต์คนละตัวได้ ดังนั้นเซิร์ฟเวอร์หนึ่งตัวจึงโฮสต์หมายเลขโทรศัพท์หลายหมายเลขได้โดยไม่ผสมเซสชันกัน

    หากคุณต้องการบัญชีเริ่มต้นทั้งช่องทางเมื่อไม่ได้ระบุ accountId ให้ตั้งค่า channels.<channel>.defaultAccount (ไม่บังคับ) เมื่อไม่ได้ตั้งค่า OpenClaw จะ fallback ไปยัง default หากมี มิฉะนั้นใช้ ID บัญชีที่กำหนดค่าไว้รายการแรก (เรียงลำดับแล้ว)

    ช่องทางทั่วไปที่รองรับรูปแบบนี้ ได้แก่:

    • whatsapp, telegram, discord, slack, signal, imessage
    • irc, line, googlechat, mattermost, matrix, nextcloud-talk
    • bluebubbles, zalo, zalouser, nostr, feishu

    แนวคิด

    • agentId: "สมอง" หนึ่งชุด (พื้นที่ทำงาน, การยืนยันตัวตนต่อเอเจนต์, ที่เก็บเซสชันต่อเอเจนต์)
    • accountId: อินสแตนซ์บัญชีช่องทางหนึ่งชุด (เช่น บัญชี WhatsApp "personal" เทียบกับ "biz")
    • binding: กำหนดเส้นทางข้อความขาเข้าไปยัง agentId ตาม (channel, accountId, peer) และ ID ของ guild/team ที่ไม่บังคับ
    • แชตโดยตรงจะยุบเป็น agent:<agentId>:<mainKey> ("หลัก" ต่อเอเจนต์; session.mainKey)

    ตัวอย่างแพลตฟอร์ม

    บอต Discord ต่อเอเจนต์

    แต่ละบัญชีบอต Discord จะจับคู่กับ accountId ที่ไม่ซ้ำกัน ผูกแต่ละบัญชีกับเอเจนต์ และเก็บรายการอนุญาตแยกตามบอต

    {
      agents: {
        list: [
          { id: "main", workspace: "~/.openclaw/workspace-main" },
          { id: "coding", workspace: "~/.openclaw/workspace-coding" },
        ],
      },
      bindings: [
        { agentId: "main", match: { channel: "discord", accountId: "default" } },
        { agentId: "coding", match: { channel: "discord", accountId: "coding" } },
      ],
      channels: {
        discord: {
          groupPolicy: "allowlist",
          accounts: {
            default: {
              token: "DISCORD_BOT_TOKEN_MAIN",
              guilds: {
                "123456789012345678": {
                  channels: {
                    "222222222222222222": { allow: true, requireMention: false },
                  },
                },
              },
            },
            coding: {
              token: "DISCORD_BOT_TOKEN_CODING",
              guilds: {
                "123456789012345678": {
                  channels: {
                    "333333333333333333": { allow: true, requireMention: false },
                  },
                },
              },
            },
          },
        },
      },
    }
    
    • เชิญบอทแต่ละตัวเข้ากิลด์และเปิดใช้งาน Message Content Intent
    • โทเค็นอยู่ใน channels.discord.accounts.<id>.token (บัญชีเริ่มต้นสามารถใช้ DISCORD_BOT_TOKEN ได้)
    บอท Telegram ต่อ agent
    {
      agents: {
        list: [
          { id: "main", workspace: "~/.openclaw/workspace-main" },
          { id: "alerts", workspace: "~/.openclaw/workspace-alerts" },
        ],
      },
      bindings: [
        { agentId: "main", match: { channel: "telegram", accountId: "default" } },
        { agentId: "alerts", match: { channel: "telegram", accountId: "alerts" } },
      ],
      channels: {
        telegram: {
          accounts: {
            default: {
              botToken: "123456:ABC...",
              dmPolicy: "pairing",
            },
            alerts: {
              botToken: "987654:XYZ...",
              dmPolicy: "allowlist",
              allowFrom: ["tg:123456789"],
            },
          },
        },
      },
    }
    
    • สร้างบอทหนึ่งตัวต่อ agent ด้วย BotFather แล้วคัดลอกแต่ละโทเค็น
    • โทเค็นอยู่ใน channels.telegram.accounts.<id>.botToken (บัญชีเริ่มต้นสามารถใช้ TELEGRAM_BOT_TOKEN ได้)
    หมายเลข WhatsApp ต่อ agent

    เชื่อมโยงแต่ละบัญชีก่อนเริ่ม Gateway:

    openclaw channels login --channel whatsapp --account personal
    openclaw channels login --channel whatsapp --account biz
    

    ~/.openclaw/openclaw.json (JSON5):

    {
      agents: {
        list: [
          {
            id: "home",
            default: true,
            name: "Home",
            workspace: "~/.openclaw/workspace-home",
            agentDir: "~/.openclaw/agents/home/agent",
          },
          {
            id: "work",
            name: "Work",
            workspace: "~/.openclaw/workspace-work",
            agentDir: "~/.openclaw/agents/work/agent",
          },
        ],
      },
    
      // Deterministic routing: first match wins (most-specific first).
      bindings: [
        { agentId: "home", match: { channel: "whatsapp", accountId: "personal" } },
        { agentId: "work", match: { channel: "whatsapp", accountId: "biz" } },
    
        // Optional per-peer override (example: send a specific group to work agent).
        {
          agentId: "work",
          match: {
            channel: "whatsapp",
            accountId: "personal",
            peer: { kind: "group", id: "[email protected]" },
          },
        },
      ],
    
      // Off by default: agent-to-agent messaging must be explicitly enabled + allowlisted.
      tools: {
        agentToAgent: {
          enabled: false,
          allow: ["home", "work"],
        },
      },
    
      channels: {
        whatsapp: {
          accounts: {
            personal: {
              // Optional override. Default: ~/.openclaw/credentials/whatsapp/personal
              // authDir: "~/.openclaw/credentials/whatsapp/personal",
            },
            biz: {
              // Optional override. Default: ~/.openclaw/credentials/whatsapp/biz
              // authDir: "~/.openclaw/credentials/whatsapp/biz",
            },
          },
        },
      },
    }
    

    รูปแบบทั่วไป

    WhatsApp รายวัน + งานเชิงลึกบน Telegram

    แยกตามช่องทาง: ส่ง WhatsApp ไปยัง agent สำหรับการใช้งานประจำวันที่รวดเร็ว และส่ง Telegram ไปยัง agent Opus

    {
      agents: {
        list: [
          {
            id: "chat",
            name: "Everyday",
            workspace: "~/.openclaw/workspace-chat",
            model: "anthropic/claude-sonnet-4-6",
          },
          {
            id: "opus",
            name: "Deep Work",
            workspace: "~/.openclaw/workspace-opus",
            model: "anthropic/claude-opus-4-6",
          },
        ],
      },
      bindings: [
        { agentId: "chat", match: { channel: "whatsapp" } },
        { agentId: "opus", match: { channel: "telegram" } },
      ],
    }
    

    หมายเหตุ:

    • หากคุณมีหลายบัญชีสำหรับช่องทางหนึ่ง ให้เพิ่ม accountId ใน binding (เช่น { channel: "whatsapp", accountId: "personal" })
    • หากต้องการส่ง DM/กลุ่มเดียวไปยัง Opus โดยให้ที่เหลือยังอยู่บน chat ให้เพิ่ม binding match.peer สำหรับ peer นั้น การจับคู่ peer จะชนะกฎทั้งช่องทางเสมอ

    ช่องทางเดียวกัน ส่งหนึ่ง peer ไปยัง Opus

    ให้ WhatsApp อยู่บน agent ที่รวดเร็ว แต่ส่งหนึ่ง DM ไปยัง Opus:

    {
      agents: {
        list: [
          {
            id: "chat",
            name: "Everyday",
            workspace: "~/.openclaw/workspace-chat",
            model: "anthropic/claude-sonnet-4-6",
          },
          {
            id: "opus",
            name: "Deep Work",
            workspace: "~/.openclaw/workspace-opus",
            model: "anthropic/claude-opus-4-6",
          },
        ],
      },
      bindings: [
        {
          agentId: "opus",
          match: { channel: "whatsapp", peer: { kind: "direct", id: "+15551234567" } },
        },
        { agentId: "chat", match: { channel: "whatsapp" } },
      ],
    }
    

    binding ของ peer จะชนะเสมอ ดังนั้นให้วางไว้เหนือกฎทั้งช่องทาง

    agent ครอบครัวที่ผูกกับกลุ่ม WhatsApp

    ผูก agent สำหรับครอบครัวโดยเฉพาะกับกลุ่ม WhatsApp กลุ่มเดียว พร้อมการกั้นด้วยการกล่าวถึงและนโยบายเครื่องมือที่เข้มงวดยิ่งขึ้น:

    {
      agents: {
        list: [
          {
            id: "family",
            name: "Family",
            workspace: "~/.openclaw/workspace-family",
            identity: { name: "Family Bot" },
            groupChat: {
              mentionPatterns: ["@family", "@familybot", "@Family Bot"],
            },
            sandbox: {
              mode: "all",
              scope: "agent",
            },
            tools: {
              allow: [
                "exec",
                "read",
                "sessions_list",
                "sessions_history",
                "sessions_send",
                "sessions_spawn",
                "session_status",
              ],
              deny: ["write", "edit", "apply_patch", "browser", "canvas", "nodes", "cron"],
            },
          },
        ],
      },
      bindings: [
        {
          agentId: "family",
          match: {
            channel: "whatsapp",
            peer: { kind: "group", id: "[email protected]" },
          },
        },
      ],
    }
    

    หมายเหตุ:

    • รายการอนุญาต/ปฏิเสธเครื่องมือคือ เครื่องมือ ไม่ใช่ Skills หาก skill ต้องเรียกใช้ไบนารี ให้ตรวจสอบว่าอนุญาต exec และไบนารีนั้นมีอยู่ใน sandbox
    • สำหรับการกั้นที่เข้มงวดยิ่งขึ้น ให้ตั้งค่า agents.list[].groupChat.mentionPatterns และเปิดใช้งาน allowlist ของกลุ่มสำหรับช่องทางไว้

    การกำหนดค่า sandbox และเครื่องมือต่อ agent

    agent แต่ละตัวสามารถมี sandbox และข้อจำกัดเครื่องมือของตนเองได้:

    {
      agents: {
        list: [
          {
            id: "personal",
            workspace: "~/.openclaw/workspace-personal",
            sandbox: {
              mode: "off",  // No sandbox for personal agent
            },
            // No tool restrictions - all tools available
          },
          {
            id: "family",
            workspace: "~/.openclaw/workspace-family",
            sandbox: {
              mode: "all",     // Always sandboxed
              scope: "agent",  // One container per agent
              docker: {
                // Optional one-time setup after container creation
                setupCommand: "apt-get update && apt-get install -y git curl",
              },
            },
            tools: {
              allow: ["read"],                    // Only read tool
              deny: ["exec", "write", "edit", "apply_patch"],    // Deny others
            },
          },
        ],
      },
    }
    

    ประโยชน์:

    • การแยกด้านความปลอดภัย: จำกัดเครื่องมือสำหรับ agent ที่ไม่น่าเชื่อถือ
    • การควบคุมทรัพยากร: sandbox เฉพาะ agent บางตัว ขณะที่ให้ตัวอื่นอยู่บนโฮสต์
    • นโยบายที่ยืดหยุ่น: สิทธิ์แตกต่างกันตาม agent

    ดู sandbox และเครื่องมือแบบหลาย agent สำหรับตัวอย่างโดยละเอียด

    ที่เกี่ยวข้อง

    • agent ACP — การรันชุดเครื่องมือเขียนโค้ดภายนอก
    • การกำหนดเส้นทางช่องทาง — วิธีที่ข้อความถูกกำหนดเส้นทางไปยัง agent
    • Presence — presence และความพร้อมใช้งานของ agent
    • Session — การแยก session และการกำหนดเส้นทาง
    • Sub-agents — การ spawn การรัน agent เบื้องหลัง