Интерактивные агенты в стиле REPL
Циклы ввода, история и многоходовый диалог в агентах терминала.
«Интерактивные агенты в стиле REPL» — бесплатный урок AI Agents на CoddyKit. Это урок 2 из 4. Ты можешь прочитать весь урок бесплатно ниже — а потом практиковать его прямо в браузере с встроенным редактором кода и ИИ-репетитором 24/7. Это часть пути обучения AI Agents, и твой прогресс синхронизируется между веб-версией и приложением CoddyKit. Курс AI Agents содержит 4 уроков всего.
Что такое агент в стиле REPL
REPL (цикл чтения, вычисления и вывода) — это интерактивный сеанс в терминале: Вы вводите данные, получаете вывод и продолжаете работу. Агенты для общения в терминале работают именно так: пользователь вводит сообщение, агент отвечает, и разговор продолжается, пока пользователь не выйдет.
Базовый цикл REPL
Простейший REPL — это цикл while True, который читает пользовательский ввод с помощью input(), обрабатывает его и выводит результат. Цикл выполняется бесконечно, пока пользователь не подаст сигнал завершения.
def run_agent_repl(inputs):
print('Agent started. Type /exit to quit.')
conversation_history = []
for user_input in inputs:
user_input = user_input.strip()
if not user_input:
continue
if user_input == '/exit':
print('Goodbye!')
break
conversation_history.append({'role': 'user', 'content': user_input})
agent_reply = f'[Agent reply to: {user_input}]'
conversation_history.append({'role': 'assistant', 'content': agent_reply})
print(f'Agent: {agent_reply}')
run_agent_repl(['Hello agent', 'What can you do?', '/exit'])Корректная обработка KeyboardInterrupt
Когда пользователь нажимает Ctrl+C, Python вызывает исключение KeyboardInterrupt. Обработайте его на верхнем уровне, чтобы вывести вежливое сообщение о завершении вместо трассировки стека.
def run_agent_repl():
print('Agent ready. Press Ctrl+C to exit.')
conversation_history = []
try:
while True:
user_input = input('You: ').strip()
if not user_input:
continue
conversation_history.append({'role': 'user', 'content': user_input})
reply = '[mocked agent response]'
conversation_history.append({'role': 'assistant', 'content': reply})
print(f'Agent: {reply}')
except KeyboardInterrupt:
print('\nSession ended. Goodbye!')
except EOFError:
# Triggered when stdin is closed (piped input ends)
print('\nEnd of input. Exiting.')
if __name__ == '__main__':
import builtins
_demo_inputs = iter(['What agents can you build?', EOFError])
def _fake_input(prompt=''):
val = next(_demo_inputs)
if val is EOFError:
raise EOFError
print(prompt + val)
return val
builtins.input = _fake_input
run_agent_repl()
Сохранение истории многошагового разговора
Чтобы агент помнил разговор, храните список сообщений и передавайте LLM всю историю на каждом шаге. Начните с системного сообщения, задающего роль агента.
import openai
client = openai.OpenAI(api_key='YOUR_API_KEY')
def run_chat_repl():
history = [
{'role': 'system', 'content': 'You are a helpful AI assistant.'}
]
print('Chat started. Type /exit to quit.')
try:
while True:
user_input = input('You: ').strip()
if not user_input or user_input == '/exit':
break
history.append({'role': 'user', 'content': user_input})
response = client.chat.completions.create(
model='gpt-4o-mini',
messages=history
)
reply = response.choices[0].message.content
history.append({'role': 'assistant', 'content': reply})
print(f'Agent: {reply}')
except KeyboardInterrupt:
print('\nGoodbye!')readline: история и редактирование ввода
Модуль readline (Unix/Mac) позволяет перемещаться клавишами со стрелками по предыдущим введённым данным, как в настоящей оболочке. Пользователи могут нажать стрелку вверх, чтобы восстановить предыдущие сообщения.
try:
import readline # available on Unix/Mac, not Windows
# Enable persistent history between sessions
import os
HISTORY_FILE = os.path.expanduser('~/.agent_history')
try:
readline.read_history_file(HISTORY_FILE)
except FileNotFoundError:
pass
import atexit
atexit.register(readline.write_history_file, HISTORY_FILE)
readline.set_history_length(500)
print('History enabled (use up/down arrows)')
except ImportError:
pass # readline not available on Windows
# After this, input() automatically has history supportВстроенные команды со слешем
Добавьте в агент REPL встроенные команды со слешем для часто выполняемых действий: /help, /exit, /clear для сброса разговора и /history для просмотра предыдущих сообщений.
def handle_command(cmd: str, history: list) -> bool:
'Returns True if the command was handled, False if it is a regular message'
if cmd == '/exit' or cmd == '/quit':
print('Goodbye!')
raise SystemExit(0)
elif cmd == '/help':
print('Commands: /exit, /clear, /history, /help')
return True
elif cmd == '/clear':
# Keep only the system message
system = history[0] if history and history[0]['role'] == 'system' else None
history.clear()
if system:
history.append(system)
print('[Conversation cleared]')
return True
elif cmd == '/history':
for i, msg in enumerate(history):
if msg['role'] != 'system':
print(f' [{msg["role"]}] {msg["content"][:80]}')
return True
return False # not a command, treat as regular message
if __name__ == '__main__':
demo_history = [
{'role': 'system', 'content': 'You are a helpful agent.'},
{'role': 'user', 'content': 'Hi there'},
{'role': 'assistant', 'content': 'Hello! How can I help?'},
]
handle_command('/help', demo_history)
handle_command('/history', demo_history)
Ограничение длины истории разговора
Передача LLM всей истории разговора на каждом шаге увеличивает количество токенов линейно. Обрезайте историю до последних N сообщений, сохраняя системное сообщение, чтобы контролировать расходы.
def trim_history(history: list, max_turns: int = 10) -> list:
system_messages = [m for m in history if m['role'] == 'system']
non_system = [m for m in history if m['role'] != 'system']
# Keep the last max_turns messages (each turn = 1 user + 1 assistant)
max_messages = max_turns * 2
trimmed = non_system[-max_messages:]
return system_messages + trimmed
# Usage in the REPL loop:
# history.append({'role': 'user', 'content': user_input})
# trimmed = trim_history(history, max_turns=10)
# response = client.chat.completions.create(model='gpt-4o-mini', messages=trimmed)
print('Trimming history prevents token count from growing unboundedly')Отображение приветственного баннера
Приветственный баннер делает REPL более продуманным. Выведите его один раз при запуске, указав название агента, версию и доступные команды. Используйте простой рисунок ASCII или библиотеку rich для добавления цвета.
def print_banner():
banner = '''
====================================
AI Research Agent v1.0
====================================
Type your question to begin.
Commands: /help /clear /exit
====================================
'''
print(banner)
# Or with rich colors:
# from rich.console import Console
# from rich.panel import Panel
# console = Console()
# console.print(Panel('[bold cyan]AI Research Agent[/] v1.0', subtitle='Type /help for commands'))
print_banner()Отображение индикаторов обработки
Вызовы LLM могут занимать несколько секунд. Пока Вы ждёте, показывайте сообщение «обдумывает…», чтобы пользователи знали, что агент обрабатывает запрос. Уберите его, когда придёт ответ.
import sys
import threading
import time
def thinking_spinner(stop_event: threading.Event):
frames = ['|', '/', '-', '\\']
i = 0
while not stop_event.is_set():
sys.stdout.write(f'\rAgent is thinking... {frames[i % 4]}')
sys.stdout.flush()
time.sleep(0.1)
i += 1
sys.stdout.write('\r' + ' ' * 30 + '\r') # clear the line
sys.stdout.flush()
def call_agent_with_spinner(query: str) -> str:
stop = threading.Event()
spinner = threading.Thread(target=thinking_spinner, args=(stop,))
spinner.start()
# result = agent.run(query) # the actual slow call
time.sleep(1) # simulating work
result = 'The answer is 42.'
stop.set()
spinner.join()
return result
if __name__ == '__main__':
answer = call_agent_with_spinner('What is 6 * 7?')
print(f'\nFinal answer: {answer}')
Сохранение сеанса в файл
Позвольте пользователям сохранять разговор в файл в конце сеанса. Это полезно для исследовательских сеансов, результаты которых нужно сохранить.
import json
import datetime
import os
def save_session(history: list, directory: str = 'sessions'):
os.makedirs(directory, exist_ok=True)
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
filename = os.path.join(directory, f'session_{timestamp}.json')
with open(filename, 'w') as f:
json.dump({
'saved_at': timestamp,
'messages': history
}, f, indent=2)
print(f'Session saved to {filename}')
# Usage in REPL on /save command:
# save_session(conversation_history)
if __name__ == '__main__':
import tempfile
demo_dir = tempfile.mkdtemp()
demo_history = [
{'role': 'user', 'content': 'Hello'},
{'role': 'assistant', 'content': 'Hi! How can I help?'},
]
save_session(demo_history, directory=demo_dir)
Полный шаблон агента REPL
Объединим всё вместе: готовый к использованию шаблон REPL с управлением историей, командами со слешем, обработкой прерывания с клавиатуры и сохранением сеанса.
def run_repl(agent):
history = [{'role': 'system', 'content': 'You are a helpful AI assistant.'}]
print('Agent ready. Type /help for commands.')
try:
while True:
user_input = input('You: ').strip()
if not user_input:
continue
if user_input.startswith('/'):
handle_command(user_input, history)
continue
history.append({'role': 'user', 'content': user_input})
trimmed = trim_history(history, max_turns=10)
reply = agent.run(trimmed)
history.append({'role': 'assistant', 'content': reply})
print(f'Agent: {reply}')
except KeyboardInterrupt:
print('\nSession ended.')
save_session(history)Проверка знаний: агенты в стиле REPL
Проверьте, насколько хорошо Вы понимаете работу интерактивных агентов REPL.
Итоги: интерактивные агенты в стиле REPL
Теперь Вы умеете создавать полнофункционального интерактивного агента для терминала:
- Используйте
while True: input()как основной цикл чтения - Перехватывайте
KeyboardInterruptдля корректного выхода по Ctrl+C - Храните список
conversation_historyдля памяти о многошаговом разговоре - Используйте
readlineдля хранения истории ввода с клавишами-стрелками - Реализуйте команды со слешем
/help,/clearи/exit - Обрезайте историю, чтобы предотвратить неограниченный рост количества токенов
- Показывайте анимацию или индикатор выполнения во время вызовов LLM
Часто задаваемые вопросы
Урок «Интерактивные агенты в стиле REPL» бесплатный?
Да — полный текст урока «Интерактивные агенты в стиле REPL» бесплатно доступен здесь в веб-версии. Чтобы практиковать его интерактивно (встроенный редактор кода и ИИ-репетитор 24/7) и разблокировать остальной курс AI Agents, подпишись на CoddyKit PRO. Курс AI Agents содержит 4 уроков всего.
Чему я научусь в уроке «Интерактивные агенты в стиле REPL»?
Циклы ввода, история и многоходовый диалог в агентах терминала. Ты практикуешь AI Agents с помощью реального кода, который запускаешь прямо в браузере, и ИИ-репетитор 24/7 отвечает на твои вопросы во время урока.
Нужен ли мне опыт, чтобы начать AI Agents?
Предыдущий опыт не требуется. AI Agents на CoddyKit структурирован для всех уровней — от новичков до продвинутых, поэтому ты можешь начать отсюда или с самого начала и учиться в своем темпе. Это урок 2 из 4.
Сколько времени занимает урок «Интерактивные агенты в стиле REPL»?
Большинство уроков CoddyKit занимают около 5–10 минут. Каждый из них компактный и интерактивный, поэтому ты постоянно делаешь прогресс и продолжаешь с того же места в веб-версии и приложении.
Можно ли писать и запускать код в этом уроке AI Agents?
Да. Каждый урок AI Agents включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.
Все уроки этого курса
- Создание интерфейсов агентов командной строки
- Интерактивные агенты в стиле REPL
- Разбор аргументов и справочный текст
- Потоковый вывод в агентах CLI