""" nl2agent.py — 一句话搭建智能体 Natural Language → YAML Config → Lambda Term → Execute 用法: python nl2agent.py "帮我搭一个深度调研助手,能搜索、写代码、做计算,最多20步,带记忆" python nl2agent.py --interactive # 交互模式 """ from __future__ import annotations import os import sys import json import yaml import time import tempfile from typing import Optional # ============================================================ # 1. NL → YAML: 用 LLM 将自然语言描述编译为 agent 配置 # ============================================================ SYSTEM_PROMPT = '''你是一个 Agent 配置编译器。用户用自然语言描述需要什么样的智能体,你输出对应的 YAML 配置。 你必须严格输出合法 YAML,不要输出任何其他内容(无解释、无 markdown 代码块标记)。 可用的 agent type: - simple: 单步对话 agent - react: 多步推理 agent (带工具调用, Y 组合子循环) - chain: 流水线 agent (多步顺序执行) - router: 路由 agent (根据输入分类到不同子 agent) - parallel: 并行 agent (多个 agent 同时执行后合并) 可用的 MCP 工具 (onlineTool 下 example-mcp-server 服务器): - everything_get_sum: 搜索工具,可搜索互联网信息 - chat_improve_prompt: 优化 prompt 的工具 本地工具 (localTools): - terminate: 结束任务(react 类型必须包含) 模型选择: - dashscope 的 qwen3-max-2026-01-23(默认) - anthropic 的 claude-sonnet-4-20250514 配置结构参考: ```yaml agentId: string name: string description: string type: react|chain|router|parallel|simple systemPrompt: | 系统提示词... model: provider: dashscope name: qwen3-max-2026-01-23 temperature: 0.7 maxTokens: 4096 react: # type=react 时 maxSteps: 10 observationEnabled: true toolTimeout: 30 chain: # type=chain 时 steps: - name: step_name prompt: "步骤提示词" router: # type=router 时 classifier: prompt: "分类提示词" categories: [cat1, cat2] routes: cat1: {type: simple, systemPrompt: "..."} cat2: {type: simple, systemPrompt: "..."} default: {type: simple, systemPrompt: "..."} parallel: # type=parallel 时 agents: - {name: agent1, systemPrompt: "..."} - {name: agent2, systemPrompt: "..."} merge: custom mergePrompt: "合并提示词" memory: enabled: true strategy: local size: 20 ttl: 7200 guard: validator: "len(x) > 100" retry: 1 mcp: onlineTool: example-mcp-server: - everything_get_sum localTools: - terminate policy: mode: auto app: mcp: custom: nodes: example-mcp-server: url: https://your-mcp-endpoint.example.com endpoint: /mcp/airouting headers: Authorization: "${MCP_AUTH_TOKEN}" ``` 规则: 1. react 类型必须在 localTools 中包含 terminate 2. systemPrompt 要详细、专业、有针对性 3. 根据用户描述的复杂度选择合适的 type 4. 如果用户提到"搜索"、"查资料",用 everything_get_sum 工具 5. 如果用户提到多个独立视角/角度,考虑用 parallel 6. 如果用户提到步骤/流程,考虑用 chain 7. 如果用户提到分类/路由/不同情况,考虑用 router 8. app.mcp.custom.nodes 的配置固定不变(如上面的参考) 9. 只输出 YAML,不要任何其他文字''' def nl_to_yaml(description: str, model: str = "qwen3-max-2026-01-23") -> str: """ 用 LLM 将自然语言描述转为 YAML 配置。 Lambda 语义: nl_to_yaml = λ(description). LLM_{compiler}(description) """ provider = _detect_provider(model) if provider == "dashscope": return _call_dashscope(model, SYSTEM_PROMPT, description) elif provider == "anthropic": return _call_anthropic(model, SYSTEM_PROMPT, description) else: return _call_dashscope("qwen3-max-2026-01-23", SYSTEM_PROMPT, description) def _detect_provider(model: str) -> str: if "qwen" in model.lower() or "dashscope" in model.lower(): return "dashscope" elif "claude" in model.lower() or "anthropic" in model.lower(): return "anthropic" return "dashscope" def _call_dashscope(model, system, user) -> str: import urllib.request api_key = os.environ.get("DASHSCOPE_API_KEY", "") if not api_key: raise RuntimeError("请设置 DASHSCOPE_API_KEY 环境变量") url = "https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions" body = json.dumps({ "model": model, "messages": [ {"role": "system", "content": system}, {"role": "user", "content": user}, ], "temperature": 0.3, "max_tokens": 4096, }).encode("utf-8") req = urllib.request.Request(url, data=body, headers={ "Content-Type": "application/json", "Authorization": f"Bearer {api_key}", }) with urllib.request.urlopen(req, timeout=60) as resp: data = json.loads(resp.read().decode("utf-8")) return data["choices"][0]["message"]["content"].strip() def _call_anthropic(model, system, user) -> str: import anthropic client = anthropic.Anthropic() resp = client.messages.create( model=model, max_tokens=4096, temperature=0.3, system=system, messages=[{"role": "user", "content": user}], ) return resp.content[0].text.strip() # ============================================================ # 2. YAML → Lambda Term → Execute # ============================================================ def build_and_run(yaml_str: str, user_input: str, verbose: bool = True) -> str: """ 从 YAML 字符串编译 Agent 并执行。 完整流程: YAML string → parse → Term → term(input) → result | | | 解析 编译 β-规约 """ from lambdagent.fromconfig import from_config, lint_config, format_lint from lambdagent.fromconfig import to_lambda_expr, describe_config # 写入临时文件 with tempfile.NamedTemporaryFile(mode='w', suffix='.yml', delete=False, encoding='utf-8') as f: f.write(yaml_str) tmp_path = f.name try: # Lint if verbose: results = lint_config(tmp_path) print(format_lint(results, "generated-agent.yml")) print() # Lambda 结构 if verbose: print(describe_config(tmp_path)) print() print("Lambda expression:") print(to_lambda_expr(tmp_path)) print() # 编译 if verbose: print("=" * 60) print(" Compiling YAML → Lambda Term...") agent = from_config(tmp_path) if verbose: print(f" Compiled: {agent}") print("=" * 60) print() # 执行 if verbose: print(f" Executing: agent(\"{user_input[:50]}...\")") print(" " + "-" * 56) t0 = time.time() result = agent(user_input) duration = time.time() - t0 if verbose: print(f"\n Done in {duration:.1f}s") print("=" * 60) return result finally: os.unlink(tmp_path) # ============================================================ # 3. 完整流程: 一句话 → Agent # ============================================================ def one_sentence_to_agent(description: str, user_input: Optional[str] = None, model: str = "qwen3-max-2026-01-23", verbose: bool = True): """ 一句话搭建并运行 Agent。 Lambda 语义: one_sentence_to_agent = λ(desc, input). let yaml = LLM_{compiler}(desc) in ← 意图解析 let term = from_config(yaml) in ← 编译 term(input) ← β-规约 """ print("=" * 60) print(" lambdagent — 一句话搭建智能体") print("=" * 60) print() print(f" 描述: \"{description}\"") print() # Step 1: NL → YAML print(" [Step 1] 解析意图,生成配置...") t0 = time.time() yaml_str = nl_to_yaml(description, model) # 清理: 去掉可能的 markdown 代码块标记 yaml_str = yaml_str.strip() if yaml_str.startswith("```"): lines = yaml_str.split("\n") # Remove first and last lines if they are code fences if lines[0].startswith("```"): lines = lines[1:] if lines and lines[-1].strip() == "```": lines = lines[:-1] yaml_str = "\n".join(lines) gen_time = time.time() - t0 print(f" Done ({gen_time:.1f}s)") print() # 显示生成的 YAML print(" [Generated YAML Config]") print(" " + "-" * 56) for line in yaml_str.split("\n"): print(f" {line}") print(" " + "-" * 56) print() # 验证 YAML 合法性 try: cfg = yaml.safe_load(yaml_str) if not isinstance(cfg, dict): print(" [ERROR] Generated YAML is not a valid config dict") return yaml_str except yaml.YAMLError as e: print(f" [ERROR] Invalid YAML: {e}") return yaml_str # Step 2: 保存 YAML config_path = "generated_agent.yml" with open(config_path, "w", encoding="utf-8") as f: f.write(yaml_str) print(f" Config saved to: {config_path}") print() # Step 3: Lint + Lambda 结构 from lambdagent.fromconfig import lint_config, format_lint, describe_config, to_lambda_expr results = lint_config(cfg) print(format_lint(results, "generated_agent.yml")) print() print(describe_config(cfg)) print() print(" [Lambda Expression]") print(f" {to_lambda_expr(cfg)}") print() # Step 4: 编译 from lambdagent.fromconfig import from_config print(" [Step 2] Compiling YAML → Lambda Term...") agent = from_config(config_path) print(f" Compiled: {agent}") print() # Step 5: 如果有输入就执行 if user_input: print(f" [Step 3] Executing agent(\"{user_input[:60]}\")") print(" " + "-" * 56) t0 = time.time() result = agent(user_input) duration = time.time() - t0 print() print(f" [Result] ({duration:.1f}s)") print(" " + "-" * 56) print(result) print(" " + "-" * 56) return result else: print(" Agent compiled successfully! Use agent(input) to execute.") print() print(" Example:") print(f' agent("{description[:30]}相关的问题")') return agent # ============================================================ # 4. 交互模式 # ============================================================ def interactive_mode(model: str = "qwen3-max-2026-01-23"): """交互式一句话搭建 Agent。""" print("=" * 60) print(" lambdagent 交互模式") print(" 输入自然语言描述来搭建智能体") print(" 输入 :quit 退出") print("=" * 60) print() while True: try: desc = input("描述你需要的 Agent:\n> ").strip() except (EOFError, KeyboardInterrupt): print("\nBye!") break if not desc or desc == ":quit": print("Bye!") break # 询问是否有具体任务 try: task = input("要执行什么任务? (直接回车跳过):\n> ").strip() except (EOFError, KeyboardInterrupt): print("\nBye!") break print() one_sentence_to_agent(desc, task or None, model) print() # ============================================================ # 5. 预置 Demo # ============================================================ DEMO_DESCRIPTIONS = { "research": ( "帮我搭一个深度调研助手,能用搜索工具查资料,能分析整理信息," "如果信息不够就继续搜索,最多研究20步,带记忆功能," "最后输出一份完整的、超过500字的调研报告", "请调研:大语言模型在软件工程领域的最新应用进展,包括代码生成、代码审查、自动化测试等方面" ), "customer_service": ( "搭一个智能客服系统,能自动识别用户是咨询技术问题、账号问题还是投诉建议," "分别路由给不同的专家处理,技术专家能调用搜索工具查文档", "我的账号登录不了了,显示密码错误,但我确认密码是对的" ), "code_review": ( "做一个代码审查流水线,第一步检查安全漏洞,第二步检查代码风格和可读性," "第三步检查性能问题,第四步生成综合审查报告,每一步的输出要超过100字", "def login(user, pwd):\n q = f\"SELECT * FROM users WHERE name='{user}' AND pass='{pwd}'\"\n return db.execute(q)" ), "translator": ( "做一个多语言翻译系统,把输入同时翻译成英文、日文、韩文三个版本并行执行," "然后由一个总结专家对比三个翻译版本的质量并给出最终推荐", "人工智能正在深刻改变软件开发的方式,从代码自动生成到智能调试,开发者的工作效率得到了显著提升。" ), } def run_demo(name: str = "research", model: str = "qwen3-max-2026-01-23"): """运行预置 Demo。""" if name not in DEMO_DESCRIPTIONS: print(f"Available demos: {list(DEMO_DESCRIPTIONS.keys())}") return desc, task = DEMO_DESCRIPTIONS[name] return one_sentence_to_agent(desc, task, model) # ============================================================ # Main # ============================================================ if __name__ == "__main__": import argparse parser = argparse.ArgumentParser(description="一句话搭建智能体") parser.add_argument("description", nargs="?", default=None, help="Agent 的自然语言描述") parser.add_argument("--task", "-t", default=None, help="要执行的任务") parser.add_argument("--interactive", "-i", action="store_true", help="交互模式") parser.add_argument("--demo", "-d", default=None, choices=list(DEMO_DESCRIPTIONS.keys()), help="运行预置 Demo") parser.add_argument("--model", "-m", default="qwen3-max-2026-01-23", help="用于生成配置的模型") parser.add_argument("--generate-only", "-g", action="store_true", help="只生成 YAML,不执行") args = parser.parse_args() if args.interactive: interactive_mode(args.model) elif args.demo: run_demo(args.demo, args.model) elif args.description: task = args.task if not args.generate_only else None one_sentence_to_agent(args.description, task, args.model) else: parser.print_help() print("\n示例:") print(' python nl2agent.py "搭一个能搜索和分析的调研助手" -t "调研大模型最新进展"') print(' python nl2agent.py --demo research') print(' python nl2agent.py --interactive') print(f'\n预置 Demo: {list(DEMO_DESCRIPTIONS.keys())}')