从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从入门到落地本文案例代码可在评论区获取完整项目源码如果您觉得本文有帮助欢迎交流分享您的支持是我持续输出的动力。