| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- """
- 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())}')
|