Coverage for agentos/agent/agent_builder.py: 0%

87 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-05 22:09 +0800

1""" 

2Agent Builder — 一键构建生产级 ToolAgent。 

3 

4自动发现所有 BaseTool 子类并通过 Bridge 注册到 ToolExecutor。 

5支持多种 LLM Provider 的自动探测。 

6""" 

7 

8from __future__ import annotations 

9 

10import os 

11import json 

12import importlib 

13import inspect 

14import pkgutil 

15from typing import Optional 

16 

17from agentos.tools.base import BaseTool 

18from agentos.tools.registry import ToolRegistry 

19from agentos.tools.bridge import bridge_registry_to_executor 

20from agentos.agent.tool_agent import ToolAgent, ToolExecutor, AgentConfig 

21from agentos.llm.base import LLMProvider 

22 

23 

24# ── Tool Discovery ── 

25 

26def discover_tools(package_path: str = "agentos.tools") -> list[BaseTool]: 

27 """自动发现 agentos.tools 包下所有 BaseTool 子类并实例化。 

28 

29 排除已知不能独立执行的基础类。 

30 """ 

31 tools: list[BaseTool] = [] 

32 seen: set[str] = set() 

33 

34 package = importlib.import_module(package_path) 

35 package_dir = os.path.dirname(package.__file__) 

36 

37 for _, module_name, is_pkg in pkgutil.iter_modules([package_dir]): 

38 if module_name.startswith("_"): 

39 continue 

40 try: 

41 mod = importlib.import_module(f"{package_path}.{module_name}") 

42 except Exception: 

43 continue 

44 

45 for name, obj in inspect.getmembers(mod, inspect.isclass): 

46 if not issubclass(obj, BaseTool) or obj is BaseTool: 

47 continue 

48 if name in seen: 

49 continue 

50 # Skip abstract/helper base classes 

51 if getattr(obj, "name", None) is None: 

52 continue 

53 try: 

54 instance = obj() 

55 tools.append(instance) 

56 seen.add(name) 

57 except Exception: 

58 continue 

59 

60 return tools 

61 

62 

63# ── Provider Auto-Detection ── 

64 

65def create_provider() -> LLMProvider: 

66 """自动探测可用的 LLM Provider。 

67 

68 优先级: DEEPSEEK_API_KEY > OPENAI_API_KEY > Mock (dev fallback) 

69 """ 

70 # DeepSeek 

71 if os.getenv("DEEPSEEK_API_KEY"): 

72 from agentos.llm.providers.deepseek import DeepSeekProvider 

73 return DeepSeekProvider(model="deepseek-chat") 

74 

75 # OpenAI 

76 if os.getenv("OPENAI_API_KEY"): 

77 from agentos.llm.providers.openai import OpenAIProvider 

78 return OpenAIProvider(model="gpt-4o-mini") 

79 

80 # Anthropic 

81 if os.getenv("ANTHROPIC_API_KEY"): 

82 from agentos.llm.providers.anthropic import AnthropicProvider 

83 return AnthropicProvider(model="claude-3-5-sonnet-20241022") 

84 

85 # Mock (dev fallback) 

86 from agentos.agent.agent_builder import _MockProvider 

87 return _MockProvider() 

88 

89 

90class _MockProvider(LLMProvider): 

91 """仅用于开发环境的 Mock Provider。""" 

92 

93 provider_name = "mock-dev" 

94 

95 def __init__(self): 

96 super().__init__(model="mock") 

97 

98 def chat(self, messages, **kwargs): 

99 return self._make("Mock provider: no API key configured. Set DEEPSEEK_API_KEY.") 

100 

101 async def achat(self, messages, **kwargs): 

102 return self.chat(messages, **kwargs) 

103 

104 def _make(self, content): 

105 from agentos.llm.base import ( 

106 CompletionResult, CompletionChoice, CompletionUsage, 

107 Message, MessageRole, 

108 ) 

109 return CompletionResult( 

110 choices=[CompletionChoice( 

111 index=0, 

112 message=Message(role=MessageRole.ASSISTANT, content=content), 

113 finish_reason="stop", 

114 )], 

115 usage=CompletionUsage(prompt_tokens=0, completion_tokens=0, total_tokens=0), 

116 model="mock", 

117 ) 

118 

119 

120# ── Builder ── 

121 

122def build_agent( 

123 *, 

124 tools: Optional[list[BaseTool]] = None, 

125 provider: Optional[LLMProvider] = None, 

126 system_prompt: Optional[str] = None, 

127 max_steps: int = 10, 

128 verbose: bool = False, 

129 discover_all: bool = True, 

130 include_skills: bool = True, 

131) -> ToolAgent: 

132 """一键构建生产级 ToolAgent。 

133 

134 Args: 

135 tools: 手动指定的工具列表。为 None 且 discover_all=True 时自动发现全部。 

136 provider: LLM Provider。为 None 时自动探测。 

137 system_prompt: 系统提示词。为 None 时使用默认中文提示。 

138 max_steps: 最大推理步数。 

139 verbose: 是否打印详细日志。 

140 discover_all: 是否自动发现所有 BaseTool。 

141 

142 Returns: 

143 配置完成的 ToolAgent 实例。 

144 

145 Example: 

146 agent = build_agent() 

147 result = agent.run("列出 /tmp 目录下的所有 .txt 文件") 

148 """ 

149 # Tools 

150 if tools is None and discover_all: 

151 tools = discover_tools() 

152 

153 # Skills — auto-discover and wrap as tools 

154 if include_skills: 

155 try: 

156 from agentos.tools.skill_tool import discover_skills 

157 skill_tools = discover_skills() 

158 if tools is None: 

159 tools = list(skill_tools) 

160 else: 

161 tools = list(tools) + list(skill_tools) 

162 except Exception: 

163 pass 

164 

165 if tools is None: 

166 tools = [] 

167 

168 reg = ToolRegistry() 

169 for tool in tools: 

170 reg.register(tool) 

171 

172 executor = ToolExecutor() 

173 bridge_registry_to_executor(reg, executor) 

174 

175 # Provider 

176 if provider is None: 

177 provider = create_provider() 

178 

179 # System prompt 

180 if system_prompt is None: 

181 tool_names = ", ".join(t.name for t in tools) if tools else "无" 

182 system_prompt = ( 

183 "你是一个智能助手,可以使用工具来完成任务。\n" 

184 f"可用工具: {tool_names}\n" 

185 "当你可以给出最终答案时直接回答,不要再调用工具。\n" 

186 "用简洁的中文回答。" 

187 ) 

188 

189 agent = ToolAgent( 

190 provider=provider, 

191 tool_executor=executor, 

192 config=AgentConfig(max_steps=max_steps, verbose=verbose), 

193 system_prompt=system_prompt, 

194 ) 

195 

196 return agent