Главная / Статьи / Практические заметки: Руководство по архитектурам с множеством агентов

Практические заметки: Руководство по архитектурам с множеством агентов

Пошаговое руководство по использованию «Практических заметок: руководства по архитектурам с несколькими агентами: контракты, проверки и слоты для вставки кода для команд, использующих эту модель».

3174 слов

В этом руководстве пошагово описывается путь от сырья до готовой к работе системы для: Руководства по архитектурамам с множеством агентов. Основное внимание уделяется выполнимым шагам, явным проверкам и коду, который можно просто добавить в репозиторий без необходимости догадываться о его назначении. На этапе обзора необходимо определить входные данные, ответственного за шаг и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Укажите названия элементов, определите критерии успеха и не допускайте безответственного частичного выполнения задачи.

1. Иерархические системы с множеством агентов

При работе над первым этапом «Иерархические многокомпонентные системы» сначала запишите условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой список поможет избежать ошибок при последующих изменениях кода. Рядом с функциональными результатами записывайте время выполнения и стоимость токенов или запросов. Очевидность затрат с самого начала предотвращает неожиданные расходы при переходе от демо-версии к общедоступным средам. Создавайте контрольные точки после дорогостоящих операций. При возобновлении работы система не должна снова взимать плату за один и тот же вызов большой языковой модели, если оператор пытается выполнить последующий шаг.

from fundamental_analysis_tools import evaluate_fundamentals
from langchain.agents import create_agent
from technical_analysis_tools import technical_analysis

agent = create_agent(
    model=llm,
    tools=[technical_analysis, evaluate_fundamentals]
)
Supervisor
├── Technical Analysis Tool
└── Fundamental Analysis Tool
Supervisor
├── Technical Analyst Agent
│   ├── Tool A
│   ├── Tool B
│   └── ...
└── Fundamental Analyst Agent
    ├── Tool C
    ├── Tool D
    └── ...
from langchain.tools import tool
from langgraph_supervisor import create_supervisor

@tool
def get_weather(city: str) -> str:
    """Use this tool to get the weather of a city or location"""
    return f"The weather is sunny in {city}"

weather_expert = create_agent(
    model=llm,
    tools=[get_weather],
    name="weather_expert"
)

technical_analyst_agent = create_agent(
    model=llm,
    tools=[technical_analysis],
    name="technical_analyst"
)

fundamental_analyst_agent = create_agent(
    model=llm,
    tools=[evaluate_fundamentals],
    name="fundamental_analyst"
)

analysis_squad = create_supervisor(
    [technical_analyst_agent, fundamental_analyst_agent],
    model=llm,
    supervisor_name="analysis_supervisor",
    prompt=(
        "You are a team supervisor managing a fundamental analyst and a "
        "technical analyst..."
    )
)

analysis_app = analysis_squad.compile(
    name="fundamental_and_technical_analyst"
)

supervisor_graph = create_supervisor(
    [analysis_app, weather_expert],
    model=llm,
    prompt=(
        "You are a team supervisor managing a fundamental analyst, a "
        "technical analyst and a weather expert..."
    )
)

supervisor = supervisor_graph.compile()

Когда следует использовать иерархическую архитектуру?

При разработке этапа «Когда следует использовать этот этап» сначала запишите условия контракта: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Создавайте контрольные точки после дорогостоящих операций. Система возобновления работы не должна повторно взимать плату за один и тот же вызов большой языковой модели, когда оператор пытается выполнить следующий шаг.

2. Явные рабочие процессы с несколькими агентами

При работе над этапом 2 «Явные многократные агенты» сначала запишите условия взаимодействия: необходимые входные данные, сигнал о успехе и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Документируйте как успешный, так и восстановительный пути выполнения. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом доработки позже. Создавайте контрольные точки после дорогостоящих операций. Механизм возобновления работы не должен повторно оплачивать один и тот же вызов большой языковой модели, когда оператор пытается выполнить следующий этап. При работе над этапом 2 «Явные многократные агенты» сначала запишите условия взаимодействия: необходимые входные данные, сигнал о успехе и действия при частичной неудаче. Такой чек-лист поможет сохранять честность при последующих изменениях кода. Рассматривайте этот этап как договор между входными данными и проверенными результатами. Дайте названия создаваемым элементам, определите критерии успеха и не допускайте безусловного частичного завершения работы.

def execute_plan(
    plan: Plan,
    state: State,
) -> Command[
    Literal[
        "fundamental_analysis_agent",
        "technical_analysis_agent",
        "respond",
    ]
]:
    gotos = [
        Send(
            step.action.agent_to_use,
            {"query": step.action.query_to_send},
        )
        for step in plan.steps
    ]

    if not gotos:
        gotos.append(
            Send("respond", {"messages": state["messages"]})
        )

    return Command(goto=gotos)

def router(state: State) -> Command[
    Literal["fundamental_analysis_agent", "technical_analysis_agent", "respond"]
]:
    # Get plan
    query = state['messages'][-1].content
    response = llm_planner.invoke(query) #invoke planner
    return execute_plan(response, state)

Когда использовать явные рабочие процессы с множеством агентов

Подход «Когда использовать явные этапы» наилучшим образом работает, когда его рассматривают как измеримую структуру. Сначала соберите один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию, прежде чем расширять объём работ. Записывайте временные показатели, а также стоимость токенов или запросов рядом с функциональными результатами. Отображение стоимости на ранних этапах предотвращает неожиданные счёты при переходе от демо-среды к общедоступным средам. Сохраняйте структуру графа простой и типизированной; вложенные структуры скрывают информацию о том, какой узел заполнил тот или иной поле, и могут нарушить возобновление работы после прерываний.

3. Рой агентов

Этап «3 Agent Swarm» работает наилучшим образом, если рассматривать его как измеримую поверхность. Соберите один пример успешной работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объёма задачи. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Сохраняйте состояние графа простым и типизированным. Вложенные структуры данных скрывают информацию о том, какой узел заполнил тот или иной поле, и приводят к нарушению возобновления работы после перерывов.

from langgraph_swarm import (
    create_handoff_tool,
    create_swarm,
)

## We'll have to redefine our all agents to include the handoff tool
weather_expert = create_agent(
    model=llm,
    tools=[
        get_weather,
        create_handoff_tool(
            agent_name="technical_analyst",
            description="Transfer for technical analysis related questions"
        ),
        create_handoff_tool(
            agent_name="fundamental_analyst",
            description="Transfer for fundamental analysis related questions"
        ),
    ],
    name="weather_expert"
)
technical_analyst_agent = ...
fundamental_analyst_agent = ...

swarm_workflow = create_swarm(
    [fundamental_analyst_agent, technical_analyst_agent, weather_expert],
    default_active_agent="weather_expert" #a default agent must be specified.
)

swarm = swarm_workflow.compile()

Когда следует использовать swarm?

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

4. Многопроцессные системы Blackboard

Для этапа 4 Blackboard Multi Agent необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отображение затрат с самого начала предотвращает неожиданные расходы при переходе с демо-среды в общедоступные среды. На операции, связанные с тратой денег или изменением производственных данных, должно быть наложено одобрение человека. Подключение компонентов во время компиляции не гарантирует полноты реализации бизнес-логики.

a. Черная доска

Для этапа «доска объявлений» необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь код. Вводите ручное утверждение для операций, связанных с тратой денег или изменением производственных данных. Подключение на этапе компиляции не гарантирует полноты охвата бизнес-логики.

class Blackboard(TypedDict, total=False):
    query: str

    # Problem frame
    problem_framed: bool
    ticker: str | None
    location: str | None
    wants_technical: bool
    wants_fundamental: bool

    # Specialist panels
    technical: dict | None
    fundamental: dict | None
    environmental: dict | None

    # Integrated solution
    synthesis: str | None

    # Control state
    next_knowledge_source: str | None
    cycles: int

b. Источники знаний

На этапе b «Источники знаний» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо задокументировать как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. Внедрять утверждение человеком для операций, связанных с тратой денег или изменением производственных данных. Настройка на этапе компиляции не гарантирует полноты функционала продукта. На этапе b «Источники знаний» необходимо определить входные данные, ответственного за выполнение шага и критерии завершения перед внесением изменений в код. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия элементам, определите критерии успеха и не допускайте безответственного частичного выполнения задач.

def can_analyse_technicals(board: Blackboard) -> bool:
    return (
        board.get("ticker") is not None
        and board.get("wants_technical", False)
        and board.get("technical") is None
    )

в. Компонент управления

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

def control_component(board: Blackboard) -> dict:
    eligible = [
        source
        for source in KNOWLEDGE_SOURCES #these are subagents
        if source.precondition(board)
    ]
    if not eligible:
        return {"next_knowledge_source": None}
    chosen = max(
        eligible,
        key=lambda source: source.priority,
    )
    return {
        "next_knowledge_source": chosen.name,
        "cycles": board.get("cycles", 0) + 1,
    }
inspect → identify eligible specialists → activate one
       → contribute → inspect again

Выполнение происходит намеренно последовательно

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

Когда следует использовать архитектуру «черной доски»?

При работе над этапом «Когда следует использовать это решение?» сначала запишите условия работы: необходимые входные данные, сигнал о успехе и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Документируйте как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не элементами последующей доработки. Устанавливайте контрольные точки после дорогостоящих операций. Механизм возобновления работы не должен повторно взимать плату за один и тот же вызов большой языковой модели при повторной попытке обработки определенного узла. При работе над этапом «Когда следует использовать это решение?» сначала запишите условия работы: необходимые входные данные, сигнал о успехе и действия при частичной неудаче. Такой чек-лист помогает сохранять честность при последующих изменениях кода. Рассматривайте этот этап как контракт между входными данными и проверенными результатами. Дайте названия соответствующим элементам, определите критерии успеха и не допускайте безусловного частичного завершения работы.

5. Системы с множеством агентов типа Actor-Critic (адверсарные)

Метод 5 актор-критиков в адверсарной многоступенчатой схеме работает наилучшим образом, когда рассматривается как измеримая структура. Сначала необходимо зафиксировать один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию, прежде чем расширять объем задачи. Рядом с функциональными результатами следует записывать временные показатели и стоимость токенов или запросов. Очевидность затрат с самого начала предотвращает неожиданные расходы при переходе от демо-среды к общедоступным средам. Состояние графа должно оставаться простым и типизированным; вложенные структуры скрывают информацию о том, какой узел заполнил тот или иной поле, и могут нарушить возобновление работы после прерываний.

actor → critic → judge
  ↑                 |
  └──── revise ─────┘

a. Актор

Модель «Актер» работает наилучшим образом, когда рассматривается как измеримая структура. Соберите один пример успешной работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объёма работы. Храните конфигурацию вне кода приложения. Файлы среды, хранилища секретов и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь граф. Сохраняйте состояние графа простым и типизированным. Вложенные структуры данных маскируют информацию о том, какой узел заполнил тот или иной поле, и приводят к нарушению возобновления работы после перерывов.

def actor(state: AdversarialState) -> dict:
    if state.get("latest_critique") is None:
        prompt = f"Produce a first draft.\n\nTASK: {state['task']}"
    else:
        prompt = (
            "Revise the current draft.\n\n"
            f"TASK:\n{state['task']}\n\n"
            f"CURRENT DRAFT:\n{state['draft']}\n\n"
            f"CRITIQUE:\n{state['latest_critique']}\n\n"
            f"JUDGE'S PRIORITY:\n{state['latest_verdict']['focus']}"
        )

    draft = llm.invoke(prompt).content

    return {
        "draft": draft,
        "round": state.get("round", 0) + 1,
    }

b. Критик

Этап критической оценки работает наилучшим образом, когда его рассматривают как измеримую поверхность. Зафиксируйте один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Документируйте как успешный путь выполнения, так и путь восстановления. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не этапом последующей доработки. Сохраняйте структуру графа простой и типизированной; вложенные структуры скрывают информацию о том, какой узел заполнил тот или иной поле, и могут нарушить возобновление работы после прерываний. Этап критической оценки работает наилучшим образом, когда его рассматривают как измеримую поверхность. Зафиксируйте один идеальный пример работы, один случай сбоя и записку о возврате к предыдущему состоянию перед расширением объема работ. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия соответствующим документам, определите критерии успеха и не соглашайтесь на молчаливое частичное выполнение задач.

class Issue(BaseModel):
    severity: Literal["blocking", "major", "minor"]
    description: str

class Critique(BaseModel):
    issues: list[Issue]
    summary: str
def weighted_issue_score(issues: list[dict]) -> int:
    weights = {
        "blocking": 5,
        "major": 2,
        "minor": 1,
    }

    return sum(
        weights[issue["severity"]]
        for issue in issues
    )

c. Судья

На этапе судейства необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рядом с функциональными результатами следует записывать время выполнения и стоимость токенов или запросов. Отображение затрат с самого начала предотвращает неожиданные расходы при переходе с демо-среды в общедоступные среды. Необходимо ввести человеческое утверждение для операций, связанных с тратой денег или изменением производственных данных. Подключение компонентов во время компиляции не гарантирует полноты реализации бизнес-логики.

class Verdict(BaseModel):
    decision: Literal["accept", "revise"]
    reasoning: str
    focus: str

def judge(state: AdversarialState) -> dict:
    verdict = llm.with_structured_output(Verdict).invoke(
        [
            HumanMessage(
                content=(
                    "Evaluate the critic's findings on their merits. "
                    "Accept if only minor issues remain. "
                    "Request revision only for blocking or material problems.\n\n"
                    f"DRAFT:\n{state['draft']}\n\n"
                    f"CRITIQUE:\n{state['latest_critique']}"
                )
            )
        ]
    )

    return {
        "latest_verdict": verdict.model_dump(),
    }

Почему все еще необходим контролирующий механизм

Чтобы понять, почему используется механизм наблюдения за выполнением шагов, необходимо заранее определить входные данные, ответственного за выполнение шага и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии системы. Конфигурацию следует хранить отдельно от кода приложения. Файлы среды, хранилища конфиденциальных данных и флаги функций должны находиться в одном месте, чтобы операторы могли их проверять, не читая весь кодовый граф. Для операций, связанных с тратой денег или изменением производственных данных, необходимо предусмотреть утверждение человеком. Настройки, заданные на этапе компиляции, не гарантируют полноты обработки бизнес-логики.

round 1: 12
round 2: 8
round 3: 8
round 4: 9
def watchdog(state: AdversarialState) -> dict:
    round_number = state.get("round", 0)
    scores = state.get("issue_counts", [])

    if round_number >= HARD_ROUND_CAP:
        return {
            "halt_reason": "hard round cap reached",
        }

    if len(scores) >= STALEMATE_WINDOW:
        window = scores[-STALEMATE_WINDOW:]

        if window[-1] >= window[0]:
            return {
                "halt_reason": (
                    f"stalemate detected: {window}"
                ),
            }

    return {}

Финализация должна отличать успех от неудачи

Для завершения необходимо четко определить этап успеха, указать входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Необходимо задокументировать как успешный, так и восстановительный сценарии работы. Повторные попытки, проверки человеком и обработка неработоспособных сообщений являются частью продукта, а не последующими улучшениями. Внедрять человеческое одобрение для операций, связанных с тратой денег или изменением производственных данных. Конфигурация во время компиляции не заменяет полноты бизнес-логики. Для завершения необходимо четко определить этап успеха, указать входные данные, ответственного за шаг и критерии завершения перед изменением кода. Операторы должны иметь возможность перезапустить шаг с известной точки контроля, не догадываясь о скрытом состоянии. Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия элементам, определите критерии успеха и не допускайте безответных частичных завершений.

Когда следует использовать архитектуру с противоборствующими компонентами?

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

Выбор архитектуры

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

Заключительные мысли

На этапе «Заключительные мысли» сначала запишите условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой список помогает сохранять честность при последующих изменениях кода. Документируйте одновременно успешный и восстановительный сценарии работы. Повторные попытки, проверки со стороны оператора и обработка некорректных сообщений являются частью продукта, а не элементами последующей доработки. Устанавливайте контрольные точки после дорогостоящих операций. Система возобновления работы не должна снова оплачивать один и тот же вызов большой языковой модели, если оператор пытается выполнить следующий этап заново. На этапе «Заключительные мысли» сначала запишите условия работы: необходимые входные данные, сигнал о успешном выполнении и действия при частичной неудаче. Такой список помогает сохранять честность при последующих изменениях кода. Рассматривайте этот этап как договор между входными данными и проверенными результатами. Дайте названия всем элементам, определите критерии успеха и не допускайте безответственного частичного выполнения задач.

Чек-лист операций

Этап операционного чек-листа работает наилучшим образом, когда его рассматривают как измеримую структуру. Соберите один эталонный пример работы, один случай сбоя и запись о возврате к предыдущему состоянию перед расширением объема работ.

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

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

Каждый раз, когда это позволяют бюджетные ограничения, добавляйте тест на базовую работоспособность, который проверяет критически важный путь в рамках CI с использованием фикстчеров, а не реальных платных API.

Рассматривайте этот этап как контракт между входными данными и проверенными выходными результатами. Дайте названия всем элементам, определите критерии успеха и не соглашайтесь на молчаливое частичное выполнение задачи.

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

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

Примечание для f6f8c689c406: не храните ключи поставщика в репозитории, установите лимит токенов на одну сессию и сохраняйте отчеты рядом с фиксами для оценки, чтобы позже можно было сравнивать результаты работы разных моделей.