function_knowledge.py 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714
  1. '''
  2. 方法知识获取模块
  3. 1. 输入:问题 + 帖子信息 + 账号人设信息
  4. 2. 将输入的问题转化成query,调用大模型,prompt在 function_knowledge_generate_query_prompt.md 中
  5. 3. 从已有方法工具库中尝试选择合适的方法工具(调用大模型执行,prompt在 function_knowledge_select_tools_prompt.md 中),如果有,则返回选择的方法工具,否则:
  6. - 调用 multi_search_knowledge.py 获取知识
  7. - 返回新的方法工具知识
  8. - 异步从新方法知识中获取新工具(调用大模型执行,prompt在 function_knowledge_generate_new_tool_prompt.md 中),调用工具库系统,接入新的工具
  9. 4. 调用选择的方法工具执行验证,返回工具执行结果
  10. '''
  11. import os
  12. import sys
  13. import json
  14. import threading
  15. from loguru import logger
  16. import re
  17. # 设置路径以便导入工具类
  18. current_dir = os.path.dirname(os.path.abspath(__file__))
  19. root_dir = os.path.dirname(current_dir)
  20. sys.path.insert(0, root_dir)
  21. from utils.qwen_client import QwenClient
  22. from utils.gemini_client import generate_text
  23. from knowledge_v2.tools_library import call_tool, save_tool_info, get_all_tool_infos, get_tool_info, get_tool_params, default_call_hot_tool
  24. from knowledge_v2.multi_search_knowledge import get_knowledge as get_multi_search_knowledge
  25. from knowledge_v2.cache_manager import CacheManager
  26. class FunctionKnowledge:
  27. """方法知识获取类"""
  28. def __init__(self, use_cache: bool = True):
  29. """
  30. 初始化
  31. Args:
  32. use_cache: 是否启用缓存,默认启用
  33. """
  34. logger.info("=" * 80)
  35. logger.info("初始化 FunctionKnowledge - 方法知识获取入口")
  36. self.prompt_dir = os.path.join(current_dir, "prompt")
  37. self.use_cache = use_cache
  38. self.cache = CacheManager() if use_cache else None
  39. logger.info(f"缓存状态: {'启用' if use_cache else '禁用'}")
  40. logger.info("=" * 80)
  41. def _load_prompt(self, filename: str) -> str:
  42. """加载prompt文件内容"""
  43. prompt_path = os.path.join(self.prompt_dir, filename)
  44. if not os.path.exists(prompt_path):
  45. raise FileNotFoundError(f"Prompt文件不存在: {prompt_path}")
  46. with open(prompt_path, 'r', encoding='utf-8') as f:
  47. return f.read().strip()
  48. def find_tools(self, combined_question: str, input_info: str) -> str:
  49. """
  50. 查找合适的工具
  51. :param combined_question: 组合问题
  52. :param input_info: 输入的需求信息,json字符串格式,包含字段:人设介绍、选题模式
  53. :return:
  54. """
  55. logger.info(f"[步骤0] 查找合适的工具...")
  56. try:
  57. # 尝试从缓存获取
  58. if self.use_cache:
  59. cached_data = self.cache.get(combined_question, 'function_knowledge', 'find_tools_result.json')
  60. if cached_data:
  61. result = cached_data.get('result', cached_data.get('response', ''))
  62. logger.info(f"✓ 使用缓存的工具查找结果")
  63. return result
  64. # 解析 input_info,提取人设介绍和选题模式
  65. try:
  66. # 尝试解析为 JSON
  67. input_dict = json.loads(input_info)
  68. account_info = input_dict.get('人设介绍', '')
  69. xuanti_pattern = input_dict.get('选题模式', '')
  70. logger.info(f"✓ 解析输入信息成功,人设介绍: {account_info}, 选题模式: {xuanti_pattern}")
  71. except Exception as e:
  72. raise ValueError(f"输入信息格式错误,请检查输入信息是否为JSON字符串,错误信息: {e}")
  73. # 加载prompt
  74. prompt_template = self._load_prompt("function_knowledge_find_tools_prompt.md")
  75. prompt = prompt_template.replace('{人设介绍}', account_info).replace('{选题模式}', xuanti_pattern)
  76. # 调用大模型执行
  77. logger.info(" → 调用Gemini查找工具...")
  78. result = generate_text(prompt=prompt)
  79. result = result.strip()
  80. logger.info(f"✓ 工具查找完成,结果长度: {len(result)} 字符")
  81. # 保存到缓存
  82. if self.use_cache:
  83. cache_data = {
  84. "prompt": prompt,
  85. "result": result
  86. }
  87. self.cache.set(combined_question, 'function_knowledge', 'find_tools_result.json', cache_data)
  88. return result
  89. except Exception as e:
  90. logger.error(f"✗ 查找工具失败: {e}")
  91. import traceback
  92. logger.error(traceback.format_exc())
  93. return f"查找工具失败: {str(e)}"
  94. def call_default_hot_tool(self, combined_question: str, input_info: str) -> str:
  95. """
  96. 调用默认的热榜工具
  97. :param combined_question: 组合问题
  98. :param input_info: 输入的需求信息
  99. :return: 热榜数据分析结果
  100. """
  101. logger.info(f"[步骤0] 调用默认热榜工具...")
  102. try:
  103. # 尝试从缓存获取
  104. if self.use_cache:
  105. cached_data = self.cache.get(combined_question, 'function_knowledge', 'default_hot_tool_result.json')
  106. if cached_data:
  107. result = cached_data.get('analysis_result', cached_data.get('result', ''))
  108. logger.info(f"✓ 使用缓存的热榜分析结果")
  109. return result
  110. # 加载提取参数prompt
  111. extract_params_prompt = self._load_prompt("function_default_hot_tool_extract_params_prompt.md")
  112. extract_params_prompt = extract_params_prompt.replace('{input_info}', input_info)
  113. # 调用大模型生成参数
  114. logger.info(" → 调用Gemini提取热榜工具参数...")
  115. params_text = generate_text(prompt=extract_params_prompt)
  116. params_json_str = self.extract_and_validate_json(params_text)
  117. if not params_json_str:
  118. logger.error("✗ 默认热榜工具参数提取失败")
  119. return "默认热榜工具参数提取失败"
  120. # 解析参数
  121. params = json.loads(params_json_str)
  122. category = params.get('category', '全部')
  123. rankDate = params.get('rankDate')
  124. logger.info(f"✓ 提取参数成功: category={category}, rankDate={rankDate}")
  125. # 调用默认热榜工具
  126. logger.info(" → 调用默认热榜工具...")
  127. hot_data = default_call_hot_tool(category=category, rankDate=rankDate)
  128. if not hot_data or (isinstance(hot_data, str) and len(hot_data.strip()) == 0):
  129. logger.warning("⚠ 热榜工具返回数据为空")
  130. return "热榜工具返回数据为空,无法进行分析"
  131. logger.info(f"✓ 获取热榜数据成功,数据长度: {len(hot_data)} 字符")
  132. # 分析热榜数据
  133. logger.info(" → 调用Gemini分析热榜数据...")
  134. analyze_prompt = self._load_prompt("function_default_hot_tool_result_analzye_prompt.md")
  135. analyze_prompt = analyze_prompt.replace('{input_info}', input_info).replace('{hot_data}', hot_data)
  136. analysis_result = generate_text(prompt=analyze_prompt)
  137. analysis_result = analysis_result.strip()
  138. logger.info(f"✓ 热榜数据分析完成")
  139. # 保存到缓存
  140. if self.use_cache:
  141. cache_data = {
  142. "extract_params_prompt": extract_params_prompt,
  143. "params": params,
  144. "hot_data": hot_data,
  145. "analyze_prompt": analyze_prompt,
  146. "analysis_result": analysis_result
  147. }
  148. self.cache.set(combined_question, 'function_knowledge', 'default_hot_tool_result.json', cache_data)
  149. return analysis_result
  150. except Exception as e:
  151. logger.error(f"✗ 调用默认热榜工具失败: {e}")
  152. import traceback
  153. logger.error(traceback.format_exc())
  154. return f"调用默认热榜工具失败: {str(e)}"
  155. def generate_query(self, question: str, post_info: str, persona_info: str) -> str:
  156. """
  157. 生成查询语句
  158. Returns:
  159. str: 生成的查询语句
  160. """
  161. logger.info(f"[步骤1] 生成Query...")
  162. # 组合问题的唯一标识
  163. combined_question = f"{question}||{post_info}||{persona_info}"
  164. try:
  165. prompt_template = self._load_prompt("function_generate_query_prompt.md")
  166. prompt = prompt_template.format(
  167. question=question,
  168. post_info=post_info,
  169. persona_info=persona_info
  170. )
  171. # 尝试从缓存读取
  172. if self.use_cache:
  173. cached_data = self.cache.get(combined_question, 'function_knowledge', 'generated_query.json')
  174. if cached_data:
  175. query = cached_data.get('query', cached_data.get('response', ''))
  176. logger.info(f"✓ 使用缓存的Query: {query}")
  177. return query
  178. logger.info("→ 调用Gemini生成Query...")
  179. query = generate_text(prompt=prompt)
  180. query = query.strip()
  181. logger.info(f"✓ 生成Query: {query}")
  182. # 保存到缓存(包含完整的prompt和response)
  183. if self.use_cache:
  184. query_data = {
  185. "prompt": prompt,
  186. "response": query,
  187. "query": query
  188. }
  189. self.cache.set(combined_question, 'function_knowledge', 'generated_query.json', query_data)
  190. return query
  191. except Exception as e:
  192. logger.error(f"✗ 生成Query失败: {e}")
  193. return question # 降级使用原问题
  194. def select_tool(self, combined_question: str, input_info: str) -> dict:
  195. """
  196. 选择合适的工具
  197. Returns:
  198. str: 工具名称,如果没有合适的工具则返回"None"
  199. """
  200. logger.info(f"[步骤2] 选择工具...")
  201. # 目前没有工具,直接返回空字典
  202. return {}
  203. try:
  204. all_tool_infos = self._load_prompt("all_tools_infos.md")
  205. if not all_tool_infos:
  206. logger.info(" 工具库为空,无可用工具")
  207. return "None"
  208. prompt_template = self._load_prompt("function_knowledge_select_tools_prompt.md")
  209. prompt = prompt_template.replace("{all_tool_infos}", all_tool_infos).replace("input_info", input_info)
  210. # 尝试从缓存读取
  211. if self.use_cache:
  212. cached_data = self.cache.get(combined_question, 'function_knowledge', 'selected_tool.json')
  213. if cached_data:
  214. result_json = cached_data.get('response', {})
  215. logger.info(f"✓ 使用缓存的工具: {result_json}")
  216. return result_json
  217. logger.info("→ 调用Gemini选择工具...")
  218. result = generate_text(prompt=prompt)
  219. result = self.extract_and_validate_json(result)
  220. if not result:
  221. logger.error("✗ 选择工具失败: 无法提取有效JSON")
  222. return "None"
  223. result_json = json.loads(result)
  224. logger.info(f"✓ 选择结果: {result_json.get('工具名', 'None')}")
  225. # 保存到缓存(包含完整的prompt和response)
  226. if self.use_cache:
  227. tool_data = {
  228. "prompt": prompt,
  229. "response": result_json
  230. }
  231. self.cache.set(combined_question, 'function_knowledge', 'selected_tool.json', tool_data)
  232. return result_json
  233. except Exception as e:
  234. logger.error(f"✗ 选择工具失败: {e}")
  235. return "None"
  236. def extract_and_validate_json(self, text: str):
  237. """
  238. 从字符串中提取 JSON 部分,并返回标准的 JSON 字符串。
  239. 如果无法提取或解析失败,返回 None (或者你可以改为抛出异常)。
  240. """
  241. # 1. 使用正则表达式寻找最大的 JSON 块
  242. # r"(\{[\s\S]*\}|\[[\s\S]*\])" 的含义:
  243. # - \{[\s\S]*\} : 匹配以 { 开头,} 结尾的最长字符串([\s\S] 包含换行符)
  244. # - | : 或者
  245. # - \[[\s\S]*\] : 匹配以 [ 开头,] 结尾的最长字符串(处理 JSON 数组)
  246. match = re.search(r"(\{[\s\S]*\}|\[[\s\S]*\])", text)
  247. if match:
  248. json_str = match.group(0)
  249. try:
  250. # 2. 尝试解析提取出的字符串,验证是否为合法 JSON
  251. parsed_json = json.loads(json_str)
  252. # 3. 重新转储为标准字符串 (去除原本可能存在的缩进、多余空格等)
  253. # ensure_ascii=False 保证中文不会变成 \uXXXX
  254. return json.dumps(parsed_json, ensure_ascii=False)
  255. except json.JSONDecodeError as e:
  256. print(f"提取到了类似JSON的片段,但解析失败: {e}")
  257. return None
  258. else:
  259. print("未在文本中发现 JSON 结构")
  260. return None
  261. def extract_tool_params(self, combined_question: str, input_info: str, tool_id: str, tool_instructions: str) -> dict:
  262. """
  263. 根据工具信息和查询提取调用参数
  264. Args:
  265. combined_question: 组合问题(用于缓存)
  266. tool_name: 工具名称
  267. query: 查询内容
  268. Returns:
  269. dict: 提取的参数字典
  270. """
  271. logger.info(f"[步骤3] 提取工具参数...")
  272. try:
  273. # 获取工具信息
  274. tool_params = get_tool_params(tool_id)
  275. if not tool_params:
  276. logger.warning(f" ⚠ 未找到工具 {tool_id} 的信息,使用默认参数")
  277. return {"keyword": input_info}
  278. # 加载prompt
  279. prompt_template = self._load_prompt("function_knowledge_extract_tool_params_prompt.md")
  280. prompt = prompt_template.format(
  281. tool_mcp_name=tool_id,
  282. input_info=input_info,
  283. all_tool_params=tool_params
  284. )
  285. # 尝试从缓存读取
  286. if self.use_cache:
  287. cached_data = self.cache.get(combined_question, 'function_knowledge', 'extracted_params.json')
  288. if cached_data:
  289. params = cached_data.get('params', {})
  290. logger.info(f"✓ 使用缓存的参数: {params}")
  291. return params
  292. # 调用LLM提取参数
  293. logger.info(" → 调用Gemini提取参数...")
  294. response_text = generate_text(prompt=prompt)
  295. # 解析JSON
  296. logger.info(" → 解析参数JSON...")
  297. try:
  298. # 清理可能的markdown标记
  299. response_text = response_text.strip()
  300. if response_text.startswith("```json"):
  301. response_text = response_text[7:]
  302. if response_text.startswith("```"):
  303. response_text = response_text[3:]
  304. if response_text.endswith("```"):
  305. response_text = response_text[:-3]
  306. response_text = response_text.strip()
  307. params = json.loads(response_text)
  308. logger.info(f"✓ 提取参数成功: {params}")
  309. # 保存到缓存(包含完整的prompt和response)
  310. if self.use_cache:
  311. params_data = {
  312. "prompt": prompt,
  313. "response": response_text,
  314. "params": params
  315. }
  316. self.cache.set(combined_question, 'function_knowledge', 'extracted_params.json', params_data)
  317. return params
  318. except json.JSONDecodeError as e:
  319. logger.error(f" ✗ 解析JSON失败: {e}")
  320. logger.error(f" 响应内容: {response_text}")
  321. # 降级:使用input_info作为keyword
  322. default_params = {"keyword": input_info}
  323. logger.warning(f" 使用默认参数: {default_params}")
  324. return default_params
  325. except Exception as e:
  326. logger.error(f"✗ 提取工具参数失败: {e}")
  327. # 降级:使用input_info作为keyword
  328. return {"keyword": input_info}
  329. def save_knowledge_to_file(self, knowledge: str, combined_question: str):
  330. """保存获取到的知识到文件"""
  331. try:
  332. logger.info("[保存知识] 开始保存知识到文件...")
  333. # 获取问题hash
  334. import hashlib
  335. question_hash = hashlib.md5(combined_question.encode('utf-8')).hexdigest()[:12]
  336. # 获取缓存目录(和execution_record.json同级)
  337. if self.use_cache and self.cache:
  338. cache_dir = os.path.join(self.cache.base_cache_dir, question_hash)
  339. else:
  340. cache_dir = os.path.join(os.path.dirname(__file__), '.cache', question_hash)
  341. os.makedirs(cache_dir, exist_ok=True)
  342. # 保存到knowledge.txt
  343. knowledge_file = os.path.join(cache_dir, 'knowledge.txt')
  344. with open(knowledge_file, 'w', encoding='utf-8') as f:
  345. f.write(knowledge)
  346. logger.info(f"✓ 知识已保存到: {knowledge_file}")
  347. logger.info(f" 知识长度: {len(knowledge)} 字符")
  348. except Exception as e:
  349. logger.error(f"✗ 保存知识失败: {e}")
  350. def organize_tool_result(self, tool_result: dict) -> dict:
  351. """
  352. 组织工具调用结果,确保包含必要字段
  353. Args:
  354. tool_result: 原始工具调用结果
  355. Returns:
  356. dict: 组织后的工具调用结果
  357. """
  358. prompt_template = self._load_prompt("tool_result_prettify_prompt.md")
  359. prompt = prompt_template.format(
  360. input=tool_result,
  361. )
  362. # qwen_client = QwenClient()
  363. # organized_result = qwen_client.chat(user_prompt=prompt)
  364. # organized_result = generate_text(prompt=prompt)
  365. # organized_result = organized_result.strip()
  366. # return organized_result
  367. try:
  368. result = tool_result.get('result')
  369. if not result:
  370. return tool_result
  371. else:
  372. return result
  373. except Exception as e:
  374. logger.error(f"✗ 组织工具调用结果失败: {e}")
  375. return tool_result
  376. def evaluate_tool_result(self, combined_question: str, input_info: str, tool_result) -> dict:
  377. """
  378. 评估工具执行结果是否可以回答输入的需求
  379. Args:
  380. combined_question: 组合问题(用于缓存)
  381. input_info: 输入的需求信息
  382. tool_result: 工具执行结果(可以是dict、list、str等任意类型)
  383. Returns:
  384. dict: 评估结果,包含"是否可以回答"和"理由"
  385. """
  386. logger.info(f"[步骤5] 评估工具执行结果...")
  387. try:
  388. # 加载prompt
  389. prompt_template = self._load_prompt("function_knowledge_tool_result_eval_prompt.md")
  390. # 将tool_result转换为字符串格式,便于在prompt中使用
  391. if isinstance(tool_result, (dict, list)):
  392. tool_result_str = json.dumps(tool_result, ensure_ascii=False, indent=2)
  393. else:
  394. tool_result_str = str(tool_result)
  395. prompt = prompt_template.replace('{tool_call_result}', tool_result_str).replace('{input_info}', input_info)
  396. # 尝试从缓存读取
  397. if self.use_cache:
  398. cached_data = self.cache.get(combined_question, 'function_knowledge', 'tool_result_eval.json')
  399. if cached_data:
  400. eval_result = cached_data.get('eval_result', {})
  401. logger.info(f"✓ 使用缓存的评估结果: {eval_result}")
  402. return eval_result
  403. # 调用LLM进行评估
  404. logger.info(" → 调用Gemini评估工具执行结果...")
  405. response_text = generate_text(prompt=prompt)
  406. # 解析JSON
  407. logger.info(" → 解析评估结果JSON...")
  408. try:
  409. # 清理可能的markdown标记
  410. response_text = response_text.strip()
  411. if response_text.startswith("```json"):
  412. response_text = response_text[7:]
  413. if response_text.startswith("```"):
  414. response_text = response_text[3:]
  415. if response_text.endswith("```"):
  416. response_text = response_text[:-3]
  417. response_text = response_text.strip()
  418. # 使用extract_and_validate_json提取JSON
  419. json_str = self.extract_and_validate_json(response_text)
  420. if json_str:
  421. eval_result = json.loads(json_str)
  422. else:
  423. # 如果提取失败,尝试直接解析
  424. eval_result = json.loads(response_text)
  425. logger.info(f"✓ 评估完成: {eval_result.get('是否可以回答', '未知')}")
  426. # 保存到缓存(包含完整的prompt和response)
  427. if self.use_cache:
  428. eval_data = {
  429. "prompt": prompt,
  430. "response": response_text,
  431. "eval_result": eval_result
  432. }
  433. self.cache.set(combined_question, 'function_knowledge', 'tool_result_eval.json', eval_data)
  434. return eval_result
  435. except json.JSONDecodeError as e:
  436. logger.error(f" ✗ 解析JSON失败: {e}")
  437. logger.error(f" 响应内容: {response_text}")
  438. # 降级:返回默认评估结果
  439. default_eval = {
  440. "是否可以回答": "未知",
  441. "理由": f"评估失败,无法解析LLM响应: {str(e)}"
  442. }
  443. logger.warning(f" 使用默认评估结果: {default_eval}")
  444. return default_eval
  445. except Exception as e:
  446. logger.error(f"✗ 评估工具执行结果失败: {e}")
  447. # 降级:返回默认评估结果
  448. return {
  449. "是否可以回答": "未知",
  450. "理由": f"评估过程出错: {str(e)}"
  451. }
  452. def get_knowledge(self, input_info: str) -> dict:
  453. """
  454. 获取方法知识的主流程(重构后)
  455. Returns:
  456. dict: 完整的执行记录
  457. """
  458. import time
  459. timestamp = time.strftime("%Y-%m-%d %H:%M:%S")
  460. start_time = time.time()
  461. logger.info("=" * 80)
  462. logger.info(f"Function Knowledge - 开始处理")
  463. logger.info(f"输入: {input_info}")
  464. logger.info("=" * 80)
  465. # 组合问题的唯一标识
  466. combined_question = input_info
  467. try:
  468. # 步骤0: 查找合适的工具
  469. find_tools_result = self.find_tools(combined_question, input_info)
  470. logger.info(f"✓ 查找合适的工具结果: {find_tools_result}")
  471. # 步骤0: 调用默认的热榜工具
  472. # default_hot_result = self.call_default_hot_tool(combined_question, input_info)
  473. # logger.info(f"✓ 默认热榜工具结果: {default_hot_result}")
  474. # 步骤1: 生成Query
  475. # query = self.generate_query(question, post_info, persona_info)
  476. # 步骤2: 选择工具
  477. # tool_info = self.select_tool(combined_question, input_info)
  478. # # tool_name = tool_info.get("工具名")
  479. # tool_id = tool_info.get("工具调用ID")
  480. # # tool_instructions = tool_info.get("使用方法")
  481. # if tool_id and len(tool_id) > 0:
  482. # # 路径A: 使用工具
  483. # # 步骤3: 提取参数
  484. # arguments = self.extract_tool_params(combined_question, input_info, tool_id, None)
  485. # # 步骤4: 调用工具
  486. # logger.info(f"[步骤4] 调用工具: {tool_id}")
  487. # # 检查工具调用缓存
  488. # if self.use_cache:
  489. # cached_tool_call = self.cache.get(combined_question, 'function_knowledge', 'tool_call.json')
  490. # if cached_tool_call:
  491. # logger.info(f"✓ 使用缓存的工具调用结果")
  492. # response = cached_tool_call.get('response', {})
  493. # tool_result = self.organize_tool_result(response)
  494. # # 保存工具调用信息(包含工具名、入参、结果)
  495. # tool_call_data = {
  496. # "tool_name": tool_id,
  497. # "arguments": arguments,
  498. # "result": tool_result,
  499. # "response": response
  500. # }
  501. # self.cache.set(combined_question, 'function_knowledge', 'tool_call.json', tool_call_data)
  502. # else:
  503. # logger.info(f" → 调用工具,参数: {arguments}")
  504. # rs = call_tool(tool_id, arguments)
  505. # tool_result = self.organize_tool_result(rs)
  506. # # 保存工具调用信息(包含工具名、入参、结果)
  507. # tool_call_data = {
  508. # "tool_name": tool_id,
  509. # "arguments": arguments,
  510. # "result": tool_result,
  511. # "response": rs
  512. # }
  513. # self.cache.set(combined_question, 'function_knowledge', 'tool_call.json', tool_call_data)
  514. # else:
  515. # logger.info(f" → 调用工具,参数: {arguments}")
  516. # rs = call_tool(tool_id, arguments)
  517. # tool_result = self.organize_tool_result(rs)
  518. # logger.info(f"✓ 工具调用完成")
  519. # # 步骤5: 评估工具执行结果
  520. # eval_result = self.evaluate_tool_result(combined_question, input_info, tool_result)
  521. # logger.info(f" 评估结果: {eval_result.get('是否可以回答', '未知')}")
  522. # if eval_result.get('理由'):
  523. # logger.info(f" 评估理由: {eval_result.get('理由')}")
  524. # else:
  525. # # 路径B: 知识搜索
  526. # logger.info("[步骤4] 未找到合适工具,调用 MultiSearch...")
  527. # knowledge = get_multi_search_knowledge(input_info, cache_key=combined_question)
  528. # # 异步保存知识到文件
  529. # logger.info("[后台任务] 保存知识到文件...")
  530. # threading.Thread(target=self.save_knowledge_to_file, args=(knowledge, combined_question)).start()
  531. # 计算执行时间
  532. execution_time = time.time() - start_time
  533. # 收集所有执行记录
  534. logger.info("=" * 80)
  535. logger.info("收集执行记录...")
  536. logger.info("=" * 80)
  537. from knowledge_v2.execution_collector import collect_and_save_execution_record
  538. execution_record = collect_and_save_execution_record(
  539. combined_question,
  540. input_info
  541. )
  542. logger.info("=" * 80)
  543. logger.info(f"✓ Function Knowledge 完成")
  544. logger.info(f" 执行时间: {execution_time}秒")
  545. logger.info("=" * 80 + "\n")
  546. return execution_record
  547. except Exception as e:
  548. logger.error(f"✗ 执行失败: {e}")
  549. import traceback
  550. logger.error(traceback.format_exc())
  551. # 即使失败也尝试收集记录
  552. try:
  553. execution_time = time.time() - start_time
  554. from knowledge_v2.execution_collector import collect_and_save_execution_record
  555. execution_record = collect_and_save_execution_record(
  556. combined_question,
  557. input_info
  558. )
  559. return execution_record
  560. except Exception as collect_error:
  561. logger.error(f"收集执行记录也失败: {collect_error}")
  562. # 返回基本错误信息
  563. return {
  564. "input": f"{input_info}",
  565. "result": {
  566. "type": "error",
  567. "content": f"执行失败: {str(e)}"
  568. },
  569. "metadata": {
  570. "errors": [str(e)]
  571. }
  572. }
  573. if __name__ == "__main__":
  574. # 测试代码
  575. input_info = """{ \"人设介绍\": \"这是一个动物梗图表情包搞笑类的账号。\", \"选题模式\": \"⭐ 选题模式:拟人化主体穿搭萌宠模式\\n📝 聚焦于拟人化穿搭内容灵感,借助拟人化主体与视觉构图版式的关键特征,最终实现趣味分享意图并呈现萌宠主题内容。\\n\\n⭐ 选题模式:校园学生场景化植入推广模式\\n📝 本模式以校园学生人设为内容灵感,运用场景化产品植入的方式,以实现商业推广意图和商业产品推广为主要目的。\\n\\n⭐ 选题模式:萌宠日常图文叙事模式\\n📝 该模式内容聚焦于日常生活演绎,借助图文叙事结构的表现形式,以呈现萌宠主题内容及实现趣味分享意图为核心导向\\n\\n⭐ 选题模式:视觉隐喻构图趣味分享模式\\n📝 该模式以视觉隐喻作为主要的内容灵感来源,结合视觉构图版式的关键特征进行呈现,最终达成趣味分享意图与多元生活趣闻的内容目的。\"}"""
  576. try:
  577. agent = FunctionKnowledge()
  578. execution_result = agent.get_knowledge(input_info=input_info)
  579. print("=" * 50)
  580. print("执行结果:")
  581. print("=" * 50)
  582. print(json.dumps(execution_result, ensure_ascii=False, indent=2))
  583. print(f"\n完整JSON已保存到缓存目录")
  584. except Exception as e:
  585. logger.error(f"测试失败: {e}")