LangChain实战:构建具备RAG与计算能力的AI Agent

1. 从零构建AI Agent:LangChain实战指南

最近在开发一个需要结合知识检索和数学计算的智能助手项目时,我深入研究了LangChain框架的Agent机制。与普通聊天机器人不同,Agent能够自主调用工具、进行多轮决策,真正实现"会思考"的AI系统。下面分享我的实战经验,手把手教你构建一个具备RAG知识库查询和精确计算能力的智能Agent。

2. AI Agent核心架构解析

2.1 Agent的四大核心组件

一个完整的AI Agent需要具备以下能力:

  • 大脑(LLM):使用通义千问(qwen-plus)作为推理引擎
  • 记忆系统
    • 短期记忆:维护对话历史(message数组)
    • 长期记忆:基于FAISS构建的RAG知识库
  • 规划能力:通过多轮工具调用实现复杂任务分解
  • 工具集:本例包含两个关键工具:
    • 公司知识检索(rag_search)
    • 数学计算器(calculator)

2.2 工具调用机制详解

LangChain的工具调用流程遵循以下范式:

  1. 用户输入查询(HumanMessage)
  2. LLM判断是否需要调用工具
  3. 若需调用,返回工具名称和参数(tool_calls)
  4. 执行具体工具函数
  5. 将结果封装为ToolMessage返回对话历史
  6. LLM基于工具结果生成最终回复

这个过程中最精妙的是第5步——通过ToolMessage将工具执行结果重新注入对话流,使得LLM能基于工具输出进行后续推理。

3. 实战开发步骤

3.1 环境准备与依赖安装

首先确保已安装必要库:

pip install langchain-core langchain-community faiss-cpu dashscope

注意:FAISS在不同平台可能有兼容性问题,Mac用户建议使用conda安装:

conda install -c conda-forge faiss-cpu

3.2 工具函数实现

3.2.1 知识检索工具
@tool def rag_search(query: str) -> str: """ 从RAG知识库检索公司内部信息,包括: - 项目计划(名称/代号) - 技术方案 - 预算信息 - 截止日期 示例查询: - "深蓝计划的预算是多少" - "项目截止日期是什么时候" """ # 初始化向量数据库(具体实现见下文) ...

关键点:

  • 使用@tool装饰器声明工具函数
  • 文档字符串必须详细说明功能、参数和返回示例
  • 返回类型必须为字符串
3.2.2 数学计算工具
@tool def calculator(expression: str) -> str: """ 执行精确数学计算,支持: - 四则运算(+ - * /) - 百分比计算 - 括号优先级 示例: - "2 + 3 * 5" → "17.0" - "500 * 0.8" → "400.0" """ # 安全计算实现见3.4节 ...

3.3 多轮对话引擎实现

核心循环逻辑:

def run_agent(query: str, max_turns=5): tool_maps = {"rag_search": rag_search, "calculator": calculator} llm = ChatTongyi(model_name="qwen-plus") tool_llm = llm.bind_tools(tools=list(tool_maps.values())) messages = [HumanMessage(content=query)] for turn in range(max_turns): # 获取LLM响应 response = tool_llm.invoke(messages) messages.append(response) if not response.tool_calls: return response.content # 处理工具调用 for tool_call in response.tool_calls: tool_name = tool_call["name"] if tool_name in tool_maps: tool_output = tool_maps[tool_name](**tool_call["args"]) messages.append( ToolMessage( content=tool_output, tool_call_id=tool_call["id"], name=tool_name ) ) return "超过最大对话轮数"

关键设计:通过max_turns参数防止无限循环,实测表明复杂任务通常3轮内可完成。

4. 安全增强方案

4.1 eval函数的安全隐患

原始计算器实现直接使用eval()存在严重风险:

# 危险实现! return str(eval(expression)) # 可能执行恶意代码

4.2 三种加固方案

方案1:输入过滤
import re def safe_eval(expr: str) -> str: if not re.match(r'^[\d+\-*/(). ]+$', expr): return "非法输入" return str(eval(expr))
方案2:使用ast.literal_eval
import ast def safe_eval(expr: str) -> str: try: node = ast.parse(expr, mode='eval') if not all(isinstance(n, (ast.Num, ast.BinOp, ast.UnaryOp)) for n in ast.walk(node)): raise ValueError return str(eval(expr)) except: return "计算错误"
方案3:自定义解析器(推荐)
from operator import add, sub, mul, truediv ops = {'+': add, '-': sub, '*': mul, '/': truediv} def safe_calculate(expr: str) -> str: try: tokens = expr.split() stack = [] for token in tokens: if token in ops: b, a = stack.pop(), stack.pop() stack.append(ops[token](a, b)) else: stack.append(float(token)) return str(stack[0]) except: return "计算错误"

实测表明方案3安全性最佳,虽然仅支持二元运算,但已满足大多数计算场景。

5. 性能优化技巧

5.1 向量数据库缓存

避免每次调用都重建FAISS索引:

RAG_PATH = "faiss_index" if os.path.exists(RAG_PATH): ragdb = FAISS.load_local(RAG_PATH, embeddings) else: ragdb = FAISS.from_documents(docs, embeddings) ragdb.save_local(RAG_PATH)

5.2 对话历史管理

过长的对话历史会影响性能,建议:

  1. 设置最大历史长度(如最近10条)
  2. 对历史消息进行摘要压缩
  3. 重要信息显式存入知识库

5.3 工具调用优化

通过bind_toolstool_choice参数可以控制工具调用行为:

# 强制使用特定工具 llm.bind_tools(..., tool_choice={"type": "function", "function": {"name": "calculator"}}) # 禁止工具调用 llm.bind_tools(..., tool_choice="none")

6. 典型问题排查

6.1 工具未被识别

现象:LLM不调用预期工具 排查步骤:

  1. 检查工具描述是否完整(参数、示例缺一不可)
  2. 验证工具是否正确绑定:
    print(tool_llm.tools) # 应显示已绑定的工具
  3. 测试直接工具调用是否正常

6.2 无限循环问题

现象:Agent持续调用同一工具 解决方案:

  1. 检查ToolMessage是否正确返回
  2. 限制最大对话轮数(如前文max_turns)
  3. 在工具描述中明确使用边界条件

6.3 中文处理异常

现象:中文查询结果不准确 优化方案:

  1. 调整文本分块策略:
    text_splitter = RecursiveCharacterTextSplitter( chunk_size=500, # 增大分块大小 chunk_overlap=100, separators=["\n\n", "\n", "。", "!", "?"] # 中文友好分隔符 )
  2. 使用支持中文的embedding模型:
    embeddings = DashScopeEmbeddings(model="text-embedding-v2")

7. 扩展应用场景

基于此框架可轻松扩展更多实用场景:

7.1 电商客服Agent

  • 新增工具:
    • 订单查询
    • 物流跟踪
    • 退换货政策检索

7.2 数据分析Agent

  • 集成:
    • SQL查询
    • 可视化生成
    • 统计计算

7.3 智能家居控制

  • 对接:
    • 设备状态查询
    • 情景模式切换
    • 能耗统计

我在实际项目中发现,当工具数量超过5个时,建议使用ToolExecutor进行统一调度管理,避免工具冲突。另外,为每个工具添加明确的元数据描述(如适用场景、输入输出示例)能显著提升调用准确率。