| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465 |
- """
- ToolSearch — 工具懒加载元工具
- 借鉴 Claude Code 的 deferred tools 模式:
- 不在 system prompt 中注入全部工具描述,
- 而是提供一个 ToolSearch 元工具, LLM 需要时才查找具体工具。
- Lambda 语义:
- ToolSearch = λquery. filter(Γ_tools, query)
- 效果: system prompt 从 42 个工具描述缩减为 1 个元工具描述
- → token 节省 ~60%, 首次响应快 2-3x
- 用法:
- LLM 输出: {"action": "ToolSearch", "input": {"query": "文件操作"}}
- 返回: 匹配的工具列表 + 简要描述, LLM 在下一步选择具体工具
- """
- from __future__ import annotations
- import json
- from typing import Any, Dict, List, Optional
- from lambdagent.core import Term, Context
- # ════════════════════════════════════════════════════════════
- # 工具目录: 所有可用工具的元数据 (不含实际实现)
- # ════════════════════════════════════════════════════════════
- TOOL_CATALOG: Dict[str, Dict[str, Any]] = {
- # ── 文件操作 (5) ──
- "ReadFile": {
- "category": "file",
- "description": "读取文件内容",
- "input_schema": {"path": "str, 文件路径", "offset": "int, 起始行(可选)", "limit": "int, 行数(可选)"},
- "tags": ["文件", "读取", "查看", "file", "read"],
- },
- "EditFile": {
- "category": "file",
- "description": "编辑文件: 查找并替换指定文本片段",
- "input_schema": {"path": "str", "old_text": "str", "new_text": "str"},
- "tags": ["文件", "编辑", "修改", "替换", "file", "edit"],
- },
- "WriteFile": {
- "category": "file",
- "description": "写入/创建文件 (覆盖写入)",
- "input_schema": {"path": "str", "content": "str"},
- "tags": ["文件", "写入", "创建", "保存", "file", "write", "create"],
- },
- "ListFiles": {
- "category": "file",
- "description": "列出目录下的文件和子目录",
- "input_schema": {"path": "str, 目录路径", "pattern": "str, glob模式(可选)"},
- "tags": ["文件", "列表", "目录", "ls", "file", "list", "directory"],
- },
- "SearchContent": {
- "category": "file",
- "description": "在文件内容中搜索关键词 (grep)",
- "input_schema": {"pattern": "str, 正则表达式", "path": "str, 搜索目录", "glob": "str, 文件过滤(可选)"},
- "tags": ["搜索", "grep", "内容", "查找", "search", "find"],
- },
- # ── 代码分析 (3) ──
- "CodeSearch": {
- "category": "code",
- "description": "在代码库中搜索函数/类/变量定义",
- "input_schema": {"query": "str, 搜索词", "language": "str, 语言(可选)"},
- "tags": ["代码", "搜索", "函数", "类", "code", "search", "definition"],
- },
- "ProjectMap": {
- "category": "code",
- "description": "生成项目结构图和代码地图",
- "input_schema": {"path": "str, 项目根目录"},
- "tags": ["项目", "结构", "地图", "架构", "project", "structure", "map"],
- },
- "RunTests": {
- "category": "code",
- "description": "运行测试用例 (pytest/unittest/maven test)",
- "input_schema": {"path": "str, 测试文件或目录", "framework": "str, 框架(可选)"},
- "tags": ["测试", "运行", "pytest", "test", "run"],
- },
- # ── Shell (1) ──
- "Bash": {
- "category": "shell",
- "description": "执行 Shell 命令 (支持持久 CWD 和后台执行)",
- "input_schema": {"command": "str, 命令", "timeout": "int, 超时秒数(可选)", "background": "bool(可选)"},
- "tags": ["终端", "命令", "shell", "bash", "terminal", "命令行"],
- },
- # ── Git (5) ──
- "GitStatus": {
- "category": "git",
- "description": "查看 Git 工作区状态 (git status)",
- "input_schema": {"path": "str, 仓库路径(可选)"},
- "tags": ["git", "状态", "status"],
- },
- "GitDiff": {
- "category": "git",
- "description": "查看 Git 差异 (git diff)",
- "input_schema": {"path": "str(可选)", "staged": "bool(可选)"},
- "tags": ["git", "差异", "diff", "变更"],
- },
- "GitLog": {
- "category": "git",
- "description": "查看 Git 提交历史 (git log)",
- "input_schema": {"path": "str(可选)", "limit": "int(可选)"},
- "tags": ["git", "历史", "log", "提交"],
- },
- "GitCommit": {
- "category": "git",
- "description": "创建 Git 提交 (git add + commit)",
- "input_schema": {"message": "str", "files": "list[str](可选)"},
- "tags": ["git", "提交", "commit"],
- },
- "GitBranch": {
- "category": "git",
- "description": "Git 分支操作 (create/switch/list/delete)",
- "input_schema": {"action": "str, create|switch|list|delete", "name": "str(可选)"},
- "tags": ["git", "分支", "branch"],
- },
- # ── Web (2) ──
- "WebSearch": {
- "category": "web",
- "description": "搜索引擎查询 (返回摘要和链接)",
- "input_schema": {"query": "str, 搜索词"},
- "tags": ["网络", "搜索", "web", "search", "google", "查询"],
- },
- "WebFetch": {
- "category": "web",
- "description": "获取网页内容 (HTML → Markdown)",
- "input_schema": {"url": "str, 网址"},
- "tags": ["网络", "网页", "抓取", "web", "fetch", "url"],
- },
- # ── Notebook & 文档 (4) ──
- "NotebookEdit": {
- "category": "doc",
- "description": "编辑 Jupyter Notebook 单元格",
- "input_schema": {"path": "str", "cell_index": "int", "content": "str", "cell_type": "str(可选)"},
- "tags": ["notebook", "jupyter", "编辑", "cell"],
- },
- "DocGen": {
- "category": "doc",
- "description": "文档生成 (Markdown → HTML/PDF/Word)",
- "input_schema": {"content": "str", "format": "str, html|pdf|docx", "output_path": "str"},
- "tags": ["文档", "生成", "导出", "doc", "pdf", "word"],
- },
- "ChunkSplit": {
- "category": "doc",
- "description": "文本分块 (用于 RAG 或长文处理)",
- "input_schema": {"text": "str", "chunk_size": "int(可选)", "overlap": "int(可选)"},
- "tags": ["分块", "切分", "chunk", "split", "RAG"],
- },
- "OCR": {
- "category": "doc",
- "description": "图片文字识别 (OCR)",
- "input_schema": {"image_path": "str"},
- "tags": ["OCR", "图片", "文字", "识别", "image"],
- },
- # ── 知识库 (4) ──
- "KBCreate": {
- "category": "knowledge",
- "description": "创建知识库",
- "input_schema": {"name": "str", "description": "str(可选)"},
- "tags": ["知识库", "创建", "knowledge", "create", "KB"],
- },
- "KBAdd": {
- "category": "knowledge",
- "description": "向知识库添加文档",
- "input_schema": {"kb_name": "str", "content": "str", "metadata": "dict(可选)"},
- "tags": ["知识库", "添加", "导入", "knowledge", "add"],
- },
- "KBSearch": {
- "category": "knowledge",
- "description": "在知识库中检索",
- "input_schema": {"kb_name": "str", "query": "str", "top_k": "int(可选)"},
- "tags": ["知识库", "检索", "搜索", "knowledge", "search", "query"],
- },
- "KBList": {
- "category": "knowledge",
- "description": "列出所有知识库",
- "input_schema": {},
- "tags": ["知识库", "列表", "knowledge", "list"],
- },
- # ── 任务/记忆/调度 (10) ──
- "TaskCreate": {
- "category": "task",
- "description": "创建任务",
- "input_schema": {"title": "str", "description": "str(可选)", "priority": "str(可选)"},
- "tags": ["任务", "创建", "todo", "task", "create"],
- },
- "TaskUpdate": {
- "category": "task",
- "description": "更新任务状态",
- "input_schema": {"task_id": "str", "status": "str", "note": "str(可选)"},
- "tags": ["任务", "更新", "task", "update"],
- },
- "TaskList": {
- "category": "task",
- "description": "列出任务",
- "input_schema": {"status": "str(可选)", "limit": "int(可选)"},
- "tags": ["任务", "列表", "task", "list"],
- },
- "MemoryStore": {
- "category": "memory",
- "description": "保存记忆 (持久化存储)",
- "input_schema": {"key": "str", "value": "str", "tags": "list[str](可选)"},
- "tags": ["记忆", "保存", "存储", "memory", "store", "save"],
- },
- "MemoryRecall": {
- "category": "memory",
- "description": "回忆 (按 key 或语义检索)",
- "input_schema": {"query": "str", "limit": "int(可选)"},
- "tags": ["记忆", "回忆", "检索", "memory", "recall", "retrieve"],
- },
- "MemoryList": {
- "category": "memory",
- "description": "列出所有记忆",
- "input_schema": {},
- "tags": ["记忆", "列表", "memory", "list"],
- },
- "MemoryForget": {
- "category": "memory",
- "description": "删除记忆",
- "input_schema": {"key": "str"},
- "tags": ["记忆", "删除", "遗忘", "memory", "forget", "delete"],
- },
- "ScheduleCreate": {
- "category": "schedule",
- "description": "创建定时任务",
- "input_schema": {"cron": "str, cron表达式", "task": "str, 要执行的任务"},
- "tags": ["调度", "定时", "schedule", "cron", "定时任务"],
- },
- "ScheduleList": {
- "category": "schedule",
- "description": "列出定时任务",
- "input_schema": {},
- "tags": ["调度", "列表", "schedule", "list"],
- },
- "ScheduleDelete": {
- "category": "schedule",
- "description": "删除定时任务",
- "input_schema": {"schedule_id": "str"},
- "tags": ["调度", "删除", "schedule", "delete"],
- },
- # ── 系统控制 (4, macOS) ──
- "browser": {
- "category": "system",
- "description": "浏览器控制 (打开URL, 获取标签页, 执行JS)",
- "input_schema": {"action": "str, open|tabs|execute_js", "url": "str(可选)", "script": "str(可选)"},
- "tags": ["浏览器", "browser", "网页", "URL", "系统"],
- },
- "app": {
- "category": "system",
- "description": "应用控制 (启动/切换/关闭 macOS 应用)",
- "input_schema": {"action": "str, launch|switch|quit|list", "name": "str(可选)"},
- "tags": ["应用", "app", "启动", "切换", "系统"],
- },
- "system": {
- "category": "system",
- "description": "系统信息查询 (CPU/内存/磁盘/网络)",
- "input_schema": {"query": "str, cpu|memory|disk|network|all"},
- "tags": ["系统", "信息", "system", "info", "CPU", "内存"],
- },
- "screenshot": {
- "category": "system",
- "description": "屏幕截图",
- "input_schema": {"region": "str, full|window|area(可选)", "save_path": "str(可选)"},
- "tags": ["截图", "screenshot", "屏幕", "系统"],
- },
- # ── 画像 & 学习 (4) ──
- "ProfileGet": {
- "category": "profile",
- "description": "获取用户画像",
- "input_schema": {},
- "tags": ["画像", "profile", "用户", "偏好"],
- },
- "ProfileUpdate": {
- "category": "profile",
- "description": "更新用户画像",
- "input_schema": {"key": "str", "value": "str"},
- "tags": ["画像", "更新", "profile", "update"],
- },
- "LearningFeedback": {
- "category": "profile",
- "description": "提交学习反馈 (帮助agent自我改进)",
- "input_schema": {"feedback": "str", "rating": "int(可选)"},
- "tags": ["学习", "反馈", "learning", "feedback"],
- },
- "LearningStrategies": {
- "category": "profile",
- "description": "查看当前学习策略",
- "input_schema": {},
- "tags": ["学习", "策略", "learning", "strategies"],
- },
- # ── 通知 & 事件 (3) ──
- "Notify": {
- "category": "notify",
- "description": "发送通知 (终端/macOS 通知中心)",
- "input_schema": {"message": "str", "title": "str(可选)", "sound": "bool(可选)"},
- "tags": ["通知", "notify", "提醒", "alert"],
- },
- "EventSubscribe": {
- "category": "notify",
- "description": "订阅事件 (文件变化/定时/webhook)",
- "input_schema": {"event_type": "str", "config": "dict"},
- "tags": ["事件", "订阅", "event", "subscribe", "watch"],
- },
- "EventList": {
- "category": "notify",
- "description": "列出已订阅的事件",
- "input_schema": {},
- "tags": ["事件", "列表", "event", "list"],
- },
- }
- # 按类别索引
- CATEGORIES = {}
- for tool_name, meta in TOOL_CATALOG.items():
- cat = meta["category"]
- if cat not in CATEGORIES:
- CATEGORIES[cat] = []
- CATEGORIES[cat].append(tool_name)
- # ════════════════════════════════════════════════════════════
- # ToolSearch: 懒加载元工具
- # ════════════════════════════════════════════════════════════
- class ToolSearch(Term):
- """
- 工具懒加载元工具。
- Lambda 语义:
- ToolSearch = λquery. filter(TOOL_CATALOG, query)
- 不执行任何工具, 只返回匹配的工具描述,
- 让 LLM 在下一步选择具体工具。
- 搜索策略:
- 1. 按类别名精确匹配 (如 "file", "git", "web")
- 2. 按工具名精确匹配 (如 "ReadFile", "Bash")
- 3. 按 tags 模糊匹配 (如 "搜索", "编辑")
- 4. 按 description 模糊匹配
- """
- def __init__(self):
- super().__init__("ToolSearch")
- def apply(self, input: Any, ctx: Context | None = None) -> str:
- ctx = ctx or Context()
- # 解析输入
- if isinstance(input, str):
- try:
- data = json.loads(input)
- query = data.get("query", data.get("keyword", input))
- category = data.get("category", "")
- except (json.JSONDecodeError, AttributeError):
- query = input.strip()
- category = ""
- elif isinstance(input, dict):
- query = input.get("query", input.get("keyword", ""))
- category = input.get("category", "")
- else:
- query = str(input)
- category = ""
- results = self.search(query, category)
- if not results:
- return json.dumps({
- "matched": 0,
- "hint": f"没有找到匹配 '{query}' 的工具。可用类别: {', '.join(sorted(CATEGORIES.keys()))}",
- "categories": {cat: tools for cat, tools in sorted(CATEGORIES.items())},
- }, ensure_ascii=False, indent=2)
- return json.dumps({
- "matched": len(results),
- "tools": results,
- "hint": "请从以上工具中选择一个, 用 JSON 代码块调用它。",
- }, ensure_ascii=False, indent=2)
- def search(self, query: str, category: str = "") -> List[dict]:
- """搜索工具目录"""
- results = []
- query_lower = query.lower()
- query_words = query_lower.split()
- for tool_name, meta in TOOL_CATALOG.items():
- score = 0
- # 按类别精确匹配
- if category and meta["category"] == category:
- score += 10
- # 按工具名匹配
- if query_lower in tool_name.lower():
- score += 8
- # 按类别名匹配
- if query_lower == meta["category"]:
- score += 10
- # 按 tags 匹配
- tags_text = " ".join(meta["tags"]).lower()
- for word in query_words:
- if word in tags_text:
- score += 3
- # 按 description 匹配
- desc_lower = meta["description"].lower()
- for word in query_words:
- if word in desc_lower:
- score += 2
- if score > 0:
- results.append({
- "name": tool_name,
- "category": meta["category"],
- "description": meta["description"],
- "input_schema": meta["input_schema"],
- "_score": score,
- })
- # 按 score 排序, 返回前 10 个
- results.sort(key=lambda x: -x["_score"])
- for r in results:
- del r["_score"]
- return results[:10]
- @staticmethod
- def list_categories() -> dict:
- """列出所有工具类别"""
- return {
- cat: {
- "count": len(tools),
- "tools": tools,
- }
- for cat, tools in sorted(CATEGORIES.items())
- }
- @staticmethod
- def get_tool_description() -> str:
- """返回 ToolSearch 自身的工具描述 (注入 system prompt)"""
- category_summary = ", ".join(
- f"{cat}({len(tools)})" for cat, tools in sorted(CATEGORIES.items())
- )
- return (
- f"ToolSearch: 搜索可用工具。共 {len(TOOL_CATALOG)} 个工具, "
- f"分 {len(CATEGORIES)} 类: {category_summary}。\n"
- f"输入: {{\"query\": \"关键词\"}} 或 {{\"category\": \"类别名\"}}\n"
- f"返回: 匹配的工具列表 + 调用格式。\n"
- f"你应该先用 ToolSearch 找到需要的工具, 再调用具体工具。"
- )
- # 单例
- tool_search = ToolSearch()
|