Update tools.py

This commit is contained in:
2026-06-04 19:19:28 +00:00
parent 484d0a1038
commit 0e5024b658
+49 -83
View File
@@ -1,95 +1,61 @@
"""Tool implementations used by the Deep Agent. """
Custom tools for the deep agent.
The tools are simple wrappers around the virtual file system and a This module defines two tools:
websearch helper. They expose a ``name`` and ``description`` that are
used by the LangChain agent. * ``search`` performs a web search using DuckDuckGo and returns a short snippet of the first result.
* ``write_file`` writes content to the inmemory virtual file system.
The tools are decorated with ``@tool`` from LangChain so that they can be
automatically discovered by the agent.
""" """
from __future__ import annotations from langchain.tools import tool
import requests
from bs4 import BeautifulSoup
from .vfs import vfs
from typing import Dict, Any @tool
def search(query: str) -> str:
"""Search the web for *query* and return a short excerpt.
from langchain_core.tools import BaseTool The implementation uses DuckDuckGo's HTML interface. It fetches the
from langchain_ollama import OllamaEmbeddings first result link, downloads the page and extracts the first five
from langchain_community.tools.tavily_search import TavilySearchResults paragraphs.
from virtual_fs import VirtualFileSystem
# ---------------------------------------------------------------------------
# WebSearch tool
# ---------------------------------------------------------------------------
class WebSearch(BaseTool):
"""Search the web using Tavily.
The tool returns a short string containing the top results.
""" """
try:
# DuckDuckGo HTML search
url = f"https://duckduckgo.com/html/?q={query}"
resp = requests.get(url, timeout=10)
resp.raise_for_status()
soup = BeautifulSoup(resp.text, "html.parser")
name: str = "web_search" # Find first result link
description: str = "Search the web for information. Input should be a query." results = soup.find_all("a", {"class": "result__a"}, limit=1)
if not results:
return "No results found."
link = results[0].get("href")
if not link:
return "No link found."
def _run(self, query: str) -> str: # Fetch the page content
tavily = TavilySearchResults(max_results=3) content_resp = requests.get(link, timeout=10)
results = tavily.run(query) content_resp.raise_for_status()
return "\n".join(f"{i+1}. {r['title']} {r['url']}" for i, r in enumerate(results)) content_soup = BeautifulSoup(content_resp.text, "html.parser")
def _arun(self, query: str) -> str: # pragma: no cover # Extract text from first few paragraphs
return self._run(query) paragraphs = content_soup.find_all("p")
excerpt = "\n".join(p.get_text() for p in paragraphs[:5])
return excerpt.strip() or "No text extracted."
except Exception as e:
return f"Error during search: {str(e)}"
# --------------------------------------------------------------------------- @tool
# CreateVirtualFile tool def write_file(file_path: str, content: str) -> str:
# --------------------------------------------------------------------------- """Write *content* to a virtual file named *file_path*.
class CreateVirtualFile(BaseTool): The file is stored in the inmemory virtual file system and can be
"""Create or overwrite a virtual file. exported to disk later.
Input format: ``filename:content``. The tool splits on the first
colon and stores the content in the shared virtual file system.
""" """
vfs.write_file(file_path, content)
name: str = "create_virtual_file" return f"File {file_path} written."
description: str = (
"Create or overwrite a virtual file. Input format: 'filename:content'."
)
def __init__(self, vfs: VirtualFileSystem):
super().__init__()
self.vfs = vfs
def _run(self, input_text: str) -> str:
if ":" not in input_text:
raise ValueError("Input must be in the form 'filename:content'")
name, content = input_text.split(":", 1)
self.vfs.create_file(name.strip(), content.strip())
return f"File '{name.strip()}' created with {len(content.strip())} characters."
def _arun(self, input_text: str) -> str: # pragma: no cover
return self._run(input_text)
# ---------------------------------------------------------------------------
# ExportVirtualFiles tool
# ---------------------------------------------------------------------------
class ExportVirtualFiles(BaseTool):
"""Export all virtual files to the real filesystem.
Input can optionally specify a target directory. If omitted, the
current working directory is used.
"""
name: str = "export_virtual_files"
description: str = (
"Export all virtual files to disk. Input is an optional directory path."
)
def __init__(self, vfs: VirtualFileSystem):
super().__init__()
self.vfs = vfs
def _run(self, input_text: str | None = None) -> str:
target = input_text.strip() if input_text else "."
self.vfs.export_to_disk(target)
return f"Exported {len(self.vfs.files)} files to {target}"
def _arun(self, input_text: str | None = None) -> str: # pragma: no cover
return self._run(input_text)