第6课__本地工具调用(文件操作)

本地文件操作是 Agent 处理离线数据的核心能力 ------ 通过读取、写入、搜索本地文件,Agent 可以管理个人文档、整理笔记、分析数据等,无需依赖网络。今天我们将学习如何设计文件操作工具,实现对本地文本、Excel 等文件的基础操作,并确保工具调用的安全性。

一、本地文件操作的核心场景与工具设计

1. 核心应用场景

本地文件工具主要解决三类问题:

  • 读取文件:提取文档内容(如简历、合同、笔记)供 Agent 分析(例:"总结《项目计划书.docx》的核心目标");
  • 写入 / 修改文件:让 Agent 生成内容并保存(例:"把今天的会议纪要写入 meeting_notes.txt");
  • 搜索文件:在指定文件夹中按关键词查找相关文件(例:"找出含'2024 预算'的 Excel 文件")。

2. 工具功能拆分

为了让工具职责清晰、易于维护,我们将文件操作拆分为 3 个独立工具,分别对应不同功能:

工具名称 功能描述 核心参数
read_file 读取指定路径的文件内容(支持 txt、docx 等) file_path(文件路径,必填)
write_file 向指定文件写入内容(覆盖或追加) file_pathcontent(内容)、mode(模式:覆盖 / 追加,默认覆盖)
search_files 在文件夹中按关键词搜索文件 folder_path(文件夹路径)、keyword(关键词)、file_type(文件类型,如 txt、xlsx,可选)

二、文件操作工具的 Schema 设计

延续第四天的 API 设计规范,为每个工具定义 Schema,明确功能、参数和格式,让 LLM 能准确调用。

1. read_file工具 Schema

json

复制代码
{
  "name": "read_file",
  "description": "读取本地文件的内容,支持txt、docx、pdf(基础文本)、xlsx(表格数据)等格式",
  "parameters": {
    "type": "object",
    "properties": {
      "file_path": {
        "type": "string",
        "description": "文件的绝对路径或相对路径(如'C:/docs/report.txt'或'./notes/meeting.docx'),必须确保文件存在"
      }
    },
    "required": ["file_path"]
  }
}

2. write_file工具 Schema

json

复制代码
{
  "name": "write_file",
  "description": "向本地文件写入内容,若文件不存在则创建;若文件已存在,可选择覆盖或追加内容",
  "parameters": {
    "type": "object",
    "properties": {
      "file_path": {
        "type": "string",
        "description": "文件的绝对路径或相对路径(如'C:/output/result.txt')"
      },
      "content": {
        "type": "string",
        "description": "需要写入的内容(文本格式)"
      },
      "mode": {
        "type": "string",
        "enum": ["overwrite", "append"],
        "description": "写入模式:'overwrite'表示覆盖原有内容,'append'表示追加到文件末尾,默认'overwrite'"
      }
    },
    "required": ["file_path", "content"]
  }
}

3. search_files工具 Schema

json

复制代码
{
  "name": "search_files",
  "description": "在指定文件夹中搜索含关键词的文件,可按文件类型筛选",
  "parameters": {
    "type": "object",
    "properties": {
      "folder_path": {
        "type": "string",
        "description": "文件夹的绝对路径或相对路径(如'C:/docs'或'./data')"
      },
      "keyword": {
        "type": "string",
        "description": "搜索的关键词(如'项目计划''2024')"
      },
      "file_type": {
        "type": "string",
        "description": "文件类型筛选(如'txt''docx''xlsx',不填则搜索所有类型)"
      }
    },
    "required": ["folder_path", "keyword"]
  }
}

三、代码实现:文件操作工具的核心功能

1. 依赖库安装

处理不同格式文件需要特定库,先安装依赖:

复制代码
pip install python-docx  # 处理docx文件
pip install PyPDF2       # 处理pdf文件(基础文本提取)
pip install openpyxl     # 处理xlsx表格

2. 工具代码实现(file_tools.py

python

python 复制代码
import os
from pathlib import Path
import docx
from PyPDF2 import PdfReader
from openpyxl import load_workbook

# ------------------------------
# 1. 读取文件工具(read_file)
# ------------------------------
def read_file(file_path):
    """读取文件内容,根据后缀名处理不同格式"""
    # 检查文件是否存在
    if not os.path.exists(file_path):
        return {"error": f"文件不存在:{file_path}"}
    if not os.path.isfile(file_path):
        return {"error": f"{file_path}不是文件"}
    
    # 获取文件后缀(小写)
    file_ext = Path(file_path).suffix.lower()
    
    try:
        if file_ext == ".txt":
            # 读取txt文件
            with open(file_path, "r", encoding="utf-8") as f:
                content = f.read()
            return {"content": content, "file_type": "txt"}
        
        elif file_ext == ".docx":
            # 读取docx文件
            doc = docx.Document(file_path)
            content = "\n".join([para.text for para in doc.paragraphs])
            return {"content": content, "file_type": "docx"}
        
        elif file_ext == ".pdf":
            # 读取pdf文件(提取文本)
            reader = PdfReader(file_path)
            content = "\n".join([page.extract_text() or "" for page in reader.pages])
            return {"content": content, "file_type": "pdf"}
        
        elif file_ext == ".xlsx":
            # 读取xlsx表格(提取前5行数据作为示例)
            wb = load_workbook(file_path, read_only=True)
            sheet = wb.active
            data = []
            for row in sheet.iter_rows(min_row=1, max_row=5, values_only=True):  # 只读前5行
                data.append([str(cell) if cell is not None else "" for cell in row])
            wb.close()
            return {"content": f"表格前5行数据:{data}", "file_type": "xlsx"}
        
        else:
            return {"error": f"不支持的文件类型:{file_ext}"}
    
    except Exception as e:
        return {"error": f"读取失败:{str(e)}"}


# ------------------------------
# 2. 写入文件工具(write_file)
# ------------------------------
def write_file(file_path, content, mode="overwrite"):
    """写入内容到文件,支持覆盖或追加"""
    try:
        # 创建父文件夹(如果不存在)
        parent_dir = os.path.dirname(file_path)
        if parent_dir and not os.path.exists(parent_dir):
            os.makedirs(parent_dir, exist_ok=True)
        
        # 选择写入模式:覆盖(w)或追加(a)
        open_mode = "w" if mode == "overwrite" else "a"
        
        with open(file_path, open_mode, encoding="utf-8") as f:
            f.write(content + "\n")  # 自动换行
        
        return {"status": "成功", "message": f"已{mode}文件:{file_path}"}
    
    except Exception as e:
        return {"error": f"写入失败:{str(e)}"}


# ------------------------------
# 3. 搜索文件工具(search_files)
# ------------------------------
def search_files(folder_path, keyword, file_type=None):
    """在文件夹中搜索含关键词的文件"""
    if not os.path.exists(folder_path):
        return {"error": f"文件夹不存在:{folder_path}"}
    if not os.path.isdir(folder_path):
        return {"error": f"{folder_path}不是文件夹"}
    
    matched_files = []
    # 遍历文件夹下所有文件(包括子文件夹)
    for root, dirs, files in os.walk(folder_path):
        for file in files:
            # 筛选文件类型(如果指定)
            if file_type and not file.lower().endswith(f".{file_type.lower()}"):
                continue
            
            # 检查文件名是否含关键词(简单匹配)
            if keyword.lower() in file.lower():
                file_path = os.path.join(root, file)
                matched_files.append({
                    "file_name": file,
                    "file_path": file_path,
                    "reason": f"文件名包含关键词'{keyword}'"
                })
    
    return {
        "count": len(matched_files),
        "files": matched_files
    }


# 测试工具功能
if __name__ == "__main__":
    # 测试读取txt文件
    print("测试read_file(txt):")
    print(read_file("test.txt"))  # 确保当前目录有test.txt
    
    # 测试写入文件
    print("\n测试write_file:")
    print(write_file("output.txt", "这是测试内容", mode="overwrite"))
    
    # 测试搜索文件
    print("\n测试search_files:")
    print(search_files("./", "test", file_type="txt"))  # 搜索当前目录含test的txt文件

3. 代码关键解析

  • 格式兼容 :通过文件后缀判断类型,分别处理 txt(直接读)、docx(用python-docx)、pdf(用PyPDF2)、xlsx(用openpyxl提取表格数据);
  • 容错处理:检查文件 / 文件夹是否存在、是否为有效文件 / 目录,捕获读写异常(如权限不足),返回友好错误信息;
  • 写入安全:自动创建父文件夹(避免 "路径不存在" 错误),支持覆盖 / 追加模式,满足不同场景需求;
  • 搜索逻辑 :通过os.walk遍历文件夹,按文件名关键词和类型筛选,返回文件路径和匹配原因。

四、安全控制:限制 Agent 的文件操作范围

本地文件操作存在风险(如误删重要文件、访问敏感目录),必须添加安全限制:

  1. 白名单目录 :仅允许 Agent 操作指定文件夹(如./agent_files/),禁止访问系统目录(如C:/Windows//root/)。

    python

    python 复制代码
    # 安全检查函数(示例)
    def is_safe_path(file_path, allowed_dirs=["./agent_files"]):
        """检查文件路径是否在允许的目录内"""
        normalized_path = os.path.normpath(file_path)
        for allowed in allowed_dirs:
            allowed_path = os.path.normpath(allowed)
            if normalized_path.startswith(allowed_path):
                return True
        return False
    
    # 在工具中调用安全检查(如read_file)
    def read_file(file_path):
        if not is_safe_path(file_path):
            return {"error": f"禁止访问:{file_path},仅允许操作{allowed_dirs}"}
        # 后续读取逻辑...
  2. 禁止删除 / 修改系统文件:工具只保留 "读""写""搜索" 功能,不实现删除、移动文件的操作(如需删除,需单独设计严格权限);

  3. 权限提示:在工具描述中明确告知用户 "Agent 只能访问指定文件夹",避免误解。

五、与 LLM 集成:让 Agent 自动处理文件

结合 OpenAI API,让模型根据用户需求调用文件工具:

python

python 复制代码
import openai
import os
from file_tools import read_file, write_file, search_files
from dotenv import load_dotenv

load_dotenv()
openai.api_key = os.getenv("OPENAI_API_KEY")

def agent_file_operation(user_query):
    # 定义工具列表(包含3个文件工具)
    tools = [
        {"type": "function", "function": read_file_schema},  # 填入read_file的Schema
        {"type": "function", "function": write_file_schema}, # 填入write_file的Schema
        {"type": "function", "function": search_files_schema}# 填入search_files的Schema
    ]
    
    # 调用LLM,判断是否需要调用工具
    response = openai.ChatCompletion.create(
        model="gpt-3.5-turbo",
        messages=[{"role": "user", "content": user_query}],
        tools=tools,
        tool_choice="auto"
    )
    
    # 处理工具调用(类似第五天的联网搜索集成逻辑)
    response_message = response.choices[0].message
    if response_message.get("tool_calls"):
        # 解析调用参数,执行对应工具,返回结果给模型生成回答
        # (代码略,参考第五天的集成逻辑)
        pass
    else:
        return response_message["content"]

# 测试:让Agent总结本地文件
if __name__ == "__main__":
    print(agent_file_operation("总结./notes/project.txt的内容,并提取3个关键目标"))

六、实践任务

  1. ./agent_files文件夹中创建test.docx(写入一段项目描述),用read_file工具读取并打印内容;
  2. 调用write_file工具,将 "2024 年 Q1 工作重点:1. 完成产品迭代;2. 拓展 3 个客户" 写入./agent_files/plan.txt
  3. 实现is_safe_path函数,限制工具只能访问./agent_files,测试访问../secret.txt是否被禁止;
  4. 让 Agent 调用search_files工具,在./agent_files中搜索含 "计划" 的 txt 文件。

通过今天的学习,你已掌握本地文件工具的设计、实现和安全控制 ------ 这是 Agent 处理离线数据的基础,也是构建 "个人助理" 类应用的核心能力。明天我们将综合前几天的工具,实现多工具协同的 Agent 应用。

相关推荐
clorisqqq6 小时前
人工智能现代方法笔记 第1章 绪论(1/2)
人工智能·笔记
kisshuan123966 小时前
YOLO11-RepHGNetV2实现甘蔗田杂草与作物区域识别详解
人工智能·计算机视觉·目标跟踪
焦耳热科技前沿6 小时前
北京科技大学/理化所ACS Nano:混合价态Cu₂Sb金属间化合物实现高效尿素电合成
大数据·人工智能·自动化·能源·材料工程
C+-C资深大佬7 小时前
Creo 11.0 全功能解析:多体设计 + 仿真制造,机械设计效率翻倍下载安装
人工智能
IDC02_FEIYA7 小时前
SQL Server 2025数据库安装图文教程(附SQL Server2025数据库下载安装包)
数据库·windows
浔川python社7 小时前
【维护期间重要提醒】请勿使用浔川 AI 翻译 v6.0 翻译违规内容
人工智能
CS创新实验室7 小时前
AI 与编程
人工智能·编程·编程语言
Kevin Wang7277 小时前
欧拉系统服务部署注意事项
网络·windows
min1811234567 小时前
深度伪造内容的检测与溯源技术
大数据·网络·人工智能