文档中心 / SDK 教程 / 工具篇

工具直接调用

在 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_directorygrep 等。

你手动调用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())

返回值:ToolResultresult

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_toolsmcp_call(见 第 08 篇)。下表按用途归纳,便于查阅。

分类 工具名 说明
文件 I/O read_filewrite_fileedit_filelist_directory 读/写/精确替换编辑/列目录,受工作目录与安全策略约束
搜索与补丁 grepglobpatchsearch_files 正则搜索、路径匹配、应用补丁、语义/文件名搜索
命令执行 exec_command 在受控环境中执行 shell 命令
Web webfetchwebsearch 抓取 URL、联网搜索(视配置而定)
LSP lsp_diagnosticslsp_definitionlsp_referenceslsp_hover 诊断、跳转定义、引用、悬停信息(需语言服务器)
文件历史 undo_fileredo_filelist_file_changes 基于会话内历史的撤销/重做与变更列表
任务列表 todo_writetodo_read 读写 Agent 任务清单
Skills list_skillsread_skill 发现与读取已安装的 Skill 文档
子代理 spawn_agentget_agent_statuskill_agentresume_agent 并行子任务、查状态、终止、恢复
记忆 save_memory 写入项目级记忆条目
用户交互 question 向用户发起澄清问题(人机协同流程)
MCP(可选) mcp_list_toolsmcp_call 连接外部 MCP 服务时注册;不在默认 28 之内

run / stream 里过滤工具

当你仍希望由 qwen3.5-plus 自主推理,但只想缩小工具面(例如禁止执行 shell),可以在每次 runstream 传入:

互斥:include_toolsexclude_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)
上述接口依赖本机已安装并能在工作区启动的语言服务器。例如 Python 常见选择是 pylsppip 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 对话。

← 上一篇 流式输出全解