fix:FFUF
This commit is contained in:
@@ -78,7 +78,6 @@ class ChatBot:
|
||||
groupinfo = message.message_info.group_info
|
||||
userinfo = message.message_info.user_info
|
||||
|
||||
|
||||
if userinfo.user_id in global_config.ban_user_id:
|
||||
logger.debug(f"用户{userinfo.user_id}被禁止回复")
|
||||
return
|
||||
|
||||
@@ -328,7 +328,9 @@ def split_into_sentences_w_remove_punctuation(text: str) -> List[str]:
|
||||
final_sentences = [content for content, sep in merged_segments if content] # 只保留有内容的段
|
||||
|
||||
# 清理可能引入的空字符串和仅包含空白的字符串
|
||||
final_sentences = [s for s in final_sentences if s.strip()] # 过滤掉空字符串以及仅包含空白(如换行符、空格)的字符串
|
||||
final_sentences = [
|
||||
s for s in final_sentences if s.strip()
|
||||
] # 过滤掉空字符串以及仅包含空白(如换行符、空格)的字符串
|
||||
|
||||
logger.debug(f"分割并合并后的句子: {final_sentences}")
|
||||
return final_sentences
|
||||
|
||||
@@ -2,6 +2,7 @@ import asyncio
|
||||
import time
|
||||
import traceback
|
||||
from typing import List, Optional, Dict, Any, TYPE_CHECKING
|
||||
|
||||
# import json # 移除,因为使用了json_utils
|
||||
from src.plugins.chat.message import MessageRecv, BaseMessageInfo, MessageThinking, MessageSending
|
||||
from src.plugins.chat.message import MessageSet, Seg # Local import needed after move
|
||||
@@ -17,7 +18,7 @@ from src.plugins.heartFC_chat.heartFC_generator import HeartFCGenerator
|
||||
from src.do_tool.tool_use import ToolUser
|
||||
from ..chat.message_sender import message_manager # <-- Import the global manager
|
||||
from src.plugins.chat.emoji_manager import emoji_manager
|
||||
from src.plugins.utils.json_utils import extract_tool_call_arguments, safe_json_dumps, process_llm_tool_response # 导入新的JSON工具
|
||||
from src.plugins.utils.json_utils import process_llm_tool_response # 导入新的JSON工具
|
||||
# --- End import ---
|
||||
|
||||
|
||||
@@ -37,7 +38,7 @@ if TYPE_CHECKING:
|
||||
# Keep this if HeartFCController methods are still needed elsewhere,
|
||||
# but the instance variable will be removed from HeartFChatting
|
||||
# from .heartFC_controler import HeartFCController
|
||||
from src.heart_flow.heartflow import SubHeartflow, heartflow # <-- 同时导入 heartflow 实例用于类型检查
|
||||
from src.heart_flow.heartflow import SubHeartflow # <-- 同时导入 heartflow 实例用于类型检查
|
||||
|
||||
PLANNER_TOOL_DEFINITION = [
|
||||
{
|
||||
@@ -327,7 +328,6 @@ class HeartFChatting:
|
||||
with Timer("Wait New Msg", cycle_timers): # <--- Start Wait timer
|
||||
wait_start_time = time.monotonic()
|
||||
while True:
|
||||
|
||||
# 检查是否有新消息
|
||||
has_new = await observation.has_new_messages_since(planner_start_db_time)
|
||||
if has_new:
|
||||
@@ -424,7 +424,7 @@ class HeartFChatting:
|
||||
observed_messages: List[dict] = []
|
||||
|
||||
current_mind: Optional[str] = None
|
||||
llm_error = False
|
||||
llm_error = False
|
||||
|
||||
try:
|
||||
observation = self.sub_hf._get_primary_observation()
|
||||
@@ -434,19 +434,17 @@ class HeartFChatting:
|
||||
except Exception as e:
|
||||
logger.error(f"{log_prefix}[Planner] 获取观察信息时出错: {e}")
|
||||
|
||||
|
||||
try:
|
||||
current_mind, _past_mind = await self.sub_hf.do_thinking_before_reply()
|
||||
except Exception as e_subhf:
|
||||
logger.error(f"{log_prefix}[Planner] SubHeartflow 思考失败: {e_subhf}")
|
||||
current_mind = "[思考时出错]"
|
||||
|
||||
|
||||
# --- 使用 LLM 进行决策 --- #
|
||||
action = "no_reply" # 默认动作
|
||||
emoji_query = "" # 默认表情查询
|
||||
reasoning = "默认决策或获取决策失败"
|
||||
llm_error = False # LLM错误标志
|
||||
emoji_query = "" # 默认表情查询
|
||||
reasoning = "默认决策或获取决策失败"
|
||||
llm_error = False # LLM错误标志
|
||||
|
||||
try:
|
||||
prompt = await self._build_planner_prompt(observed_messages_str, current_mind, self.sub_hf.structured_info)
|
||||
@@ -475,21 +473,17 @@ class HeartFChatting:
|
||||
|
||||
# 使用辅助函数处理工具调用响应
|
||||
success, arguments, error_msg = process_llm_tool_response(
|
||||
response,
|
||||
expected_tool_name="decide_reply_action",
|
||||
log_prefix=f"{log_prefix}[Planner] "
|
||||
response, expected_tool_name="decide_reply_action", log_prefix=f"{log_prefix}[Planner] "
|
||||
)
|
||||
|
||||
|
||||
if success:
|
||||
# 提取决策参数
|
||||
action = arguments.get("action", "no_reply")
|
||||
reasoning = arguments.get("reasoning", "未提供理由")
|
||||
emoji_query = arguments.get("emoji_query", "")
|
||||
|
||||
|
||||
# 记录决策结果
|
||||
logger.debug(
|
||||
f"{log_prefix}[Planner] 决策结果: {action}, 理由: {reasoning}, 表情查询: '{emoji_query}'"
|
||||
)
|
||||
logger.debug(f"{log_prefix}[Planner] 决策结果: {action}, 理由: {reasoning}, 表情查询: '{emoji_query}'")
|
||||
else:
|
||||
# 处理工具调用失败
|
||||
logger.warning(f"{log_prefix}[Planner] {error_msg}")
|
||||
@@ -584,7 +578,7 @@ class HeartFChatting:
|
||||
"""优雅关闭HeartFChatting实例,取消活动循环任务"""
|
||||
log_prefix = self._get_log_prefix()
|
||||
logger.info(f"{log_prefix} 正在关闭HeartFChatting...")
|
||||
|
||||
|
||||
# 取消循环任务
|
||||
if self._loop_task and not self._loop_task.done():
|
||||
logger.info(f"{log_prefix} 正在取消HeartFChatting循环任务")
|
||||
@@ -605,17 +599,19 @@ class HeartFChatting:
|
||||
if self._processing_lock.locked():
|
||||
self._processing_lock.release()
|
||||
logger.warning(f"{log_prefix} 已释放处理锁")
|
||||
|
||||
|
||||
logger.info(f"{log_prefix} HeartFChatting关闭完成")
|
||||
|
||||
async def _build_planner_prompt(self, observed_messages_str: str, current_mind: Optional[str], structured_info: Dict[str, Any]) -> str:
|
||||
async def _build_planner_prompt(
|
||||
self, observed_messages_str: str, current_mind: Optional[str], structured_info: Dict[str, Any]
|
||||
) -> str:
|
||||
"""构建 Planner LLM 的提示词"""
|
||||
|
||||
prompt = f"你的名字是 {global_config.BOT_NICKNAME}。是一个女大二学生,正在QQ聊天,正在决定是否以及如何回应当前的聊天。\n"
|
||||
|
||||
if structured_info:
|
||||
prompt += f"以下是一些额外的信息:\n{structured_info}\n"
|
||||
|
||||
|
||||
if observed_messages_str:
|
||||
prompt += "观察到的最新聊天内容如下 (最近的消息在最后):\n---\n"
|
||||
prompt += observed_messages_str
|
||||
|
||||
@@ -72,7 +72,13 @@ class HeartFCGenerator:
|
||||
return None
|
||||
|
||||
async def _generate_response_with_model(
|
||||
self, structured_info: str, current_mind_info: str, reason: str, message: MessageRecv, model: LLMRequest, thinking_id: str
|
||||
self,
|
||||
structured_info: str,
|
||||
current_mind_info: str,
|
||||
reason: str,
|
||||
message: MessageRecv,
|
||||
model: LLMRequest,
|
||||
thinking_id: str,
|
||||
) -> str:
|
||||
sender_name = ""
|
||||
|
||||
|
||||
@@ -81,13 +81,22 @@ class PromptBuilder:
|
||||
self.activate_messages = ""
|
||||
|
||||
async def build_prompt(
|
||||
self, build_mode, reason, current_mind_info, structured_info, message_txt: str, sender_name: str = "某人", chat_stream=None
|
||||
self,
|
||||
build_mode,
|
||||
reason,
|
||||
current_mind_info,
|
||||
structured_info,
|
||||
message_txt: str,
|
||||
sender_name: str = "某人",
|
||||
chat_stream=None,
|
||||
) -> Optional[tuple[str, str]]:
|
||||
if build_mode == "normal":
|
||||
return await self._build_prompt_normal(chat_stream, message_txt, sender_name)
|
||||
|
||||
elif build_mode == "focus":
|
||||
return await self._build_prompt_focus(reason, current_mind_info, structured_info, chat_stream, message_txt, sender_name)
|
||||
return await self._build_prompt_focus(
|
||||
reason, current_mind_info, structured_info, chat_stream, message_txt, sender_name
|
||||
)
|
||||
return None
|
||||
|
||||
async def _build_prompt_focus(
|
||||
|
||||
@@ -711,7 +711,7 @@ class LLMRequest:
|
||||
reasoning_content = ""
|
||||
content = ""
|
||||
tool_calls = None # 初始化工具调用变量
|
||||
|
||||
|
||||
async for line_bytes in response.content:
|
||||
try:
|
||||
line = line_bytes.decode("utf-8").strip()
|
||||
@@ -733,7 +733,7 @@ class LLMRequest:
|
||||
if delta_content is None:
|
||||
delta_content = ""
|
||||
accumulated_content += delta_content
|
||||
|
||||
|
||||
# 提取工具调用信息
|
||||
if "tool_calls" in delta:
|
||||
if tool_calls is None:
|
||||
@@ -741,7 +741,7 @@ class LLMRequest:
|
||||
else:
|
||||
# 合并工具调用信息
|
||||
tool_calls.extend(delta["tool_calls"])
|
||||
|
||||
|
||||
# 检测流式输出文本是否结束
|
||||
finish_reason = chunk["choices"][0].get("finish_reason")
|
||||
if delta.get("reasoning_content", None):
|
||||
@@ -774,23 +774,19 @@ class LLMRequest:
|
||||
if think_match:
|
||||
reasoning_content = think_match.group(1).strip()
|
||||
content = re.sub(r"<think>.*?</think>", "", content, flags=re.DOTALL).strip()
|
||||
|
||||
|
||||
# 构建消息对象
|
||||
message = {
|
||||
"content": content,
|
||||
"reasoning_content": reasoning_content,
|
||||
}
|
||||
|
||||
|
||||
# 如果有工具调用,添加到消息中
|
||||
if tool_calls:
|
||||
message["tool_calls"] = tool_calls
|
||||
|
||||
|
||||
result = {
|
||||
"choices": [
|
||||
{
|
||||
"message": message
|
||||
}
|
||||
],
|
||||
"choices": [{"message": message}],
|
||||
"usage": usage,
|
||||
}
|
||||
return result
|
||||
@@ -1128,9 +1124,9 @@ class LLMRequest:
|
||||
|
||||
response = await self._execute_request(endpoint="/chat/completions", payload=data, prompt=prompt)
|
||||
# 原样返回响应,不做处理
|
||||
|
||||
|
||||
return response
|
||||
|
||||
|
||||
async def generate_response_tool_async(self, prompt: str, tools: list, **kwargs) -> Union[str, Tuple]:
|
||||
"""异步方式根据输入的提示生成模型的响应"""
|
||||
# 构建请求体,不硬编码max_tokens
|
||||
@@ -1139,7 +1135,7 @@ class LLMRequest:
|
||||
"messages": [{"role": "user", "content": prompt}],
|
||||
**self.params,
|
||||
**kwargs,
|
||||
"tools": tools
|
||||
"tools": tools,
|
||||
}
|
||||
|
||||
logger.debug(f"向模型 {self.model_name} 发送工具调用请求,包含 {len(tools)} 个工具")
|
||||
@@ -1150,7 +1146,7 @@ class LLMRequest:
|
||||
logger.debug(f"收到工具调用响应,包含 {len(tool_calls) if tool_calls else 0} 个工具调用")
|
||||
return content, reasoning_content, tool_calls
|
||||
else:
|
||||
logger.debug(f"收到普通响应,无工具调用")
|
||||
logger.debug("收到普通响应,无工具调用")
|
||||
return response
|
||||
|
||||
async def get_embedding(self, text: str) -> Union[list, None]:
|
||||
|
||||
@@ -303,7 +303,9 @@ async def build_readable_messages(
|
||||
)
|
||||
|
||||
readable_read_mark = translate_timestamp_to_human_readable(read_mark, mode=timestamp_mode)
|
||||
read_mark_line = f"\n\n--- 以上消息已读 (标记时间: {readable_read_mark}) ---\n--- 请关注你上次思考之后以下的新消息---\n"
|
||||
read_mark_line = (
|
||||
f"\n\n--- 以上消息已读 (标记时间: {readable_read_mark}) ---\n--- 请关注你上次思考之后以下的新消息---\n"
|
||||
)
|
||||
|
||||
# 组合结果,确保空部分不引入多余的标记或换行
|
||||
if formatted_before and formatted_after:
|
||||
|
||||
@@ -1,27 +1,28 @@
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Dict, Optional, TypeVar, Generic, List, Union, Callable, Tuple
|
||||
from typing import Any, Dict, TypeVar, List, Union, Callable, Tuple
|
||||
|
||||
# 定义类型变量用于泛型类型提示
|
||||
T = TypeVar('T')
|
||||
T = TypeVar("T")
|
||||
|
||||
# 获取logger
|
||||
logger = logging.getLogger("json_utils")
|
||||
|
||||
|
||||
def safe_json_loads(json_str: str, default_value: T = None) -> Union[Any, T]:
|
||||
"""
|
||||
安全地解析JSON字符串,出错时返回默认值
|
||||
|
||||
|
||||
参数:
|
||||
json_str: 要解析的JSON字符串
|
||||
default_value: 解析失败时返回的默认值
|
||||
|
||||
|
||||
返回:
|
||||
解析后的Python对象,或在解析失败时返回default_value
|
||||
"""
|
||||
if not json_str:
|
||||
return default_value
|
||||
|
||||
|
||||
try:
|
||||
return json.loads(json_str)
|
||||
except json.JSONDecodeError as e:
|
||||
@@ -31,66 +32,67 @@ def safe_json_loads(json_str: str, default_value: T = None) -> Union[Any, T]:
|
||||
logger.error(f"JSON解析过程中发生意外错误: {e}")
|
||||
return default_value
|
||||
|
||||
def extract_tool_call_arguments(tool_call: Dict[str, Any],
|
||||
default_value: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||
|
||||
def extract_tool_call_arguments(tool_call: Dict[str, Any], default_value: Dict[str, Any] = None) -> Dict[str, Any]:
|
||||
"""
|
||||
从LLM工具调用对象中提取参数
|
||||
|
||||
|
||||
参数:
|
||||
tool_call: 工具调用对象字典
|
||||
default_value: 解析失败时返回的默认值
|
||||
|
||||
|
||||
返回:
|
||||
解析后的参数字典,或在解析失败时返回default_value
|
||||
"""
|
||||
default_result = default_value or {}
|
||||
|
||||
|
||||
if not tool_call or not isinstance(tool_call, dict):
|
||||
logger.error(f"无效的工具调用对象: {tool_call}")
|
||||
return default_result
|
||||
|
||||
|
||||
try:
|
||||
# 提取function参数
|
||||
function_data = tool_call.get("function", {})
|
||||
if not function_data or not isinstance(function_data, dict):
|
||||
logger.error(f"工具调用缺少function字段或格式不正确: {tool_call}")
|
||||
return default_result
|
||||
|
||||
|
||||
# 提取arguments
|
||||
arguments_str = function_data.get("arguments", "{}")
|
||||
if not arguments_str:
|
||||
return default_result
|
||||
|
||||
|
||||
# 解析JSON
|
||||
return safe_json_loads(arguments_str, default_result)
|
||||
|
||||
|
||||
except Exception as e:
|
||||
logger.error(f"提取工具调用参数时出错: {e}")
|
||||
return default_result
|
||||
|
||||
def get_json_value(json_obj: Dict[str, Any], key_path: str,
|
||||
default_value: T = None,
|
||||
transform_func: Callable[[Any], T] = None) -> Union[Any, T]:
|
||||
|
||||
def get_json_value(
|
||||
json_obj: Dict[str, Any], key_path: str, default_value: T = None, transform_func: Callable[[Any], T] = None
|
||||
) -> Union[Any, T]:
|
||||
"""
|
||||
从JSON对象中按照路径提取值,支持点表示法路径,如"data.items.0.name"
|
||||
|
||||
|
||||
参数:
|
||||
json_obj: JSON对象(已解析的字典)
|
||||
key_path: 键路径,使用点表示法,如"data.items.0.name"
|
||||
default_value: 获取失败时返回的默认值
|
||||
transform_func: 可选的转换函数,用于对获取的值进行转换
|
||||
|
||||
|
||||
返回:
|
||||
路径指向的值,或在获取失败时返回default_value
|
||||
"""
|
||||
if not json_obj or not key_path:
|
||||
return default_value
|
||||
|
||||
|
||||
try:
|
||||
# 分割路径
|
||||
keys = key_path.split(".")
|
||||
current = json_obj
|
||||
|
||||
|
||||
# 遍历路径
|
||||
for key in keys:
|
||||
# 处理数组索引
|
||||
@@ -108,7 +110,7 @@ def get_json_value(json_obj: Dict[str, Any], key_path: str,
|
||||
return default_value
|
||||
else:
|
||||
return default_value
|
||||
|
||||
|
||||
# 应用转换函数(如果提供)
|
||||
if transform_func and current is not None:
|
||||
return transform_func(current)
|
||||
@@ -117,17 +119,17 @@ def get_json_value(json_obj: Dict[str, Any], key_path: str,
|
||||
logger.error(f"从JSON获取值时出错: {e}, 路径: {key_path}")
|
||||
return default_value
|
||||
|
||||
def safe_json_dumps(obj: Any, default_value: str = "{}", ensure_ascii: bool = False,
|
||||
pretty: bool = False) -> str:
|
||||
|
||||
def safe_json_dumps(obj: Any, default_value: str = "{}", ensure_ascii: bool = False, pretty: bool = False) -> str:
|
||||
"""
|
||||
安全地将Python对象序列化为JSON字符串
|
||||
|
||||
|
||||
参数:
|
||||
obj: 要序列化的Python对象
|
||||
default_value: 序列化失败时返回的默认值
|
||||
ensure_ascii: 是否确保ASCII编码(默认False,允许中文等非ASCII字符)
|
||||
pretty: 是否美化输出JSON
|
||||
|
||||
|
||||
返回:
|
||||
序列化后的JSON字符串,或在序列化失败时返回default_value
|
||||
"""
|
||||
@@ -141,13 +143,14 @@ def safe_json_dumps(obj: Any, default_value: str = "{}", ensure_ascii: bool = Fa
|
||||
logger.error(f"JSON序列化过程中发生意外错误: {e}")
|
||||
return default_value
|
||||
|
||||
|
||||
def merge_json_objects(*objects: Dict[str, Any]) -> Dict[str, Any]:
|
||||
"""
|
||||
合并多个JSON对象(字典)
|
||||
|
||||
|
||||
参数:
|
||||
*objects: 要合并的JSON对象(字典)
|
||||
|
||||
|
||||
返回:
|
||||
合并后的字典,后面的对象会覆盖前面对象的相同键
|
||||
"""
|
||||
@@ -157,109 +160,110 @@ def merge_json_objects(*objects: Dict[str, Any]) -> Dict[str, Any]:
|
||||
result.update(obj)
|
||||
return result
|
||||
|
||||
|
||||
def normalize_llm_response(response: Any, log_prefix: str = "") -> Tuple[bool, List[Any], str]:
|
||||
"""
|
||||
标准化LLM响应格式,将各种格式(如元组)转换为统一的列表格式
|
||||
|
||||
|
||||
参数:
|
||||
response: 原始LLM响应
|
||||
log_prefix: 日志前缀
|
||||
|
||||
|
||||
返回:
|
||||
元组 (成功标志, 标准化后的响应列表, 错误消息)
|
||||
"""
|
||||
# 检查是否为None
|
||||
if response is None:
|
||||
return False, [], "LLM响应为None"
|
||||
|
||||
|
||||
# 记录原始类型
|
||||
logger.debug(f"{log_prefix}LLM响应原始类型: {type(response).__name__}")
|
||||
|
||||
|
||||
# 将元组转换为列表
|
||||
if isinstance(response, tuple):
|
||||
logger.debug(f"{log_prefix}将元组响应转换为列表")
|
||||
response = list(response)
|
||||
|
||||
|
||||
# 确保是列表类型
|
||||
if not isinstance(response, list):
|
||||
return False, [], f"无法处理的LLM响应类型: {type(response).__name__}"
|
||||
|
||||
|
||||
# 处理工具调用部分(如果存在)
|
||||
if len(response) == 3:
|
||||
content, reasoning, tool_calls = response
|
||||
|
||||
|
||||
# 将工具调用部分转换为列表(如果是元组)
|
||||
if isinstance(tool_calls, tuple):
|
||||
logger.debug(f"{log_prefix}将工具调用元组转换为列表")
|
||||
tool_calls = list(tool_calls)
|
||||
response[2] = tool_calls
|
||||
|
||||
|
||||
return True, response, ""
|
||||
|
||||
|
||||
def process_llm_tool_calls(response: List[Any], log_prefix: str = "") -> Tuple[bool, List[Dict[str, Any]], str]:
|
||||
"""
|
||||
处理并提取LLM响应中的工具调用列表
|
||||
|
||||
|
||||
参数:
|
||||
response: 标准化后的LLM响应列表
|
||||
log_prefix: 日志前缀
|
||||
|
||||
|
||||
返回:
|
||||
元组 (成功标志, 工具调用列表, 错误消息)
|
||||
"""
|
||||
# 确保响应格式正确
|
||||
if len(response) != 3:
|
||||
return False, [], f"LLM响应元素数量不正确: 预期3个元素,实际{len(response)}个"
|
||||
|
||||
|
||||
# 提取工具调用部分
|
||||
tool_calls = response[2]
|
||||
|
||||
|
||||
# 检查工具调用是否有效
|
||||
if tool_calls is None:
|
||||
return False, [], "工具调用部分为None"
|
||||
|
||||
|
||||
if not isinstance(tool_calls, list):
|
||||
return False, [], f"工具调用部分不是列表: {type(tool_calls).__name__}"
|
||||
|
||||
|
||||
if len(tool_calls) == 0:
|
||||
return False, [], "工具调用列表为空"
|
||||
|
||||
|
||||
# 检查工具调用是否格式正确
|
||||
valid_tool_calls = []
|
||||
for i, tool_call in enumerate(tool_calls):
|
||||
if not isinstance(tool_call, dict):
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]不是字典: {type(tool_call).__name__}")
|
||||
continue
|
||||
|
||||
|
||||
if tool_call.get("type") != "function":
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]不是函数类型: {tool_call.get('type', '未知')}")
|
||||
continue
|
||||
|
||||
|
||||
if "function" not in tool_call or not isinstance(tool_call["function"], dict):
|
||||
logger.warning(f"{log_prefix}工具调用[{i}]缺少function字段或格式不正确")
|
||||
continue
|
||||
|
||||
|
||||
valid_tool_calls.append(tool_call)
|
||||
|
||||
|
||||
# 检查是否有有效的工具调用
|
||||
if not valid_tool_calls:
|
||||
return False, [], "没有找到有效的工具调用"
|
||||
|
||||
|
||||
return True, valid_tool_calls, ""
|
||||
|
||||
|
||||
def process_llm_tool_response(
|
||||
response: Any,
|
||||
expected_tool_name: str = None,
|
||||
log_prefix: str = ""
|
||||
response: Any, expected_tool_name: str = None, log_prefix: str = ""
|
||||
) -> Tuple[bool, Dict[str, Any], str]:
|
||||
"""
|
||||
处理LLM返回的工具调用响应,进行常见错误检查并提取参数
|
||||
|
||||
|
||||
参数:
|
||||
response: LLM的响应,预期是[content, reasoning, tool_calls]格式的列表或元组
|
||||
expected_tool_name: 预期的工具名称,如不指定则不检查
|
||||
log_prefix: 日志前缀,用于标识日志来源
|
||||
|
||||
|
||||
返回:
|
||||
三元组(成功标志, 参数字典, 错误描述)
|
||||
- 如果成功解析,返回(True, 参数字典, "")
|
||||
@@ -269,29 +273,29 @@ def process_llm_tool_response(
|
||||
success, normalized_response, error_msg = normalize_llm_response(response, log_prefix)
|
||||
if not success:
|
||||
return False, {}, error_msg
|
||||
|
||||
|
||||
# 使用新的工具调用处理函数
|
||||
success, valid_tool_calls, error_msg = process_llm_tool_calls(normalized_response, log_prefix)
|
||||
if not success:
|
||||
return False, {}, error_msg
|
||||
|
||||
|
||||
# 检查是否有工具调用
|
||||
if not valid_tool_calls:
|
||||
return False, {}, "没有有效的工具调用"
|
||||
|
||||
|
||||
# 获取第一个工具调用
|
||||
tool_call = valid_tool_calls[0]
|
||||
|
||||
|
||||
# 检查工具名称(如果提供了预期名称)
|
||||
if expected_tool_name:
|
||||
actual_name = tool_call.get("function", {}).get("name")
|
||||
if actual_name != expected_tool_name:
|
||||
return False, {}, f"工具名称不匹配: 预期'{expected_tool_name}',实际'{actual_name}'"
|
||||
|
||||
|
||||
# 提取并解析参数
|
||||
try:
|
||||
arguments = extract_tool_call_arguments(tool_call, {})
|
||||
return True, arguments, ""
|
||||
except Exception as e:
|
||||
logger.error(f"{log_prefix}解析工具参数时出错: {e}")
|
||||
return False, {}, f"解析参数失败: {str(e)}"
|
||||
return False, {}, f"解析参数失败: {str(e)}"
|
||||
|
||||
Reference in New Issue
Block a user