1
0
Fork 0
forked from zovos/bot_tg

feat: multi-user awareness in chat, per-user /talk prompt, thinking tokens for /talk

This commit is contained in:
zovos 2026-05-20 10:29:44 +00:00
parent acb7421cc0
commit 89a14f2ebf

View file

@ -41,6 +41,8 @@ SUMMARY_LINE_CHAR_LIMIT = 220
HISTORY_LINE_CHAR_LIMIT = 450 HISTORY_LINE_CHAR_LIMIT = 450
RETRY_MIN_MAX_TOKENS = 512 RETRY_MIN_MAX_TOKENS = 512
RETRY_MAX_MAX_TOKENS = 1_024 RETRY_MAX_MAX_TOKENS = 1_024
TALK_MAX_TOKENS = 1536
TALK_THINKING_BUDGET = 1024
REPLY_RULES = { REPLY_RULES = {
"mention": {"cooldown": 25, "min_user_messages": 1}, "mention": {"cooldown": 25, "min_user_messages": 1},
@ -128,10 +130,21 @@ BOT_STYLE_PROMPT = (
SYSTEM_PROMPT = ( SYSTEM_PROMPT = (
f"{BOT_STYLE_PROMPT}\n\n" f"{BOT_STYLE_PROMPT}\n\n"
"Ты отвечаешь как живой участник одного Telegram-чата. " "Ты отвечаешь как живой участник Telegram-чата с несколькими разными людьми. "
"Сообщения в истории записаны в формате «Имя: текст» — каждое имя это отдельный человек, не путай их. "
"Отвечай тому, кто написал последним, учитывай контекст именно его сообщений. "
"Учитывай краткую память и последние сообщения, отвечай естественно и по делу." "Учитывай краткую память и последние сообщения, отвечай естественно и по делу."
) )
TALK_SYSTEM_PROMPT = (
f"{BOT_STYLE_PROMPT}\n\n"
"Ты ведёшь личный разговор тет-а-тет с одним конкретным человеком через /talk. "
"Помни всё, что он говорил раньше — ты видишь историю этого разговора и должен её учитывать. "
"Замечай если человек продолжает старую тему или переходит к новой. "
"Отвечай КОРОТКО: 13 предложения максимум. "
"Только если тебя прямо попросили объяснить, развернуть или написать подробно — тогда можно больше."
)
AUTOREPLY_SYSTEM_PROMPT = ( AUTOREPLY_SYSTEM_PROMPT = (
f"{BOT_STYLE_PROMPT}\n\n" f"{BOT_STYLE_PROMPT}\n\n"
"Ты иногда сам коротко и уместно влезаешь в разговор в Telegram-чате. " "Ты иногда сам коротко и уместно влезаешь в разговор в Telegram-чате. "
@ -533,6 +546,7 @@ async def _call_single_model(
temperature: float, temperature: float,
top_p: float, top_p: float,
disable_thinking: bool, disable_thinking: bool,
reasoning_budget: int = 0,
) -> str: ) -> str:
url = f"{api_url.rstrip('/')}/v1/chat/completions" url = f"{api_url.rstrip('/')}/v1/chat/completions"
headers = {"Content-Type": "application/json"} headers = {"Content-Type": "application/json"}
@ -551,6 +565,8 @@ async def _call_single_model(
"reasoning_format": "none", "reasoning_format": "none",
"chat_template_kwargs": {"enable_thinking": False, "thinking": False}, "chat_template_kwargs": {"enable_thinking": False, "thinking": False},
}) })
elif reasoning_budget > 0:
payload["reasoning_budget"] = reasoning_budget
timeout = aiohttp.ClientTimeout(total=120) timeout = aiohttp.ClientTimeout(total=120)
async with aiohttp.ClientSession(timeout=timeout) as session: async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(url, json=payload, headers=headers) as resp: async with session.post(url, json=payload, headers=headers) as resp:
@ -582,7 +598,7 @@ async def _call_single_model(
messages, messages,
api_url=api_url, api_key=api_key, model=model, api_url=api_url, api_key=api_key, model=model,
max_tokens=retry_max_tokens, temperature=temperature, top_p=top_p, max_tokens=retry_max_tokens, temperature=temperature, top_p=top_p,
disable_thinking=True, disable_thinking=True, reasoning_budget=0,
) )
return cleaned_content return cleaned_content
@ -594,6 +610,7 @@ async def _call_llm(
temperature: float, temperature: float,
top_p: float, top_p: float,
disable_thinking: bool | None = None, disable_thinking: bool | None = None,
reasoning_budget: int = 0,
) -> str: ) -> str:
disable_thinking = FORCE_DISABLE_THINKING if disable_thinking is None else disable_thinking disable_thinking = FORCE_DISABLE_THINKING if disable_thinking is None else disable_thinking
@ -606,6 +623,7 @@ async def _call_llm(
temperature=temperature, temperature=temperature,
top_p=top_p, top_p=top_p,
disable_thinking=disable_thinking, disable_thinking=disable_thinking,
reasoning_budget=reasoning_budget,
) )
# Если основная модель не смогла ответить — пробуем fallback # Если основная модель не смогла ответить — пробуем fallback
@ -622,6 +640,7 @@ async def _call_llm(
temperature=temperature, temperature=temperature,
top_p=top_p, top_p=top_p,
disable_thinking=disable_thinking, disable_thinking=disable_thinking,
reasoning_budget=reasoning_budget,
) )
if fallback_result: if fallback_result:
return f"{fallback_result}\n\n<i>🤖 {fallback_model}</i>" return f"{fallback_result}\n\n<i>🤖 {fallback_model}</i>"
@ -696,6 +715,8 @@ async def _generate_response(
max_tokens: int, max_tokens: int,
temperature: float, temperature: float,
top_p: float, top_p: float,
disable_thinking: bool | None = None,
reasoning_budget: int = 0,
) -> str: ) -> str:
await _maybe_refresh_summary(chat_id, user_id=user_id) await _maybe_refresh_summary(chat_id, user_id=user_id)
if current_content is None: if current_content is None:
@ -721,6 +742,8 @@ async def _generate_response(
max_tokens=max_tokens, max_tokens=max_tokens,
temperature=temperature, temperature=temperature,
top_p=top_p, top_p=top_p,
disable_thinking=disable_thinking,
reasoning_budget=reasoning_budget,
) )
@ -1040,11 +1063,13 @@ async def handle_talk(message: Message) -> None:
await message.bot.send_chat_action(chat_id=chat_id, action="typing") await message.bot.send_chat_action(chat_id=chat_id, action="typing")
response = await _generate_response( response = await _generate_response(
chat_id, chat_id,
system_prompt=SYSTEM_PROMPT, system_prompt=TALK_SYSTEM_PROMPT,
user_id=user_id, user_id=user_id,
max_tokens=768, max_tokens=TALK_MAX_TOKENS,
temperature=0.7, temperature=0.7,
top_p=0.95, top_p=0.95,
disable_thinking=False,
reasoning_budget=TALK_THINKING_BUDGET,
) )
except Exception: except Exception:
logger.exception("Talk command generation failed") logger.exception("Talk command generation failed")