Практичні зауваження: Створення агента підтримки LEPA за допомогою LangGraph
Покрокова інструкція з практичних нотаток: створення агента підтримки LEPA за допомогою LangGraph: контракти, перевірки та слоти для коду для команд, які використовують цю схему.
Використовуйте цей документ як оновлену версію ідей з «Створення агента підтримки LEPA за допомогою LangGraph» для спеціалістів: чіткі етапи, впорядковані блоки коду та примітки щодо відновлення, які зберігаються після передачі обов’язків. Етап Огляду найкраще функціонує, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний запис розмови, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій.
Що ви хотіли, щоб робив перший граф
Щоб досягти бажаного результату на цьому етапі, необхідно спочатку визначити вхідні дані, виконавця кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви для кожних елементів, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи. Необхідно забезпечити людське схвалення для тих операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Компіляційна налаштування не є гарантією повності виконання бізнес-завдань.
User message
↓
Notice who is speaking (teacher / admin / unknown)
↓
Classify the topic (grades, login, …)
↓
Too vague? Ask a clarifying question
↓
Otherwise continue toward docs + an answer
Налаштування проекту (навмисно зроблено нудним)
Для налаштування проекту навмисно використовується етапна структура: необхідно визначити вхідні дані, власника кожного кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ. Встановлюйте людське схвалення для кроків, які вимагають фінансових витрат чи змінюють дані у продакшені. Підключення під час компіляції не є гарантією повності бізнес-функціоналу.
PRJ-02/
├── app/
│ ├── state.py # SupportState
│ ├── graph.py # StateGraph wiring
│ ├── agents/ # intake, knowledge, support
│ ├── nodes/ # classify, ask_clarification
│ └── tools/ # search_knowledge (next article)
├── knowledge/ # LEPA support Markdown
├── api/ # FastAPI (later article)
└── tests/
Стан: об’єкт, який переміщується по графі
Для опису стану об’єкта на певній стадії необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь алгоритм. Встановлюйте людське схвалення для кроків, які спричиняють витрати грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-функціоналу. Для опису стану об’єкта на певній стадії необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Віддавайте перевагу невеликим, тестованим одиницям перед величезними скриптами. Коли крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних.
e.messages # conversation turns (add_messages reducer)
user_role # teacher / admin / unknown
issue_category # grades, authentication, …
clarification_needed # should we ask for more detail?
clarification_question # what we ask
retrieved_documents # doc snippets (later step)
final_answer # what we return to the user
conversation_summary # reserved for later — unused in v1
messages: Annotated[list, add_messages]
{"user_role": "teacher"}
{"issue_category": "grades", "clarification_needed": False}
Вузли: по одній задачі на кожен
Під час роботи з вузлами, де кожен етап має по одній задачі, спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Призначте назви елементам, визначте критерії успіху та не допускайте безповідомного часткового виконання. Робіть контрольні пункти після дорогих кроків. Система відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор перезапускає пізніший вузол.
(state) → partial update
Прийом даних
Під час роботи на етапі прийому даних спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних. Робіть контрольні пункти після дорогих кроків. Система повторного запуску не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перепробовує пізніший етап.
Класифікувати
Під час роботи над етапом Classify спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Створюйте контрольні точки після дорогих кроків. Функція відновлення не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізніший етап. Під час роботи над етапом Classify спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді. Віддавайте перевагу невеликим, тестованим одиницям коду перед величезними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану структуру обробки даних.
Запитати уточнення
Етап уточнення запиту працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок невдачі та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Зберігайте стан графа у простому та типованому вигляді. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.
Знання + підтримка
Етап підтримки знань працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ. Зберігайте стан графа у випрямленому та типованому форматі. Вкладені блоки приховують інформацію про те, який вузол заповнив яке поле, та ускладнюють продовження роботи після перерв.
Краї: як відбувається керування
Механізм керування рухом елементів на сцені працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний приклад роботи, один випадок збою та запис про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.
Звичайні елементи з’єднання
Етап «Звичайні краї» функціонує найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний приклад виконання, один випадок збою та примітку про скасування змін перед розширенням обсягу. Документуйте як шлях успішного виконання, так і шлях відновлення разом. Повторні спроби, людські перевірки та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графу у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
graph.add_edge(START, "intake")
graph.add_edge("intake", "classify")
graph.add_edge("knowledge", "support")
graph.add_edge("support", END)
When this node finishes, always go there next.
Етап умовних країв працює найкраще, коли його розглядають як вимірювану поверхню. Запишіть один ідеальний приклад виконання, один випадок збою та примітку про скасування дій перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям перед складними скриптами. Коли якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
def route_after_classify(state: SupportState) -> Literal["clarify", "continue"]:
if state.get("clarification_needed"):
return "clarify"
return "continue"
graph.add_conditional_edges(
"classify",
route_after_classify,
{
"clarify": "ask_clarification",
"continue": "knowledge",
},
)
"help" → clarify → ask_clarification → END
"How do I enter grades?" → continue → knowledge → support → END
ПОЧАТОК та КІНЕЦЬ
Етапи START та END найкраще функціонують, якщо їх розглядати як вимірювану поверхню. Збережіть один ідеальний зразок результату, один випадок невдачі та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив певне поле, що ускладнює продовження роботи після перерв.
START = where the runtime begins
END = where this run stops
START → intake → classify → …
…
ask_clarification → END
support → END
Повна топологія (у стані побудови)
Повна топологія як етап найкраще функціонує, коли її розглядають як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ. Тримайте стан графа простим та типованим. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, і ускладнюють продовження роботи після перерв.
START
→ intake
→ classify
→ conditional
├─ clarify → ask_clarification → END
└─ continue → knowledge → support → END
Як запит проходить через граф
Механізм руху запиту через різні етапи працює найкраще, коли його розглядають як вимірювану структуру. Збережіть один ідеальний запис дії, один випадок збою та примітку про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв.
Чітке запитання
Етап чіткого формулювання запитань працює найкраще, коли його розглядають як вимірювану поверхню. Зафіксуйте один ідеальний варіант запису, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний сценарії роботи. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графа простим та типованим. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв.
User: "Why aren't grades showing?"
↓
intake → user_role = unknown (unless they said teacher/admin)
↓
classify → issue_category = grades, clarification_needed = False
↓
route → "continue"
↓
knowledge → search docs (next article)
↓
support → final_answer
↓
END
Нечітке запитання
User: "help"
↓
intake
↓
classify → unknown + clarification_needed = True
↓
route → "clarify"
↓
ask_clarification → asks which LEPA area
↓
END
compile() та invoke(): момент виконання
return graph.compile(checkpointer=checkpointer)
from langchain_core.messages import HumanMessage
from app.graph import app_graph
result = app_graph.invoke(
{"messages": [HumanMessage(content="How do I enter grades?")]}
)
print(result["final_answer"])