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

86 statements  

« prev     ^ index     » next       coverage.py v7.14.3, created at 2026-07-06 11:37 +0800

1""" 

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

3 

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

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

6""" 

7 

8from __future__ import annotations 

9 

10import os 

11import importlib 

12import inspect 

13import pkgutil 

14from typing import Optional 

15 

16from agentos.tools.base import BaseTool 

17from agentos.tools.registry import ToolRegistry 

18from agentos.tools.bridge import bridge_registry_to_executor 

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

20from agentos.llm.base import LLMProvider 

21 

22 

23# ── Tool Discovery ── 

24 

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

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

27 

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

29 """ 

30 tools: list[BaseTool] = [] 

31 seen: set[str] = set() 

32 

33 package = importlib.import_module(package_path) 

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

35 

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

37 if module_name.startswith("_"): 

38 continue 

39 try: 

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

41 except Exception: 

42 continue 

43 

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

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

46 continue 

47 if name in seen: 

48 continue 

49 # Skip abstract/helper base classes 

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

51 continue 

52 try: 

53 instance = obj() 

54 tools.append(instance) 

55 seen.add(name) 

56 except Exception: 

57 continue 

58 

59 return tools 

60 

61 

62# ── Provider Auto-Detection ── 

63 

64def create_provider() -> LLMProvider: 

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

66 

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

68 """ 

69 # DeepSeek 

70 if os.getenv("DEEPSEEK_API_KEY"): 

71 from agentos.llm.providers.deepseek import DeepSeekProvider 

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

73 

74 # OpenAI 

75 if os.getenv("OPENAI_API_KEY"): 

76 from agentos.llm.providers.openai import OpenAIProvider 

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

78 

79 # Anthropic 

80 if os.getenv("ANTHROPIC_API_KEY"): 

81 from agentos.llm.providers.anthropic import AnthropicProvider 

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

83 

84 # Mock (dev fallback) 

85 from agentos.agent.agent_builder import _MockProvider 

86 return _MockProvider() 

87 

88 

89class _MockProvider(LLMProvider): 

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

91 

92 provider_name = "mock-dev" 

93 

94 def __init__(self): 

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

96 

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

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

99 

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

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

102 

103 def _make(self, content): 

104 from agentos.llm.base import ( 

105 CompletionResult, CompletionChoice, CompletionUsage, 

106 Message, MessageRole, 

107 ) 

108 return CompletionResult( 

109 choices=[CompletionChoice( 

110 index=0, 

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

112 finish_reason="stop", 

113 )], 

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

115 model="mock", 

116 ) 

117 

118 

119# ── Builder ── 

120 

121def build_agent( 

122 *, 

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

124 provider: Optional[LLMProvider] = None, 

125 system_prompt: Optional[str] = None, 

126 max_steps: int = 10, 

127 verbose: bool = False, 

128 discover_all: bool = True, 

129 include_skills: bool = True, 

130) -> ToolAgent: 

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

132 

133 Args: 

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

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

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

137 max_steps: 最大推理步数。 

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

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

140 

141 Returns: 

142 配置完成的 ToolAgent 实例。 

143 

144 Example: 

145 agent = build_agent() 

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

147 """ 

148 # Tools 

149 if tools is None and discover_all: 

150 tools = discover_tools() 

151 

152 # Skills — auto-discover and wrap as tools 

153 if include_skills: 

154 try: 

155 from agentos.tools.skill_tool import discover_skills 

156 skill_tools = discover_skills() 

157 if tools is None: 

158 tools = list(skill_tools) 

159 else: 

160 tools = list(tools) + list(skill_tools) 

161 except Exception: 

162 pass 

163 

164 if tools is None: 

165 tools = [] 

166 

167 reg = ToolRegistry() 

168 for tool in tools: 

169 reg.register(tool) 

170 

171 executor = ToolExecutor() 

172 bridge_registry_to_executor(reg, executor) 

173 

174 # Provider 

175 if provider is None: 

176 provider = create_provider() 

177 

178 # System prompt 

179 if system_prompt is None: 

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

181 system_prompt = ( 

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

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

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

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

186 ) 

187 

188 agent = ToolAgent( 

189 provider=provider, 

190 tool_executor=executor, 

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

192 system_prompt=system_prompt, 

193 ) 

194 

195 return agent