Галоўная / Артыкулы

Лакальны агент падтрымкі на SmolLM3: think-рэжым, інструменты і абмежаванні

Мадэль на 3B на ноўтбуку як агент падтрымкі: калі ўжываць think і no_think, як працуе выклік інструментаў і якія ахоўнікі спыняюць выклікі з падстаўнымі значэннямі.

Выдавец: REACTAPP.TOP9 хв чытання1979 слоў

Крыніцы: **A Local Support Agent with SmolLM3: Think Mode, Tools, and When Not to Reason**

Чы можа мадэль з 3 мільярдамі параметрыв апрацоўваты запіты кліентськоі підтрымкі без допомогі мадэлі з 70 мільярдамі параметрыв? Для 90% звічайніх запітыв — так, але лішэ за умові, што ві перэстанеце покладатыся на власні функціі пошуку інформацыі мадэлі. Тут SmolLM3 працюе локально як невелікій агент підтрымкі і показуе, дэ яна поводыцца належнім чыном, а дэ код навколо неі мусіць буты суворышым, ніж сама мадэль.

Служба підтрымкі кліентыв — не найкрашчэ місце для вікорыстання загального чат-бота. Звічайні запіты вімагаюць пошуку інформацыі, прысвоення класіфікаціі і короткоі адказы згідно з правіламі, тоды як менша колькасць запітыв пов’язана з подвійнімі оплатамі, загубленімі замовленнямі чы незадоволенімі кліентамі. Шчоб ефектывно обслуговуваты обідва тыпі запітыв за допомогою одніеі мадэлі, іі потрыбно працюваты двома рызнімі способамі.

SmolLM3-3B підходыць для гэтага з трох прычын, які прыховуе колькасць параметрыв:

  • У яго вбудовано два рэжымі міркування. Додавання /no_think змушуе яго відповідаты негайно, а /think змушуе спочатку створыты прыхованій запіс працэсу міркувань.
  • Вбудована падчынаінгу інструментам: інструменты, апісаныя як JSON-шэмы, падаюць у шаблон чату, а мадэль адпавядае блокам <tool_call>, калі паўтарыць ўжыванне інструмента.
  • Эта прыстройка дастаткова малая для ноутбука. Усё тут працавала на MacBook з 24 ГБ аб’еднанай памяці: мадэль высокай точнасці за дапамою Transformers на MPS, а таксама 8-бітная версія MLX для цыклу агента.
  • Няма ніякога трэніравання, і вагі мадэлі ніколі не змянююцца. Праца складаецца з раздзелення працэсу размышлення і дзеяння, а таксама з зроблена ўсіх супакоўваючых кодаў строгаміяшымі за саму мадэль.

    Настройка

    Два скрыпты выкорыстоўваюць два рэнтаймы, але адную сям’ю модэляў. Для пораўнення у рэжыме размышлення выбіранне значэнняў адбываецца за прыкладам модэля, з temperature=0.6 і top_p=0.95. Агент працуе эфектывней пры temperature=0.3, таму JSON-даныя інструмента застаюцца стабільнымі.

    На гэтым апараты ўрадзіце паўтарны перадачы 8-бітнага агента зайняла прыблізна чатыры секунды. Полны 3B-мадэль па прынцыпу Transformers стварала адносна 10 токэнаў за секунду, што падходзіць для пазнейшага чытання лог-файлаў, але занадта медленна для кліента, які спаглядае вікно чату. Transformers ўжытковая для аднавання шаблона. Калі на другай стороне знаходзіцца рэальная людзь, трэба перайсці на MLX або llama.cpp.

    Рашэнне пра спосаб мышлення — гэта рашэнне па продукту

    SmolLM3 за замовчаннем працуе па прынцыпе мышлення. Існуюць два аднойчыны эфектыўных спосабу ўвесь гэты рэжым выключыць. Адны з іх — гэта сістэмная паведамленне:

    messages = [
        {"role": "system", "content": "/no_think"},
        {"role": "user", "content": prompt},
    ]
    

    Іншы варыянт — гэта флаг пад выводзе накшталёна:

    tokenizer.apply_chat_template(
        messages,
        tokenize=False,
        add_generation_prompt=True,
        enable_thinking=False,  # the /no_think flag in the system prompt wins if both are set
    )
    

    Шаблон рабочы не толькі змінюе значэння булева параметра. Ён запісвае рэжым моўчання ў заголовак сістэмы, а калі выбіраецца /no_think — ўтаму дадаўна дыяграму моўчання, тады генераванне працюе працэсаўна з адпаведным адказам:

    <|im_start|>assistant
    <think>
    
    </think>
    

    Самэтна просты выклік generate() у рэжыме без раздумаў все ж можа вернуць <think></think> у тэксце. Для адпаведзі да кліента яго трэба адзьмяць:

    final = re.sub(r"<think>.*?</think>", "", raw, flags=re.DOTALL).strip()
    

    У обох рэжымах была выклікана тая самая просьба пад падтрымкі: кліенту выкалічуецца два разы за адны замовленне, тады якія тры конкрэтныя крокі павінны здзейсніць спецыялісты з падтрымкі?

    Два гаджэты для відліку часу

    Працэздатнасць у обох рэжымах была прыбліжна однаковая, але колькісна характэрыстыка працы — няўпако. Рэжым «think» выкарыстоўваў большасць своіх 939 токенав у блоке «think». Кліент ніколі не чытае гэтыя даныя, але все рава чакае ўсё гэта.

    Две інструкцыі

    /no_think пераводзіць працэс безпосерэдна да стандартнага пераказу: паўнестаць замовленне, звяжыцца з продавцам, вернуць суму. Гэта працюе як першы варыянт, але ён трохі не падходзіць для власнай команды падтрымкі продавца, адколі «звяжыцца з продавцам» — гэта парада, яку дае банк, а не тое, што агент з власнай кампаніі кажа своему кліенту.

    /think пасла прыблызу адну сторанку часоў, спарачваючыся з сабой па кантэксту вярнення грошаў проты крэдыту, падыграння сложнасці і па тым, чы хацецца пераканаліцься ў стане доставкі, а пасля выйшла да болей строгага плану:

    1. Прагтарыцца і з’явіцься номер замовлення, дату і спосаб адплатаў.
    2. Знайсці дублюючую транзакцыю і ануляваць яе.
    3. Паспяшыць кліенту, што адбылося, і падтвердзіць, што грошы будуць вярнутыя, у вастаўе вярнення грошаў або крэдыту ў магазіне.

    Раздзеленыя задачы, якія трэба атрымліваць праз доставку, вынікаюць з гэтага:

    • /no_think для вялікага калібру работ: класыфікацыя намеру, запытанні „Дзе маё замовленне?“, готавыя адпаведзі на праблемы і вызовы інструментаў.
    • /think там, дзе неправы крок можа коштаць дорага: вярнення грошаў, анулявання платэжаў, прыбліжнасці да безпекі, і ўсё, што патрабуе перагляду па правілам, як у людзях, прытымкі да адпаведзі.

    Своія паказаныя цыфры Hugging Face таксама адказваюць пра тое, як выкарыстоўваецца гэты інструмент. У тэстах BFCL SmolLM3 паказаны з рэзультатам 92,3 без расшырэнага размышлення і 88,8 з яго. Размышленне не ўзлегкае, і для такіх задач яно нават не падняло карыстоўнасці. Агент ніжэй выкорыстоўвае параметр enable_thinking=False, таму што пошук замовлення павінен быць проста вызывам функцыі, а не эсэ.

    Адны інструмент і цікл з двух крокаў

    SmolLM3 чакае інструменты у вачыні об’ектаў JSON-schema, якія пасылаюцца праз xml_tools. Шаблон копіюе іх у блок tools і прабуджае мадэль да адпаведзення ў такім фармаце:

    <tool_call>
    {"name": <function-name>, "arguments": <args-json-object>}
    </tool_call>
    

    Інструмент падпіркі ўмышлена мае вузкі функцыянал:

    TOOLS = [
        {
            "name": "lookup_order_status",
            "description": (
                "Look up the current status, estimated delivery date, and carrier "
                "for a specific customer order. Call this when the customer mentions "
                "an order number or asks where their order is."
            ),
            "parameters": {
                "type": "object",
                "properties": {
                    "order_id": {
                        "type": "string",
                        "description": "The order ID, usually in the format ORD-XXXXXX.",
                    }
                },
                "required": ["order_id"],
            },
        }
    ]
    

    За ягою знаходзіцца фальшывы база дадзеных. У рэальных умовах гэта могла б быць SQL-запытка або вызов службы замовэнняў.

    ORDERS = {
        "ORD-4821": {"status": "shipped", "eta": "June 18, 2026", "carrier": "DHL"},
        "ORD-3307": {"status": "processing", "eta": "June 20, 2026", "carrier": None},
        "ORD-1190": {"status": "delivered", "eta": None, "carrier": "FedEx"},
    }
    

    Гэтая петля ўсьмо твора. Мадэль прымае паведамленне ад кліента, стварае першую адпаведзь, і як толькі гэта ўсё ж такі запыт да інструмента, код запускае функцыю, вяртае рэзультат і прасіць пра другую адпаведзь:

    def respond_with_tools(user_message: str) -> str:
        messages = [{"role": "user", "content": user_message}]
        turn1 = generate_reply(messages)
        name, args = parse_tool_call(turn1)
        if name == "lookup_order_status":
            result = lookup_order_status(**args)
            messages += [
                {"role": "assistant", "content": turn1},
                {"role": "tool", "content": json.dumps(result), "name": name},
            ]
            return generate_reply(messages)
        return turn1
    

    Шчаслівы шлях

    Чэрз тыдзень кліент запытаеся, дзе знаходзіцца замовленне ORD-4821. Мадэль не адпавядае. Яна вызывае інструмент:

    <tool_call>
    {"name": "lookup_order_status", "arguments": {"order_id": "ORD-4821"}}
    </tool_call>
    

    Функцыя вяртае інфармацыю пра адправку: DHL, 18 чэрвеня 2026 года. Пасля другога запуску, які трывае 4,2 секунды, прыходзіць адпаведна адказа, што замовленне ўжо адправлена і яго маюць прыбыць 18 чэрвеня праз DHL.

    Для ORD-3307 статус — апрацоўка, перавазчык не вказан, а прыбыць прызначаюць на 20 чэрвеня. Мадэль заявіла, што перавазчык недоступны, у замяне таго, каб выдумаць якога-небудзь, і гэта самэ тое, чаго хочаце ад маленькай мадэлі: чытаць JSON-файл інструмента і не дагадваць яго додатковым элементам.

    Для ORD-9999 вызов інструмента быў правільны, а функцыя адпаведзела not_found. У адказе была прабачэнне і просьба да кліента пераканаліцца з номерам. Сама структураваная адпаведзь not_found з боку функцыі была дастаткова, без якіх-небудзь дадатковых паведамленняў.

    Дзе гінуе наўважны цыкл

    Трэці запіт быў прызначаны як негатыўны контроль: кліент проста хоча змяніць адрэсу электронной пошты. Не існуе жадных інструментаў для ціх цэлей, таму хорашы агент гэта зазначае і завершае працу. Няўмелы цыкл зрабіў што-інша:

    1. У ён пачаўся правільна, зазначыўшы, што нельга змяніць персональную інфармацыю, і ўтварыўшы працэс зв’язку з службай падтрымкі.
    2. Потым ён продаўжыў робіць свою справу і выдаў запіт да інструмента, у якім быў выкарыстаны месца для заполнення:
    <tool_call>
    {"name": "lookup_order_status", "arguments": {"order_id": "your_order_id"}}
    </tool_call>
    
    1. Парсер выканала ўсё, што было абгорнутае ў <tool_call>. Ён шукаў у фальшывай базе дадзеных your_order_id, але паветраў not_found і перадаў гэта значэнне модэлю.
    2. У другі раз модэль паказала клянэту схему інструмента і прабавалала яму заменіць your_order_id на рэальны номер замовлення.

    Чатыры рэшткі паўсталі адразу, адна на другой. Частка модэлю — гэта сама менш значныя частка. Маленькія модэлі эвалююць, і гэта нормальна. Калі ваш цыкл складаецца з рэгулярнага выраза для <tool_call>, пасля чаго вы калічыце функцыю з усімі наяўнымі аргументамі, то ў канечнай лініі вы запускаце lookup_order_status("your_order_id") у рэальных умовах, а гэта не будзе відбывацца пад час дамавы.

    Захоўнікі, якія не трэба налаштаваць

    Две простыя правілы рашаюць большасць проблем без змены ваг.

    Паўначальна, запускайце вызов інструмента толькі тады, калі весь тур складаецца выключна з гэтага вызову. Абарот, які следуе за вызамом, лячыцца звычайным тэкстам, і XML трэба адмахнуць прытым, калі кліент яго не пабачыць.

    Друга, пераканайцеся ў правільнасці аргументаў прытым, калі яны не дасягнулі базы дадзенаў. ID замовленняў тут маюць фармат ORD- плюс цыфры, таму your_order_id ніколі не будзе прыйняты.

    ORDER_RE = re.compile(r"^ORD-\d{4,}quot;)
    PURE_TOOL_RE = re.compile(r"^\s*<tool_call>(.*?)</tool_call>\s*quot;, re.DOTALL)
    
    def parse_executable_tool_call(output: str):
        cleaned = re.sub(r"<think>.*?</think>", "", output, flags=re.DOTALL).strip()
        match = PURE_TOOL_RE.match(cleaned)
        if not match:
            return None
        payload = json.loads(match.group(1).strip())
        order_id = str(payload.get("arguments", {}).get("order_id", "")).strip()
        if not ORDER_RE.match(order_id):
            return None
        return payload
    

    Таксама трэба падкрутыць апісанне інструмента, паказавшы, што яго трэба выклікаць толькі тады, калі кліент ужэ надаў конкрэтны ID замовлення. Пасля гэтых змян у пытанні ў электронным лісце пад час захоўванага выканання не было выклікана жаднага інструмента, чыя трываласць склікала 3,5 секунды проты 15,4 у нефункцыйнае двухэтапная версія. У адказе пасцягнуліся з выбачэнням, сказалі, што не можаць адрабатваць змяны на рахунку, і паслалі на службу падтрымкі. Гэта яшчэ застаецца загальным рашэнням, але без інструмента update_email не павінна быць жадная магічная самаслужбовая схема. Прынеймна ён перастаў прэтварацца, што шукае замовленне.

    Пошукі па «шчаслівай» схеме продаваліся, і ORD-9999 яшчэ залучвалася як зніклае. Мадэль не стала розумней. Вважайце гэта бягавым рэменем, а не трансплантатам.

    Што гэта не дазваляе падтвердзіць

    Гэта толькі дама, а справжній копілят для падтрымкі патрэбуе набаго больш, чым адна функцыя і словнік з трох замовленняў. Некалькі твэрджэнняў трэба адрываць:

    • Гэта не планаванне з выкарыстоўваннем калькулятара. Адна з апранатак являецца засобам для пошуку, а пры выкарыстоўванні двух апранатак мадэлі 3B пачынаюць ствараць ідэнтыфікаторы замовлень, каб задовольніць схемы, якія ўсё ж не розумеюць цалком.
    • База дадзеных ўявная, таму аутантыкацыя, персональныя даны і праблема „чы гэты корыстнік можа бачыць гэта замовленне“ ніколі не былі проблемай самой мадэлі.
    • Увесь час была актываўана функцыя выбору прыкладоў, таму паўтарны запуск запіту для электранявіскаў не дастане тых сабе словаў. Тое, што павтарылася з кожным запускам, — гэта сама неудача, вызов апранатакі, заполнены ўсё тым жа заместнікам. Точныя фразы разлічаюцца, таму не можна на гэта рыляцца.
    • Нічога не было паўнаравана з хмарным модэлем або не было перадпрацавана з рэальным экспортом тыкетаў, таму ціфры апісваюць толькі адну машыну, а не показнік эфектывнасці.

    Гэта дае болей часткава тлумачэнне, ніж «маленькія мадэлі можаць выказваць падтрымку»: мадэль на 3 мільярды параметраў можа вызваць адну добра апісаную інструмент, падсумаваць рэзультат і завершыць свою працу, за ўмовы, што сам вызов інструмента спрацьвоўваецца як ўсё, што трэба пераканацца ў дазволе на выкарыстоўванне, а не як ўсё, чаму можна даверыць таму, што яго створыла мадэль.

    Выкананне

    У каталозе праекта, дзе актываўана віртуальная асяроддзе, запускайце тры скрыпты па парадку: порэванне з і без размышленняў, просты агент і захаваны агент.

    python llm-learning/smollm3-local-support-agent/code/think_vs_nothink.py
    python llm-learning/smollm3-local-support-agent/code/support_agent.py
    python llm-learning/smollm3-local-support-agent/code/support_agent_guarded.py
    

    Шрыфты ствараюць транскрыпціі, якія можна прачытаць пазней, каб парабіліцаваць выконанне задач.