fix:表情包发送无记录,两次wait结果,notice不显示msg_id,展示每次token上下文

This commit is contained in:
SengokuCola
2026-04-01 12:36:33 +08:00
parent daf7c644a6
commit a7310916e6
17 changed files with 340 additions and 162 deletions

View File

@@ -10,6 +10,7 @@ from typing import Any, Dict, List, Optional, Sequence
import asyncio
import json
import random
import re
from PIL import Image as PILImage
from pydantic import BaseModel, Field as PydanticField
@@ -27,7 +28,7 @@ from src.config.config import global_config
from src.core.tooling import ToolRegistry, ToolSpec
from src.know_u.knowledge import extract_category_ids_from_result
from src.llm_models.model_client.base_client import BaseClient
from src.llm_models.payload_content.message import Message, MessageBuilder, RoleType
from src.llm_models.payload_content.message import ImageMessagePart, Message, MessageBuilder, RoleType, TextMessagePart
from src.llm_models.payload_content.resp_format import RespFormat, RespFormatType
from src.llm_models.payload_content.tool_option import ToolCall, ToolDefinitionInput, ToolOption, normalize_tool_options
from src.services.llm_service import LLMServiceClient
@@ -137,7 +138,7 @@ class MaisakaChatLoopService:
try:
self._chat_system_prompt = load_prompt(
"maidairy_chat",
"maisaka_chat",
file_tools_section=tools_section,
bot_name=global_config.bot.nickname,
identity=self._personality_prompt,
@@ -695,6 +696,61 @@ class MaisakaChatLoopService:
padding=(0, 1),
)
@staticmethod
def _estimate_text_tokens(text: str) -> int:
"""估算单段文本的输入 token 数。"""
normalized_text = text.strip()
if not normalized_text:
return 0
cjk_char_count = sum(1 for char in normalized_text if "\u4e00" <= char <= "\u9fff")
latin_chunks = re.findall(r"[A-Za-z0-9_]+", normalized_text)
latin_token_count = sum(max(1, (len(chunk) + 3) // 4) for chunk in latin_chunks)
punctuation_count = len(re.findall(r"[^\w\s]", normalized_text))
whitespace_bonus = max(1, normalized_text.count("\n"))
return cjk_char_count + latin_token_count + punctuation_count + whitespace_bonus
@classmethod
def _estimate_request_tokens(cls, messages: Sequence[Message]) -> int:
"""估算本轮请求消息的总输入 token 数。"""
total_tokens = 0
for message in messages:
total_tokens += 4
total_tokens += cls._estimate_text_tokens(str(message.role.value))
if message.tool_call_id:
total_tokens += cls._estimate_text_tokens(message.tool_call_id)
if message.tool_calls:
for tool_call in message.tool_calls:
total_tokens += cls._estimate_text_tokens(getattr(tool_call, "func_name", "") or "")
total_tokens += cls._estimate_text_tokens(
json.dumps(getattr(tool_call, "args", {}) or {}, ensure_ascii=False)
)
for part in message.parts:
if isinstance(part, TextMessagePart):
total_tokens += cls._estimate_text_tokens(part.text)
continue
if isinstance(part, ImageMessagePart):
total_tokens += max(256, len(part.image_base64) // 12)
return total_tokens
@staticmethod
def _build_prompt_stats_text(
*,
selected_history_count: int,
built_message_count: int,
input_token_count: int,
) -> str:
"""构造本轮 prompt 的统计信息文本。"""
if input_token_count >= 10_000:
input_token_text = f"{input_token_count / 1000:.1f}k"
else:
input_token_text = str(input_token_count)
return (
f"已选上下文消息数={selected_history_count} "
f"大模型消息数={built_message_count} "
f"估算输入Token={input_token_text}"
)
async def chat_loop_step(self, chat_history: List[LLMContextMessage]) -> ChatResponse:
"""执行一轮 Maisaka 规划器请求。
@@ -708,6 +764,13 @@ class MaisakaChatLoopService:
await self.ensure_chat_prompt_loaded()
selected_history, selection_reason = self._select_llm_context_messages(chat_history)
built_messages = self._build_request_messages(selected_history)
input_token_count = self._estimate_request_tokens(built_messages)
prompt_stats_text = self._build_prompt_stats_text(
selected_history_count=len(selected_history),
built_message_count=len(built_messages),
input_token_count=input_token_count,
)
display_subtitle = f"{selection_reason} | {prompt_stats_text}"
def message_factory(_client: BaseClient) -> List[Message]:
"""返回当前轮次已经构建好的请求消息。
@@ -743,7 +806,7 @@ class MaisakaChatLoopService:
Panel(
Group(*ordered_panels),
title="MaiSaka 大模型请求 - 对话单步",
subtitle=selection_reason,
subtitle=display_subtitle,
border_style="cyan",
padding=(0, 1),
)
@@ -757,6 +820,7 @@ class MaisakaChatLoopService:
f"工具数={len(all_tools)} "
f"启用打断={self._interrupt_flag is not None}"
)
logger.info(f"??Prompt??: {prompt_stats_text}")
generation_result = await self._llm_chat.generate_response_with_messages(
message_factory=message_factory,
options=LLMGenerationOptions(