@@ -29,7 +29,9 @@ from .time_utils import format_local_dt, to_utc_iso, utc_now
logger = logging . getLogger ( __name__ )
def build_assistant_context ( storage : AssistantStorage , user_id : int , tz : ZoneInfo ) - > str :
def build_assistant_context (
storage : AssistantStorage , user_id : int , tz : ZoneInfo
) - > str :
memories = storage . list_memories ( user_id , limit = 20 )
notes = storage . list_notes ( user_id , limit = 10 )
reminders = storage . list_reminders ( user_id , limit = 10 )
@@ -82,27 +84,27 @@ def build_agent_tool_prompt(tz: ZoneInfo) -> str:
" Если нужно выполнить действие, верни tool_calls. Если действие уже выполнено "
" или tool не нужен, верни final. \n \n "
" Форматы ответа: \n "
" { \ " tool_calls\ " :[ {\ " name\ " :\ " create_note\ " ,\ " arguments\ " : {\ " text\ " :\ " ...\ " }}],\ " reset_context\ " :false}\n "
" { \ " final\ " :\ " Короткий ответ пользователю\ " ,\ " reset_context\ " :false}\n \n "
' { " tool_calls " :[ { " name " : " create_note " , " arguments " : { " text " : " ... " }}], " reset_context " :false}\n '
' { " final " : " Короткий ответ пользователю " , " reset_context " :false}\n \n '
" Доступные tools: \n "
" - get_current_datetime {} - узнать текущее локальное и UTC-время. \n "
" - remember {\ " text\ " : string} - сохранить важный долгосрочный факт о пользователе.\n "
" - list_memory {\ " limit\ " : number} - показать сохраненную память.\n "
" - delete_memory {\ " id\ " : number} - удалить запись памяти.\n "
" - create_note {\ " text\ " : string} - сохранить заметку.\n "
" - list_notes {\ " limit\ " : number} - показать заметки.\n "
" - delete_note {\ " id\ " : number} - удалить заметку.\n "
" - create_reminder {\ " when\ " : string, \ " text\ " : string} - поставить напоминание. "
' - remember { " text " : string} - сохранить важный долгосрочный факт о пользователе.\n '
' - list_memory { " limit " : number} - показать сохраненную память.\n '
' - delete_memory { " id " : number} - удалить запись памяти.\n '
' - create_note { " text " : string} - сохранить заметку.\n '
' - list_notes { " limit " : number} - показать заметки.\n '
' - delete_note { " id " : number} - удалить заметку.\n '
' - create_reminder { " when " : string, " text " : string} - поставить напоминание. '
" when можно указывать как ' 30m ' , ' через 2 часа ' , ' 18:30 ' , ' 2026-07-16 18:30 ' . "
" Если пользователь говорит ' завтра/послезавтра/через неделю ' , сам рассчитай "
" дату от текущего локального времени и передай ' YYYY-MM-DD HH:MM ' . \n "
" - list_reminders {\ " limit\ " : number} - показать активные напоминания.\n "
" - cancel_reminder {\ " id\ " : number} - отменить напоминание.\n "
" - create_status {\ " title\ " : string, \ " status\ " : string} - начать отслеживать статус.\n "
" - list_statuses {\ " limit\ " : number} - показать отслеживаемые статусы.\n "
" - update_status {\ " id\ " : number, \ " status\ " : string} - обновить статус.\n "
" - delete_status {\ " id\ " : number} - удалить отслеживаемый объект.\n \n "
" - search_conversation {\ " query\ " : string, \ " limit\ " : number} - найти старое обсуждение "
' - list_reminders { " limit " : number} - показать активные напоминания.\n '
' - cancel_reminder { " id " : number} - отменить напоминание.\n '
' - create_status { " title " : string, " status " : string} - начать отслеживать статус.\n '
' - list_statuses { " limit " : number} - показать отслеживаемые статусы.\n '
' - update_status { " id " : number, " status " : string} - обновить статус.\n '
' - delete_status { " id " : number} - удалить отслеживаемый объект.\n \n '
' - search_conversation { " query " : string, " limit " : number} - найти старое обсуждение '
" во всей сохраненной переписке по содержательным ключевым словам. \n \n "
" Правила: \n "
" - Для просьб ' запомни ' , ' сохрани как факт ' , ' учти на будущее ' используй remember. \n "
@@ -121,7 +123,9 @@ def build_agent_tool_prompt(tz: ZoneInfo) -> str:
def extract_json_object ( raw_text : str ) - > dict [ str , Any ] | None :
candidates = [ raw_text . strip ( ) ]
code_block = re . search ( r " ```(?:json)? \ s*(.*?)``` " , raw_text , re . IGNORECASE | re . DOTALL )
code_block = re . search (
r " ```(?:json)? \ s*(.*?)``` " , raw_text , re . IGNORECASE | re . DOTALL
)
if code_block :
candidates . insert ( 0 , code_block . group ( 1 ) . strip ( ) )
@@ -211,7 +215,9 @@ def coerce_required_text(arguments: dict[str, Any], key: str) -> str:
return str ( value ) . strip ( )
def parse_agent_reminder ( when : str , text : str , tz : ZoneInfo ) - > ReminderParseResult | None :
def parse_agent_reminder (
when : str , text : str , tz : ZoneInfo
) - > ReminderParseResult | None :
parsed = parse_reminder ( f " { when } { text } " . strip ( ) , tz )
if parsed :
return parsed
@@ -254,7 +260,9 @@ def execute_agent_tool(
return { " ok " : True , " id " : memory_id , " text " : text }
if tool_name == " list_memory " :
rows = storage . list_memories ( user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 ) )
rows = storage . list_memories (
user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 )
)
return { " ok " : True , " items " : rows }
if tool_name == " delete_memory " :
@@ -271,7 +279,9 @@ def execute_agent_tool(
return { " ok " : True , " id " : note_id , " text " : text }
if tool_name == " list_notes " :
rows = storage . list_notes ( user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 ) )
rows = storage . list_notes (
user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 )
)
return { " ok " : True , " items " : rows }
if tool_name == " delete_note " :
@@ -291,7 +301,9 @@ def execute_agent_tool(
if parsed . remind_at_utc < = utc_now ( ) :
return { " ok " : False , " error " : " reminder time must be in the future " }
reminder_id = storage . add_reminder ( user_id , chat_id , parsed . text , parsed . remind_at_utc )
reminder_id = storage . add_reminder (
user_id , chat_id , parsed . text , parsed . remind_at_utc
)
return {
" ok " : True ,
" id " : reminder_id ,
@@ -300,7 +312,9 @@ def execute_agent_tool(
}
if tool_name == " list_reminders " :
rows = storage . list_reminders ( user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 ) )
rows = storage . list_reminders (
user_id , coerce_positive_int ( arguments . get ( " limit " ) , 20 )
)
for row in rows :
row [ " remind_at_local " ] = format_local_dt ( row [ " remind_at " ] , tz )
return { " ok " : True , " items " : rows }
@@ -320,7 +334,9 @@ def execute_agent_tool(
return { " ok " : True , " id " : item_id , " title " : title , " status " : status }
if tool_name == " list_statuses " :
rows = storage . list_tracked_items ( user_id , coerce_positive_int ( arguments . get ( " limit " ) , 30 ) )
rows = storage . list_tracked_items (
user_id , coerce_positive_int ( arguments . get ( " limit " ) , 30 )
)
return { " ok " : True , " items " : rows }
if tool_name == " update_status " :
@@ -328,7 +344,11 @@ def execute_agent_tool(
status = coerce_required_text ( arguments , " status " )
if item_id is None or not status :
return { " ok " : False , " error " : " valid id and status are required " }
return { " ok " : storage . set_tracked_status ( user_id , item_id , status ) , " id " : item_id , " status " : status }
return {
" ok " : storage . set_tracked_status ( user_id , item_id , status ) ,
" id " : item_id ,
" status " : status ,
}
if tool_name == " delete_status " :
item_id = parse_positive_int ( str ( arguments . get ( " id " , " " ) ) )
@@ -440,7 +460,12 @@ async def run_agent_prompt(
)
last_tool_results = tool_results
messages . append ( { " role " : " assistant " , " content " : json_dumps ( { " tool_calls " : decision . tool_calls } ) } )
messages . append (
{
" role " : " assistant " ,
" content " : json_dumps ( { " tool_calls " : decision . tool_calls } ) ,
}
)
messages . append (
{
" role " : " user " ,
@@ -496,8 +521,7 @@ async def run_agent_prompt(
hint = f " Проверь, что Ollama запущена и модель установлена: ollama pull { model } "
else :
hint = (
" Проверь YANDEX_CLOUD_FOLDER, YC_API_KEY "
" и доступ к выбранной модели. "
" Проверь YANDEX_CLOUD_FOLDER, YC_API_KEY и доступ к выбранной модели. "
)
await placeholder . edit_text (
f " Н е получилось вызвать { ai_client . display_name } . \n { exc } \n \n { hint } "