我的第一个工具调用Agent:从零构建“知识库+计算器+时间+文件”四合一智能体
从RAG到Agent我用一个周末实现了AI从知道到做到的跃迁一、引言为什么从RAG升级到Agent作为制造业数字化转型从业者2026年上半年我完成了几个本地化AI应用实践——环境搭建、多模态图搜、RAG知识库问答甚至还把橘子洲头做成了AI客服。这些应用让AI知道了很多事但总感觉缺了点什么AI只能被动回答问题不能主动调用工具去解决问题。直到我开始接触AI Agent才真正理解了这个核心差异RAG检索增强生成AI 大脑模型 资料库知识库能知道并回答Agent智能体AI 大脑模型 资料库知识库 手脚工具能做到并完成本案例的目标是构建一个具备知识库检索、数学计算、时间查询、文件读写四项能力的Agent通过实践完整理解Agent的核心机制。这对于我们后续在制造企业落地数据质量巡检、主数据处理、数仓分层设计等场景具有直接的参考价值。运行效果图二、技术方案与选型我的开发环境比较特殊可能也代表了一部分开发者的现状Windows 11 WSL2Ubuntu 22.04 32GB内存 无独显大模型通过Ollama安装在Windows侧提供API服务代码在WSL中运行。组件选型说明大模型Qwen2.5:7BOllama中文友好7B参数量在CPU上可运行Agent框架LangChain 0.3.x社区成熟ReAct模式原生支持向量数据库Chroma轻量级与之前RAG案例无缝衔接嵌入模型all-MiniLM-L6-v2384维CPU友好中文效果尚可Web框架Flask轻量级快速构建交互界面工程化原则贯穿整个专栏配置与代码分离config.py工具函数独立封装tools/模块完整的错误处理与日志记录单元测试覆盖每个工具函数项目结构清晰可一键运行三、项目结构工程化的起点case1_tool_agent/ ├── config.py # 配置集中管理路径、模型、端口 ├── requirements.txt # 依赖锁定版本 ├── .env.example # 环境变量模板 ├── tools/ # 工具模块可复用 │ ├── __init__.py │ ├── calculator.py # 计算器工具 │ ├── time_tool.py # 时间工具 │ ├── file_tool.py # 文件读写工具 │ └── rag_retriever.py # 知识库检索工具 ├── agent/ # Agent核心 │ ├── __init__.py │ └── builder.py # Agent构建工厂 ├── web/ # Web服务 │ ├── app.py # Flask主应用 │ └── templates/ │ └── index.html # 极简Chat界面 ├── scripts/ # 辅助脚本 │ ├── test_tools.py # 工具单元测试 │ └── test_cli.py # 命令行交互测试 ├── chroma_db/ # 知识库复用已有 │ └── chroma.sqlite3 └── workspace/ # 文件工具工作目录工程心得我一直信奉好的项目结构是成功的一半。清晰的模块划分让工具可以在后续案例中直接复用配置集中管理避免硬编码单元测试保障每次修改不引入新Bug。四、核心实现四个工具的定义与编码4.1 计算器工具tools/calculator.py# -*- coding: utf-8 -*- # author: javy21 # blog: https://blog.csdn.net/javy21 计算器工具执行数学表达式计算 import logging from langchain.tools import tool logger logging.getLogger(__name__) tool def calculate(expression: str) - str: 执行数学计算输入数学表达式返回计算结果。 Args: expression: 数学表达式如 35 或 10*2/3 Returns: 计算结果字符串或错误信息 # 安全检查只允许数字、运算符、括号、空格 allowed_chars set(0123456789-*/().% ) if not all(c in allowed_chars for c in expression): error_msg f表达式包含非法字符{expression} logger.warning(error_msg) return f计算错误{error_msg} try: result eval(expression) logger.info(f计算成功: {expression} {result}) return f计算结果{result} except ZeroDivisionError: return 计算错误除数不能为零 except Exception as e: return f计算错误表达式格式错误 - {str(e)}设计要点使用tool装饰器将函数转换为LangChain工具输入校验防止代码注入生产环境建议用numexpr完整的异常处理返回友好信息而非堆栈4.2 时间工具tools/time_tool.py# -*- coding: utf-8 -*- # author: javy21 # blog: https://blog.csdn.net/javy21 时间工具获取当前日期和时间 import logging from datetime import datetime from langchain.tools import tool logger logging.getLogger(__name__) tool def get_current_time(format: str %Y-%m-%d %H:%M:%S) - str: 获取当前日期和时间可指定输出格式。 Args: format: 时间格式字符串默认 %Y-%m-%d %H:%M:%S Returns: 格式化后的当前时间字符串 now datetime.now() result now.strftime(format) logger.info(f获取当前时间: {result}) return result4.3 文件读写工具tools/file_tool.py# -*- coding: utf-8 -*- # author: javy21 # blog: https://blog.csdn.net/javy21 文件读写工具在workspace目录下读写文件防路径穿越 import logging from pathlib import Path from langchain.tools import tool from config import WORKSPACE_DIR logger logging.getLogger(__name__) tool def read_file(filename: str) - str: 读取指定文件的内容。 # 安全检查防止路径穿越攻击 if .. in filename or filename.startswith(/): return 读取失败非法文件名 filepath WORKSPACE_DIR / filename if not filepath.exists(): return f读取失败文件 {filename} 不存在 try: content filepath.read_text(encodingutf-8) logger.info(f读取文件成功: {filename}) return f文件内容\n{content} except Exception as e: return f读取失败{str(e)} tool def write_file(filename: str, content: str) - str: 将内容写入指定文件。 if .. in filename or filename.startswith(/): return 写入失败非法文件名 filepath WORKSPACE_DIR / filename try: filepath.write_text(content, encodingutf-8) logger.info(f写入文件成功: {filename}) return f成功写入文件{filename} except Exception as e: return f写入失败{str(e)}⚠️安全提醒文件工具是企业场景中最容易出安全问题的环节一定要做好路径穿越防护。4.4 知识库检索工具tools/rag_retriever.py# -*- coding: utf-8 -*- # author: javy21 # blog: https://blog.csdn.net/javy21 知识库检索工具基于Chroma向量库的语义检索 import logging from langchain.tools import tool from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from config import CHROMA_DIR, EMBEDDING_MODEL, RETRIEVAL_TOP_K logger logging.getLogger(__name__) _vectordb None def _get_vectordb(): 懒加载向量数据库单例模式 global _vectordb if _vectordb is None: logger.info(f正在加载向量数据库: {CHROMA_DIR}) embeddings HuggingFaceEmbeddings(model_nameEMBEDDING_MODEL) _vectordb Chroma( persist_directorystr(CHROMA_DIR), embedding_functionembeddings, ) logger.info(向量数据库加载完成) return _vectordb tool def search_knowledge(query: str) - str: 从知识库中检索与查询相关的文档片段。 try: vectordb _get_vectordb() docs vectordb.similarity_search(query, kRETRIEVAL_TOP_K) if not docs: return 未找到相关信息。 contents [doc.page_content for doc in docs] result \n\n---\n\n.join(contents) logger.info(f检索成功: {query} - {len(docs)} 个片段) return f检索结果\n{result} except Exception as e: return f检索失败{str(e)}五、Agent构建让AI学会使用工具Agent构建是本案例的核心环节代码位于agent/builder.py# -*- coding: utf-8 -*- # author: javy21 # blog: https://blog.csdn.net/javy21 Agent构建器创建和配置LangChain Agent import logging from langchain_community.llms import Ollama from langchain.agents import initialize_agent, AgentType from langchain.memory import ConversationBufferMemory from config import OLLAMA_BASE_URL, MODEL_NAME, TEMPERATURE from tools import calculate, get_current_time, read_file, write_file, search_knowledge logger logging.getLogger(__name__) def create_agent(verbose: bool True): 创建并返回一个配置好的Agent实例 # 1. 初始化大模型 logger.info(f初始化模型: {MODEL_NAME} {OLLAMA_BASE_URL}) llm Ollama( modelMODEL_NAME, base_urlOLLAMA_BASE_URL, temperatureTEMPERATURE, ) # 2. 注册工具顺序影响决策优先级 tools [ search_knowledge, # 知识库检索 calculate, # 计算器 get_current_time, # 时间查询 read_file, # 文件读 write_file, # 文件写 ] # 3. 设置记忆对话缓冲 memory ConversationBufferMemory( memory_keychat_history, return_messagesTrue, ) # 4. 创建Agent使用支持多参数工具的Agent类型 agent initialize_agent( tools, llm, agentAgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION, memorymemory, verboseverbose, handle_parsing_errorsTrue, max_iterations10, early_stopping_methodgenerate, ) logger.info(Agent创建成功) return agent关键决策我选择了STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION而非CONVERSATIONAL_REACT_DESCRIPTION因为前者支持多参数工具如write_file需要 filename content 两个参数后者只支持单参数工具。六、Web交互界面极简Chat风格Flask应用提供简洁的聊天界面用户在浏览器中输入问题Agent自动决策并调用相应工具核心路由逻辑web/app.pyapp.route(/api/chat, methods[POST]) def chat(): data request.get_json() user_input data.get(message, ).strip() if not user_input: return jsonify({error: 消息不能为空}), 400 try: response agent.run(user_input) return jsonify({response: response}) except Exception as e: logger.error(f处理失败{str(e)}) return jsonify({error: f处理失败{str(e)}}), 500七、运行效果展示7.1 单元测试通过$ python scripts/test_tools.py 开始工具单元测试 测试计算器... ✅ 计算器测试通过 测试时间工具... ✅ 当前时间: 2026-07-05 22:32:26 测试文件工具... ✅ 文件工具测试通过 测试知识库检索... ✅ 检索结果长度: 1024 字符 ✅ 所有测试通过 7.2 Web交互实测用户输入Agent行为结果现在几点了调用时间工具返回当前时间帮我算一下 123 * 456调用计算器返回 56088十五五规划中关于人工智能的内容调用知识库检索返回相关文档片段把 Hello Agent 写入 test.txt调用文件写工具返回成功信息读取 test.txt 的内容调用文件读工具返回文件内容明天上午10点我要开会帮我算一下还有多少秒组合调用时间计算器计算差值并返回八、踩坑记录与解决方案坑1ModuleNotFoundError: No module named tools.calculator现象运行测试脚本时Python找不到自定义模块。原因直接运行python scripts/test_tools.py时项目根目录不在sys.path中。解决在每个脚本开头添加路径处理python复制下载import sys from pathlib import Path sys.path.insert(0, str(Path(__file__).parent.parent))坑2向量库为空检索无结果现象search_knowledge总是返回未找到相关信息。原因从之前项目复制的chroma_db只包含空表没有实际向量数据。解决重新构建知识库用PDF文档填充向量数据python复制下载vectordb Chroma.from_documents( documentschunks, embeddingembeddings, persist_directorystr(CHROMA_DIR) )坑3ConversationalAgent does not support multi-input tool现象启动Web服务时报错提示write_file是多参数工具不被支持。原因ConversationalAgent要求所有工具都是单输入只有一个参数。解决更换为AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION该类型支持多参数工具。坑4Ollama连接失败Connection refused现象Agent调用大模型时返回HTTPConnectionPool(hostlocalhost, port11434)... Connection refused。原因我的Ollama安装在Windows上而代码在WSL中运行WSL中的localhost指向WSL自身而非Windows宿主机。解决在Windows上配置Ollama监听所有接口set OLLAMA_HOST0.0.0.0:11434 ollama serve修改config.py中的OLLAMA_BASE_URL为Windows的IP地址九、工程化改进方向让Demo走向生产改进方向实现建议优先级工具描述优化精炼每个工具的description提高模型调用准确率P0日志与监控集成结构化日志如JSON格式便于采集分析P0超时与重试对Ollama调用增加超时和重试机制P1环境变量管理使用.envpython-dotenv管理配置P1容器化部署编写Dockerfile实现一键部署P2十、小结与下篇预告通过本案例我们完整实现了从0到1构建一个工具调用Agent的全过程涵盖了工具定义、Agent构建、Web交互、单元测试、工程化组织等多个维度。在这个过程中我深刻体会到Agent的核心不是调用工具而是决定何时调用什么工具。ReAct模式让AI具备了自主决策的能力这是与传统自动化脚本的本质区别。下篇预告下一篇理论文章《从RAG到AgentAI应用范式的演进——兼谈工具调用在制造业数据管理的落地价值》我将从理论角度深入剖析RAG与Agent的本质区别与演进逻辑ReAct模式的深层理解工具调用的关键设计原则Agent在制造业数据管理数据质量、数仓分层、数据标准中的落地场景敬请期待欢迎在评论区交流您的实践心得或遇到的问题一起探讨、共同成长。作者javy21博客javy21-CSDN博客专栏AI Agent从入门到落地本文案例代码可在评论区获取完整项目源码如果您觉得本文有帮助欢迎交流分享您的支持是我持续输出的动力。

相关新闻

3步掌握ChatPaper:让AI成为你的科研加速器

3步掌握ChatPaper:让AI成为你的科研加速器

3步掌握ChatPaper:让AI成为你的科研加速器 【免费下载链接】ChatPaper Use ChatGPT to summarize the arXiv papers. 全流程加速科研,利用chatgpt进行论文全文总结专业翻译润色审稿审稿回复 项目地址: https://gitcode.com/gh_mirrors/ch/ChatPaper …

2026/7/12 23:41:14 阅读更多 →
RapidOCR技术架构深度解析:跨平台多引擎OCR解决方案的设计哲学与实践

RapidOCR技术架构深度解析:跨平台多引擎OCR解决方案的设计哲学与实践

RapidOCR技术架构深度解析:跨平台多引擎OCR解决方案的设计哲学与实践 【免费下载链接】RapidOCR 📄 Awesome OCR multiple programing languages toolkits based on ONNX Runtime, OpenVINO, MNN, PaddlePaddle, TensorRT and PyTorch. 项目地址: http…

2026/7/12 23:39:13 阅读更多 →
定时任务“裂变”惨案:Spring Boot 分布式调度从互殴到握手言和

定时任务“裂变”惨案:Spring Boot 分布式调度从互殴到握手言和

定时任务“裂变”惨案:Spring Boot 分布式调度从互殴到握手言和 你用 Spring Boot 的 Scheduled 注解轻松写了个定时任务,单机跑得稳稳的。随着业务增长,应用多实例部署,噩梦随之降临:凌晨 3 点对账任务重复执行了 4 次…

2026/7/12 23:35:12 阅读更多 →

最新新闻

影刀RPA 医药行业自动化:药品信息采集与价格监控

影刀RPA 医药行业自动化:药品信息采集与价格监控

影刀RPA 医药行业自动化:药品信息采集与价格监控作者:林焱一、什么情况用影刀处理医药数据 医药行业的信息管理痛点很明显:国家医保局每季度更新医保目录,各省市的药品挂网价格频繁调整,药企的市场部门需要实时掌握竞品…

2026/7/13 0:43:31 阅读更多 →
Java 大厂面试高频题:别再只会 new 了!Java 创建对象竟然有这么多种方式

Java 大厂面试高频题:别再只会 new 了!Java 创建对象竟然有这么多种方式

在学习 Java 时,我们最先接触的对象创建方式通常是: Person person new Person();因此,很多初学者会认为 Java 创建对象只能使用 new。 实际上,Java 中常见的对象创建方式不止一种。除了直接使用 new,还可以通过反射、…

2026/7/13 0:35:28 阅读更多 →
GHelper完整指南:5步轻松掌控华硕笔记本性能,告别臃肿官方软件

GHelper完整指南:5步轻松掌控华硕笔记本性能,告别臃肿官方软件

GHelper完整指南:5步轻松掌控华硕笔记本性能,告别臃肿官方软件 【免费下载链接】g-helper Lightweight Armoury Crate alternative for Asus laptops with nearly the same functionality. Works with ROG Zephyrus, Flow, TUF, Strix, Scar, ProArt, Vi…

2026/7/13 0:33:28 阅读更多 →
【车间调度FJSP】基于全球邻域和爬山优化算法的模糊柔性车间调度问题研究(Matlab代码实现)

【车间调度FJSP】基于全球邻域和爬山优化算法的模糊柔性车间调度问题研究(Matlab代码实现)

👨‍🎓个人主页 💥💥💞💞欢迎来到本博客❤️❤️💥💥 🏆博主优势:🌞🌞🌞博客内容尽量做到思维缜密,逻辑清晰&a…

2026/7/13 0:33:28 阅读更多 →
后端开发中数据库索引优化与查询性能提升

后端开发中数据库索引优化与查询性能提升

实际上,很多后端开发者在面对慢查询时,第一反应就是把能加的索引全加上,仿佛索引是万能止痛药。但当你真正深入过线上系统的灵魂拷问——一个看似简单的SELECT语句,在百万级数据面前拖垮了整个微服务——你就会明白,索…

2026/7/13 0:27:25 阅读更多 →
单片机光学自动打靶机寻点流程分析

单片机光学自动打靶机寻点流程分析

光学自动打靶机寻点流程分析Y轴自动加减速寻点程序通过分析图寻点机制实现精确定位,支持负数存储(如454负数案例)及仿真取模运算。硬件层面明确定义引脚功能,软件端集成字库旋转技术以优化显示效果,配套图解代码提供直…

2026/7/13 0:25:25 阅读更多 →

日新闻

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改

Palworld存档编辑完全掌握:从零开始实现游戏数据可视化修改 【免费下载链接】palworld-save-tools Tools for converting Palworld .sav files to JSON and back 项目地址: https://gitcode.com/gh_mirrors/pa/palworld-save-tools 你是否曾经想要调整Palwor…

2026/7/13 0:01:19 阅读更多 →
浦东旧模块回收哪家强?专业评测带你一探究竟

浦东旧模块回收哪家强?专业评测带你一探究竟

于科技迅猛飞速迭代的当下此刻, 旧模块的回收处置, 不但关联着资源的再度利用, 而且更牵扯到数据安全以及环保合规事宜。你是不是也正为那堆积得如同山峦般的旧模块而发愁? 是不是不清楚该怎样安全且高效地去处理它们? 别忧心烦恼, 就在今日, 我会以具备权威影响力的自媒体博…

2026/7/13 0:01:19 阅读更多 →
卡梅德生物技术快报|重组蛋白的表达和纯化:IMAC 金属螯合色谱全流程工艺手册|基质 - 配基 - 金属离子匹配与蛋白质分离纯化参数优化

卡梅德生物技术快报|重组蛋白的表达和纯化:IMAC 金属螯合色谱全流程工艺手册|基质 - 配基 - 金属离子匹配与蛋白质分离纯化参数优化

1 研究背景与现存技术痛点(提出问题)基因工程、蛋白质组学、生物制药研发流程中,蛋白质分离纯化是决定下游实验成败的关键环节。当前实验室常规蛋白质分离纯化工艺存在三类难以标准化的技术瓶颈:传统离子交换、分子筛层析无特异性…

2026/7/13 0:05:20 阅读更多 →

周新闻

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨

互联网大厂 Java 求职面试:燕双非的搞笑回答与技术探讨 在一个阳光明媚的上午,互联网大厂的面试官坐在桌前,准备迎接他的面试候选人——燕双非,一个以搞笑和幽默著称的程序员。第一轮提问 面试官:燕双非,作…

2026/7/12 0:03:13 阅读更多 →
车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估

车载以太网PMA测试设备选型:示波器、VNA、信号源3类仪器关键参数与预算评估在智能驾驶和车联网技术快速发展的今天,车载以太网作为新一代车载网络的核心传输技术,其物理层性能直接决定了数据传输的可靠性和稳定性。1000BASE-T1作为当前主流的…

2026/7/12 0:03:14 阅读更多 →
VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战,5步完成Keil工程转换

VSCode EIDE 插件 2.0:APM32/STM32 项目迁移实战指南嵌入式开发领域正经历一场工具链的静默革命。当传统Keil用户首次打开VSCode的扩展市场搜索EIDE时,往往会惊讶于这个看似简单的插件竟能重构十余年的开发习惯。本文将揭示如何用五个精准步骤&#xff0…

2026/7/12 0:03:14 阅读更多 →

月新闻