LightAgent 0.3.2__tar.gz → 0.3.3__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,8 +3,8 @@
3
3
 
4
4
  """
5
5
  作者: [weego/WXAI-Team]
6
- 版本: 0.3.2
7
- 最后更新: 2025-03-31
6
+ 版本: 0.3.3
7
+ 最后更新: 2025-05-05
8
8
  """
9
9
 
10
10
  import asyncio
@@ -22,12 +22,12 @@ from copy import deepcopy
22
22
  from datetime import datetime
23
23
  from functools import partial
24
24
  from typing import List, Dict, Any, Callable, Union, Optional, Generator, AsyncGenerator
25
+ from uuid import uuid4
25
26
 
26
27
  import httpx
27
28
  from mcp import ClientSession, StdioServerParameters
28
29
  from mcp.client.sse import sse_client
29
30
  from mcp.client.stdio import stdio_client
30
- from openai import OpenAI
31
31
  from openai.types.chat import ChatCompletionChunk
32
32
 
33
33
  # 全局工具注册表
@@ -36,7 +36,10 @@ _FUNCTION_INFO = {} # 工具名称 -> 工具info信息
36
36
  _OPENAI_FUNCTION_SCHEMAS = [] # OpenAI 格式的工具描述
37
37
  _PROMPT_FUNCTION_SCHEMAS = [] # prompt 格式的工具描述
38
38
 
39
- __version__ = "0.3.2" # 你可以根据需要设置版本号
39
+ __version__ = "0.3.3" # 你可以根据需要设置版本号
40
+
41
+
42
+ # openai.langfuse_auth_check()
40
43
 
41
44
 
42
45
  def register_tool_manually(tools: List[Union[str, Callable]]) -> bool:
@@ -186,7 +189,10 @@ def filter_tools_schemas(refined_content: str) -> json:
186
189
  :param refined_content: 包含工具列表的JSON字符串
187
190
  """
188
191
  # global _OPENAI_FUNCTION_SCHEMAS # 声明操作全局变量
189
-
192
+ """安全解析可能包含 Markdown 代码块的 JSON"""
193
+ refined_content = refined_content.strip()
194
+ if refined_content.startswith('```json') and refined_content.endswith('```'):
195
+ refined_content = refined_content[7:-3].strip() # 去除 ```json 和 ```
190
196
  try:
191
197
  # 解析工具列表
192
198
  parsed_data: Dict[str, List[Dict]] = json.loads(refined_content)
@@ -413,7 +419,7 @@ class MCPClientManager:
413
419
 
414
420
 
415
421
  class LightAgent:
416
- __version__ = "0.3.2" # 将版本号放在类中
422
+ __version__ = "0.3.3" # 将版本号放在类中
417
423
 
418
424
  def __init__(
419
425
  self,
@@ -425,16 +431,18 @@ class LightAgent:
425
431
  api_key: str | None = None, # 模型 api key
426
432
  base_url: str | httpx.URL | None = None, # 模型 base url
427
433
  websocket_base_url: str | httpx.URL | None = None, # 模型 websocket base url
428
- memory=None, # 支持外部传入记忆模块
434
+ memory: str | None = None, # 支持外部传入记忆模块
429
435
  tree_of_thought: bool = False, # 是否启用链式思考
430
436
  tot_model: str | None = None, # 链式思考模型
431
437
  tot_api_key: str | None = None, # 链式思考模型API密钥
432
438
  tot_base_url: str | httpx.URL | None = None, # 链式思考模型base_url
439
+ filter_tools: bool = True, # 是否启用工具过滤
433
440
  self_learning: bool = False, # 是否启用agent自我学习
434
441
  tools: List[Union[str, Callable]] = None, # 支持工具混合输入
435
442
  debug: bool = False, # 是否启用调试模式
436
443
  log_level: str = "INFO", # 日志级别(INFO, DEBUG, ERROR)
437
- log_file: Optional[str] = None # 日志文件路径
444
+ log_file: Optional[str] = None, # 日志文件路径
445
+ tracetools: Optional[dict] = None, # log跟踪工具
438
446
  ) -> None:
439
447
  """
440
448
  初始化 LightAgent。
@@ -451,10 +459,12 @@ class LightAgent:
451
459
  :param tot_model: 使用的模型名称。
452
460
  :param tot_api_key: API 密钥。
453
461
  :param tot_base_url: API 的基础 URL。
462
+ :param filter_tools: 是否启用工具过滤。
454
463
  :param tools: 工具列表,支持函数名称(字符串)或函数对象。
455
464
  :param debug: 是否启用调试模式。
456
465
  :param log_level: 日志级别(INFO, DEBUG, ERROR)。
457
466
  :param log_file: 日志文件路径。
467
+ :param tracetools: log跟踪工具。
458
468
  """
459
469
  self.mcp_setting = None
460
470
  self.mcp_client = None
@@ -477,9 +487,11 @@ class LightAgent:
477
487
  self.memory = memory
478
488
  self.tree_of_thought = tree_of_thought
479
489
  self.self_learning = self_learning
490
+ self.filter_tools = filter_tools
480
491
 
481
492
  self.debug = debug
482
493
  self.log_level = log_level.upper()
494
+ self.traceid = "" # 用于存储 traceid
483
495
  # 确保 log 目录存在
484
496
  log_dir = 'logs'
485
497
  if not os.path.exists(log_dir):
@@ -507,10 +519,6 @@ class LightAgent:
507
519
  if base_url is None:
508
520
  base_url = f"https://api.openai.com/v1"
509
521
 
510
- self.client = OpenAI(
511
- base_url=base_url,
512
- api_key=self.api_key
513
- )
514
522
  if self.tree_of_thought:
515
523
  if tot_api_key is None:
516
524
  tot_api_key = api_key
@@ -519,10 +527,36 @@ class LightAgent:
519
527
  if not tot_model:
520
528
  tot_model = "deepseek-r1" # 默认思维推理模型为deepseek-r1
521
529
  self.tot_model = tot_model
522
- self.tot_client = OpenAI(
523
- base_url=tot_base_url,
524
- api_key=tot_api_key
530
+
531
+ if tracetools is None:
532
+ self.tracetools = []
533
+ if tracetools:
534
+ self.tracetools = tracetools
535
+ # 初始化工具列表
536
+ from langfuse.openai import openai as la_openai
537
+ la_openai.langfuse_public_key = self.tracetools['TraceToolConfig']['langfuse_public_key']
538
+ la_openai.langfuse_secret_key = self.tracetools['TraceToolConfig']['langfuse_secret_key']
539
+ la_openai.langfuse_enabled = self.tracetools['TraceToolConfig'][
540
+ 'langfuse_enabled'] # Default is True, set to False to disable Langfuse
541
+ la_openai.langfuse_host = self.tracetools['TraceToolConfig']['langfuse_host'] # 🇪🇺 EU region
542
+ la_openai.base_url = base_url
543
+ la_openai.api_key = self.api_key
544
+ self.client = la_openai
545
+ if self.tree_of_thought:
546
+ la_openai.base_url = tot_base_url
547
+ la_openai.api_key = tot_api_key
548
+ self.tot_client = la_openai
549
+ else:
550
+ from openai import OpenAI as la_openai
551
+ self.client = la_openai(
552
+ base_url=base_url,
553
+ api_key=self.api_key
525
554
  )
555
+ if self.tree_of_thought:
556
+ self.tot_client = la_openai(
557
+ base_url=tot_base_url,
558
+ api_key=tot_api_key
559
+ )
526
560
 
527
561
  def get_tool(self, tool_name: str) -> Callable:
528
562
  """
@@ -629,7 +663,10 @@ class LightAgent:
629
663
  """
630
664
  if not self.debug:
631
665
  return
632
- log_message = f"{action}: {data}"
666
+ if self.traceid is not None:
667
+ log_message = f"[TraceID: {self.traceid}] {action}: {data}"
668
+ else:
669
+ log_message = f"{action}: {data}"
633
670
  if level == "DEBUG":
634
671
  self.logger.debug(log_message)
635
672
  elif level == "INFO":
@@ -671,6 +708,7 @@ class LightAgent:
671
708
  :param metadata: 元数据。
672
709
  :return: 代理的回复。
673
710
  """
711
+ self.traceid = uuid4().hex
674
712
  self.log("INFO", "run", {"query": query, "user_id": user_id, "light_swarm": light_swarm, "stream": stream})
675
713
  if history is None:
676
714
  history = []
@@ -687,19 +725,25 @@ class LightAgent:
687
725
  # self._transfer_to_agent(light_swarm.agents[target_agent_name], query, stream=stream)
688
726
  # return # 立即结束当前生成器
689
727
 
690
- # 1. 判断是否需要转移任务
728
+ # 0. 判断是否需要转移任务
691
729
  if light_swarm:
692
730
  result = self._handle_task_transfer(query, light_swarm, stream)
693
731
  if result is not None:
694
732
  return result
695
733
 
696
- # 2. 正常处理任务
734
+ # 1. 正常处理任务
697
735
  now = datetime.now()
698
736
  current_date = now.strftime("%Y-%m-%d")
699
737
  current_time = now.strftime("%H:%M:%S")
700
738
  system_prompt = f"##代理名称:{self.name} ##代理指令 /n{self.instructions} ##身份 /n {self.role} /n 请一步一步思考来完成用户的要求。尽可能完成用户的回答,如果有补充信息,请参考补充信息来调用工具,直到获取所有满足用户的提问所需的答案。 /n 今日的日期: {current_date} 当前时间: {current_time}"
701
739
  params = dict(model=self.model, stream=stream)
702
740
  memory = ''
741
+
742
+ # 2.添加langfuse的session
743
+ if self.tracetools:
744
+ params["session_id"] = self.traceid
745
+ self.log("DEBUG", "Query Trace ID", {"query": query})
746
+
703
747
  # 3. 从记忆中检索相关内容&保存记忆
704
748
  if self.memory:
705
749
  related_memories = self.memory.retrieve(query=query, user_id=user_id)
@@ -743,6 +787,7 @@ class LightAgent:
743
787
  params["messages"].append({"role": item["role"], "content": item["content"]})
744
788
  # 最后添加当前用户的查询信息
745
789
  params["messages"].append({"role": "user", "content": query})
790
+
746
791
  response = self.client.chat.completions.create(**params)
747
792
 
748
793
  result = self._core_run_logic(response, params, stream, max_retry)
@@ -1160,7 +1205,7 @@ class LightAgent:
1160
1205
  self.log("DEBUG", "run_thought", {"system_prompt": system_prompt})
1161
1206
 
1162
1207
  try:
1163
- # 第一次请求,生成初始的工具使用计划
1208
+ # 1. 第一次请求,生成初始的工具使用计划
1164
1209
  params = dict(model=tot_model,
1165
1210
  messages=[{"role": "system", "content": system_prompt}, {"role": "user", "content": query}],
1166
1211
  stream=False)
@@ -1168,7 +1213,7 @@ class LightAgent:
1168
1213
  initial_content = response.choices[0].message.content
1169
1214
  self.log("DEBUG", "initial_response", {"response": initial_content})
1170
1215
 
1171
- # 第二次请求,请求大模型反思并生成新的工具使用规划
1216
+ # 2. 第二次请求,请求大模型反思并生成新的工具使用规划
1172
1217
  reflection_prompt = "请反思你的回答,请严格按照<工具列表>中的工具来规划,不可以创造其他新的工具。请输出新的任务规划,不要输出其他分析和回答。"
1173
1218
  reflection_params = dict(model=tot_model, messages=[
1174
1219
  {"role": "user", "content": f"{system_prompt} /n 开始思考问题: {query}"},
@@ -1204,8 +1249,12 @@ class LightAgent:
1204
1249
  tool_reflection_response = self.tot_client.chat.completions.create(**tool_reflection_params)
1205
1250
  tool_reflection_result = tool_reflection_response.choices[0].message.content
1206
1251
  self.log("DEBUG", "tool_reflection_result", {"result": tool_reflection_result})
1207
- current_tools = filter_tools_schemas(tool_reflection_result)
1208
- self.log("DEBUG", "current_tools", {"get_tools": current_tools})
1252
+
1253
+ # 3.执行自适应工具过滤
1254
+ current_tools = []
1255
+ if self.filter_tools:
1256
+ current_tools = filter_tools_schemas(tool_reflection_result)
1257
+ self.log("DEBUG", "current_tools", {"get_tools": current_tools})
1209
1258
 
1210
1259
  return refined_content, current_tools
1211
1260