bootstrap.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246
  1. """
  2. agent67v2.core.bootstrap — 统一启动引导
  3. 将 agent-config.yml 编译为可运行的 Lambda Term,
  4. 自动处理 subAgents 节的子代理注册和工具注入。
  5. 用法:
  6. # 方式 1: PaaS CLI
  7. python3 -m agentpaas agent create --name lambda2 --config agentexample/agent67v2/agent-config.yml
  8. python3 -m agentpaas chat lambda2
  9. # 方式 2: Python 直接调用
  10. from agent67v2.core.bootstrap import build_agent67v2
  11. agent = build_agent67v2()
  12. result = agent.apply("帮我看看当前目录")
  13. 流程:
  14. 1. 读取 agent-config.yml
  15. 2. 解析 subAgents 节,编译每个子代理
  16. 3. 注册子代理为 Skill 到 SkillRegistry
  17. 4. 创建 call_* 元工具 + ToolSearch
  18. 5. 通过 overrides["tools"] 注入到 from_config()
  19. 6. 返回完整的协调者 Term
  20. """
  21. from __future__ import annotations
  22. import json
  23. import os
  24. import sys
  25. import yaml
  26. from pathlib import Path
  27. from typing import Any, Dict, Optional
  28. PROJECT_ROOT = Path(__file__).resolve().parent.parent.parent.parent
  29. AGENT_DIR = Path(__file__).resolve().parent.parent
  30. sys.path.insert(0, str(PROJECT_ROOT))
  31. from lambdagent.core import Term, Context
  32. from lambdagent.primitives import Tool
  33. from lambdagent.skills import Skill, SkillSignature, SkillPack, SkillRegistry
  34. # ════════════════════════════════════════════════════════════
  35. # SubAgent 编译与注册
  36. # ════════════════════════════════════════════════════════════
  37. def _compile_sub_agent(name: str, config_path: Path) -> Optional[Term]:
  38. """编译单个子代理 YAML 为 Term"""
  39. if not config_path.exists():
  40. print(f" ⚠️ 子代理配置不存在: {config_path}")
  41. return None
  42. try:
  43. from lambdagent.fromconfig.compiler import from_config
  44. return from_config(str(config_path))
  45. except Exception as e:
  46. print(f" ⚠️ 子代理 {name} 编译失败: {e}")
  47. return None
  48. def _register_sub_agents(config_path: Path, sub_agents_cfg: dict) -> Dict[str, Skill]:
  49. """
  50. 编译并注册所有子代理为 Skill。
  51. Args:
  52. config_path: agent-config.yml 所在目录
  53. sub_agents_cfg: subAgents 配置节
  54. Returns:
  55. {agent_name: Skill} 映射
  56. """
  57. skills = {}
  58. pack = SkillPack(
  59. name="agent67v2",
  60. description="agent67v2 多智能体协作系统",
  61. version="2.0.0",
  62. )
  63. for agent_name, agent_def in sub_agents_cfg.items():
  64. # 解析子代理配置文件路径 (相对于 agent-config.yml 所在目录)
  65. rel_path = agent_def.get("config", f"agents/{agent_name}.yml")
  66. agent_yml = config_path / rel_path
  67. # 编译子代理
  68. term = _compile_sub_agent(agent_name, agent_yml)
  69. if term is None:
  70. continue
  71. # 创建 Skill
  72. skill = Skill(
  73. name=agent_name,
  74. term=term,
  75. description=agent_def.get("description", ""),
  76. signature=SkillSignature(input_type="Str", output_type="Str"),
  77. tags=agent_def.get("tags", []),
  78. version="2.0.0",
  79. author="agent67v2",
  80. )
  81. pack.add(skill)
  82. skills[agent_name] = skill
  83. # 注册到全局 SkillRegistry
  84. registry = SkillRegistry()
  85. registry.register_pack(pack)
  86. return skills
  87. # ════════════════════════════════════════════════════════════
  88. # 元工具构建
  89. # ════════════════════════════════════════════════════════════
  90. def _make_agent_caller(skill: Skill):
  91. """为一个子代理 Skill 创建调用函数"""
  92. def call_agent(input_str: str) -> str:
  93. try:
  94. # 解析输入
  95. if isinstance(input_str, str):
  96. try:
  97. data = json.loads(input_str)
  98. task = data.get("task", input_str)
  99. except (json.JSONDecodeError, AttributeError):
  100. task = input_str
  101. else:
  102. task = str(input_str)
  103. ctx = Context()
  104. result = skill.apply(task, ctx)
  105. return str(result)
  106. except Exception as e:
  107. return f"[ERROR] {skill._name} 执行失败: {e}"
  108. return call_agent
  109. def _build_meta_tools(skills: Dict[str, Skill], sub_agents_cfg: dict) -> Dict[str, Any]:
  110. """
  111. 构建协调者的元工具集合。
  112. 根据 subAgents 配置中的 tool 字段,为每个子代理创建对应的 call_* 函数。
  113. """
  114. tools = {}
  115. for agent_name, agent_def in sub_agents_cfg.items():
  116. tool_name = agent_def.get("tool", f"call_{agent_name.replace('-agent', '')}")
  117. skill = skills.get(agent_name)
  118. if skill:
  119. tools[tool_name] = _make_agent_caller(skill)
  120. # ToolSearch 元工具
  121. from agent67v2.tools.tool_search import tool_search
  122. tools["ToolSearch"] = lambda x: tool_search.apply(x)
  123. return tools
  124. # ════════════════════════════════════════════════════════════
  125. # 主入口: build_agent67v2
  126. # ════════════════════════════════════════════════════════════
  127. def build_agent67v2(config_file: str = None, **overrides) -> Term:
  128. """
  129. 从 agent-config.yml 构建完整的 agent67v2 协调者。
  130. 流程:
  131. 1. 读取 agent-config.yml
  132. 2. 解析 subAgents,编译并注册子代理
  133. 3. 构建 call_* 元工具
  134. 4. 通过 overrides["tools"] 注入到 from_config()
  135. 5. 返回可执行的协调者 Term
  136. Args:
  137. config_file: agent-config.yml 路径 (默认: 自动定位)
  138. **overrides: 传递给 from_config 的覆盖参数
  139. Returns:
  140. Term: 可执行的协调者 Lambda Term
  141. """
  142. # 定位配置文件
  143. if config_file:
  144. config_path = Path(config_file).resolve()
  145. else:
  146. config_path = AGENT_DIR / "agent-config.yml"
  147. if not config_path.exists():
  148. raise FileNotFoundError(f"配置文件不存在: {config_path}")
  149. config_dir = config_path.parent
  150. # 读取配置
  151. with open(config_path, "r", encoding="utf-8") as f:
  152. cfg = yaml.safe_load(f)
  153. # 编译并注册子代理
  154. sub_agents_cfg = cfg.get("subAgents", {})
  155. if sub_agents_cfg:
  156. print(f" 🔄 编译 {len(sub_agents_cfg)} 个子代理...")
  157. skills = _register_sub_agents(config_dir, sub_agents_cfg)
  158. print(f" ✅ {len(skills)} 个子代理已注册为 Skill")
  159. # 构建元工具
  160. meta_tools = _build_meta_tools(skills, sub_agents_cfg)
  161. # 注入到 overrides
  162. existing_tools = overrides.get("tools", {})
  163. existing_tools.update(meta_tools)
  164. overrides["tools"] = existing_tools
  165. # 通过 from_config 编译协调者
  166. from lambdagent.fromconfig.compiler import from_config
  167. agent = from_config(str(config_path), **overrides)
  168. return agent
  169. def build_and_chat(config_file: str = None, **overrides):
  170. """
  171. 构建 agent67v2 并进入交互式对话。
  172. 用于 PaaS chat 模式的直接调用。
  173. """
  174. agent = build_agent67v2(config_file, **overrides)
  175. print(f"\n{'═' * 50}")
  176. print(f" 🐑 lambda v2 — 多智能体协作版")
  177. print(f" 输入 'exit' 退出")
  178. print(f"{'═' * 50}\n")
  179. while True:
  180. try:
  181. user_input = input("You: ").strip()
  182. except (EOFError, KeyboardInterrupt):
  183. print("\n👋 lambda v2 下线了!")
  184. break
  185. if not user_input:
  186. continue
  187. if user_input.lower() in ("exit", "quit", "bye"):
  188. print("👋 lambda v2 下线了!")
  189. break
  190. print()
  191. ctx = Context()
  192. result = agent.apply(user_input, ctx)
  193. print(f"🐑 lambda v2: {result}")
  194. print()