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
« prev ^ index » next coverage.py v7.14.3, created at 2026-07-06 11:37 +0800
1"""
2Agent Builder — 一键构建生产级 ToolAgent。
4自动发现所有 BaseTool 子类并通过 Bridge 注册到 ToolExecutor。
5支持多种 LLM Provider 的自动探测。
6"""
8from __future__ import annotations
10import os
11import importlib
12import inspect
13import pkgutil
14from typing import Optional
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
23# ── Tool Discovery ──
25def discover_tools(package_path: str = "agentos.tools") -> list[BaseTool]:
26 """自动发现 agentos.tools 包下所有 BaseTool 子类并实例化。
28 排除已知不能独立执行的基础类。
29 """
30 tools: list[BaseTool] = []
31 seen: set[str] = set()
33 package = importlib.import_module(package_path)
34 package_dir = os.path.dirname(package.__file__)
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
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
59 return tools
62# ── Provider Auto-Detection ──
64def create_provider() -> LLMProvider:
65 """自动探测可用的 LLM Provider。
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")
74 # OpenAI
75 if os.getenv("OPENAI_API_KEY"):
76 from agentos.llm.providers.openai import OpenAIProvider
77 return OpenAIProvider(model="gpt-4o-mini")
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")
84 # Mock (dev fallback)
85 from agentos.agent.agent_builder import _MockProvider
86 return _MockProvider()
89class _MockProvider(LLMProvider):
90 """仅用于开发环境的 Mock Provider。"""
92 provider_name = "mock-dev"
94 def __init__(self):
95 super().__init__(model="mock")
97 def chat(self, messages, **kwargs):
98 return self._make("Mock provider: no API key configured. Set DEEPSEEK_API_KEY.")
100 async def achat(self, messages, **kwargs):
101 return self.chat(messages, **kwargs)
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 )
119# ── Builder ──
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。
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。
141 Returns:
142 配置完成的 ToolAgent 实例。
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()
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
164 if tools is None:
165 tools = []
167 reg = ToolRegistry()
168 for tool in tools:
169 reg.register(tool)
171 executor = ToolExecutor()
172 bridge_registry_to_executor(reg, executor)
174 # Provider
175 if provider is None:
176 provider = create_provider()
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 )
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 )
195 return agent