Галоўная / Артыкулы / Практычныя прытамулкі: Я створыў локальнага агента AI з Ollama — і складная частка

Практычныя прытамулкі: Я створыў локальнага агента AI з Ollama — і складная частка

Практычныя прытамулкі: Я створыў локальнага агента AI з Ollama — і складныя аспекты: кантракты, пераказы і месцы для коду для команд, якія викорыстоўваюць гэты патэрн.

2143 слоў

У гэтым карыце парадкульны спосаб перадбудавання пацеку ад сыр'ёў да рабочай системы для: «Я створыў локальнага AI-агента з Ollama — і складнаю часткай не быў сама модель». Акцэнт ставіцца на практычныя крокі, чысткія перакананні та код, які можна проста дадаць у репазітарый без неабяснення меты. У стадзіі агульнага відгледу неабходна прадзефінаваць вхідныя даны, адпаведальную особу за крок та критэрыя завершэння пры перадзмене коду. Аперацыйныя працавнікі должны магчымае перадзрабатваць крок з вядомага пункта контролю, не намагаючыся з'ясавіць схованы стан. Валіце маленькія, тэставальныя елементы замест большых скрыптов. Калі крок не выйшоў, прычына нехацкага рэзультата должна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцужок задач.

Чаму вы выбралі Ollama

Калі працюеце над этапам «Чырваў, чаму вы выбралі Ollama», спачатку запісайце угоду: неабходныя даны, сигнал успеху і тое, што выканаецца у разы частковага невясковасці. Такі список контроля дапамагае залічыць пазнейшыя змены ў кодзе. Спрэцьвачайце гэты этап як угоду межаў між вхіднымі данымі і перакананымі выходнымі рэзультатамі. Дайце назвы артыфактам, задацьце правіла пераканання успеху і не прымайце часткова завершэнне без паведамлення. Зявляйце логі з ідэнтыфікаторам запиту, ідэнтыфікаторам моделі і часам адзначэння кожнага вызову. Без такога следу періодычныя памылкі прадастаўця выглядаюць як багі ў прыемніку.

ollama pull qwen3
pip install ollama
from ollama import chat
response = chat(
    model="qwen3",
    messages=[
        {"role": "user", "content": "Explain what an overdue invoice is."}
    ],
)print(response.message.content)

Чат-бот адпавядае; агент выканае крокі

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

Пачніце з простых інструментаў

Калі працуеце на стадыі «Пачніце з простых інструментаў», спачатку запісайце контракт: неабходныя даны, сигнал успеху і тое, што выканаецца у разе частковага абякання. Такі список контроля дапамагае залічыць пазнейшыя змены ў кодзе чыстымі. Зберагаюце настройкі паза кодам прыемліка. Файлы сераўіса, хранільнікі секрэтных данных і флагі функцыйяў должны знаходзіцца ў аднам месцы, куды аператары можаюць адбавіць аудыт без неабяжнага чытання всіх элементаў. Запісвайце ID запытку, ID модэлю і час адпаведзі на кожны вызов. Без такога лёгкага стэйта ўскладненыя проблемы прадаўцоў выглядаюць як багі прыемліка. Калі працуеце на стадыі «Пачніце з простых інструментаў», спачатку запісайце контракт: неабходныя даны, сигнал успеху і тое, што выканаецца у разе частковага абякання. Такі список контроля дапамагае залічыць пазнейшыя змены ў кодзе чыстымі. Валіце маленькія, тэставаныя елементы замест большых скрыптаў. Калі якісь крок абякае, прычына абякання должна вказываць на адну конкрэтную адпаведальнасць, а не на заплутаны ланцюг задач.

CUSTOMERS = {
    "acme plumbing": {
        "customer_id": "cus_1042",
        "name": "Acme Plumbing",
        "email": "billing@example.com",
    }
}
INVOICES = [
    {
        "invoice_id": "INV-2048",
        "customer_id": "cus_1042",
        "amount": 1850.00,
        "days_overdue": 18,
    }
]
def find_customer(name: str) -> dict:
    customer = CUSTOMERS.get(name.strip().lower())
    return customer or {"error": "customer_not_found"}
def get_overdue_invoices(customer_id: str) -> dict:
    matches = [
        invoice
        for invoice in INVOICES
        if invoice["customer_id"] == customer_id
        and invoice["days_overdue"] > 0
    ]
    return {"invoices": matches, "count": len(matches)}

Даюць модэлю інструменты, а не выдуманы доступ

Этап «Даюць модэлю інструменты» працюе найкраща, калі яго розглядаць як вимерную плошчу. Запісаўце адна ідеальная транскрыпцыя, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць масштабы. Разглядзіце этап як кантракт межа вхіднымі даннымі і перакананымі выходнымі рэзультатамі. Паказваце назвы артыфактаў, задаце критэрыя успеху і адмовіцеся ад тыхоўскага частковага завершэння. Фіксаваце інтэрпретара і файл з правіламі залежнасцяў пры навчанні циклу. Адхіленне межа ноутбукам і системай CI — гэта самая частая тыхая паўза ў дэманстраціях API.

import json
from ollama import chat
def find_customer(name: str) -> dict:
    """Find a customer by business name and return its verified record."""
    customer = CUSTOMERS.get(name.strip().lower())
    return customer or {"error": "customer_not_found"}
def get_overdue_invoices(customer_id: str) -> dict:
    """Return overdue invoices for a verified customer ID."""
    matches = [
        invoice
        for invoice in INVOICES
        if invoice["customer_id"] == customer_id
        and invoice["days_overdue"] > 0
    ]
    return {"invoices": matches, "count": len(matches)}
TOOLS = {
    "find_customer": find_customer,
    "get_overdue_invoices": get_overdue_invoices,
}

Ствараеце цыкл агента

Этап стварэння цыклу агента працюе наяўней, калі яго спрыяваць як мерыемую велічыну. Запісаце адна ідеальная транскрыпцыя, адзін прыклад неудачы і прыметку па адвярненню змян перш чым расширваць масштабы. Запісвайце час выканання і кост токенаў або запытаў разам з функцыйнальнымі рэзультатамі. Відразлівае паказанне костаў з’являецца перашкоду неспакойным рахункам, калі процес пераходзіць з дэмаверсіі ў спяльныя среды. Зафіксавайце інтэрпретара і файл з правіламі залежнасцяў пры навучэнні цыклу. Разлікі межаў лэптопа і системы CI ёсць найчастэйшай прычыной таямнічых збоў у дэмаверсіях API.

SYSTEM_PROMPT = """
You are an invoice assistant.
Rules:
- Never invent a customer, invoice, email address, balance, or date.
- Use find_customer before requesting invoices.
- Only use customer IDs returned by tools.
- If a tool returns an error or no records, explain that clearly.
- You may draft communication, but you cannot send it.
"""
def run_agent(user_request: str) -> str:
    messages = [
        {"role": "system", "content": SYSTEM_PROMPT},
        {"role": "user", "content": user_request},
    ]    for _ in range(6):
        response = chat(
            model="qwen3",
            messages=messages,
            tools=list(TOOLS.values()),
        )        messages.append(response.message)        if not response.message.tool_calls:
            return response.message.content        for call in response.message.tool_calls:
            name = call.function.name
            arguments = call.function.arguments            if name not in TOOLS:
                result = {"error": "tool_not_allowed"}
            else:
                try:
                    result = TOOLS[name](**arguments)
                except (TypeError, ValueError) as error:
                    result = {
                        "error": "invalid_tool_arguments",
                        "detail": str(error),
                    }            messages.append(
                {
                    "role": "tool",
                    "tool_name": name,
                    "content": json.dumps(result),
                }
            )    return "I stopped because the task exceeded the maximum number of steps."

Настоямы спосаб выправлення не быў кращы праграмацкі запит

Праўы спосаб рашэння — гэта калі стадію можна спрыяць як мерыемую паверхню. Зберагуйце адны ідеальны прыклад роботы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць масштабы. Зберагайце настройкі парадульна ад коду прыемліка. Файлы сераўнавання сяродовысці, храненні секрэтных данных і флагі функций должны знаходзіцца ў аднам месцы, куды аператары можаць адбавляць контроль без неабяжнага чытання всіх дадзеных. Закрепіце інтэрпретара і файл з інформацыяй пра залежнасці перш чым выучваць циклы. Разніця межаў між ноутбукам і системай CI є самым частым прычынам тых, што дэманстраціі API працуюць некоректна. Праўы спосаб рашэння — гэта калі стадію можна спрыяць як мерыемую паверхню. Зберагуйце адны ідеальны прыклад роботы, адзін прыклад неудачы і запіс пра вярнэнне да пачатковага стану перш чым расширваць масштабы. Валідзіце маленькія, тэставаныя елементы замест большых скрыптов. Калі якісь крок не выйшае, неудача должна вказываць на адну конкрэтную прычыну, а не на заплутаную сітку задач.

Дадзіце структураваны выхід на межы

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

from pydantic import BaseModel, Field
class ReminderReview(BaseModel):
    customer_name: str
    invoice_ids: list[str]
    total_due: float = Field(ge=0)
    draft_subject: str
    draft_body: str
    requires_approval: bool = True
review_response = chat(
    model="qwen3",
    messages=messages,
    format=ReminderReview.model_json_schema(),
)
review = ReminderReview.model_validate_json(
    review_response.message.content
)

Стан і памяць — гэта разныя рычы

Для дзеянняў, які выконваюцца на стадыі памяці, неабходна перад змянай коду адзначыць вхідныя даны, власніка крока і критэрыя завершэння. Аператары должны магчымае перадзваніць крок з вядомай точкі контролю, не прабуючы спадарожваць схованы стан. Запісвайце час выконання і вартасць токена або запиту разам з функцыональнымі рэзультатамі. Відразліва візуалізацыя вартасцей запобегае неспакоўным рахункам, калі траекторыя пераходзіць з дэмовай среды ў спакульную. Раздзеліце стварэнне кліента ад цыклу перадачы паведамленняў, ўжо тады, калі будзе можна змяніць прадаўца, не перапісваючы машыну стана дыялогу.

task_state = {
    "customer_id": "cus_1042",
    "verified_invoice_ids": ["INV-2048"],
    "approved_actions": [],
}

Локальны формат не значыць автаматычна безпечны

Калі «For the Local» не выконвае автаматычнага стэйджавання, пяред зменым коду неабходна ясная дэфініцыя вхідных даных, адміністратара крока і крэтэрыяў выходу. Аператары должны магчымае перзапускать крок з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Конфігурацыю трэба знаходзіць за межамі коду прыемлівання. Файлы сераўедовашча, хранільнікі секрэтных дадзеных і флагі функцияў должны быць у аднам месца, якое аператары можаць пераглядаць, не чытаяўшы весь граф. Раздзеляйце стварэнне кліента ад цыклу паведамленняў, каб можна было змініць прадаўцоў без перапісвання машыны стану размовы. Калі «For the Local» не выконвае автаматычнага стэйджавання, пяред зменым коду неабходна ясная дэфініцыя вхідных даных, адміністратара крока і крэтэрыяў выходу. Аператары должны магчымае перзапускать крок з вядомай точкі контролю, не спрабоўваючы здагадвацца пра схованы стан. Вядзьмі лепш маленькія, тэставаныя елементы замест большых скрыптов. Калі крок не выйшоў, прычына неудачы должна вказываць на адну конкрэтную адпаведальнасць, а не на цэлы комплекс проблем.

Це ўскладненыя трубопрацесы.

Як працаваць з агентам

Калі працуеце над этапам «Як працаваць з агентам», спачатку запісайте умовы кантракту: неабяжлівыя данні, сигнал успеху і тое, што вядзецца пад частковым нявыпаннем задачы. Такі список дапамагае заліцварыць пазнейшыя змены ў кодзе. Спрэцьвачваюце гэты этап як кантракт межаў між вхіднымі даннымі і перакананымі выходнымі рэзультатамі. Дайце назвы артыфактам, задаць критэрыя успеху і не падзеўляйцеся частковым завершэнням задачы без паведамлення. Запісвайце ідэнтыфікатор запиту, ідэнтыфікатор моделі і час адпаведзь на кожны вызов. Без такога лёгкага следу періодычныя кантэкстныя памылкі выглядаюць як багі ў самай прыемлівасці.

Як выглядала рабочая версія

Калі працюеце над стадзіяй «Што ў версіі для роботы», спачатку запішыце умовы контракту: неабяжныя даны, сігнал успеху і тое, што выканаецца у разе частковага нявыпалення. Такі список контроля дапамагае заліцьварыцца пад пазнейшыя змены коду. Запісвайце час выканення аперацый, а таксу токенаў чы роезыкаў праза функцыйнае рэзультат. Відкрытыя даны пра вартасць з’являюцца раніце, таму не будзе неспакою, калі процес пераходзіць з дэмаверсіі ў спяльныя среды. Запішвайце ідэнтыфікатор запиту, ідэнтыфікатор моделі і час затрымкі праз кожны вызов. Без такога лёгася не можна будзе адразніць перыядычныя проблэмы падаючага сервісу ад багоў самай прыемлівання.

User request
  → find_customer(name="Acme Plumbing")
  → verified customer_id: cus_1042
  → get_overdue_invoices(customer_id="cus_1042")
  → verified invoice: INV-2048, $1,850, 18 days overdue
  → generate draft
  → wait for human approval

Паўнайшы урок

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

Чэрніцкі списак для эксплуатацыі

Этап практычнай пералікі працюе найэфективней, калі яго спрыяваць як мерыемую структуру. Запісаўце адна ідеальная версія выканання, адзін прыклад неудачы і запіс парадоксу перад расшырэнням масштаба.

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

Зафіксавайце версію інтэрпретара і файл з блокаванням залежнасцяў пры практыкуванні циклу. Разлікі межаў лептапа і системы CI є найчастэйшай прычыной тых, чаму дэманстраціі API не працуюць.

Калі наступны крок — напісанне коду або вызов інструмента, валідаванне за дапамою схемы краща, чым вільны формат тэксту.

Зробіце контрольную паўзу пасля дорогіх крокаў. Система вярнення не павінна знову ставіць плату за той самы вызов LLM, калі аператар перапрыбуе пазнейшы елемент.

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

Перш чым запускать даную структуру, заморозьце версіі, зафіксавце «золаты» транскрыпты для критичных етапаў і паказвце спосабы вярнення да пачатковага стану. У спільных середавішчах неабходны ліміты швайнаў, перакананні ў належнасці тэрыторіі і чысткі власнік для змены секрэтных даных. Валіце надзейнасць працы над красавімі разовымі дэманстрацыямі.

Прыметкі для a5f763eecd03: не кладзіце ключы прадаўцаў у репазітарый, задаце верхнюю межу токенаў на сесію і зберагачыце транскрыпты празаўсюды з фіксатрамі eval, каб пазнейшыя замены модэляў заставаліся порównаннімы.