发布时间:2026/9/4 17:37:59
我的第一个工具调用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从入门到落地本文案例代码可在评论区获取完整项目源码如果您觉得本文有帮助欢迎交流分享您的支持是我持续输出的动力。

相关新闻

2026/9/4 0:20:52

从硬件限制到自由安装:Rufus如何重新定义Windows启动盘制作

从硬件限制到自由安装:Rufus如何重新定义Windows启动盘制作 【免费下载链接】rufus The Reliable USB Formatting Utility 项目地址: https://gitcode.com/GitHub_Trending/ru/rufus 当用户尝试在老旧的电脑上安装Windows 11时,经常会遇到一个令人…

2026/9/4 17:33:02

北京综合布线工程为什么要在装修前深化?甲方先确认这8项

综合布线和 Wi-Fi 一旦错过装修窗口,后续补线、开孔、拆吊顶和调整机柜的成本都会上升。甲方在选择北京综合布线工程服务团队时,不应只比较每个信息点的单价,还要确认对方能否把业务需求、装修条件、网络边界、无线覆盖和验收资料转化为可施工…

2026/9/4 17:33:02

重庆门面转让多久没咨询才该降价?先分清价格问题和表达问题

摘要:门店长时间没咨询,不应马上降价。先确认是否有有效曝光、资料是否完整、客户是否匹配,再判断价格是不是主要阻力。门店挂出去一段时间没有咨询,老板最容易想到的办法就是降转让费。“是不是价格高了?”这个问题当…

2026/9/4 17:33:02

Python函数包装:从装饰器到Wrapture,统一追踪与测试替换

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/9/4 17:28:01

毕业设计之基于SSM的仓储物流管理系统的设计与实现

题目:一、项目介绍本文首先实现了仓储物流管理的发展,随后依照传统的软件开发流程,最先为系统挑选适用的言语和软件开发平台,依据需求分析开展控制模块制做和数据库查询构造设计,随后依据系统整体功能模块的设计&#…

2026/9/3 18:28:26

vSound小提琴数字处理器实操指南:从接线到演出的完整配置

电小提琴或者原声小提琴插电演出,第一个绕不开的坎就是声音难听。原声琴的共鸣和空气感一旦进了拾音器,出来的往往是一坨干瘪、发尖、带着奇怪塑料味的信号。我当初第一次把琴接上乐队调音台,直接被主唱吐槽"你这声音像在锯钢丝"。…

2026/9/3 14:29:47

传感器接口IC如何攻克生物化学传感的微弱信号难题?

1. 从电极到比特流:为什么生物化学传感必须依赖专用接口IC 做生物化学传感的人都有过类似的经历:明明传感器本身性能很好,信号输出却一塌糊涂——噪声大、漂移明显、重复性差,怎么调都达不到预期。很多时候问题并不在传感器&#…

2026/9/3 14:30:35

STM32F411CEU6多通道ADC采集:扫描模式+DMA实现详解

1. 多通道 ADC 的用武之地把“Multichannel ADC”和“STM32F411CEU6”这两个关键字放在一起,其实就是嵌入式开发里最常遇到的一类需求:用一块不算贵的 MCU,同时采集多路模拟信号。STM32F411CEU6 是 48 引脚的 Cortex-M4F 主控,主频…

2026/9/4 0:00:58

STM32H743 SPI从机DMA双缓冲通信实战

简介:本资源是面向嵌入式开发工程师与STM32进阶学习者的SPI DMA双机通信从机端完整实现方案,聚焦STM32H743高性能Cortex-M7单片机在工业控制与高速数据交互场景下的从机通信开发痛点。压缩包含1355个文件,主体为599个C源码与321个头文件&…

2026/9/4 0:00:58

CPU开盖降温教程:20元成本让温度直降30度的原理与实践

最近很多朋友都在抱怨,自己的电脑一到夏天就变成"烤箱",玩游戏时CPU温度动不动就飙到90度以上,风扇噪音堪比直升机。更让人头疼的是,明明配置不错,却因为高温降频导致性能大打折扣。如果你也遇到了类似问题&…

2026/9/4 0:00:58

ArkTS 表单工程:场地预约页的三态场次 Grid 与校验

ArkTS 表单工程:场地预约页的三态场次 Grid 与校验 App 14「运动场地预约」场地 Tab(Func1Tab),是整 App 交互最丰富的页面——场地横向切换 三色图例 渐变预约预览卡 快捷模板 今日场次 Grid(可选/已选/已满三态&…

2026/9/3 20:43:36

USB Type-C PCB布局分区设计:电源、高速信号与PD协议全攻略

做硬件这行,Type-C接口算是典型的“看着简单,做起来全坑”的东西。光引脚就24个,高低速信号、电源、控制线全部塞在一个小小的连接器里,如果PCB布局不做规划,打样回来基本就是“插上没反应”、“高速掉线”、“静电一打…

2026/9/3 17:51:43

系统编程学习原型如何补齐稳定性边界

系统编程学习原型如何补齐稳定性边界预算有限时&#xff0c;我先优化明显多余的复制&#xff0c;而不是猜测性地换容器。用借用传递只读数据通常就能减少分配&#xff1a; fn parse(line: &str) -> Result<Item, Error> { /* ... */ }用基准确认热点确实在分配&am…

2026/9/3 21:06:57

雨花区哪家财务公司代理记账比较好?

在雨花区&#xff0c;企业处理财税事务常常面临诸多挑战&#xff0c;选择一家靠谱的财务公司至关重要。湖南巨勤财务管理咨询有限公司就是本地正规实体财税服务机构&#xff0c;深耕本地工商财税行业多年&#xff0c;熟悉当地工商局、税务局最新政策与申报流程。主营公司注册、…