工具¶
工具(BaseTool)是 Robot 在执行阶段被 Drive 调度的最小执行单元。它封装一个可被 LLM 调用的能力 —— 从 git diff、网页抓取、向量召回到 IDE 集成皆是。本页讲基础封装范式(含 yield / return 混用);上下文相关的两条进阶能力另成独立章节。
上下文能力总览¶
工具有两条独立的「上下文」通道,按需选用、可叠加:
| 能力 | 解决什么问题 | 入口字段 | 详细文档 |
|---|---|---|---|
| 运行时上下文注入 | 工具想读取/修改 Chain 当前的 intermediate、neural、current_tool_call_id |
merge_context: bool + _run 收 **kwargs |
运行时上下文注入 |
| Prompt 段位注入 | 工具想向 LLM 的 prompt 流中加入一段背景信息(如长期记忆、当前环境快照) | prompt_position: tuple[...] + 覆写 _format_2_prompt() |
Prompt 段位注入 |
二者职责不重叠:前者是「Chain → Tool」的运行时数据流;后者是「Tool → LLM」的 prompt 装配。一个工具完全可以同时用两条路径,例如「_run 内 prefetch 命中结果 → _format_2_prompt 在下次 LLM 调用时把结果注入到 system prompt」。
基础工具¶
注意事项¶
在工具的当前支持中,我们可以采用迭代器/生成器的方式输出,例如使用yield语句。此外,我们的工具现在支持在一个函数中同时使用yield和return语句。不同于传统的生成器,您不再需要捕获StopIteration异常来获取return语句的值,可以直接在循环中处理所有结果。
但需要注意这种yield与return混用的模式权支持在同步工具中使用,异步工具中不支持。因为目前Python对于Async模式下的生成器,还有没有特别好的办法直接获取最终的return值。
但无论是同步还是异步工具,我们都建议您使用for循环来获取所有生成器的结果,而不是使用next函数。也建议在封装工具时返回数据全部使用yield语句,可以配合使用return结束迭代,但不要使用return返回数据。
示例:非Async生成器工具¶
def simple_generator():
yield "yielded value 1"
yield "yielded value 2"
return "returned value"
def test_simple_generator() -> None:
gen = simple_generator()
try:
while True:
print(next(gen))
except StopIteration as e:
print("Generator return value:", e.value)
# 输出:
# yielded value 1
# yielded value 2
# Generator return value: returned value
在使用TFRobot的BaseTool基类时,您可以更简单地通过for循环来获取所有生成器的结果。
示例:Git Diff 工具封装¶
import subprocess
from pathlib import Path
from typing import ClassVar, Iterator, Optional
import cachetools
from tfrobot.drive.tool.base import BaseTool
from tfrobot.schema.drive.tool.base import MessageCategory, MessageMeta, ToolReturn
from tfrobot.schema.meta.tf_field import TFField
class GitDiffTool(BaseTool):
"""
GitDiff Tool
此工具用于查看项目的Git Diff信息,并支持通过配置的excludes参数排除不需差异对比的文件。
"""
name: ClassVar[str] = "git_diff"
version: ClassVar[str] = "0.0.1"
description: ClassVar[str] = "查看项目的Git Diff信息,支持文件排除配置。"
cache: cachetools.TTLCache = cachetools.TTLCache(maxsize=100, ttl=300)
project_root: Path
excludes: list[str] = TFField(default_factory=list)
def _run(self, tool_params: Optional[dict] = None, **kwargs) -> Iterator[ToolReturn] | ToolReturn:
# 获取所有变化的文件列表
if not self.cache.get("all_files"):
cmd = ["git", "diff", "--name-only"] + (self.excludes if self.excludes else [])
changed_files_result = subprocess.run(cmd, stdout=subprocess.PIPE, text=True, cwd=str(self.project_root))
self.cache["all_files"] = changed_files_result.stdout.splitlines()
changed_files = self.cache["all_files"]
if not changed_files:
raise ValueError("当前没有文件变化")
# 逐个显示文件的变化
for file in changed_files:
diff_result = subprocess.run(
["git", "diff", file], stdout=subprocess.PIPE, text=True, cwd=str(self.project_root)
)
res = diff_result.stdout
yield ToolReturn(
origin=res,
msg_meta=MessageMeta(category=MessageCategory.Map, summary=f"{res[:30]}...已省略...{res[-30:]}"),
)
return ToolReturn(
origin="你已经查看完所有的变更文件,请根据目前的信息再次整合所有的变更,形成一个中英双语的Commit Message",
msg_meta=MessageMeta(category=MessageCategory.Map, summary=""),
)
async def _async_run(self, tool_params: Optional[dict] = None, **kwargs) -> Iterator[ToolReturn]:
return self._run(tool_params, **kwargs)
def test_git_diff_tool() -> None:
"""
测试封装的Git Diff Tool可以正常运行
"""
git_diff_tool = GitDiffTool(
project_root=Path("project_root"), excludes=[":(exclude)poetry.lock", ":(exclude)*.png", ":(exclude)*.svg"]
)
for r in git_diff_tool.run():
print(r.origin)
在以上封装中,您仅需使用for循环即可获取所有结果,包括return语句返回的最终ToolReturn对象。