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