mirror of https://github.com/Mai-with-u/MaiBot.git
remove:移除utils_small模型,统一使用tool_use模型,移除弃用的LLM_judge类型
parent
a3c3fcf518
commit
7cbc2f1462
|
|
@ -85,7 +85,6 @@ Action采用**两层决策机制**来优化性能和决策质量:
|
||||||
| ----------- | ---------------------------------------- | ---------------------- |
|
| ----------- | ---------------------------------------- | ---------------------- |
|
||||||
| [`NEVER`](#never-激活) | 从不激活,Action对麦麦不可见 | 临时禁用某个Action |
|
| [`NEVER`](#never-激活) | 从不激活,Action对麦麦不可见 | 临时禁用某个Action |
|
||||||
| [`ALWAYS`](#always-激活) | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、不回复 |
|
| [`ALWAYS`](#always-激活) | 永远激活,Action总是在麦麦的候选池中 | 核心功能,如回复、不回复 |
|
||||||
| [`LLM_JUDGE`](#llm_judge-激活) | 通过LLM智能判断当前情境是否需要激活此Action | 需要智能判断的复杂场景 |
|
|
||||||
| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 |
|
| `RANDOM` | 基于随机概率决定是否激活 | 增加行为随机性的功能 |
|
||||||
| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 |
|
| `KEYWORD` | 当检测到特定关键词时激活 | 明确触发条件的功能 |
|
||||||
|
|
||||||
|
|
@ -117,30 +116,6 @@ class AlwaysActivatedAction(BaseAction):
|
||||||
return True, "执行了核心功能"
|
return True, "执行了核心功能"
|
||||||
```
|
```
|
||||||
|
|
||||||
#### `LLM_JUDGE` 激活
|
|
||||||
|
|
||||||
`ActionActivationType.LLM_JUDGE`会使得这个 Action 根据 LLM 的判断来决定是否加入候选池。
|
|
||||||
|
|
||||||
而 LLM 的判断是基于代码中预设的`llm_judge_prompt`和自动提供的聊天上下文进行的。
|
|
||||||
|
|
||||||
因此使用此种方法需要实现`llm_judge_prompt`属性。
|
|
||||||
|
|
||||||
```python
|
|
||||||
class LLMJudgedAction(BaseAction):
|
|
||||||
activation_type = ActionActivationType.LLM_JUDGE # 通过LLM判断激活
|
|
||||||
# LLM判断提示词
|
|
||||||
llm_judge_prompt = (
|
|
||||||
"判定是否需要使用这个动作的条件:\n"
|
|
||||||
"1. 用户希望调用XXX这个动作\n"
|
|
||||||
"...\n"
|
|
||||||
"请回答\"是\"或\"否\"。\n"
|
|
||||||
)
|
|
||||||
|
|
||||||
async def execute(self) -> Tuple[bool, str]:
|
|
||||||
# 根据LLM判断是否执行
|
|
||||||
return True, "执行了LLM判断功能"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### `RANDOM` 激活
|
#### `RANDOM` 激活
|
||||||
|
|
||||||
`ActionActivationType.RANDOM`会使得这个 Action 根据随机概率决定是否加入候选池。
|
`ActionActivationType.RANDOM`会使得这个 Action 根据随机概率决定是否加入候选池。
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,295 @@
|
||||||
|
"""
|
||||||
|
表达方式评估脚本
|
||||||
|
|
||||||
|
功能:
|
||||||
|
1. 随机读取10条表达方式,获取其situation和style
|
||||||
|
2. 使用LLM对表达方式进行评估(每个表达方式单独评估)
|
||||||
|
3. 如果合适,就通过,如果不合适,就丢弃
|
||||||
|
4. 不真正修改数据库,只是做评估
|
||||||
|
"""
|
||||||
|
|
||||||
|
import asyncio
|
||||||
|
import random
|
||||||
|
import json
|
||||||
|
import sys
|
||||||
|
import os
|
||||||
|
|
||||||
|
# 添加项目根目录到路径
|
||||||
|
project_root = os.path.abspath(os.path.join(os.path.dirname(__file__), ".."))
|
||||||
|
sys.path.insert(0, project_root)
|
||||||
|
|
||||||
|
from src.common.database.database_model import Expression
|
||||||
|
from src.common.database.database import db
|
||||||
|
from src.llm_models.utils_model import LLMRequest
|
||||||
|
from src.config.config import model_config
|
||||||
|
from src.common.logger import get_logger
|
||||||
|
|
||||||
|
logger = get_logger("expression_evaluator")
|
||||||
|
|
||||||
|
|
||||||
|
def get_random_expressions(count: int = 10) -> list[Expression]:
|
||||||
|
"""
|
||||||
|
随机读取指定数量的表达方式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
count: 要读取的数量,默认10条
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
表达方式列表
|
||||||
|
"""
|
||||||
|
try:
|
||||||
|
# 查询所有表达方式
|
||||||
|
all_expressions = list(Expression.select())
|
||||||
|
|
||||||
|
if not all_expressions:
|
||||||
|
logger.warning("数据库中没有表达方式记录")
|
||||||
|
return []
|
||||||
|
|
||||||
|
# 如果总数少于请求数量,返回所有
|
||||||
|
if len(all_expressions) <= count:
|
||||||
|
logger.info(f"数据库中共有 {len(all_expressions)} 条表达方式,全部返回")
|
||||||
|
return all_expressions
|
||||||
|
|
||||||
|
# 随机选择指定数量
|
||||||
|
selected = random.sample(all_expressions, count)
|
||||||
|
logger.info(f"从 {len(all_expressions)} 条表达方式中随机选择了 {len(selected)} 条")
|
||||||
|
return selected
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"随机读取表达方式失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def create_evaluation_prompt(situation: str, style: str) -> str:
|
||||||
|
"""
|
||||||
|
创建评估提示词
|
||||||
|
|
||||||
|
Args:
|
||||||
|
situation: 情境
|
||||||
|
style: 风格
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
评估提示词
|
||||||
|
"""
|
||||||
|
prompt = f"""请评估以下表达方式是否合适:
|
||||||
|
|
||||||
|
情境(situation):{situation}
|
||||||
|
风格(style):{style}
|
||||||
|
|
||||||
|
请从以下方面进行评估:
|
||||||
|
1. 情境描述是否清晰、准确
|
||||||
|
2. 风格表达是否合理、自然
|
||||||
|
3. 情境和风格是否匹配
|
||||||
|
4. 是否存在不当内容或表达
|
||||||
|
|
||||||
|
请以JSON格式输出评估结果:
|
||||||
|
{{
|
||||||
|
"suitable": true/false,
|
||||||
|
"reason": "评估理由(如果不合适,请说明原因)"
|
||||||
|
}}
|
||||||
|
|
||||||
|
如果合适,suitable设为true;如果不合适,suitable设为false,并在reason中说明原因。
|
||||||
|
请严格按照JSON格式输出,不要包含其他内容。"""
|
||||||
|
|
||||||
|
return prompt
|
||||||
|
|
||||||
|
|
||||||
|
async def evaluate_expression(expression: Expression, llm: LLMRequest) -> dict:
|
||||||
|
"""
|
||||||
|
使用LLM评估单个表达方式
|
||||||
|
|
||||||
|
Args:
|
||||||
|
expression: 表达方式对象
|
||||||
|
llm: LLM请求实例
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
评估结果字典,包含:
|
||||||
|
- expression_id: 表达方式ID
|
||||||
|
- situation: 情境
|
||||||
|
- style: 风格
|
||||||
|
- suitable: 是否合适
|
||||||
|
- reason: 评估理由
|
||||||
|
- error: 错误信息(如果有)
|
||||||
|
"""
|
||||||
|
result = {
|
||||||
|
"expression_id": expression.id,
|
||||||
|
"situation": expression.situation,
|
||||||
|
"style": expression.style,
|
||||||
|
"suitable": None,
|
||||||
|
"reason": None,
|
||||||
|
"error": None
|
||||||
|
}
|
||||||
|
|
||||||
|
try:
|
||||||
|
# 创建评估提示词
|
||||||
|
prompt = create_evaluation_prompt(expression.situation, expression.style)
|
||||||
|
|
||||||
|
# 调用LLM进行评估
|
||||||
|
logger.info(f"正在评估表达方式 ID: {expression.id}, Situation: {expression.situation}, Style: {expression.style}")
|
||||||
|
response, (reasoning, model_name, _) = await llm.generate_response_async(
|
||||||
|
prompt=prompt,
|
||||||
|
temperature=0.3,
|
||||||
|
max_tokens=500
|
||||||
|
)
|
||||||
|
|
||||||
|
logger.debug(f"LLM响应: {response}")
|
||||||
|
logger.debug(f"使用模型: {model_name}")
|
||||||
|
|
||||||
|
# 解析JSON响应
|
||||||
|
try:
|
||||||
|
# 尝试直接解析
|
||||||
|
evaluation = json.loads(response)
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
# 如果直接解析失败,尝试提取JSON部分
|
||||||
|
import re
|
||||||
|
json_match = re.search(r'\{[^{}]*"suitable"[^{}]*\}', response, re.DOTALL)
|
||||||
|
if json_match:
|
||||||
|
evaluation = json.loads(json_match.group())
|
||||||
|
else:
|
||||||
|
raise ValueError("无法从响应中提取JSON格式的评估结果")
|
||||||
|
|
||||||
|
# 提取评估结果
|
||||||
|
result["suitable"] = evaluation.get("suitable", False)
|
||||||
|
result["reason"] = evaluation.get("reason", "未提供理由")
|
||||||
|
|
||||||
|
logger.info(f"表达方式 ID: {expression.id} 评估结果: {'通过' if result['suitable'] else '不通过'}")
|
||||||
|
if result["reason"]:
|
||||||
|
logger.info(f"评估理由: {result['reason']}")
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"评估表达方式 ID: {expression.id} 时出错: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
result["error"] = str(e)
|
||||||
|
result["suitable"] = False
|
||||||
|
result["reason"] = f"评估过程出错: {str(e)}"
|
||||||
|
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
async def main():
|
||||||
|
"""主函数"""
|
||||||
|
logger.info("=" * 60)
|
||||||
|
logger.info("开始表达方式评估")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
# 初始化数据库连接
|
||||||
|
try:
|
||||||
|
db.connect(reuse_if_open=True)
|
||||||
|
logger.info("数据库连接成功")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"数据库连接失败: {e}")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 1. 随机读取10条表达方式
|
||||||
|
logger.info("\n步骤1: 随机读取10条表达方式")
|
||||||
|
expressions = get_random_expressions(10)
|
||||||
|
|
||||||
|
if not expressions:
|
||||||
|
logger.error("没有可用的表达方式,退出")
|
||||||
|
return
|
||||||
|
|
||||||
|
logger.info(f"成功读取 {len(expressions)} 条表达方式")
|
||||||
|
for i, expr in enumerate(expressions, 1):
|
||||||
|
logger.info(f" {i}. ID: {expr.id}, Situation: {expr.situation}, Style: {expr.style}")
|
||||||
|
|
||||||
|
# 2. 创建LLM实例
|
||||||
|
logger.info("\n步骤2: 创建LLM实例")
|
||||||
|
try:
|
||||||
|
llm = LLMRequest(
|
||||||
|
model_set=model_config.model_task_config.tool_use,
|
||||||
|
request_type="expression_evaluator"
|
||||||
|
)
|
||||||
|
logger.info("LLM实例创建成功")
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"创建LLM实例失败: {e}")
|
||||||
|
import traceback
|
||||||
|
logger.error(traceback.format_exc())
|
||||||
|
return
|
||||||
|
|
||||||
|
# 3. 对每个表达方式进行评估
|
||||||
|
logger.info("\n步骤3: 开始评估表达方式")
|
||||||
|
results = []
|
||||||
|
|
||||||
|
for i, expression in enumerate(expressions, 1):
|
||||||
|
logger.info(f"\n--- 评估进度: {i}/{len(expressions)} ---")
|
||||||
|
result = await evaluate_expression(expression, llm)
|
||||||
|
results.append(result)
|
||||||
|
|
||||||
|
# 添加短暂延迟,避免请求过快
|
||||||
|
if i < len(expressions):
|
||||||
|
await asyncio.sleep(0.5)
|
||||||
|
|
||||||
|
# 4. 汇总结果
|
||||||
|
logger.info("\n" + "=" * 60)
|
||||||
|
logger.info("评估结果汇总")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
passed = [r for r in results if r["suitable"] is True]
|
||||||
|
failed = [r for r in results if r["suitable"] is False]
|
||||||
|
errors = [r for r in results if r["error"] is not None]
|
||||||
|
|
||||||
|
logger.info(f"\n总计: {len(results)} 条")
|
||||||
|
logger.info(f"通过: {len(passed)} 条")
|
||||||
|
logger.info(f"不通过: {len(failed)} 条")
|
||||||
|
if errors:
|
||||||
|
logger.info(f"出错: {len(errors)} 条")
|
||||||
|
|
||||||
|
# 详细结果
|
||||||
|
logger.info("\n--- 通过的表达方式 ---")
|
||||||
|
if passed:
|
||||||
|
for r in passed:
|
||||||
|
logger.info(f" ID: {r['expression_id']}")
|
||||||
|
logger.info(f" Situation: {r['situation']}")
|
||||||
|
logger.info(f" Style: {r['style']}")
|
||||||
|
if r['reason']:
|
||||||
|
logger.info(f" 理由: {r['reason']}")
|
||||||
|
else:
|
||||||
|
logger.info(" 无")
|
||||||
|
|
||||||
|
logger.info("\n--- 不通过的表达方式 ---")
|
||||||
|
if failed:
|
||||||
|
for r in failed:
|
||||||
|
logger.info(f" ID: {r['expression_id']}")
|
||||||
|
logger.info(f" Situation: {r['situation']}")
|
||||||
|
logger.info(f" Style: {r['style']}")
|
||||||
|
if r['reason']:
|
||||||
|
logger.info(f" 理由: {r['reason']}")
|
||||||
|
if r['error']:
|
||||||
|
logger.info(f" 错误: {r['error']}")
|
||||||
|
else:
|
||||||
|
logger.info(" 无")
|
||||||
|
|
||||||
|
# 保存结果到JSON文件(可选)
|
||||||
|
output_file = os.path.join(project_root, "data", "expression_evaluation_results.json")
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(output_file), exist_ok=True)
|
||||||
|
with open(output_file, "w", encoding="utf-8") as f:
|
||||||
|
json.dump({
|
||||||
|
"total": len(results),
|
||||||
|
"passed": len(passed),
|
||||||
|
"failed": len(failed),
|
||||||
|
"errors": len(errors),
|
||||||
|
"results": results
|
||||||
|
}, f, ensure_ascii=False, indent=2)
|
||||||
|
logger.info(f"\n评估结果已保存到: {output_file}")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"保存结果到文件失败: {e}")
|
||||||
|
|
||||||
|
logger.info("\n" + "=" * 60)
|
||||||
|
logger.info("评估完成")
|
||||||
|
logger.info("=" * 60)
|
||||||
|
|
||||||
|
# 关闭数据库连接
|
||||||
|
try:
|
||||||
|
db.close()
|
||||||
|
logger.info("数据库连接已关闭")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning(f"关闭数据库连接时出错: {e}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
asyncio.run(main())
|
||||||
|
|
||||||
|
|
@ -226,7 +226,7 @@ async def simulate_merge(
|
||||||
if use_llm:
|
if use_llm:
|
||||||
try:
|
try:
|
||||||
summary_model = LLMRequest(
|
summary_model = LLMRequest(
|
||||||
model_set=model_config.model_task_config.utils_small,
|
model_set=model_config.model_task_config.tool_use,
|
||||||
request_type="expression.summary"
|
request_type="expression.summary"
|
||||||
)
|
)
|
||||||
print("✅ LLM 模型已初始化,将进行实际总结")
|
print("✅ LLM 模型已初始化,将进行实际总结")
|
||||||
|
|
|
||||||
|
|
@ -89,7 +89,7 @@ class ExpressionLearner:
|
||||||
model_set=model_config.model_task_config.utils, request_type="expression.learner"
|
model_set=model_config.model_task_config.utils, request_type="expression.learner"
|
||||||
)
|
)
|
||||||
self.summary_model: LLMRequest = LLMRequest(
|
self.summary_model: LLMRequest = LLMRequest(
|
||||||
model_set=model_config.model_task_config.utils_small, request_type="expression.summary"
|
model_set=model_config.model_task_config.utils, request_type="expression.summary"
|
||||||
)
|
)
|
||||||
self.chat_id = chat_id
|
self.chat_id = chat_id
|
||||||
self.chat_stream = get_chat_manager().get_stream(chat_id)
|
self.chat_stream = get_chat_manager().get_stream(chat_id)
|
||||||
|
|
|
||||||
|
|
@ -45,7 +45,7 @@ def init_prompt():
|
||||||
class ExpressionSelector:
|
class ExpressionSelector:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.llm_model = LLMRequest(
|
self.llm_model = LLMRequest(
|
||||||
model_set=model_config.model_task_config.utils_small, request_type="expression.selector"
|
model_set=model_config.model_task_config.tool_use, request_type="expression.selector"
|
||||||
)
|
)
|
||||||
|
|
||||||
def can_use_expression_for_chat(self, chat_id: str) -> bool:
|
def can_use_expression_for_chat(self, chat_id: str) -> bool:
|
||||||
|
|
|
||||||
|
|
@ -444,7 +444,7 @@ class BrainPlanner:
|
||||||
if action_info.activation_type == ActionActivationType.NEVER:
|
if action_info.activation_type == ActionActivationType.NEVER:
|
||||||
logger.debug(f"{self.log_prefix}动作 {action_name} 设置为 NEVER 激活类型,跳过")
|
logger.debug(f"{self.log_prefix}动作 {action_name} 设置为 NEVER 激活类型,跳过")
|
||||||
continue
|
continue
|
||||||
elif action_info.activation_type in [ActionActivationType.LLM_JUDGE, ActionActivationType.ALWAYS]:
|
elif action_info.activation_type == ActionActivationType.ALWAYS:
|
||||||
filtered_actions[action_name] = action_info
|
filtered_actions[action_name] = action_info
|
||||||
elif action_info.activation_type == ActionActivationType.RANDOM:
|
elif action_info.activation_type == ActionActivationType.RANDOM:
|
||||||
if random.random() < action_info.random_activation_probability:
|
if random.random() < action_info.random_activation_probability:
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,9 @@
|
||||||
import random
|
import random
|
||||||
import asyncio
|
|
||||||
import hashlib
|
|
||||||
import time
|
import time
|
||||||
from typing import List, Dict, TYPE_CHECKING, Tuple
|
from typing import List, Dict, TYPE_CHECKING, Tuple
|
||||||
|
|
||||||
from src.common.logger import get_logger
|
from src.common.logger import get_logger
|
||||||
from src.config.config import global_config, model_config
|
from src.config.config import global_config
|
||||||
from src.llm_models.utils_model import LLMRequest
|
|
||||||
from src.chat.message_receive.chat_stream import get_chat_manager, ChatMessageContext
|
from src.chat.message_receive.chat_stream import get_chat_manager, ChatMessageContext
|
||||||
from src.chat.planner_actions.action_manager import ActionManager
|
from src.chat.planner_actions.action_manager import ActionManager
|
||||||
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages
|
from src.chat.utils.chat_message_builder import get_raw_msg_before_timestamp_with_chat, build_readable_messages
|
||||||
|
|
@ -35,14 +32,6 @@ class ActionModifier:
|
||||||
|
|
||||||
self.action_manager = action_manager
|
self.action_manager = action_manager
|
||||||
|
|
||||||
# 用于LLM判定的小模型
|
|
||||||
self.llm_judge = LLMRequest(model_set=model_config.model_task_config.utils_small, request_type="action.judge")
|
|
||||||
|
|
||||||
# 缓存相关属性
|
|
||||||
self._llm_judge_cache = {} # 缓存LLM判定结果
|
|
||||||
self._cache_expiry_time = 30 # 缓存过期时间(秒)
|
|
||||||
self._last_context_hash = None # 上次上下文的哈希值
|
|
||||||
|
|
||||||
async def modify_actions(
|
async def modify_actions(
|
||||||
self,
|
self,
|
||||||
message_content: str = "",
|
message_content: str = "",
|
||||||
|
|
@ -159,9 +148,6 @@ class ActionModifier:
|
||||||
"""
|
"""
|
||||||
deactivated_actions = []
|
deactivated_actions = []
|
||||||
|
|
||||||
# 分类处理不同激活类型的actions
|
|
||||||
llm_judge_actions: Dict[str, ActionInfo] = {}
|
|
||||||
|
|
||||||
actions_to_check = list(actions_with_info.items())
|
actions_to_check = list(actions_with_info.items())
|
||||||
random.shuffle(actions_to_check)
|
random.shuffle(actions_to_check)
|
||||||
|
|
||||||
|
|
@ -185,9 +171,6 @@ class ActionModifier:
|
||||||
deactivated_actions.append((action_name, reason))
|
deactivated_actions.append((action_name, reason))
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
||||||
|
|
||||||
elif activation_type == ActionActivationType.LLM_JUDGE:
|
|
||||||
llm_judge_actions[action_name] = action_info
|
|
||||||
|
|
||||||
elif activation_type == ActionActivationType.NEVER:
|
elif activation_type == ActionActivationType.NEVER:
|
||||||
reason = "激活类型为never"
|
reason = "激活类型为never"
|
||||||
deactivated_actions.append((action_name, reason))
|
deactivated_actions.append((action_name, reason))
|
||||||
|
|
@ -196,194 +179,8 @@ class ActionModifier:
|
||||||
else:
|
else:
|
||||||
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
|
logger.warning(f"{self.log_prefix}未知的激活类型: {activation_type},跳过处理")
|
||||||
|
|
||||||
# 并行处理LLM_JUDGE类型
|
|
||||||
if llm_judge_actions:
|
|
||||||
llm_results = await self._process_llm_judge_actions_parallel(
|
|
||||||
llm_judge_actions,
|
|
||||||
chat_content,
|
|
||||||
)
|
|
||||||
for action_name, should_activate in llm_results.items():
|
|
||||||
if not should_activate:
|
|
||||||
reason = "LLM判定未激活"
|
|
||||||
deactivated_actions.append((action_name, reason))
|
|
||||||
logger.debug(f"{self.log_prefix}未激活动作: {action_name},原因: {reason}")
|
|
||||||
|
|
||||||
return deactivated_actions
|
return deactivated_actions
|
||||||
|
|
||||||
def _generate_context_hash(self, chat_content: str) -> str:
|
|
||||||
"""生成上下文的哈希值用于缓存"""
|
|
||||||
context_content = f"{chat_content}"
|
|
||||||
return hashlib.md5(context_content.encode("utf-8")).hexdigest()
|
|
||||||
|
|
||||||
async def _process_llm_judge_actions_parallel(
|
|
||||||
self,
|
|
||||||
llm_judge_actions: Dict[str, ActionInfo],
|
|
||||||
chat_content: str = "",
|
|
||||||
) -> Dict[str, bool]:
|
|
||||||
"""
|
|
||||||
并行处理LLM判定actions,支持智能缓存
|
|
||||||
|
|
||||||
Args:
|
|
||||||
llm_judge_actions: 需要LLM判定的actions
|
|
||||||
chat_content: 聊天内容
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
Dict[str, bool]: action名称到激活结果的映射
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 生成当前上下文的哈希值
|
|
||||||
current_context_hash = self._generate_context_hash(chat_content)
|
|
||||||
current_time = time.time()
|
|
||||||
|
|
||||||
results = {}
|
|
||||||
tasks_to_run: Dict[str, ActionInfo] = {}
|
|
||||||
|
|
||||||
# 检查缓存
|
|
||||||
for action_name, action_info in llm_judge_actions.items():
|
|
||||||
cache_key = f"{action_name}_{current_context_hash}"
|
|
||||||
|
|
||||||
# 检查是否有有效的缓存
|
|
||||||
if (
|
|
||||||
cache_key in self._llm_judge_cache
|
|
||||||
and current_time - self._llm_judge_cache[cache_key]["timestamp"] < self._cache_expiry_time
|
|
||||||
):
|
|
||||||
results[action_name] = self._llm_judge_cache[cache_key]["result"]
|
|
||||||
logger.debug(
|
|
||||||
f"{self.log_prefix}使用缓存结果 {action_name}: {'激活' if results[action_name] else '未激活'}"
|
|
||||||
)
|
|
||||||
else:
|
|
||||||
# 需要进行LLM判定
|
|
||||||
tasks_to_run[action_name] = action_info
|
|
||||||
|
|
||||||
# 如果有需要运行的任务,并行执行
|
|
||||||
if tasks_to_run:
|
|
||||||
logger.debug(f"{self.log_prefix}并行执行LLM判定,任务数: {len(tasks_to_run)}")
|
|
||||||
|
|
||||||
# 创建并行任务
|
|
||||||
tasks = []
|
|
||||||
task_names = []
|
|
||||||
|
|
||||||
for action_name, action_info in tasks_to_run.items():
|
|
||||||
task = self._llm_judge_action(
|
|
||||||
action_name,
|
|
||||||
action_info,
|
|
||||||
chat_content,
|
|
||||||
)
|
|
||||||
tasks.append(task)
|
|
||||||
task_names.append(action_name)
|
|
||||||
|
|
||||||
# 并行执行所有任务
|
|
||||||
try:
|
|
||||||
task_results = await asyncio.gather(*tasks, return_exceptions=True)
|
|
||||||
|
|
||||||
# 处理结果并更新缓存
|
|
||||||
for action_name, result in zip(task_names, task_results, strict=False):
|
|
||||||
if isinstance(result, Exception):
|
|
||||||
logger.error(f"{self.log_prefix}LLM判定action {action_name} 时出错: {result}")
|
|
||||||
results[action_name] = False
|
|
||||||
else:
|
|
||||||
results[action_name] = result
|
|
||||||
|
|
||||||
# 更新缓存
|
|
||||||
cache_key = f"{action_name}_{current_context_hash}"
|
|
||||||
self._llm_judge_cache[cache_key] = {"result": result, "timestamp": current_time}
|
|
||||||
|
|
||||||
logger.debug(f"{self.log_prefix}并行LLM判定完成,耗时: {time.time() - current_time:.2f}s")
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix}并行LLM判定失败: {e}")
|
|
||||||
# 如果并行执行失败,为所有任务返回False
|
|
||||||
for action_name in tasks_to_run:
|
|
||||||
results[action_name] = False
|
|
||||||
|
|
||||||
# 清理过期缓存
|
|
||||||
self._cleanup_expired_cache(current_time)
|
|
||||||
|
|
||||||
return results
|
|
||||||
|
|
||||||
def _cleanup_expired_cache(self, current_time: float):
|
|
||||||
"""清理过期的缓存条目"""
|
|
||||||
expired_keys = []
|
|
||||||
expired_keys.extend(
|
|
||||||
cache_key
|
|
||||||
for cache_key, cache_data in self._llm_judge_cache.items()
|
|
||||||
if current_time - cache_data["timestamp"] > self._cache_expiry_time
|
|
||||||
)
|
|
||||||
for key in expired_keys:
|
|
||||||
del self._llm_judge_cache[key]
|
|
||||||
|
|
||||||
if expired_keys:
|
|
||||||
logger.debug(f"{self.log_prefix}清理了 {len(expired_keys)} 个过期缓存条目")
|
|
||||||
|
|
||||||
async def _llm_judge_action(
|
|
||||||
self,
|
|
||||||
action_name: str,
|
|
||||||
action_info: ActionInfo,
|
|
||||||
chat_content: str = "",
|
|
||||||
) -> bool: # sourcery skip: move-assign-in-block, use-named-expression
|
|
||||||
"""
|
|
||||||
使用LLM判定是否应该激活某个action
|
|
||||||
|
|
||||||
Args:
|
|
||||||
action_name: 动作名称
|
|
||||||
action_info: 动作信息
|
|
||||||
observed_messages_str: 观察到的聊天消息
|
|
||||||
chat_context: 聊天上下文
|
|
||||||
extra_context: 额外上下文
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
bool: 是否应该激活此action
|
|
||||||
"""
|
|
||||||
|
|
||||||
try:
|
|
||||||
# 构建判定提示词
|
|
||||||
action_description = action_info.description
|
|
||||||
action_require = action_info.action_require
|
|
||||||
custom_prompt = action_info.llm_judge_prompt
|
|
||||||
|
|
||||||
# 构建基础判定提示词
|
|
||||||
base_prompt = f"""
|
|
||||||
你需要判断在当前聊天情况下,是否应该激活名为"{action_name}"的动作。
|
|
||||||
|
|
||||||
动作描述:{action_description}
|
|
||||||
|
|
||||||
动作使用场景:
|
|
||||||
"""
|
|
||||||
for req in action_require:
|
|
||||||
base_prompt += f"- {req}\n"
|
|
||||||
|
|
||||||
if custom_prompt:
|
|
||||||
base_prompt += f"\n额外判定条件:\n{custom_prompt}\n"
|
|
||||||
|
|
||||||
if chat_content:
|
|
||||||
base_prompt += f"\n当前聊天记录:\n{chat_content}\n"
|
|
||||||
|
|
||||||
base_prompt += """
|
|
||||||
请根据以上信息判断是否应该激活这个动作。
|
|
||||||
只需要回答"是"或"否",不要有其他内容。
|
|
||||||
"""
|
|
||||||
|
|
||||||
# 调用LLM进行判定
|
|
||||||
response, _ = await self.llm_judge.generate_response_async(prompt=base_prompt)
|
|
||||||
|
|
||||||
# 解析响应
|
|
||||||
response = response.strip().lower()
|
|
||||||
|
|
||||||
# print(base_prompt)
|
|
||||||
# print(f"LLM判定动作 {action_name}:响应='{response}'")
|
|
||||||
|
|
||||||
should_activate = "是" in response or "yes" in response or "true" in response
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
f"{self.log_prefix}LLM判定动作 {action_name}:响应='{response}',结果={'激活' if should_activate else '不激活'}"
|
|
||||||
)
|
|
||||||
return should_activate
|
|
||||||
|
|
||||||
except Exception as e:
|
|
||||||
logger.error(f"{self.log_prefix}LLM判定动作 {action_name} 时出错: {e}")
|
|
||||||
# 出错时默认不激活
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _check_keyword_activation(
|
def _check_keyword_activation(
|
||||||
self,
|
self,
|
||||||
action_name: str,
|
action_name: str,
|
||||||
|
|
|
||||||
|
|
@ -591,7 +591,7 @@ class ActionPlanner:
|
||||||
if action_info.activation_type == ActionActivationType.NEVER:
|
if action_info.activation_type == ActionActivationType.NEVER:
|
||||||
logger.debug(f"{self.log_prefix}动作 {action_name} 设置为 NEVER 激活类型,跳过")
|
logger.debug(f"{self.log_prefix}动作 {action_name} 设置为 NEVER 激活类型,跳过")
|
||||||
continue
|
continue
|
||||||
elif action_info.activation_type in [ActionActivationType.LLM_JUDGE, ActionActivationType.ALWAYS]:
|
elif action_info.activation_type == ActionActivationType.ALWAYS:
|
||||||
filtered_actions[action_name] = action_info
|
filtered_actions[action_name] = action_info
|
||||||
elif action_info.activation_type == ActionActivationType.RANDOM:
|
elif action_info.activation_type == ActionActivationType.RANDOM:
|
||||||
if random.random() < action_info.random_activation_probability:
|
if random.random() < action_info.random_activation_probability:
|
||||||
|
|
|
||||||
|
|
@ -774,7 +774,7 @@ class PrivateReplyer:
|
||||||
expression_habits_block, selected_expressions = results_dict["expression_habits"]
|
expression_habits_block, selected_expressions = results_dict["expression_habits"]
|
||||||
expression_habits_block: str
|
expression_habits_block: str
|
||||||
selected_expressions: List[int]
|
selected_expressions: List[int]
|
||||||
relation_info: str = results_dict["relation_info"]
|
relation_info: str = results_dict.get("relation_info") or ""
|
||||||
tool_info: str = results_dict["tool_info"]
|
tool_info: str = results_dict["tool_info"]
|
||||||
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
prompt_info: str = results_dict["prompt_info"] # 直接使用格式化后的结果
|
||||||
actions_info: str = results_dict["actions_info"]
|
actions_info: str = results_dict["actions_info"]
|
||||||
|
|
|
||||||
|
|
@ -105,9 +105,6 @@ class ModelTaskConfig(ConfigBase):
|
||||||
utils: TaskConfig
|
utils: TaskConfig
|
||||||
"""组件模型配置"""
|
"""组件模型配置"""
|
||||||
|
|
||||||
utils_small: TaskConfig
|
|
||||||
"""组件小模型配置"""
|
|
||||||
|
|
||||||
replyer: TaskConfig
|
replyer: TaskConfig
|
||||||
"""normal_chat首要回复模型模型配置"""
|
"""normal_chat首要回复模型模型配置"""
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -57,7 +57,7 @@ TEMPLATE_DIR = os.path.join(PROJECT_ROOT, "template")
|
||||||
|
|
||||||
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
# 考虑到,实际上配置文件中的mai_version是不会自动更新的,所以采用硬编码
|
||||||
# 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/
|
# 对该字段的更新,请严格参照语义化版本规范:https://semver.org/lang/zh-CN/
|
||||||
MMC_VERSION = "0.12.0"
|
MMC_VERSION = "0.12.1"
|
||||||
|
|
||||||
|
|
||||||
def get_key_comment(toml_table, key):
|
def get_key_comment(toml_table, key):
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ from src.chat.message_receive.chat_stream import get_chat_manager
|
||||||
logger = get_logger("person_info")
|
logger = get_logger("person_info")
|
||||||
|
|
||||||
relation_selection_model = LLMRequest(
|
relation_selection_model = LLMRequest(
|
||||||
model_set=model_config.model_task_config.utils_small, request_type="relation_selection"
|
model_set=model_config.model_task_config.tool_use, request_type="relation_selection"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,6 @@ class BaseAction(ABC):
|
||||||
- keyword_case_sensitive: 关键词是否区分大小写
|
- keyword_case_sensitive: 关键词是否区分大小写
|
||||||
- parallel_action: 是否允许并行执行
|
- parallel_action: 是否允许并行执行
|
||||||
- random_activation_probability: 随机激活概率
|
- random_activation_probability: 随机激活概率
|
||||||
- llm_judge_prompt: LLM判断提示词
|
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(
|
def __init__(
|
||||||
|
|
@ -81,8 +80,6 @@ class BaseAction(ABC):
|
||||||
"""激活类型"""
|
"""激活类型"""
|
||||||
self.random_activation_probability: float = getattr(self.__class__, "random_activation_probability", 0.0)
|
self.random_activation_probability: float = getattr(self.__class__, "random_activation_probability", 0.0)
|
||||||
"""当激活类型为RANDOM时的概率"""
|
"""当激活类型为RANDOM时的概率"""
|
||||||
self.llm_judge_prompt: str = getattr(self.__class__, "llm_judge_prompt", "") # 已弃用
|
|
||||||
"""协助LLM进行判断的Prompt"""
|
|
||||||
self.activation_keywords: list[str] = getattr(self.__class__, "activation_keywords", []).copy()
|
self.activation_keywords: list[str] = getattr(self.__class__, "activation_keywords", []).copy()
|
||||||
"""激活类型为KEYWORD时的KEYWORDS列表"""
|
"""激活类型为KEYWORD时的KEYWORDS列表"""
|
||||||
self.keyword_case_sensitive: bool = getattr(self.__class__, "keyword_case_sensitive", False)
|
self.keyword_case_sensitive: bool = getattr(self.__class__, "keyword_case_sensitive", False)
|
||||||
|
|
@ -504,7 +501,6 @@ class BaseAction(ABC):
|
||||||
keyword_case_sensitive=getattr(cls, "keyword_case_sensitive", False),
|
keyword_case_sensitive=getattr(cls, "keyword_case_sensitive", False),
|
||||||
parallel_action=getattr(cls, "parallel_action", True),
|
parallel_action=getattr(cls, "parallel_action", True),
|
||||||
random_activation_probability=getattr(cls, "random_activation_probability", 0.0),
|
random_activation_probability=getattr(cls, "random_activation_probability", 0.0),
|
||||||
llm_judge_prompt=getattr(cls, "llm_judge_prompt", ""),
|
|
||||||
# 使用正确的字段名
|
# 使用正确的字段名
|
||||||
action_parameters=getattr(cls, "action_parameters", {}).copy(),
|
action_parameters=getattr(cls, "action_parameters", {}).copy(),
|
||||||
action_require=getattr(cls, "action_require", []).copy(),
|
action_require=getattr(cls, "action_require", []).copy(),
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,6 @@ class ActionActivationType(Enum):
|
||||||
|
|
||||||
NEVER = "never" # 从不激活(默认关闭)
|
NEVER = "never" # 从不激活(默认关闭)
|
||||||
ALWAYS = "always" # 默认参与到planner
|
ALWAYS = "always" # 默认参与到planner
|
||||||
LLM_JUDGE = "llm_judge" # LLM判定是否启动该action到planner
|
|
||||||
RANDOM = "random" # 随机启用action到planner
|
RANDOM = "random" # 随机启用action到planner
|
||||||
KEYWORD = "keyword" # 关键词触发启用action到planner
|
KEYWORD = "keyword" # 关键词触发启用action到planner
|
||||||
|
|
||||||
|
|
@ -128,7 +127,6 @@ class ActionInfo(ComponentInfo):
|
||||||
normal_activation_type: ActionActivationType = ActionActivationType.ALWAYS # 已弃用
|
normal_activation_type: ActionActivationType = ActionActivationType.ALWAYS # 已弃用
|
||||||
activation_type: ActionActivationType = ActionActivationType.ALWAYS
|
activation_type: ActionActivationType = ActionActivationType.ALWAYS
|
||||||
random_activation_probability: float = 0.0
|
random_activation_probability: float = 0.0
|
||||||
llm_judge_prompt: str = ""
|
|
||||||
activation_keywords: List[str] = field(default_factory=list) # 激活关键词列表
|
activation_keywords: List[str] = field(default_factory=list) # 激活关键词列表
|
||||||
keyword_case_sensitive: bool = False
|
keyword_case_sensitive: bool = False
|
||||||
# 模式和并行设置
|
# 模式和并行设置
|
||||||
|
|
|
||||||
|
|
@ -28,7 +28,7 @@
|
||||||
"type": "action",
|
"type": "action",
|
||||||
"name": "tts_action",
|
"name": "tts_action",
|
||||||
"description": "将文本转换为语音进行播放",
|
"description": "将文本转换为语音进行播放",
|
||||||
"activation_modes": ["llm_judge", "keyword"],
|
"activation_modes": ["keyword"],
|
||||||
"keywords": ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"]
|
"keywords": ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,9 @@ class TTSAction(BaseAction):
|
||||||
"""TTS语音转换动作处理类"""
|
"""TTS语音转换动作处理类"""
|
||||||
|
|
||||||
# 激活设置
|
# 激活设置
|
||||||
activation_type = ActionActivationType.LLM_JUDGE
|
activation_type = ActionActivationType.KEYWORD
|
||||||
|
activation_keywords = ["语音", "tts", "播报", "读出来", "语音播放", "听", "朗读"]
|
||||||
|
keyword_case_sensitive = False
|
||||||
parallel_action = False
|
parallel_action = False
|
||||||
|
|
||||||
# 动作基本信息
|
# 动作基本信息
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
[inner]
|
[inner]
|
||||||
version = "1.9.1"
|
version = "1.10.1"
|
||||||
|
|
||||||
# 配置文件版本号迭代规则同bot_config.toml
|
# 配置文件版本号迭代规则同bot_config.toml
|
||||||
|
|
||||||
|
|
@ -141,12 +141,6 @@ temperature = 0.2 # 模型温度,新V3建议0.1-0.3
|
||||||
max_tokens = 4096 # 最大输出token数
|
max_tokens = 4096 # 最大输出token数
|
||||||
slow_threshold = 15.0 # 慢请求阈值(秒),模型等待回复时间超过此值会输出警告日志
|
slow_threshold = 15.0 # 慢请求阈值(秒),模型等待回复时间超过此值会输出警告日志
|
||||||
|
|
||||||
[model_task_config.utils_small] # 在麦麦的一些组件中使用的小模型,消耗量较大,建议使用速度较快的小模型
|
|
||||||
model_list = ["qwen3-30b","qwen3-next-80b"]
|
|
||||||
temperature = 0.7
|
|
||||||
max_tokens = 2048
|
|
||||||
slow_threshold = 10.0
|
|
||||||
|
|
||||||
[model_task_config.tool_use] #工具调用模型,需要使用支持工具调用的模型
|
[model_task_config.tool_use] #工具调用模型,需要使用支持工具调用的模型
|
||||||
model_list = ["qwen3-30b","qwen3-next-80b"]
|
model_list = ["qwen3-30b","qwen3-next-80b"]
|
||||||
temperature = 0.7
|
temperature = 0.7
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue