1. AI Agent开发实战:从零构建具备工具调用能力的智能体
最近在开发一个需要结合知识检索和数学计算的企业级AI应用时,我深入研究了基于LangChain框架的AI Agent开发。与普通聊天机器人不同,真正的AI Agent应该具备感知环境、自主决策和调用工具的能力。下面分享我在开发过程中的完整实现方案和踩坑经验。
这个智能体的核心功能是能够同时处理两类任务:查询企业内部的RAG知识库(包含项目信息和机密数据),以及执行精确的数学计算。通过多轮对话机制,它可以智能判断何时调用哪个工具,并将结果整合成自然语言回复。这种架构特别适合需要结合结构化数据和非结构化知识的业务场景。
2. 核心架构设计
2.1 Agent四大核心组件
现代AI Agent的完整架构通常包含四个关键部分:
-
大语言模型(LLM):作为大脑负责推理和决策。我选择通义千问的qwen-plus模型,它在中文场景表现优异且API稳定。
-
记忆系统:
- 短期记忆:维护对话历史(message数组)
- 长期记忆:基于FAISS实现的RAG知识库,存储企业文档数据
-
规划模块:通过多轮对话循环实现,控制工具调用流程
-
工具集:本例中包含两个工具:
- 企业知识检索(rag_search)
- 数学计算器(calculator)
2.2 工具调用机制设计
工具调用的核心流程采用"请求-执行-反馈"的循环模式:
- 用户输入query
- LLM判断是否需要调用工具
- 若需要,返回工具名称和参数
- 执行对应工具函数
- 将结果封装为ToolMessage
- 将消息重新输入LLM生成最终回复
这种设计的关键在于:
- 工具描述必须清晰准确(LLM靠这个决定调用哪个工具)
- 需要设置最大循环次数防止死循环
- 要处理工具不存在或执行失败的情况
3. 完整实现详解
3.1 环境准备与依赖安装
首先需要安装必要的Python包:
bash复制pip install langchain langchain-community faiss-cpu dashscope
设置API密钥(以阿里云DashScope为例):
python复制import os
os.environ["DASHSCOPE_API_KEY"] = "your_api_key_here"
3.2 工具函数实现
3.2.1 知识检索工具
python复制from langchain_community.vectorstores import FAISS
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document
@tool
def rag_search(query: str) -> str:
"""
从公司内部数据库搜索文档,包含项目计划、预算等机密信息。
参数:
query: 搜索关键词,如"深蓝计划预算"
返回:
相关文档内容,如"预算:50万元"
"""
# 初始化向量数据库
raw_text = """【公司内部机密:代号"深蓝计划"...】""" # 实际项目文档
docs = [Document(page_content=raw_text)]
text_splitter = RecursiveCharacterTextSplitter(chunk_size=100, chunk_overlap=20)
split_docs = text_splitter.split_documents(docs)
embeddings = DashScopeEmbeddings(model="text-embedding-v1")
if os.path.exists("faiss_index"):
ragdb = FAISS.load_local("faiss_index", embeddings, allow_dangerous_deserialization=True)
else:
ragdb = FAISS.from_documents(split_docs, embeddings)
ragdb.save_local("faiss_index")
return "\n\n".join(doc.page_content for doc in ragdb.similarity_search(query, k=2))
关键点说明:
- 使用FAISS作为向量数据库,轻量且高效
- 文本分割时设置overlap保证上下文连贯
- allow_dangerous_deserialization=True仅在可信环境使用
3.2.2 数学计算工具
python复制import re
from numexpr import evaluate # 更安全的替代eval的方案
@tool
def calculator(expression: str) -> str:
"""
执行数学计算,支持加减乘除和简单函数。
参数:
expression: 数学表达式,如"(50*0.8)+100"
返回:
计算结果字符串,如"140.0"
"""
# 安全校验
if not re.match(r'^[\d+\-*/().\s]+$', expression):
return "错误:包含非法字符"
try:
return str(evaluate(expression))
except Exception as e:
return f"计算错误: {e}"
安全改进:
- 使用正则表达式过滤危险字符
- 用numexpr替代原生eval,更安全
- 异常捕获避免崩溃
3.3 Agent核心逻辑实现
python复制from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage, ToolMessage
def run_agent(query: str, max_turns=5):
# 初始化工具和模型
tools = {"rag_search": rag_search, "calculator": calculator}
llm = ChatTongyi(model_name="qwen-plus")
tool_llm = llm.bind_tools(tools=list(tools.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 not in tools:
output = f"错误:工具{tool_name}不存在"
else:
try:
output = tools[tool_name].invoke(tool_call["args"])
except Exception as e:
output = f"工具执行错误: {str(e)}"
messages.append(
ToolMessage(
content=output,
tool_call_id=tool_call["id"],
name=tool_name
)
)
return "超过最大对话轮数"
4. 实战案例演示
4.1 查询项目信息
python复制print(run_agent("深蓝计划的预算是多少?"))
输出流程:
- LLM决定调用rag_search工具
- 检索到"预算:50万元"
- LLM生成自然语言回复:"深蓝计划的当前预算是50万元"
4.2 复合计算场景
python复制print(run_agent("如果深蓝计划预算增加30%后是多少?"))
处理步骤:
- 先调用rag_search获取原始预算
- 再调用calculator计算50*1.3
- 最终回复:"预算增加30%后为65万元"
4.3 错误处理演示
python复制print(run_agent("计算system('rm -rf /')"))
安全防护生效:
- 计算器检测到非法字符
- 返回"错误:包含非法字符"
- 不会被当作命令执行
5. 性能优化与安全实践
5.1 性能优化技巧
-
向量检索优化:
- 对长文档使用更好的分块策略(我采用25%重叠的滑动窗口)
- 调整FAISS的nprobe参数平衡速度与精度
-
工具调用加速:
- 对工具函数添加@lru_cache缓存装饰器
- 预加载向量数据库避免重复初始化
-
对话流程优化:
- 设置合理的max_turns(通常3-5轮足够)
- 添加超时机制防止长时间挂起
5.2 安全防护方案
-
输入过滤:
python复制def sanitize_input(text: str) -> str: # 移除特殊字符和危险关键词 return re.sub(r'[;|&$\'"<>]', '', text) -
权限控制:
- 为不同工具设置访问权限级别
- 敏感操作需要二次确认
-
审计日志:
python复制def log_tool_call(tool_name, args, result): with open("tool_audit.log", "a") as f: f.write(f"{datetime.now()} - {tool_name}: {args} => {result}\n")
6. 常见问题排查
6.1 工具不被识别
现象:LLM从不调用某个工具
排查:
- 检查工具描述是否完整清晰
- 确认bind_tools时包含该工具
- 测试直接调用工具函数是否正常
6.2 循环调用问题
现象:Agent陷入无限工具调用循环
解决:
- 检查工具返回值是否符合预期
- 降低max_turns值
- 在prompt中明确限制调用次数
6.3 中文处理异常
现象:中文查询返回乱码或无关结果
优化:
- 确保Embedding模型支持中文(如text-embedding-v1)
- 调整文本分块策略适应中文特点
- 在prompt中强调使用中文回复
7. 扩展应用场景
这个基础框架可以扩展更多实用场景:
-
客户服务助手:
- 添加产品数据库查询工具
- 集成工单系统API
-
数据分析助手:
- 接入SQL查询工具
- 添加可视化图表生成
-
智能运维助手:
- 集成服务器状态检查
- 添加日志分析工具
实际开发中发现,工具描述的质量直接影响调用准确率。建议为每个工具编写详细的文档字符串,包含:
- 工具用途
- 参数说明和示例
- 返回格式示例
- 使用场景说明
这种AI Agent架构虽然强大,但也带来新的挑战。最大的教训是:任何工具调用都必须考虑安全问题,特别是当Agent能执行代码或访问系统资源时。我在初期就曾因为直接使用eval()而遭遇潜在的安全风险,后来通过严格的输入验证和替代方案才解决。
