Skip to content

About

๐Ÿ›  Chrome Built In AI ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๐Ÿ› 

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Latest commit

ย 

History

16 Commits

Folders and files

Repository files navigation

My Little Agent

Chrome Built-in AI ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์กฐ๋ฆฝ ํ”„๋ ˆ์ž„์›Œํฌ
์„œ๋ฒ„, API ํ‚ค, ํ† ํฐ ๋น„์šฉ ์—†์ด ์‚ฌ์šฉ์ž ๋ธŒ๋ผ์šฐ์ € ์•ˆ์—์„œ ์ „๋ถ€ ๋๋‚ฉ๋‹ˆ๋‹ค.

gzip 6.17 kB ์˜์กด์„ฑ 0๊ฐœ TypeScript strict MIT license

ํ•œ๊ตญ์–ด | English

import { Agent, chain, summarizer, tool, translator } from 'my-little-agent'

// 1. ์—์ด์ „ํŠธ๊ฐ€ ๋ถ€๋ฅด๋Š” ์ปค์Šคํ…€ ํ•จ์ˆ˜
const findOrder = tool<{ id: string }>({
  name: 'findOrder',
  description: '์ฃผ๋ฌธ๋ฒˆํ˜ธ๋กœ ์ฃผ๋ฌธ ์ •๋ณด๋ฅผ ์กฐํšŒํ•œ๋‹ค',
  parameters: {
    type: 'object',
    properties: { id: { type: 'string' } },
    required: ['id'],
  },
  execute: ({ id }) => db.orders.get(id),
})

// 2. ๋„๊ตฌ๋ฅผ ์ฅ” ์—์ด์ „ํŠธ
const desk = new Agent({
  instruction: '๊ณ ๊ฐ ์ƒ๋‹ด์›. ์ฃผ๋ฌธ ๊ด€๋ จ ์งˆ๋ฌธ์€ ๋ฐ˜๋“œ์‹œ ๋„๊ตฌ๋กœ ํ™•์ธํ•˜๊ณ  ๋‹ตํ•œ๋‹ค.',
  tools: [findOrder],
  stateless: true,
})

// 3. ๋‹ต๋ณ€์„ ์ค„์ด๊ณ  ํ•œ๊ตญ์–ด๋กœ. ๋’ค ๋‘ ๋‹จ๊ณ„๋Š” Chrome ์ „์šฉ ๋ชจ๋ธ์ด๋ผ ์—์ด์ „ํŠธ์˜ ์ปจํ…์ŠคํŠธ ์ฐฝ์„ 1ํ† ํฐ๋„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค.
const flow = chain(desk, summarizer({ length: 'short' }), translator({ to: 'ko' }))

await flow.run('Where is my order A-1024?')

์ด๊ฑธ ์“ฐ๋ฉด ๋ญ๊ฐ€ ์ข‹๋‚˜์š”?

Built-in AI์—์„œ ์˜ค๋Š” ์žฅ์ 

  1. ์„œ๋ฒ„๋ฆฌ์Šค โ€” ๋ชจ๋ธ์ด Chrome ์•ˆ์— ์žˆ์Šต๋‹ˆ๋‹ค. ํ”„๋ก์‹œ๋„, ํ‚ค ๊ด€๋ฆฌ๋„, ๊ณผ๊ธˆ ๋Œ€์‹œ๋ณด๋“œ๋„ ์—†์Šต๋‹ˆ๋‹ค.
  2. ๋น„์šฉ 0์› โ€” ๊ฐœ๋ฐœ์ž์—๊ฒŒ ํ† ํฐ ์š”๊ธˆ์ด ์ฒญ๊ตฌ๋˜์ง€ ์•Š๊ณ , ์‚ฌ์šฉ์ž๊ฐ€ ์ž๊ธฐ API ํ‚ค๋ฅผ ๋ถ™์—ฌ๋„ฃ์„ ํ•„์š”๋„ ์—†์Šต๋‹ˆ๋‹ค.
  3. ์ •๋ณด ๋ณดํ˜ธ โ€” ์˜๋ฃŒยท๊ธˆ์œตยท์‚ฌ๋‚ด ๋ฌธ์„œ์ฒ˜๋Ÿผ ์™ธ๋ถ€ ์ „์†ก์ด ๋ง‰ํžŒ ๋ฐ์ดํ„ฐ๋„ ๋‹ค๋ฃฐ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค. ์˜คํ”„๋ผ์ธ์—์„œ๋„ ๋•๋‹ˆ๋‹ค.

My Little Agent์—์„œ ์ถ”๊ฐ€๋กœ ์˜ค๋Š” ์žฅ์ 

  1. ๋„๊ตฌ ํ˜ธ์ถœ โ€” ๋ชจ๋ธ์—๊ฒŒ ํ•จ์ˆ˜๋ฅผ ์ฅ์—ฌ์ฃผ๋ ค๋ฉด, ์ถœ๋ ฅ์„ JSON Schema๋กœ ๋ฌถ๊ณ  โ†’ ํŒŒ์‹ฑํ•˜๊ณ  โ†’ ์—†๋Š” ๋„๊ตฌ๋ฅผ ์ง€๋ชฉํ•˜๋ฉด ๋˜๋Œ๋ฆฌ๊ณ  โ†’ ์‹คํ–‰ ๊ฒฐ๊ณผ๋ฅผ ๋‹ค์‹œ ๋„ฃ์–ด์ฃผ๊ณ  โ†’ ์ตœ์ข… ๋‹ต์ด ๋‚˜์˜ฌ ๋•Œ๊นŒ์ง€ ๋ฐ˜๋ณตํ•ด์•ผ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๋ฃจํ”„๋ฅผ tools: [...] ํ•œ ์ค„๋กœ ์ค„์˜€์Šต๋‹ˆ๋‹ค.

  2. ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ์กฐ๋ฆฝ โ€” ์ˆœ์ฐจ(chain) ยท ๋™์‹œ(parallel) ยท ๋ถ„๊ธฐ(router) ยท ์žฌ์ž‘์„ฑ(refine) ๋„ค ๊ฐ€์ง€. ๊ฒฐ๊ณผ๋ฌผ๋„ ๋‹ค์‹œ ๋ถ€ํ’ˆ์ด๋ผ ์„œ๋กœ ์ค‘์ฒฉ๋ฉ๋‹ˆ๋‹ค. chain(parallel([a, b]), router({ โ€ฆ })) ์ฒ˜๋Ÿผ์š”.

  3. ์ปจํ…์ŠคํŠธ ์ฐฝ ๊ด€๋ฆฌ โ€” ์˜จ๋””๋ฐ”์ด์Šค ๋ชจ๋ธ์˜ ์ฐฝ์€ ์ˆ˜์ฒœ ํ† ํฐ๋ฟ์ด๋ผ ๊ธˆ๋ฐฉ ๋„˜์นฉ๋‹ˆ๋‹ค. ์—์ด์ „ํŠธ๋งˆ๋‹ค ์ฐฝ์„ ๋”ฐ๋กœ ์”๋‹ˆ๋‹ค. ํ๋ฆ„์„ ๋‹ค์‹œ ๋Œ๋ ค๋„ ์ง€๋‚œ ๋Œ€ํ™”๊ฐ€ ์Œ“์ด์ง€ ์•Š๊ณ , ํฐ ๋„๊ตฌ ๊ฒฐ๊ณผ๋Š” ์ž˜๋ ค์„œ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๋„˜์น  ๋งŒํ•œ ์‹ค์ˆ˜๋Š” ์ฝ˜์†”์ด ๋จผ์ € ์งš์–ด์ค๋‹ˆ๋‹ค.

  4. ๋ชจ๋ธ ๋ฐ– ๊ธฐ์–ต โ€” ์ฐฝ์— ๋‹ค ๋‹ด์„ ์ˆ˜ ์—†๋Š” ๊ฒƒ์€ ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ์— ๋‘๊ณ  ํ•„์š”ํ•  ๋•Œ๋งŒ ๊บผ๋‚ด ์”๋‹ˆ๋‹ค. ๊ฐ™์€ ์ €์žฅ์†Œ๋ฅผ ์—ฌ๋Ÿฌ ์—์ด์ „ํŠธ์—๊ฒŒ ๋„˜๊ธฐ๋ฉด ๊ทธ๋Œ€๋กœ ๊ณต์œ  ์ž‘์—…ํŒ์ด ๋ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ๋‚จ์Šต๋‹ˆ๋‹ค.

  5. ๋ชป ์“ฐ๋Š” ํ™˜๊ฒฝ ์šฐํšŒ โ€” Chrome ์ „์šฉ ๋ชจ๋ธ์€ ์–ธ์–ด์Œ๋งˆ๋‹ค, ๊ธฐ๋Šฅ๋งˆ๋‹ค ์‚ฌ์šฉ ๊ฐ€๋Šฅ ์—ฌ๋ถ€๊ฐ€ ๋‹ค๋ฆ…๋‹ˆ๋‹ค. ์—ฌ์„ฏ ๊ตฐ๋ฐ๋ฅผ ์ผ์ผ์ด ํ™•์ธํ•˜๋Š” ๋Œ€์‹  fallback์— ๋Œ€์ฒด ์—์ด์ „ํŠธ๋ฅผ ํ•˜๋‚˜ ๊ฑธ์–ด๋‘๋ฉด ๋ฉ๋‹ˆ๋‹ค.

๋จผ์ € ์•Œ์•„๋‘˜ ๊ฒƒ โ€” ํ•„์ˆ˜ ๊ธฐ๋Šฅ์—๋Š” ์“ฐ์ง€ ๋งˆ์„ธ์š”

๋ชจ๋ธ์ด ์‚ฌ์šฉ์ž ๊ธฐ๊ธฐ์—์„œ ๋Œ๊ธฐ ๋•Œ๋ฌธ์—, ๊ธฐ๊ธฐ ์‚ฌ์ •์ด ์•ˆ ๋งž์œผ๋ฉด ๊ธฐ๋Šฅ ์ž์ฒด๊ฐ€ ์•„์˜ˆ ์ผœ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค.

๋ธŒ๋ผ์šฐ์ € Chrome 138 ์ด์ƒ ๋ฐ์Šคํฌํ†ฑ (๋ชจ๋ฐ”์ผ ๋ฏธ์ง€์›)
์ €์žฅ ๊ณต๊ฐ„ ๋‹ค์šด๋กœ๋“œ ์‹œ์ž‘ ์ „ 22GB ์ด์ƒ ์—ฌ์œ , ์ดํ›„ 10GB ๋ฐ‘์œผ๋กœ ๋–จ์–ด์ง€๋ฉด ๋ชจ๋ธ ์‚ญ์ œ
๊ธฐ๊ธฐ ์„ฑ๋Šฅ VRAM 4GB ์ดˆ๊ณผ, ๋˜๋Š” RAM 16GB + 4์ฝ”์–ด ์ด์ƒ
์ฒซ ์‹คํ–‰ ๋ชจ๋ธ ์•ฝ 4GB ๋‹ค์šด๋กœ๋“œ. ๊ทธ๋™์•ˆ ๋Œ€๊ธฐ

๋ชจ๋ธ์€ ์‚ฌ์ดํŠธ๋ณ„์ด ์•„๋‹ˆ๋ผ ๋ธŒ๋ผ์šฐ์ € ์ „์—ญ์ด๋ผ, ์‚ฌ์šฉ์ž๊ฐ€ ๋‹ค๋ฅธ ๊ณณ์—์„œ ์ด๋ฏธ ํ•œ ๋ฒˆ ๋ฐ›์•˜๋‹ค๋ฉด ๊ทธ๋Œ€๋กœ ์”๋‹ˆ๋‹ค.

"์žˆ์œผ๋ฉด ๋” ์ข‹์€" ์ž๋ฆฌ์— ์“ฐ์„ธ์š”. ์—†์œผ๋ฉด ์„œ๋น„์Šค๊ฐ€ ์•ˆ ๋Œ์•„๊ฐ€๋Š” ์ž๋ฆฌ์—๋Š” ์“ฐ์ง€ ๋งˆ์„ธ์š”. ๊ธฐ๋Šฅ์„ ์ˆจ๊ธฐ๊ฑฐ๋‚˜, ์„œ๋ฒ„ ๊ฒฝ๋กœ๋ฅผ ๋”ฐ๋กœ ๋‘๊ฑฐ๋‚˜, fallback์„ ์ง€์ •ํ•ด ๋‘์„ธ์š”.

๋ฌธ์„œ

์‹œ์ž‘ํ•˜๊ธฐ

pnpm add my-little-agent

๋ชจ๋ธ์€ Chrome์ด ์•Œ์•„์„œ ๋‚ด๋ ค๋ฐ›๊ณ  ๊ด€๋ฆฌํ•ฉ๋‹ˆ๋‹ค. ์•ฑ ๋ฒˆ๋“ค์—๋Š” ์•„๋ฌด๊ฒƒ๋„ ๋Š˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ง€๊ธˆ ์ƒํƒœ๋Š” chrome://on-device-internals์—์„œ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

๊ธฐ๋ณธ์€ ์„ธ ์ค„์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค

import { Agent } from 'my-little-agent'

const agent = new Agent({ instruction: 'ํ•œ๊ตญ์–ด๋กœ ์งง๊ณ  ๋ช…ํ™•ํ•˜๊ฒŒ ๋‹ตํ•œ๋‹ค.' })
console.log(await agent.send('์žฌ๊ท€๋ฅผ ํ•œ ๋ฌธ์žฅ์œผ๋กœ ์„ค๋ช…ํ•ด์ค˜'))

์ด ์„ธ ์ค„๋งŒ ๋†“๊ณ  ๋ณด๋ฉด Chrome API๋ฅผ ์ง์ ‘ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๊ฑฐ์˜ ๊ฐ™์Šต๋‹ˆ๋‹ค. ์ฐจ์ด๋Š” ๋„๊ตฌ๋ฅผ ๋ถ™์ด๊ณ  ์—ฌ๋Ÿฌ ๊ฐœ๋ฅผ ์ด์–ด ๋ถ™์ด๋Š” ์ˆœ๊ฐ„๋ถ€ํ„ฐ ์ƒ๊น๋‹ˆ๋‹ค. ์•„๋ž˜๋กœ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

๊ตฌ์„ฑ ์š”์†Œ

Agent์™€ Workflow๋Š” ๊ฐ™์€ ๋ชจ์–‘์ž…๋‹ˆ๋‹ค. { name, run(input) } โ€” ์ด๋ฆ„์ด ์žˆ๊ณ , ๋ฌธ์ž์—ด์„ ๋ฐ›์•„ ๋ฌธ์ž์—ด์„ ๋Œ๋ ค์ค๋‹ˆ๋‹ค. ์ฝ”๋“œ์—์„œ๋Š” ์ด ๋ชจ์–‘์„ Runnable์ด๋ผ๊ณ  ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์–ด๋Œ‘ํ„ฐ ์—†์ด ์„œ๋กœ ๋ผ์›Œ์ง€๋Š” ์ด์œ ๊ฐ€ ์ด๊ฒƒ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค.

์š”์†Œ ๊ธฐ๋Šฅ ์˜ˆ์‹œ
Availability ์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ๋˜๋‚˜ ํ™•์ธ isSupported(), availability()
Agent AI ๋ชจ๋ธ ํ•œ ๋ช… new Agent({ instruction: '๋„ˆ๋Š” ๋ฒˆ์—ญ๊ฐ€๋‹ค' })
Tool ๋ชจ๋ธ์ด ๋ถ€๋ฅด๋Š” ์ปค์Šคํ…€ ํ•จ์ˆ˜ tool({ โ€ฆ })
Workflow Agent๋ฅผ ์ด์–ด ๋ถ™์ž„ chain, parallel, router, refine
Memory ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ localMemory(), memoryTools()

Availability์™€ Memory๋Š” Runnable์ด ์•„๋‹™๋‹ˆ๋‹ค. ๊ฐ๊ฐ ์‹œ์ž‘ ์ „ ํ™•์ธ๊ณผ ์ €์žฅ์†Œ์ด๊ณ , ๋‚˜๋จธ์ง€๊ฐ€ ๋ถ€ํ’ˆ์ž…๋‹ˆ๋‹ค.

Availability โ€” ์ด ๋ธŒ๋ผ์šฐ์ €์—์„œ ์‚ฌ์šฉ ๊ฐ€๋Šฅํ•œ์ง€ ํ™•์ธํ•˜๊ธฐ

๋ชจ๋“  ๋ฐฉ๋ฌธ์ž๊ฐ€ ๋‚ด์žฅ AI๋ฅผ ์“ธ ์ˆ˜ ์žˆ๋Š” ๊ฑด ์•„๋‹™๋‹ˆ๋‹ค. ์•ˆ ๋˜๋Š” ํ™˜๊ฒฝ์—์„œ๋„ ์˜ˆ์™ธ๋ฅผ ๋˜์ง€์ง€ ์•Š๊ณ  'unavailable'์„ ๋Œ๋ ค์ฃผ๋‹ˆ, ๋ถ„๊ธฐ๋งŒ ํ•ด ๋‘๋ฉด ๋ฉ๋‹ˆ๋‹ค.

import { availability, isSupported } from 'my-little-agent'

isSupported() // boolean โ€” ์ „์—ญ LanguageModel ์กด์žฌ ์—ฌ๋ถ€๋งŒ (๋™๊ธฐ)
await availability() // ๋ชจ๋ธ๊นŒ์ง€ ์ค€๋น„๋๋Š”์ง€ (๋น„๋™๊ธฐ)
// โ†’ 'unavailable' | 'downloadable' | 'downloading' | 'available'
๊ฐ’ ๋œป ํ•  ์ผ
'unavailable' ์ด ๊ธฐ๊ธฐ์—์„œ๋Š” ๋ชป ์”€ ๊ธฐ๋Šฅ์„ ์ˆจ๊ธฐ๊ฑฐ๋‚˜ ์„œ๋ฒ„ ๊ฒฝ๋กœ๋กœ
'downloadable' ์“ธ ์ˆ˜ ์žˆ์ง€๋งŒ ์•„์ง ์•ˆ ๋ฐ›์Œ ์ฒซ ํ˜ธ์ถœ์—์„œ ๋ฐ›๊ธฐ ์‹œ์ž‘. ์ง„ํ–‰๋ฅ ์„ ๋ณด์—ฌ์ค„ ๊ฒƒ
'downloading' ์ง€๊ธˆ ๋ฐ›๋Š” ์ค‘ ๊ธฐ๋‹ค๋ฆฌ๊ฒŒ ํ•˜๊ณ  ์ง„ํ–‰๋ฅ  ํ‘œ์‹œ
'available' ๋ฐ”๋กœ ์‚ฌ์šฉ ๊ฐ€๋Šฅ ๊ทธ๋ƒฅ ์“ฐ๋ฉด ๋จ
if ((await availability()) === 'downloadable') {
  // ์ฒซ ํ˜ธ์ถœ์—์„œ ์•ฝ 4GB๋ฅผ ๋‚ด๋ ค๋ฐ›๋Š”๋‹ค. ์ง„ํ–‰๋ฅ ์„ ๊ผญ ๋ณด์—ฌ์ฃผ์ž.
  new Agent({ onDownloadProgress: (p) => setProgress(Math.round(p * 100)) })
}

Agent โ€” AI ๋ชจ๋ธ ํ•œ ๋ช…

์—ญํ•  ํ•˜๋‚˜์— ์„ธ์…˜ ํ•˜๋‚˜์ž…๋‹ˆ๋‹ค. ์„ธ์…˜์€ ์ฒซ ํ˜ธ์ถœ ๋•Œ ๋งŒ๋“ค์–ด์ง€๋ฏ€๋กœ, ์ธ์Šคํ„ด์Šค๋ฅผ ๋ฏธ๋ฆฌ ์—ฌ๋Ÿฌ ๊ฐœ ๋งŒ๋“ค์–ด ๋‘ฌ๋„ ๋ถ€๋‹ด์ด ์—†์Šต๋‹ˆ๋‹ค.

const writer = new Agent({
  name: 'writer',
  instruction: '์ œํ’ˆ ๊ด‘๊ณ  ๋ฌธ๊ตฌ๋ฅผ ์“ฐ๋Š” ์นดํ”ผ๋ผ์ดํ„ฐ.',
})

await writer.send('์—ฌ๋ฆ„ ์„ธ์ผ ๋ฐฐ๋„ˆ ๋ฌธ๊ตฌ ์„ธ ๊ฐœ๋งŒ')

option

์ „๋ถ€ ์„ ํƒ์ž…๋‹ˆ๋‹ค. new Agent()๋งŒ ํ•ด๋„ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค.

์˜ต์…˜ ๊ธฐ๋ณธ๊ฐ’ ์–ธ์ œ ์“ฐ๋‚˜
instruction ์—†์Œ ์ด ์—์ด์ „ํŠธ์˜ ์—ญํ• . ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ๋กœ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ๊ฑฐ์˜ ํ•ญ์ƒ ์”๋‹ˆ๋‹ค
tools [] ๋ชจ๋ธ์—๊ฒŒ ์ฅ์—ฌ์ค„ ํ•จ์ˆ˜๋“ค. ํ•˜๋‚˜๋ผ๋„ ์žˆ์œผ๋ฉด ํˆด ํ˜ธ์ถœ ๋ฃจํ”„๋กœ ๋™์ž‘ํ•ฉ๋‹ˆ๋‹ค
stateless false true๋ฉด ํ˜ธ์ถœ๋งˆ๋‹ค ๋ฐฑ์ง€์—์„œ ์‹œ์ž‘. chainยทparallel์— ๋„ฃ์„ ์—์ด์ „ํŠธ๋Š” ์ผœ์„ธ์š”
name 'agent' ๋กœ๊ทธ์™€ parallel ๊ฒฐ๊ณผ ๋ผ๋ฒจ์— ์“ฐ์ž…๋‹ˆ๋‹ค. ์—ฌ๋Ÿฟ์„ ์—ฎ์„ ๋•Œ ๋ถ™์ด์„ธ์š”
today false ์˜ค๋Š˜ ๋‚ ์งœ๋ฅผ ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ์— ํ•œ ์ค„ ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋‚ ์งœยท๊ธฐ๊ฐ„์„ ๋‹ค๋ฃจ๋Š” ์—์ด์ „ํŠธ๋ฉด ์ผœ์„ธ์š”
maxSteps 5 ํˆด ๋ฃจํ”„ ๋ฐ˜๋ณต ์ƒํ•œ. ๋„๊ตฌ๋ฅผ ์—ฌ๋Ÿฌ ๋ฒˆ ๊ฑฐ์ณ์•ผ ํ•˜๋Š” ์ž‘์—…์ด๋ฉด ์˜ฌ๋ฆฌ์„ธ์š”
maxToolResultChars 4000 ๋„๊ตฌ ๊ฒฐ๊ณผ๋ฅผ ์ด ๊ธธ์ด์—์„œ ์ž๋ฆ…๋‹ˆ๋‹ค. ํฐ JSON์„ ๋Œ๋ ค์ฃผ๋Š” ๋„๊ตฌ๊ฐ€ ์žˆ์œผ๋ฉด ์ค„์ด์„ธ์š”
history [] ์ด์ „ ๋Œ€ํ™”๋ฅผ ๋„ฃ์–ด ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจ ํ›„ ์ด์–ด๊ฐ€๊ธฐ์— ์”๋‹ˆ๋‹ค
temperature / topK Chrome ๊ธฐ๋ณธ๊ฐ’ 0์— ๊ฐ€๊นŒ์šธ์ˆ˜๋ก ๊ฒฐ์ •์ . ๋‘˜ ๋‹ค ์ฃผ๊ฑฐ๋‚˜ ๋‘˜ ๋‹ค ์•ˆ ์ฃผ๊ฑฐ๋‚˜๋งŒ ๋ฉ๋‹ˆ๋‹ค
onEvent ์—†์Œ ํˆด ํ˜ธ์ถœยท๊ฒฐ๊ณผยท์˜ค๋ฅ˜ยท์ปจํ…์ŠคํŠธ ์ดˆ๊ณผ๋ฅผ ํ˜๋ ค๋ณด๋ƒ…๋‹ˆ๋‹ค. ๋กœ๊ทธ UI์— ์—ฐ๊ฒฐํ•˜์„ธ์š”
onDownloadProgress ์—†์Œ ๋ชจ๋ธ ๋‹ค์šด๋กœ๋“œ ์ง„ํ–‰๋ฅ  0~1
signal ์—†์Œ AbortSignal. ์ด ์—์ด์ „ํŠธ์˜ ์‹คํ–‰ ์ „์ฒด๋ฅผ ์ทจ์†Œํ•ฉ๋‹ˆ๋‹ค
expectedInputs / expectedOutputs ํ…์ŠคํŠธ ๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ์„ ์“ธ ๋•Œ ์„ ์–ธํ•ฉ๋‹ˆ๋‹ค. ์˜ˆ: [{ type: 'image' }]
// ์ฑ—๋ด‡ โ€” ๋Œ€ํ™”๋ฅผ ์ด์–ด๊ฐ€์•ผ ํ•˜๋‹ˆ stateless๋ฅผ ๋•๋‹ˆ๋‹ค(๊ธฐ๋ณธ๊ฐ’)
const bot = new Agent({ instruction: '์นœ์ ˆํ•œ ์ƒ๋‹ด์›.' })

// ์›Œํฌํ”Œ๋กœ์šฐ ๋‹จ๊ณ„ โ€” ๋งค๋ฒˆ ๋ฐฑ์ง€์—์„œ ์‹œ์ž‘ํ•ด์•ผ ํ•˜๋‹ˆ ์ผญ๋‹ˆ๋‹ค
const step = new Agent({ instruction: 'ํ•ต์‹ฌ๋งŒ ์„ธ ๋ฌธ์žฅ์œผ๋กœ.', stateless: true })

// ๋„๊ตฌ๋ฅผ ์“ฐ๋Š” ์—์ด์ „ํŠธ โ€” ์—ฌ๋Ÿฌ ๋ฒˆ ๊ฑฐ์ณ์•ผ ํ•˜๋ฉด maxSteps๋ฅผ ์˜ฌ๋ฆฝ๋‹ˆ๋‹ค
const researcher = new Agent({
  instruction: '๋„๊ตฌ๋กœ ์‚ฌ์‹ค์„ ํ™•์ธํ•œ ๋’ค ๋‹ตํ•œ๋‹ค.',
  tools: [searchTool, dbTool],
  maxSteps: 8,
  maxToolResultChars: 1500, // ๊ฒฐ๊ณผ๊ฐ€ ํฌ๋ฉด ์ฐฝ์„ ์•„๋ผ๋ ค๊ณ  ์ค„์ž…๋‹ˆ๋‹ค
  onEvent: (e) => console.log(e.type, e),
})

today: true๋ฅผ ์ผœ๋ฉด ์˜ค๋Š˜ ๋‚ ์งœ๊ฐ€ ๋“ค์–ด๊ฐ‘๋‹ˆ๋‹ค. ์˜จ๋””๋ฐ”์ด์Šค ๋ชจ๋ธ์€ ํ•™์Šต ์‹œ์ ์ด ๊ณ ์ •๋ผ ์žˆ๊ณ  ์‹œ๊ณ„๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค. ๋‚ ์งœ๋ฅผ ๋ฌผ์œผ๋ฉด ํ™•์‹ ์— ์ฐฌ ์˜ค๋‹ต์„ ๋ƒ…๋‹ˆ๋‹ค. ์ผœ๋ฉด ์‹œ์Šคํ…œ ํ”„๋กฌํ”„ํŠธ ๋งจ ์•ž์— Today's date: 2026-08-25 (Tuesday). ํ•œ ์ค„์ด ๋ถ™์Šต๋‹ˆ๋‹ค. 20ํ† ํฐ ๋‚จ์ง“์ž…๋‹ˆ๋‹ค.

method

๋Œ๋ ค์ฃผ๋Š” ๊ฒƒ ์–ธ์ œ ์“ฐ๋‚˜
send(input) Promise<string> ๊ธฐ๋ณธ. ์ตœ์ข… ๋‹ต๋ณ€ ํ•˜๋‚˜. ๋„๊ตฌ๊ฐ€ ์žˆ์œผ๋ฉด ๋ฃจํ”„๋ฅผ ๋๊นŒ์ง€ ๋Œ๋ฆฐ ๋’ค ๋‹ตํ•ฉ๋‹ˆ๋‹ค
stream(input) AsyncGenerator<string> ํƒ€์ดํ•‘ ํšจ๊ณผ. ๊ธ€์ž ์กฐ๊ฐ์ด ์ˆœ์„œ๋Œ€๋กœ ๋‚˜์˜ต๋‹ˆ๋‹ค
generate<T>(input, schema) Promise<T> JSON์„ ๋ฐ›๊ณ  ์‹ถ์„ ๋•Œ. ์Šคํ‚ค๋งˆ๋กœ ์ถœ๋ ฅ์„ ๊ฐ•์ œํ•˜๊ณ  ํŒŒ์‹ฑ๊นŒ์ง€ ํ•ด์„œ ๊ฐ์ฒด๋กœ ์ค๋‹ˆ๋‹ค
run(input) Promise<string> send()์˜ ๋ณ„์นญ. Runnable ๊ทœ์•ฝ์„ ๋งž์ถ”๋ ค๊ณ  ์žˆ์Šต๋‹ˆ๋‹ค
fork(name?) Promise<Agent> ์ง€๊ธˆ ๋Œ€ํ™”๋ฅผ ๋ณต์ œํ•œ ์ƒˆ ์—์ด์ „ํŠธ. ๊ฐ™์€ ๋งฅ๋ฝ์—์„œ ๊ฐˆ๋ผ์ ธ ์‹คํ—˜ํ•  ๋•Œ
ready() Promise<Session> ์„ธ์…˜์„ ๋ฏธ๋ฆฌ ๋งŒ๋“ค์–ด ๋‘ก๋‹ˆ๋‹ค. ์ฒซ ์‘๋‹ต ์ง€์—ฐ์„ ์•ž๋‹น๊ธธ ๋•Œ
history PromptMessage[] ์ง€๊ธˆ๊นŒ์ง€์˜ ๋Œ€ํ™”. JSON์œผ๋กœ ์ €์žฅํ–ˆ๋‹ค๊ฐ€ history ์˜ต์…˜์œผ๋กœ ๋ณต์›ํ•ฉ๋‹ˆ๋‹ค
usage { used, total } | null ์ปจํ…์ŠคํŠธ ํ† ํฐ ์‚ฌ์šฉ๋Ÿ‰. ์„ธ์…˜์ด ์—†์œผ๋ฉด null
stateless boolean | undefined ์„ค์ •๊ฐ’. ์ง€์ • ์•ˆ ํ–ˆ์œผ๋ฉด undefined (false๊ฐ€ ์•„๋‹™๋‹ˆ๋‹ค)
reset() โ€” ์„ธ์…˜๊ณผ ๋Œ€ํ™” ๊ธฐ๋ก์„ ๋ชจ๋‘ ๋ฒ„๋ฆฝ๋‹ˆ๋‹ค
destroy() โ€” ์„ธ์…˜๋งŒ ๋ฐ˜๋‚ฉํ•ฉ๋‹ˆ๋‹ค. ๋‹ค ์“ด ์—์ด์ „ํŠธ๋Š” ๊ผญ ํ˜ธ์ถœํ•˜์„ธ์š”
// send โ€” ๊ฐ€์žฅ ํ”ํ•œ ์‚ฌ์šฉ
const answer = await agent.send('์—ฌ๋ฆ„ ์„ธ์ผ ๋ฌธ๊ตฌ ์จ์ค˜')

// stream โ€” ํ•œ ๊ธ€์ž์”ฉ ํ™”๋ฉด์— ํ˜๋ฆฌ๊ธฐ
for await (const chunk of agent.stream('๊ธธ๊ฒŒ ์„ค๋ช…ํ•ด์ค˜')) {
  setText((prev) => prev + chunk)
}

// generate โ€” ํŒŒ์‹ฑ ๊ฑฑ์ • ์—†์ด ๊ฐ์ฒด๋กœ
const parsed = await agent.generate<{ title: string; tags: string[] }>(article, {
  type: 'object',
  properties: {
    title: { type: 'string' },
    tags: { type: 'array', items: { type: 'string' } },
  },
  required: ['title', 'tags'],
})

// ์ €์žฅํ•˜๊ณ  ๋ณต์›ํ•˜๊ธฐ
localStorage.setItem('chat', JSON.stringify(agent.history))
const restored = new Agent({ instruction, history: JSON.parse(saved).slice(-10) })

agent.usage // { used: 812, total: 4096 }
agent.destroy()

๋‚ด์žฅ Agent

Chrome์€ ํ•œ ๊ฐ€์ง€ ์ผ๋งŒ ํ•˜๋Š” ์ „์šฉ ๋ชจ๋ธ์„ ๋”ฐ๋กœ ์ค๋‹ˆ๋‹ค. ์ง์ ‘ ๋งŒ๋“  ์—์ด์ „ํŠธ์™€ ๋˜‘๊ฐ™์ด ์“ฐ๋„๋ก ๊ฐ์‹ธ ๋’€์Šต๋‹ˆ๋‹ค. ์ง€์‹œ๋ฌธ์„ ์“ธ ํ•„์š”๊ฐ€ ์—†๋Š”, ์ด๋ฏธ ์™„์„ฑ๋œ ์—์ด์ „ํŠธ๋ผ๊ณ  ๋ณด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

import {
  languageDetector,
  proofreader,
  rewriter,
  summarizer,
  translator,
  writer,
} from 'my-little-agent'

await translator({ to: 'ko' }).run('Hello') // โ†’ '์•ˆ๋…•ํ•˜์„ธ์š”' (์ถœ๋ฐœ์–ด ์ž๋™ ๊ฐ์ง€)
await summarizer({ type: 'tldr', length: 'short' }).run(longText)
await languageDetector().run('Bonjour') // โ†’ 'fr'
await writer({ tone: 'formal' }).run('ํ™˜๋ถˆ ์š”์ฒญ ๋ฉ”์ผ')
await rewriter({ tone: 'more-casual', length: 'shorter' }).run(draft)
await proofreader().run('I seen him') // โ†’ 'I saw him'
๋‚ด์žฅ Agent ํ•˜๋Š” ์ผ ์˜ต์…˜
translator() ๋ฒˆ์—ญ to (ํ•„์ˆ˜) ยท from (์ƒ๋žตํ•˜๋ฉด ์ž๋™ ๊ฐ์ง€)
summarizer() ์š”์•ฝ type ยท length ยท format ยท sharedContext
languageDetector() ๋ฌด์Šจ ์–ธ์–ด์ธ์ง€ ํŒ๋ณ„. ์–ธ์–ด ์ฝ”๋“œ๋ฅผ ๋Œ๋ ค์คŒ โ€”
writer() ์ง€์‹œ๋Œ€๋กœ ์ƒˆ ๊ธ€ ์ž‘์„ฑ tone ยท length ยท format ยท context
rewriter() ๊ธฐ์กด ๊ธ€์˜ ์–ด์กฐยท๊ธธ์ด ๋ณ€๊ฒฝ tone ยท length ยท format ยท context
proofreader() ๋ฌธ๋ฒ•ยท๊ฐ€๋…์„ฑ ๊ต์ • expectedInputLanguages

์ „๋ถ€ fallback(๋ชป ์“ธ ๋•Œ ๋Œ€์‹  ๋Œ๋ฆด Agent)๊ณผ signal์„ ๋ฐ›์Šต๋‹ˆ๋‹ค. ์•ž์˜ ์…‹์€ Chrome 138๋ถ€ํ„ฐ ์ •์‹์ด๊ณ , ๋’ค์˜ ์…‹์€ ์•„์ง ์˜ค๋ฆฌ์ง„ ํŠธ๋ผ์ด์–ผ์ด๋ผ ๋Œ€๋ถ€๋ถ„์˜ ํ™˜๊ฒฝ์—์„œ fallback์„ ํƒ‘๋‹ˆ๋‹ค.

์ด๊ฒŒ ๊ฐ€์žฅ ํฐ ์ ˆ์•ฝ์ž…๋‹ˆ๋‹ค. ์ „์šฉ ๋ชจ๋ธ์€ ๋ณ„๋„๋กœ ๋Œ๊ธฐ ๋•Œ๋ฌธ์— ๋‹ค๋ฅธ ์—์ด์ „ํŠธ์˜ ์ปจํ…์ŠคํŠธ ์ฐฝ์„ 1ํ† ํฐ๋„ ์“ฐ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ฐฝ์ด ์ˆ˜์ฒœ ํ† ํฐ๋ฟ์ธ ํ™˜๊ฒฝ์—์„œ, ๋ชจ๋ธ์—๊ฒŒ ์•ˆ ์‹œํ‚ค๊ณ  ๋๋‚ผ ์ˆ˜ ์žˆ๋Š” ์ผ์„ ๊ณจ๋ผ๋‚ด๋Š” ๊ฒƒ์ด ๊ฐ€์žฅ ํฌ๊ฒŒ ๋‚จ์Šต๋‹ˆ๋‹ค.

// Agent์™€ ๋‚˜๋ž€ํžˆ ์„ž์ž…๋‹ˆ๋‹ค
chain(analyst, summarizer(), translator({ to: 'ko' }))

// ๋ชป ์“ฐ๋Š” ํ™˜๊ฒฝ์ด๋ฉด ์ด ์—์ด์ „ํŠธ๊ฐ€ ๋Œ€์‹  ๋ฒˆ์—ญํ•ฉ๋‹ˆ๋‹ค
translator({
  to: 'ko',
  fallback: new Agent({ instruction: '์ž…๋ ฅ์„ ํ•œ๊ตญ์–ด๋กœ๋งŒ ๋ฒˆ์—ญํ•ด ์ถœ๋ ฅํ•ด๋ผ.' }),
})

proofreader๋งŒ ๊ฒฐ๊ณผ๊ฐ€ ๋ฌธ์ž์—ด์ด ์•„๋‹ˆ๋ผ ๊ฐ์ฒด๋ผ์„œ ๋‘˜๋กœ ๋‚˜๋‰ฉ๋‹ˆ๋‹ค.

const fix = proofreader()
await fix.run('I seen him') // โ†’ 'I saw him'                      (์กฐ๋ฆฝ์šฉ)
await fix.proofread('I seen him') // โ†’ { correctedInput, corrections } (๋ฐ‘์ค„ ๊ทธ์„ ๋•Œ)

Tool๊ณผ ํ—ท๊ฐˆ๋ฆฌ์ง€ ๋งˆ์„ธ์š”. Tool์€ ๋ชจ๋ธ์ด ํŒ๋‹จํ•ด์„œ ๋ถ€๋ฅด๋Š” ํ•จ์ˆ˜๋ผ tools: [...]์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ์ด์ชฝ์€ Agent๋ผ์„œ chain(...)์— ๋„ฃ์Šต๋‹ˆ๋‹ค. ๋Œ€ํ™”๋ฅผ ์ด์–ด๊ฐ€๋Š” ์—์ด์ „ํŠธ๋Š” ์•„๋‹™๋‹ˆ๋‹ค. send()ยทhistoryยทtools๋Š” ์—†๊ณ  run()๋งŒ ์žˆ์Šต๋‹ˆ๋‹ค.

Tool โ€” ๋ชจ๋ธ์ด ๋ถ€๋ฅด๋Š” ์ปค์Šคํ…€ ํ•จ์ˆ˜

const weather = tool<{ city: string }>({
  name: 'weather',
  description: '๋„์‹œ์˜ ํ˜„์žฌ ๊ธฐ์˜จ์„ ์กฐํšŒํ•œ๋‹ค', // ๋ชจ๋ธ์€ ์ด ํ•œ ์ค„๋งŒ ๋ณด๊ณ  ๊ณ ๋ฅธ๋‹ค
  parameters: { type: 'object', properties: { city: { type: 'string' } } },
  execute: async ({ city }) => fetch(`/api/weather?city=${city}`).then((r) => r.json()),
})

new Agent({ instruction: '๋‚ ์”จ ๋น„์„œ', tools: [weather] })

๋ชจ๋ธ์ด ์Šค์Šค๋กœ ํŒ๋‹จํ•ด์„œ ๋ถ€๋ฆ…๋‹ˆ๋‹ค. ์–ธ์ œ ๋ถ€๋ฅผ์ง€๋Š” description ํ•œ ์ค„๋กœ ์ •ํ•ด์ง€๋‹ˆ ๊ตฌ์ฒด์ ์œผ๋กœ ์“ฐ์„ธ์š”.

๋„๊ตฌ๊ฐ€ ์˜ˆ์™ธ๋ฅผ ๋˜์ ธ๋„ ๋ฃจํ”„๋Š” ์ฃฝ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์˜ค๋ฅ˜ ๋‚ด์šฉ์ด ๋ชจ๋ธ์—๊ฒŒ ์ „๋‹ฌ๋ผ์„œ ๋‹ค๋ฅธ ๋ฐฉ๋ฒ•์„ ์‹œ๋„ํ•ฉ๋‹ˆ๋‹ค.

๋‚ด์žฅ Tool

import { calculator, pageTools } from 'my-little-agent'

new Agent({
  instruction: '์ด ํŽ˜์ด์ง€๋ฅผ ๋ณด๋ฉฐ ๋‹ตํ•˜๋Š” ๋„์šฐ๋ฏธ.',
  tools: [calculator(), ...pageTools()],
})
๋‚ด์žฅ Tool ํ•˜๋Š” ์ผ ์ธ์ž
calculator() ์ •ํ™•ํ•œ ๊ณ„์‚ฐ ์ˆ˜์‹ ๋ฌธ์ž์—ด
pageTools() โ†’ pageInfo ํŽ˜์ด์ง€ ์ œ๋ชฉ, ์ฃผ์†Œ, ์‚ฌ์šฉ์ž๊ฐ€ ์„ ํƒํ•œ ํ…์ŠคํŠธ ์—†์Œ
pageTools() โ†’ readPage ํŽ˜์ด์ง€ ๋ณธ๋ฌธ ํ…์ŠคํŠธ ์—†์Œ

calculator() โ€” ์†Œํ˜• ๋ชจ๋ธ์ด ํ™•์‹คํžˆ ๋ชปํ•˜๋Š” ์ผ์ด ์ •ํ™•ํ•œ ๊ณ„์‚ฐ์ž…๋‹ˆ๋‹ค. ๊ทธ ์นธ์„ ๋„๊ตฌ๋กœ ๋ฉ”์›๋‹ˆ๋‹ค. ์‚ฌ์น™์—ฐ์‚ฐยท๋‚˜๋จธ์ง€(%)ยท๊ด„ํ˜ธยท์†Œ์ˆ˜๋ฅผ ์ง€์›ํ•˜๊ณ , ์ž๋ฆฟ์ˆ˜ ์‰ผํ‘œ(1,200)๋„ ๋ฐ›์•„์ค๋‹ˆ๋‹ค. ๋ฐฑ๋ถ„์œจ์€ 0.15์ฒ˜๋Ÿผ ์†Œ์ˆ˜๋กœ ์”๋‹ˆ๋‹ค.

pageTools() โ€” ์ง€๊ธˆ ์—ด๋ ค ์žˆ๋Š” ํŽ˜์ด์ง€๋ฅผ ์ฝ์Šต๋‹ˆ๋‹ค. ๋„๊ตฌ ๋‘ ๊ฐœ๊ฐ€ ๋‚˜์˜ต๋‹ˆ๋‹ค.

์„œ๋ฒ„ LLM์ด ๊ตฌ์กฐ์ ์œผ๋กœ ๋ชป ํ•˜๋Š” ์ผ์ž…๋‹ˆ๋‹ค. ์‚ฌ์šฉ์ž ํ™”๋ฉด ์•ˆ์— ์žˆ๋Š” ๊ฒƒ์€ ๋ธŒ๋ผ์šฐ์ € ์•ˆ์—์„œ ๋„๋Š” ๋ชจ๋ธ๋งŒ ๋ณผ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

pageTools({
  root: () => document.querySelector('article'), // ์ฝ์„ ๋ฒ”์œ„. ๊ธฐ๋ณธ์€ body
  maxChars: 2000, // ์ ˆ๋‹จ ๊ธฐ์ค€
  includeQuery: false, // ์ฃผ์†Œ์˜ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด ํฌํ•จ ์—ฌ๋ถ€. ๊ธฐ๋ณธ false
})

์ฃผ์†Œ๋Š” ๊ธฐ๋ณธ์ ์œผ๋กœ ์ฟผ๋ฆฌ ๋ฌธ์ž์—ด์„ ๋บ๋‹ˆ๋‹ค. ์„ธ์…˜ ํ† ํฐ์ด ์‹ค๋ฆฌ๋Š” ๊ฒฝ์šฐ๊ฐ€ ํ”ํ•œ๋ฐ, ๋ชจ๋ธ์ด ๋กœ์ปฌ์—์„œ ๋Œ๋”๋ผ๋„ ๋Œ€ํ™” ๊ธฐ๋ก์€ ์ €์žฅ๋  ์ˆ˜ ์žˆ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

์›น ๊ฒ€์ƒ‰์€ ๋„ฃ์ง€ ์•Š์•˜์Šต๋‹ˆ๋‹ค. ๋ธŒ๋ผ์šฐ์ €์— ๊ฒ€์ƒ‰ ๊ธฐ๋Šฅ์ด ์—†์–ด ์ œ3์ž API๋ฅผ ์จ์•ผ ํ•˜๋Š”๋ฐ, ๊ทธ ์ˆœ๊ฐ„ API ํ‚คยทํ”„๋ก์‹œ ์„œ๋ฒ„ยท๊ณผ๊ธˆยท์™ธ๋ถ€ ์ „์†ก์ด ์ „๋ถ€ ๋”ฐ๋ผ์˜ต๋‹ˆ๋‹ค. ์ด ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ์˜ ์ „์ œ๊ฐ€ ํ†ต์งธ๋กœ ๋ฌด๋„ˆ์ง‘๋‹ˆ๋‹ค. ํ•„์š”ํ•˜๋ฉด tool()๋กœ ์ง์ ‘ ๋งŒ๋“œ์„ธ์š”. ์—ด ์ค„์ด๋ฉด ๋ฉ๋‹ˆ๋‹ค.

Workflow โ€” ๋ถ€ํ’ˆ์„ ์ด์–ด ๋ถ™์ด๊ธฐ

ํ•˜๋Š” ์ผ ์“ฐ๋Š” ๊ณณ
chain(...steps) ์ˆœ์„œ๋Œ€๋กœ, ์•ž ๊ฒฐ๊ณผ๊ฐ€ ๋’ค ์ž…๋ ฅ ์ดˆ์•ˆ โ†’ ๊ต์ • โ†’ ๋ฒˆ์—ญ
parallel(steps, opts?) ๊ฐ™์€ ์ž…๋ ฅ์„ ๋™์‹œ์— ๋Œ๋ฆฌ๊ณ  ํ•ฉ์นจ ์—ฌ๋Ÿฌ ๊ด€์  ๋™์‹œ ๋ถ„์„
router({ classifier, routes }) ๋ถ„๋ฅ˜ํ•œ ๋’ค ํ•œ ๊ฒฝ๋กœ๋งŒ ์‹คํ–‰ ๋ฌธ์˜ ์œ ํ˜•๋ณ„ ๋ถ„๊ธฐ
refine({ worker, evaluator }) ๊ธฐ์ค€ ์ ์ˆ˜์— ๋‹ฟ์„ ๋•Œ๊นŒ์ง€ ๋‹ค์‹œ ์”€ ํ’ˆ์งˆ์ด ์ค‘์š”ํ•œ ๊ฒฐ๊ณผ๋ฌผ
step(fn) ๋ชจ๋ธ ์—†์ด ๋„๋Š” ์ˆœ์ˆ˜ ํ•จ์ˆ˜ ์ „์ฒ˜๋ฆฌ, ํ›„์ฒ˜๋ฆฌ, ๋งˆ์Šคํ‚น
// ์ „ํ™”๋ฒˆํ˜ธ๋ฅผ ์ง€์šฐ๊ณ , ์„ธ ๊ด€์ ์œผ๋กœ ๋‚˜๋ˆ  ๋ถ„์„ํ•˜๊ณ , ํ•˜๋‚˜๋กœ ํ•ฉ์นœ๋‹ค.
const flow = chain(
  step((s) => s.replace(/\d{3}-\d{4}-\d{4}/g, '[์ „ํ™”๋ฒˆํ˜ธ]')),
  parallel([pros, cons, risks]),
  summarizer(),
)

๊ฒฐ๊ณผ๋„ ๋ถ€ํ’ˆ์ด๋ผ ๋‹ค์‹œ ์กฐ๋ฆฝํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

chain(parallel([a, b, c]), router({ ... }), refine({ ... }))

์ง์ ‘ ํ•ฉ์น˜๋ ค๋ฉด reduce๋ฅผ ๋„˜๊ธฐ์„ธ์š”.

parallel([a, b], { reduce: (results) => results.join('\n---\n') })

์กฐ๋ฆฝํ•  Agent๋Š” stateless: true๋ฅผ ์ผœ์„ธ์š”. ์•ˆ ์ผœ๋ฉด ํ๋ฆ„์„ ๋‘ ๋ฒˆ์งธ ์‹คํ–‰ํ•  ๋•Œ ์ฒซ ๋ฒˆ์งธ ๋Œ€ํ™”๊ฐ€ ์ฐฝ์— ๊ทธ๋Œ€๋กœ ๋‚จ์•„ ๊ฒฐ๊ณผ๊ฐ€ ์กฐ์šฉํžˆ ๋‹ฌ๋ผ์ง‘๋‹ˆ๋‹ค. ์—๋Ÿฌ๋„ ์•ˆ ๋‚˜์„œ ๊ฐ€์žฅ ์ฐพ๊ธฐ ์–ด๋ ค์šด ์‹ค์ˆ˜๋ผ, ์กฐํ•ฉ๊ธฐ๊ฐ€ ์ฝ˜์†”๋กœ ๊ฒฝ๊ณ ํ•ด ์ค๋‹ˆ๋‹ค. ์ผ๋ถ€๋Ÿฌ ๋Œ€ํ™”๋ฅผ ์ด์–ด๊ฐˆ ์˜๋„๋ผ๋ฉด stateless: false๋ฅผ ๋ช…์‹œํ•˜๋ฉด ๊ฒฝ๊ณ ๊ฐ€ ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค.

Memory โ€” ๋ธŒ๋ผ์šฐ์ €์— ์ €์žฅ

์„ธ์…˜์€ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์‚ฌ๋ผ์ง€๊ณ , ์ฐฝ์€ ์ข์Šต๋‹ˆ๋‹ค. ๊ทธ๋ž˜์„œ ๊ธฐ์–ต์€ ๋ชจ๋ธ ๋ฐ–์— ๋‘ก๋‹ˆ๋‹ค.

const shared = localMemory('support-desk')
const tools = memoryTools(shared) // remember / recall / listMemories

new Agent({ instruction: '์•Œ์•„๋‚ธ ์‚ฌ์‹ค์€ remember๋กœ ์ €์žฅํ•œ๋‹ค.', tools })

๊ฐ™์€ ์ €์žฅ์†Œ๋ฅผ ์—ฌ๋Ÿฌ ์—์ด์ „ํŠธ์—๊ฒŒ ๋„˜๊ธฐ๋ฉด ๊ทธ๊ฒŒ ๊ณง ๊ณต์œ  ์ž‘์—…ํŒ์ž…๋‹ˆ๋‹ค. ์ž์„ธํ•œ ๋‚ด์šฉ์€ ์•„๋ž˜์—์„œ ์ด์–ด์ง‘๋‹ˆ๋‹ค.

์ปจํ…์ŠคํŠธ์™€ ๊ธฐ์–ต

์—์ด์ „ํŠธ๋ผ๋ฆฌ๋Š” ์ปจํ…์ŠคํŠธ๋ฅผ ๊ณต์œ ํ•˜์ง€ ์•Š์Šต๋‹ˆ๋‹ค

์—์ด์ „ํŠธ๋Š” ๊ฐ์ž ์ž๊ธฐ ์„ธ์…˜, ์ž๊ธฐ ์ฐฝ์„ ์”๋‹ˆ๋‹ค. ์„œ๋กœ์—๊ฒŒ ๋„˜์–ด๊ฐ€๋Š” ๊ฑด run()์ด ๋Œ๋ ค์ค€ ๋ฌธ์ž์—ด ํ•˜๋‚˜๋ฟ์ž…๋‹ˆ๋‹ค.

   [๋ถ„๋ฅ˜๊ธฐ]         [๋ถ„์„๊ฐ€]         [์š”์•ฝ๊ฐ€]
    ์ž๊ธฐ ์ฐฝ          ์ž๊ธฐ ์ฐฝ          ์ž๊ธฐ ์ฐฝ
       โ””โ”€โ”€ ๋ฌธ์ž์—ด โ”€โ”€โ†’   โ””โ”€โ”€ ๋ฌธ์ž์—ด โ”€โ”€โ†’

์ œ์•ฝ์ฒ˜๋Ÿผ ๋ณด์ด์ง€๋งŒ ๋…ธ๋ฆฐ ๊ฒ๋‹ˆ๋‹ค. ์ฐฝ์ด ์ข์„์ˆ˜๋ก ๊ณต์œ ๋Š” ๋…์ด ๋ฉ๋‹ˆ๋‹ค. ํ•œ ์—์ด์ „ํŠธ๊ฐ€ ๋งŒ๋“  ์žก์Œ์„ ๋‚˜๋จธ์ง€๊ฐ€ ๋‹ค ๊ฐ™์ด ์งŠ์–ด์ง€๋‹ˆ๊นŒ์š”. ๋”ฐ๋กœ ๋‘๋ฉด ์—์ด์ „ํŠธ N๊ฐœ๊ฐ€ ๋…๋ฆฝ๋œ ์ฐฝ N๊ฐœ๊ฐ€ ๋˜๊ณ , ์‹œ์Šคํ…œ ์ „์ฒด๊ฐ€ ๋‹ค๋ฃจ๋Š” ์ •๋ณด๋Ÿ‰์€ ์˜คํžˆ๋ ค ๋Š˜์–ด๋‚ฉ๋‹ˆ๋‹ค.

์ฐฝ์ด ๋„˜์น˜๋Š” ์ง€์ ์€ ๋„ค ๊ณณ์ž…๋‹ˆ๋‹ค

์ƒํ™ฉ ๋ฌด์Šจ ์ผ์ด ์ƒ๊ธฐ๋‚˜ ๋Œ€์‘
๊ฐ™์€ ์—์ด์ „ํŠธ๋กœ ์กฐ๋ฆฝํ•œ ํ๋ฆ„์„ ๋ฐ˜๋ณต ์‹คํ–‰ ์ง€๋‚œ ์‹คํ–‰์˜ ๋Œ€ํ™”๊ฐ€ ์ฐฝ์— ๊ทธ๋Œ€๋กœ ๋‚จ์Œ stateless: true (์•ˆ ์ผœ๋ฉด ๊ฒฝ๊ณ )
๋„๊ตฌ๊ฐ€ ํฐ JSON์„ ๋Œ๋ ค์คŒ ๊ฒฐ๊ณผ ํ•˜๋‚˜๊ฐ€ ๋Œ€ํ™”๋ฅผ ํ†ต์งธ๋กœ ๋ฐ€์–ด๋ƒ„ maxToolResultChars (๊ธฐ๋ณธ 4000์ž)
์ž…๋ ฅ ๋ฌธ์„œ ์ž์ฒด๊ฐ€ ๊น€ ์˜ค๋ž˜๋œ ๋ถ€๋ถ„์ด ์†Œ๋ฆฌ ์—†์ด ์ž˜๋ ค๋‚˜๊ฐ step()์œผ๋กœ ์ชผ๊ฐœ parallel ์ฒ˜๋ฆฌ
๋ฒˆ์—ญยท์š”์•ฝ์„ ๋ชจ๋ธ์—๊ฒŒ ์‹œํ‚ด ์•ˆ ์จ๋„ ๋  ์ฐฝ์„ ์”€ translator() ยท summarizer() (์ฐฝ ์‚ฌ์šฉ๋Ÿ‰ 0)
agent.usage // { used: 812, total: 4096 }
new Agent({ onEvent: (e) => e.type === 'context-overflow' && warn() })

์„ธ์…˜์€ ์ƒˆ๋กœ๊ณ ์นจํ•˜๋ฉด ์‚ฌ๋ผ์ง‘๋‹ˆ๋‹ค

Chrome์ด ๋””์Šคํฌ์— ๋“ค๊ณ  ์žˆ๋Š” ๊ฑด ๋ชจ๋ธ ๊ฐ€์ค‘์น˜๋ฟ์ž…๋‹ˆ๋‹ค. ๋Œ€ํ™” ๊ธฐ๋ก์€ ๋ฉ”๋ชจ๋ฆฌ์—๋งŒ ์žˆ์–ด์„œ ํƒญ์„ ๋‹ซ์œผ๋ฉด ์—†์–ด์ง‘๋‹ˆ๋‹ค. ์ €์žฅ์€ ์šฐ๋ฆฌ ๋ชซ์ด๊ณ , agent.history๊ฐ€ ๊ทธ ์ฐฝ๊ตฌ์ž…๋‹ˆ๋‹ค.

localStorage.setItem('chat', JSON.stringify(agent.history))

// ๋ณต์›์€ ์ตœ๊ทผ ๋ช‡ ํ„ด๋งŒ. ์ „๋ถ€ ๋ฐ€์–ด ๋„ฃ์œผ๋ฉด ์ฐฝ์ด ๋‹ค์‹œ ๊ฝ‰ ์ฐฌ๋‹ค.
const saved = JSON.parse(localStorage.getItem('chat') ?? '[]')
const agent = new Agent({ instruction: '...', history: saved.slice(-10) })

์ €์žฅ์€ ์ง€์†์„ฑ ๋ฌธ์ œ๋ฅผ ํ’€์ง€, ์šฉ๋Ÿ‰ ๋ฌธ์ œ๋ฅผ ํ’€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ €์žฅ์†Œ๊ฐ€ ์•„๋ฌด๋ฆฌ ์ปค๋„ ์ฐฝ์— ๋„ฃ์„ ์ˆ˜ ์žˆ๋Š” ์–‘์€ ๊ทธ๋Œ€๋กœ์ž…๋‹ˆ๋‹ค.

๊ทธ๋ž˜์„œ ๊ธฐ์–ต์€ ๋ชจ๋ธ ๋ฐ–์— ๋‘ก๋‹ˆ๋‹ค

์ปจํ…์ŠคํŠธ ์ฐฝ์€ ์ž‘์—…๋Œ€, ๋ธŒ๋ผ์šฐ์ € ์ €์žฅ์†Œ๋Š” ์ฐฝ๊ณ ์ž…๋‹ˆ๋‹ค. ์ฐฝ๊ณ ๋ฅผ ํ†ต์งธ๋กœ ์ž‘์—…๋Œ€์— ์˜ฌ๋ฆฌ์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ํ•„์š”ํ•œ ๊ฒƒ๋งŒ ๊ทธ๋•Œ๊ทธ๋•Œ ๊บผ๋‚ด ์˜ต๋‹ˆ๋‹ค.

import { Agent, chain, localMemory, memoryTools } from 'my-little-agent'

const shared = localMemory('support-desk')
const tools = memoryTools(shared)

const researcher = new Agent({
  instruction: '์ž๋ฃŒ๋ฅผ ์ฐพ์•„ ์•Œ์•„๋‚ธ ์‚ฌ์‹ค์€ remember๋กœ ์ €์žฅํ•œ๋‹ค.',
  tools: [...tools, searchTool],
  stateless: true,
})

const writer = new Agent({
  instruction: 'recall๋กœ ํ•„์š”ํ•œ ์‚ฌ์‹ค์„ ๊บผ๋‚ธ ๋’ค ๋‹ต๋ณ€์„ ์ž‘์„ฑํ•œ๋‹ค.',
  tools,
  stateless: true,
})

await chain(researcher, writer).run('๊ฒฝ์Ÿ์‚ฌ ๊ฐ€๊ฒฉ ์ •์ฑ… ์ •๋ฆฌํ•ด์ค˜')

๋‘ ์—์ด์ „ํŠธ์˜ ์ฐฝ์€ ๋๊นŒ์ง€ ๋‚จ๋‚จ์ž…๋‹ˆ๋‹ค. ์˜ค๊ฐ€๋Š” ๊ฑด ์ฐฝ๊ณ ์— ๋‚จ์€ ์งง์€ ํ•ญ๋ชฉ๋“ค๋ฟ์ด๋ผ, ํ˜‘์—…์ด ๊ธธ์–ด์ ธ๋„ ์ฐฝ์ด ํ„ฐ์ง€์ง€ ์•Š์Šต๋‹ˆ๋‹ค. ์ƒˆ๋กœ๊ณ ์นจํ•ด๋„ ์ฐฝ๊ณ ๋Š” ๊ทธ๋Œ€๋กœ ๋‚จ๊ณ ์š”.

์ฐฝ๊ณ ๋Š” ๋ฉ”์„œ๋“œ ๋„ค ๊ฐœ๋งŒ ๊ตฌํ˜„ํ•˜๋ฉด ๊ฐˆ์•„๋ผ์šธ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

interface MemoryStore {
  get(key: string): Promise<string | undefined>
  set(key: string, value: string): Promise<void>
  delete(key: string): Promise<void>
  keys(): Promise<string[]>
}

localMemory()๋Š” localStorage(์•ฝ 5 MB)๋ฅผ ์”๋‹ˆ๋‹ค. ๋” ํฐ ์ €์žฅ์ด ํ•„์š”ํ•˜๋ฉด IndexedDB๋‚˜ ์ž์ฒด API๋กœ ๊ฐ™์€ ์ธํ„ฐํŽ˜์ด์Šค๋ฅผ ๊ตฌํ˜„ํ•˜์„ธ์š”.

ํ™œ์šฉ ์˜ˆ์‹œ

๋ถ™์—ฌ๋„ฃ๊ธฐ โ†’ ํผ ์ž๋™ ์™„์„ฑ

const form = await parser.generate<{ name: string; phone: string; address: string }>(
  clipboardText,
  {
    type: 'object',
    properties: {
      name: { type: 'string' },
      phone: { type: 'string' },
      address: { type: 'string' },
    },
    required: ['name', 'phone', 'address'],
  },
)

generate()๋Š” ๋””์ฝ”๋”ฉ ์ž์ฒด๋ฅผ ๋ฌถ์–ด๋ฒ„๋ฆฝ๋‹ˆ๋‹ค. ๋ชจ๋ธ์ด ์„ค๋ช…์„ ๋ง๋ถ™์ด๊ฑฐ๋‚˜ ํ˜•์‹์„ ์–ด๊ธธ ์ˆ˜๊ฐ€ ์—†์–ด์„œ, ๊ฒฐ๊ณผ๋ฅผ ๋ฐ”๋กœ setState์— ๋„ฃ์–ด๋„ ๋ฉ๋‹ˆ๋‹ค.

์ž์—ฐ์–ด โ†’ ํด๋ผ์ด์–ธํŠธ ํ•„ํ„ฐ

const query = await parser.generate<{ category: string; maxPrice: number }>(
  '3๋งŒ์› ์•„๋ž˜ ๋Ÿฌ๋‹ํ™” ๋ณด์—ฌ์ค˜',
  {
    type: 'object',
    properties: {
      category: { type: 'string', enum: ['shoes', 'clothes', 'bags'] },
      maxPrice: { type: 'integer' },
    },
    required: ['category', 'maxPrice'],
  },
)

setItems(all.filter((i) => i.category === query.category && i.price <= query.maxPrice))

๋‹ค๊ตญ์–ด ๋ฆฌ๋ทฐ๋ฅผ ํ•œ๊ตญ์–ด๋กœ ๋ชจ์•„ ๋ณด๊ธฐ

// ์ „๋ถ€ Chrome ์ „์šฉ ๋ชจ๋ธ์ด๋ผ ์ปจํ…์ŠคํŠธ ์ฐฝ์„ ์“ฐ์ง€ ์•Š๋Š”๋‹ค
const digest = chain(summarizer({ length: 'short' }), translator({ to: 'ko' }))

const summaries = await Promise.all(reviews.map((r) => digest.run(r)))

ํŽ˜์ด์ง€ ๊ธฐ๋Šฅ์„ ๋„๊ตฌ๋กœ ๋…ธ์ถœ

const assistant = new Agent({
  instruction: '์‡ผํ•‘๋ชฐ ๋น„์„œ. ํ•„์š”ํ•˜๋ฉด ๋„๊ตฌ๋ฅผ ์“ด๋‹ค.',
  tools: [
    tool<{ keyword: string }>({
      name: 'searchProducts',
      description: '์ƒํ’ˆ์„ ํ‚ค์›Œ๋“œ๋กœ ๊ฒ€์ƒ‰ํ•œ๋‹ค',
      parameters: { type: 'object', properties: { keyword: { type: 'string' } } },
      execute: ({ keyword }) => fetch(`/api/products?q=${keyword}`).then((r) => r.json()),
    }),
    tool<{ id: string }>({
      name: 'addToCart',
      description: '์ƒํ’ˆ์„ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋‹ด๋Š”๋‹ค',
      parameters: { type: 'object', properties: { id: { type: 'string' } } },
      execute: ({ id }) => cartStore.add(id),
    }),
  ],
})

await assistant.send('์•„๊นŒ ๋ณธ ๋Ÿฌ๋‹ํ™” ์ค‘์— ์ œ์ผ ์‹ผ ๊ฑฐ ์žฅ๋ฐ”๊ตฌ๋‹ˆ์— ๋„ฃ์–ด์ค˜')

๋ฌธ์˜ ์œ„์ ฏ ๋ผ์šฐํŒ…

const desk = router({
  classifier: new Agent({ name: 'classifier', stateless: true }),
  routes: { refund: refundAgent, tech: techAgent, sales: salesAgent },
  descriptions: {
    refund: 'ํ™˜๋ถˆ, ์ทจ์†Œ, ๋ฐ˜ํ’ˆ ๋ฌธ์˜',
    tech: '์˜ค๋ฅ˜, ์‚ฌ์šฉ๋ฒ• ๋ฌธ์˜',
    sales: '๊ฐ€๊ฒฉ, ๊ตฌ๋งค ์ƒ๋‹ด',
  },
})

๋‚ด๋ณด๋‚ด๊ธฐ ์ „์— ์Šค์Šค๋กœ ๊ฒ€ํ† ์‹œํ‚ค๊ธฐ

const polished = refine({
  worker: new Agent({
    instruction: '์ œํ’ˆ ๊ด‘๊ณ  ๋ฌธ๊ตฌ๋ฅผ ์“ฐ๋Š” ์นดํ”ผ๋ผ์ดํ„ฐ.',
    stateless: true,
  }),
  evaluator: new Agent({ instruction: '๋ฌธ๊ตฌ๋ฅผ ๊น๊นํ•˜๊ฒŒ ๋ณด๋Š” ํŽธ์ง‘์žฅ.', stateless: true }),
  minScore: 85,
  maxRounds: 3,
})

React

๋ณ„๋„ ์–ด๋Œ‘ํ„ฐ๊ฐ€ ํ•„์š” ์—†์Šต๋‹ˆ๋‹ค.

function useAgent(instruction: string) {
  const ref = useRef<Agent>()
  if (!ref.current) ref.current = new Agent({ instruction }) // ์„ธ์…˜์€ ์ง€์—ฐ ์ƒ์„ฑ์ด๋ผ ๋งŒ๋“ค์–ด๋งŒ ๋‘๋ฉด ๊ณต์งœ๋‹ค
  useEffect(() => () => ref.current?.destroy(), [])
  return ref.current
}

export function Summary({ text }: { text: string }) {
  const agent = useAgent('ํ•ต์‹ฌ๋งŒ ์„ธ ๋ฌธ์žฅ์œผ๋กœ ์ค„์ธ๋‹ค.')
  const [out, setOut] = useState('')

  useEffect(() => {
    let alive = true
    setOut('')
    ;(async () => {
      for await (const chunk of agent.stream(text)) {
        if (!alive) return
        setOut((prev) => prev + chunk)
      }
    })()
    return () => {
      alive = false
    }
  }, [text])

  return <p>{out}</p>
}

๋ฉ€ํ‹ฐ๋ชจ๋‹ฌ ์ž…๋ ฅ

const vision = new Agent({
  instruction: '์‚ฌ์ง„์— ๋ณด์ด๋Š” ๊ฒƒ์„ ์„ค๋ช…ํ•œ๋‹ค.',
  expectedInputs: [{ type: 'image' }],
})

await vision.send([
  {
    role: 'user',
    content: [
      { type: 'text', value: '์ด ์‚ฌ์ง„์— ๋ญ๊ฐ€ ์žˆ์–ด?' },
      { type: 'image', value: imageBlob },
    ],
  },
])

๊ฐœ๋ฐœยท์„ค๊ณ„ ๊ทœ์น™ยท๋กœ๋“œ๋งต์€ AGENTS.md์— ์žˆ์Šต๋‹ˆ๋‹ค. ๋ผ์ด์„ ์Šค๋Š” MIT์ž…๋‹ˆ๋‹ค.

About

๐Ÿ›  Chrome Built In AI ๊ธฐ๋ฐ˜ ๋ฉ€ํ‹ฐ ์—์ด์ „ํŠธ ํ”„๋ ˆ์ž„์›Œํฌ ๐Ÿ› 

Topics

Resources

Stars

4 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages