工具直接调用
在 Cody 里,「工具」既能交给模型在 run / stream 里按需自动调用,也能由你的代码通过 tool() 直接调用。前者适合对话式 Agent,后者特别适合自动化脚本、单测、CI 流水线,以及你需要完全掌控每一步副作用的场景。本篇假定你已按第 01 篇配置好环境变量(模型使用 qwen3.5-plus,不显式写在代码里)。
await client.tool(name, params) 调用任意内置工具;会在 run / stream 里用 include_tools / exclude_tools 限制工具范围;会选用 read_file 等便捷方法简化常见操作。
两种工具使用方式
模型自动调用:你发起 client.run("请分析项目结构"),Runner 把注册好的工具交给大模型,由模型决定何时调用 list_directory、grep 等。
你手动调用:result = await client.tool("read_file", {"path": "main.py"}),不经过模型推理,立即得到工具输出。工具名与参数与 core 中注册的 Python 函数一致(见下表)。
完整示例:tool() 串联内置工具
下面这段脚本可以单独运行:先读文件、再按正则搜索、列出目录、最后执行一条 shell。跑通之后,我们再拆解释。
# 依赖环境变量:CODY_MODEL=qwen3.5-plus 等(见第 01 篇) import asyncio from cody.sdk import AsyncCodyClient async def main() -> None: client = AsyncCodyClient(workdir=".") # 读取单个文件 r1 = await client.tool("read_file", {"path": "main.py"}) print("--- read_file ---", r1.result[:200], sep="\n") # 在目录中 grep(正则 + 可选文件名 glob) r2 = await client.tool( "grep", {"pattern": r"def main", "include": "*.py", "path": "."}, ) print("--- grep ---", r2.result[:500], sep="\n") # 列出目录 r3 = await client.tool("list_directory", {"path": "."}) print("--- list_directory ---", r3.result[:500], sep="\n") # 执行命令(注意权限与安全策略) r4 = await client.tool("exec_command", {"command": "ls -la"}) print("--- exec_command ---", r4.result[:500], sep="\n") if __name__ == "__main__": asyncio.run(main())
返回值:ToolResult 与 result
await client.tool(...) 返回 ToolResult。你关心的文本输出在 result.result,类型为 str(成功时为工具格式化后的字符串;失败时 SDK 会抛出 CodyToolError)。这与模型在对话里「看到」的工具输出是同一条路径,便于你对照调试。
同一套接口还能调用写入、编辑、glob、拉取网页等(参数名与 core 工具函数一致):
await client.tool("write_file", {"path": "out.py", "content": "print('hello')"}) await client.tool( "edit_file", {"path": "main.py", "old_text": "...", "new_text": "..."}, ) await client.tool("glob", {"pattern": "**/*.py"}) await client.tool("webfetch", {"url": "https://example.com"})
28+ 内置工具分类一览
主 Agent 默认注册 28 个核心工具(名称即 Python 函数名,与 tool() 的第一个参数一致)。若启用 MCP,还会额外挂上 mcp_list_tools、mcp_call(见 第 08 篇)。下表按用途归纳,便于查阅。
| 分类 | 工具名 | 说明 |
|---|---|---|
| 文件 I/O | read_file、write_file、edit_file、list_directory |
读/写/精确替换编辑/列目录,受工作目录与安全策略约束 |
| 搜索与补丁 | grep、glob、patch、search_files |
正则搜索、路径匹配、应用补丁、语义/文件名搜索 |
| 命令执行 | exec_command |
在受控环境中执行 shell 命令 |
| Web | webfetch、websearch |
抓取 URL、联网搜索(视配置而定) |
| LSP | lsp_diagnostics、lsp_definition、lsp_references、lsp_hover |
诊断、跳转定义、引用、悬停信息(需语言服务器) |
| 文件历史 | undo_file、redo_file、list_file_changes |
基于会话内历史的撤销/重做与变更列表 |
| 任务列表 | todo_write、todo_read |
读写 Agent 任务清单 |
| Skills | list_skills、read_skill |
发现与读取已安装的 Skill 文档 |
| 子代理 | spawn_agent、get_agent_status、kill_agent、resume_agent |
并行子任务、查状态、终止、恢复 |
| 记忆 | save_memory |
写入项目级记忆条目 |
| 用户交互 | question |
向用户发起澄清问题(人机协同流程) |
| MCP(可选) | mcp_list_tools、mcp_call |
连接外部 MCP 服务时注册;不在默认 28 之内 |
在 run / stream 里过滤工具
当你仍希望由 qwen3.5-plus 自主推理,但只想缩小工具面(例如禁止执行 shell),可以在每次 run 或 stream 传入:
include_tools:白名单,仅注册列表中的工具;exclude_tools:黑名单,排除列表中的工具。
include_tools 与 exclude_tools 不要同时使用。内部实现是「有 include 则只保留这些;否则若有 exclude 则去掉这些」。
# 只允许读文件 + 搜索 await client.run( "总结 src 下的 Python 入口", include_tools=["read_file", "grep", "list_directory"], ) # 禁止执行命令,其它工具保留 await client.run("只做静态分析,不要跑 shell", exclude_tools=["exec_command"]) # 流式同样支持 async for chunk in client.stream("你的任务说明", exclude_tools=["exec_command"]): pass # 处理 chunk
便捷方法
高频工具在 AsyncCodyClient 上有薄封装:返回值直接是 str,等价于 (await client.tool(...)).result,写起来更短。
| 便捷方法 | 典型用途 |
|---|---|
read_file(path) |
读取文件内容 |
write_file(path, content) |
覆盖写入 |
edit_file(path, old_text, new_text) |
精确替换一段文本 |
list_directory(path=".") |
列目录 |
glob(pattern) |
路径 glob |
grep(pattern, include="*") |
在工作区内按正则搜索(include 过滤扩展名) |
exec_command(command) |
执行命令并取输出 |
content = await client.read_file("main.py") await client.write_file("hello.py", "print('hello')") await client.edit_file("main.py", "old", "new") files = await client.glob("**/*.py") matches = await client.grep("pattern", include="*.py") output = await client.exec_command("ls -la") dirs = await client.list_directory(".")
LSP 工具与便捷方法
语言服务器协议相关调用在自动化里同样常用:例如 CI 里先拉诊断再决定是否失败。
diags = await client.lsp_diagnostics("main.py") defn = await client.lsp_definition("main.py", line=10, column=5) refs = await client.lsp_references("main.py", line=10, column=5) hover = await client.lsp_hover("main.py", line=10, column=5)
pip install python-lsp-server),具体命令与 Cody 配置见项目文档;未就绪时工具可能返回错误或空结果。
需要最底层控制时,仍可用 await client.tool("lsp_diagnostics", {"file_path": "main.py"})、await client.tool("lsp_definition", {"file_path": "main.py", "line": 10, "character": 5}) 等形式(参数名与 cody.core.tools.lsp 中函数一致;列号字段为 character)。
小结
你现在已经掌握两条线:手动 tool() 精确驱动内置能力,以及 自动 run/stream 配合 include_tools / exclude_tools 收紧模型可见的工具集。下一篇 第 05 篇 · 注册自定义工具 会讲如何把自有函数挂进同一套工具系统,让模型与你的业务 API 对话。