Практичні поради: ваш AI-агент не є розумним. Ось як створити такого, що
Покрокове керівництво з практичних порад: ваш AI-агент не є розумним. Ось як створити такий, що включає контракти, перевірки та готові блоки коду для команд, які використовують цю схему.
Використовуйте цей документ як оновлену версію ідей з статті „Ваш AI-агент не є розумним. Ось як створити того, хто справді думає“ для фахівців-операторів: чіткі етапи, впорядковані блоки коду та примітки з відновлення, які залишаються при передачі обов’язків.
Зміст
Етап створення змісту найкраще функціонує, якщо його розглядати як вимірювану структуру. Збережіть один ідеальний запис, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Віддавайте перевагу невеликим, тестованим одиницям коду перед об’ємними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну відповідальність, а не на заплутану послідовність дій. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив певне поле, і ускладнюють відновлення роботи після перерв.
Чому більшість AI-агентів — це просто складні ланцюги запитів
Найкращий спосіб роботи з більшістю AI-агентів на цьому етапі — розглядати їх як вимірювану систему. Зафіксуйте один ідеальний результат, один випадок невдачі та примітки щодо скасування дій перед розширенням обсягу завдань. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового виконання завдань. Встановіть ліміт токенів на кожен хід та сесію. Інструменти агентів активно розширюють контекст; жорсткі обмеження запобігають тому, щоб демонстрації перетворювалися на несподівані рахунки.
Проблема підходу „Просто використовуйте ReAct“
Проблема, пов’язана з використанням лише однієї стадії, найкраще розглядати як вимірювану поверхню. Запишіть один ідеальний приклад роботи, один випадок збою та примітки щодо скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-версії до спільних середовищ. Тримайте стан графа простим та типованим. Вкладені блоки приховують інформацію про те, який вузол записав яке поле, що ускладнює продовження роботи після перерв.
Архітектура: чотири вузли, один цикл
Модель чотирьох вузлів у архітектурі працює найкраще, коли її розглядають як вимірювану поверхню. Збережіть один ідеальний приклад роботи, один випадок збою та запис про скасування змін перед розширенням обсягу. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Зберігайте стан графа у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол записав яке поле, що ускладнює продовження роботи після перерв.
START --> Planner --> Executor <--> Replanner --> Reporter --> END
Керування станом: основа всього
Етап керування станом The Backbone працює найкраще, коли його розглядають як вимірювану поверхню. Зафіксуйте один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу. Документуйте як шлях успішної роботи, так і шлях відновлення одночасно. Повторні спроби, людські контролі та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Зберігайте стан графа у вигляді плоских структур із чіткими типами даних. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, і ускладнюють продовження роботи після перерв. Етап керування станом The Backbone працює найкраще, коли його розглядають як вимірювану поверхню. Зафіксуйте один ідеальний запис, один випадок збою та примітку щодо скасування змін перед розширенням обсягу. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте безповідомного часткового завершення роботи.
import operator
from typing import Annotated, TypedDict
from pydantic import BaseModel, Field
class StrategyState(TypedDict, total=False):
"""Global state that flows through the LangGraph nodes."""
query: str
plan: list[dict]
scratchpad: Annotated[list[dict], operator.add]
current_step: int
final_report: str
replan_count: int
Структуровані схеми вихідних даних: PlanStep та Plan
Для етапу PlanStep у схемах структурованого виводу необхідно перед зміною коду визначити вхідні дані, власника кроку та критерії завершення. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись визначити прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам під час переходу з демо-середовища у спільні. Встановіть людське схвалення для кроків, які витрачають гроші або змінюють дані в продакшені. Підключення під час компіляції не є гарантією повноти бізнес-функціоналу.
AVAILABLE_TOOLS_TEXT = """
- get_metrics(ticker, metric?): Return stock metrics. 'metric' is optional
(P/E, EPS, Revenue, Market Cap, Sector).
- search_news(ticker): Return recent news headlines for a ticker.
- compare_metrics(tickers: list, metric): Compare one metric across
multiple tickers.
"""
class PlanStep(BaseModel):
"""A single executable step inside an analysis plan."""
step_id: int = Field(description="Sequential step number")
tool: str = Field(
description=f"Tool to use. Must be one of:\n{AVAILABLE_TOOLS_TEXT}"
)
args: dict = Field(description="Arguments for the tool call")
purpose: str = Field(description="Why this step is needed")
class Plan(BaseModel):
"""The full plan generated by the planner node."""
goal: str = Field(description="The overall analysis goal")
steps: list[PlanStep] = Field(
description="Ordered list of steps to execute"
)
class ReplanDecision(BaseModel):
"""Output of the replanner node."""
reasoning: str = Field(
description="Analysis of current progress and findings"
)
should_replan: bool = Field(
description="Whether the plan needs modification"
)
updated_steps: list[PlanStep] = Field(
default_factory=list,
description="Remaining steps if replan is needed. Empty if no changes.",
)
Система інструментів: три інструменти, один реєстр
Для третьої стадії системи інструментів необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Конфігурацію слід зберігати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функціоналу мають знаходитися в одному місці, яке оператори можуть перевіряти, не читаючи весь код. Аутентифікуватися потрібно на шлюзі, а повторна авторизація — на рівні обробки даних. Один лише токен-носій не є межею окремого тенантства.
The ToolRegistry
Для етапу The ToolRegistry необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Аутентифікуйтеся біля шлюзу та знову авторизуйтесь на рівні обробки даних. Один лише токен-носій не є межею окремого тенантства. Для етапу The ToolRegistry необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте елементи продукту, визначте критерії успіху та не допускайте безповідомного часткового завершення роботи.
from langchain_core.tools import BaseTool
from typing import Iterable, Mapping, Any
class ToolRegistry:
"""Namespace-aware container for LangChain tools."""
def __init__(self) -> None:
self._tools_by_toolset: dict[str, dict[str, BaseTool]] = {}
def add_tools(self, toolset: str, tools: Iterable[BaseTool]) -> None:
bucket = self._tools_by_toolset.setdefault(toolset, {})
bucket.update({t.name: t for t in tools})
def get_tools(self, toolset: str) -> tuple[BaseTool, ...]:
return tuple(self._tools_by_toolset.get(toolset, {}).values())
def invoke(
self, toolset: str, tool_name: str, tool_args: Mapping[str, Any]
) -> Any:
t = self._tools_by_toolset.get(toolset, {}).get(tool_name)
if t is None:
raise ValueError(
f"Unknown tool '{tool_name}' in toolset '{toolset}'"
)
return t.invoke(dict(tool_args))
Вузол планувальника: думайте перед тим, як діяти
Під час виконання етапу «Вузол планувальника: думайте» спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та наслідки часткової невдачі. Цей перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із функціональними результатами. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ. Створюйте контрольні точки після дорогих кроків. Функція відновлення не повинна знову стягувати плату за один і той самий виклик LLM, коли оператор перезапускає пізніший вузол.
def planner_node(state: StrategyState) -> dict:
"""Create a step-by-step research plan using structured output."""
planner = model.with_structured_output(Plan)
prompt = PLAN_PROMPT.format(
available_tools=AVAILABLE_TOOLS_TEXT,
ticker_choices=ticker_choices_text(),
metric_choices=metric_choices_text(),
query=state["query"],
)
plan: Plan = planner.invoke(prompt)
steps = [s.model_dump() for s in plan.steps]
return {"plan": steps, "current_step": 0}
PLAN_PROMPT: де знаходяться правила
Під час роботи на етапі PLANPROMPT Where the Guardrails спочатку запишіть умови контракту: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Зберігайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь код. Зберігайте у кеші стабільні інструкції системи та схеми інструментів. Повторна передача ідентичних даних є поширеною причиною надмірних витрат.
PLAN_PROMPT = """\
You are a financial research planner. Given a user's analysis request,
create a step-by-step research plan using the available tools.
Available tools:
{available_tools}
Rules:
- Use only the tools listed above.
- Every plan step must be executable with one of those tools.
- When a tool accepts 'ticker' or 'tickers', use only these exact values:
{ticker_choices}
- When a tool accepts 'metric', use one of these exact values:
{metric_choices}
- There are no other tools available. Final synthesis is handled separately.
Create an efficient plan. Group related lookups. Aim for 4-8 steps.
User request: {query}"""
Вузол виконавця: крок за кроком
Під час роботи над етапом The Executor Node One спочатку запишіть умови виконання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Одночасно задокументуйте шлях успішного виконання та шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої доробки. Робіть контрольні пункти після дорогих операцій. Функція відновлення не повинна знову стягувати плату за той самий виклик LLM, коли оператор повторює спробу з наступного етапу. Під час роботи над етапом The Executor Node One спочатку запишіть умови виконання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Цей перелік допомагає зберігати чесність у подальших змінах коду. Розглядайте цей етап як угоду між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте безповідомного часткового завершення роботи.
MAX_STEPS = 12 # Safety limit on total steps
def executor_node(state: StrategyState) -> dict:
"""Execute the next pending step from the plan."""
plan = state.get("plan", [])
current_step = state.get("current_step", 0)
if current_step >= len(plan):
return {}
if current_step >= MAX_STEPS:
return {"current_step": len(plan)}
step = plan[current_step]
tool_name = step["tool"]
tool_args = step["args"]
try:
result = str(
TOOL_REGISTRY.invoke(
AgentName.EXECUTOR.value, tool_name, tool_args
)
)
status = "Error" if result.startswith("Error:") else "Success"
except Exception as exc:
result = f"Error: {exc}"
status = "Error"
entry = {
"step": current_step + 1,
"tool": tool_name,
"args": tool_args,
"result": result,
"status": status,
}
return {
"scratchpad": [entry],
"current_step": current_step + 1,
}
The Replanner Node: Де відбувається самокорекція
Вузол Replanner, де робота на певній стадії є найкращою, якщо його розглядати як вимірювану поверхню. Запишіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Записуйте час виконання та витрати на токени чи запити поруч із функціональними результатами. Візуалізація витрат заздалегідь запобігає несподіваним рахункам під час переходу від демо-середовища до спільних середовищ. Зберігайте стан графа у вигляді простих структур із чіткими типами даних. Вбудовані блоки приховують інформацію про те, який вузол записав яке поле, та ускладнюють продовження роботи після перерв.
MAX_REPLANS = 2 # Prevent infinite replanning
def replanner_node(state: StrategyState) -> dict:
"""Review progress and optionally modify the remaining plan."""
plan = state.get("plan", [])
current_step = state.get("current_step", 0)
replan_count = state.get("replan_count", 0)
scratchpad = state.get("scratchpad", [])
remaining = plan[current_step:]
if len(remaining) = MAX_REPLANS:
return {}
scratchpad_text = "\n".join(
f"Step {e['step']}: {format_tool_call(e['tool'], e['args'])} "
f"-> [{e['status']}] {e['result'][:150]}..."
for e in scratchpad
)
remaining_text = "\n".join(
f"Step {s['step_id']}: {format_tool_call(s['tool'], s['args'])} "
f"- {s['purpose']}"
for s in remaining
)
replanner = model.with_structured_output(ReplanDecision)
prompt = REPLAN_PROMPT.format(
goal=state["query"],
scratchpad=scratchpad_text,
remaining_steps=remaining_text,
)
decision: ReplanDecision = replanner.invoke(prompt)
if decision.should_replan and decision.updated_steps:
new_steps = plan[:current_step] + [
s.model_dump() for s in decision.updated_steps
]
return {"plan": new_steps, "replan_count": replan_count + 1}
return {"replan_count": replan_count + 1}
REPLAN_PROMPT
Етап REPLANPROMPT працює найкраще, якщо його розглядати як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Тримайте конфігурацію окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь граф. Встановіть ліміти на кількість токенів за хід та за сеанс. Інструменти типу агентів активно розширюють контекст; жорсткі обмеження запобігають тому, що демонстрації перетворюються на несподівані рахунки.
REPLAN_PROMPT = """\
You are a financial research planner reviewing progress on a research task.
Original goal: {goal}
Completed steps and findings so far:
{scratchpad}
Remaining steps in the plan:
{remaining_steps}
Based on the findings so far, should the remaining plan change?
If an expected tool failed or revealed something unexpected, add a step
to investigate.
If a step is now redundant, remove it.
Use only the available executable tools already shown in the plan.
Do not add recommendation, summary, or report-writing steps."""
Підключення графа: складання LangGraph
Етап Wiring the Graph LangGraph працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Документуйте як успішний, так і відновлювальний шляхи виконання. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Зберігайте стан графу у простій та типованій формі. Вкладені структури приховують інформацію про те, який вузол заповнив яке поле, що ускладнює продовження роботи після перерв. Етап Wiring the Graph LangGraph працює найкраще, коли його розглядають як вимірювану поверхню. Збережіть один ідеальний запис, один випадок збою та примітку про скасування змін перед розширенням обсягу роботи. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Позначте всі елементи, визначте критерії успіху та не допускайте мовчазного часткового завершення роботи.
from langgraph.graph import StateGraph, END
from enum import Enum
class AgentName(Enum):
PLANNER = "planner"
EXECUTOR = "executor"
REPLANNER = "replanner"
REPORT = "report"
def build_graph():
"""Build and compile the LangGraph planning-agent workflow."""
workflow = StateGraph(StrategyState)
workflow.add_node(AgentName.PLANNER.value, planner_node)
workflow.add_node(AgentName.EXECUTOR.value, executor_node)
workflow.add_node(AgentName.REPLANNER.value, replanner_node)
workflow.add_node(AgentName.REPORT.value, report_node)
workflow.set_entry_point(AgentName.PLANNER.value)
workflow.add_edge(AgentName.PLANNER.value, AgentName.EXECUTOR.value)
workflow.add_edge(AgentName.EXECUTOR.value, AgentName.REPLANNER.value)
workflow.add_conditional_edges(
AgentName.REPLANNER.value,
should_continue_execution,
{
AgentName.EXECUTOR.value: AgentName.EXECUTOR.value,
AgentName.REPORT.value: AgentName.REPORT.value,
},
)
workflow.add_edge(AgentName.REPORT.value, END)
return workflow.compile()
def should_continue_execution(state: StrategyState) -> str:
"""Return the next node name after re-planning."""
if state.get("current_step", 0) >= len(state.get("plan", [])):
return AgentName.REPORT.value
return AgentName.EXECUTOR.value
Приклад реальної експекутації: виконання одного запиту
Для прикладу реальної реалізації на наступному етапі необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не здогадуючись про прихований стан. Записуйте час виконання та витрати на токени або запити поруч із функціональними результатами. Відображення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-середовища до спільних середовищ. Встановлюйте людське схвалення для кроків, які витрачають гроші або змінюють дані в продакшені. Підключення під час компіляції не є гарантією повноти бізнес-функціоналу.
{
"metric": "P/E",
"values": {
"NVDA": 58.3,
"AMD": 102.5
}
}
Що буде далі
На етапі «Куди далі?» необхідно визначити вхідні дані, відповідальну особу за крок та критерії завершення перед зміною коду. Оператори повинні мати можливість перезапустити крок з відомої точки контролю, не намагаючись вгадати прихований стан. Конфігурацію слід тримати окремо від коду додатку. Файли середовища, сховища конфіденційних даних та флаги функцій мають знаходитися в одному місці, де оператори можуть їх перевіряти, не читаючи весь алгоритм. Необхідно передбачити людське схвалення для операцій, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повноти бізнес-функціоналу.
Заключні міркування
На етапі „Остаточні міркування“ необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Необхідно документувати як шлях успішного виконання, так і шлях відновлення. Повторні спроби, людський контроль та обробка некоректних повідомлень є частиною продукту, а не етапом подальшої оптимізації. Встановлюйте людське схвалення для тих кроків, які призводять до витрат грошей чи змінюють дані у продакшені. Підключення на етапі компіляції не є гарантією повності функціоналу продукту. На етапі „Остаточні міркування“ необхідно визначити вхідні дані, власника кроку та критерії завершення перед зміною коду. Оператори повинні мати можливість знову виконати крок з відомої точки контролю, не намагаючись вгадати прихований стан. Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Називайте елементи продукту, визначайте критерії успіху та не допускайте безповідомного часткового завершення роботи.
Давайте продовжувати навчатися разом
Під час роботи над етапом «Давайте продовжувати вчитися» спочатку запишіть умови використання: необхідні вхідні дані, сигнал про успіх та те, що відбувається у разі часткової невдачі. Такий перелік допомагає зберігати чесність пізніших змін у коді. Запишіть час виконання та витрати на токени або запити поруч із результатами функціоналу. Чітке бачення витрат заздалегідь запобігає несподіваним рахункам, коли процес переходить від демо-версії до спільних середовищ. Робіть контрольні пункти після дорогих кроків. Система повторного запуску не повинна знову стягувати плату за один і той самий виклик ШІ, якщо оператор перезапускає пізнішу ланку.
Повідомлення від нашого засновника
Під час роботи над етапом «Повідомлення A» спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та наслідки часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді.
Перелік операційних кроків
Під час роботи над етапом переліку операційних кроків спочатку запишіть умови використання: необхідні дані вхіду, сигнал про успіх та наслідки часткової невдачі. Цей перелік допомагає зберігати чесність подальших змін у коді.
Віддавайте перевагу невеликим, тестованим одиницям коду перед складними скриптами. Якщо якийсь крок зазнає невдачі, причина має вказувати на конкретну функцію, а не на заплутану структуру обробки даних.
Чекпоїнт після дорогих кроків. При відновленні роботи не слід знову брати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.
Фіксуйте версії залежностей та записуйте дайджест зображення, яке використовувалося під час демонстрації. Відтворюваність краща за індивідуальні знання.
Розглядайте цей етап як контракт між вхідними даними та перевіреними результатами. Називайте створені елементи, визначайте критерії успіху та не допускайте мовчазного часткового виконання завдань.
Чекпоїнт після дорогих кроків. При відновленні роботи не слід знову брати плату за той самий виклик LLM, коли оператор намагається знову виконати пізнішу операцію.
Перед підвищенням рівня стеку заморожуйте версії, записуйте ідеальний текстовий запис для критичного шляху виконання та підтверджуйте кроки для скасування змін. У спільних середовищах необхідні обмеження на швидкість виконання, перевірки прав доступу та чіткий власник для зміни секретних даних. Віддавайте перевагу надійності перед креативними одноразовими демонстраціями.
Примітка до пакету fea74fe7fb83: не включайте ключі постачальників у репозиторій, встановіть ліміт токенів на сеанс та зберігайте транскрипції поруч із фіксами для оцінки, щоб подальша заміна моделей залишалася порівнянною.