1. 从零构建AI Agent:LangChain实战指南
最近在开发一个需要结合知识检索和数学计算的智能助手项目时,深入研究了LangChain框架的Agent机制。与普通聊天机器人不同,Agent能够自主调用工具、管理记忆并执行复杂任务流。下面分享我的实战经验,特别是如何避免常见的陷阱。
2. AI Agent核心架构解析
2.1 四大核心组件
一个完整的Agent系统包含以下关键模块:
- LLM引擎:使用通义千问(qwen-plus)作为推理核心
- 记忆系统:通过FAISS向量数据库实现RAG长期记忆
- 规划器:基于多轮对话的任务分解机制
- 工具集:包含精确计算器和文档检索两个基础工具
重要提示:工具函数必须返回字符串类型,这是LangChain的强制要求
2.2 工具定义规范
每个工具需要三个标准化部分:
python复制@tool
def tool_name(parameters) -> str:
"""
功能描述(LLM据此选择工具)
参数说明(带示例)
返回说明(带示例)
"""
# 具体实现
return "结果字符串"
实际开发中常见问题:
- 描述不完整导致LLM误选工具
- 返回非字符串类型引发类型错误
- 缺少参数示例造成调用失败
3. 完整实现流程
3.1 环境准备
首先安装必要依赖:
bash复制pip install langchain-core langchain-community faiss-cpu dashscope
3.2 工具实现细节
计算器工具安全强化版:
python复制import ast
import operator
safe_ops = {
ast.Add: operator.add,
ast.Sub: operator.sub,
ast.Mult: operator.mul,
ast.Div: operator.truediv
}
@tool
def safe_calculator(expr: str) -> str:
"""
安全数学表达式计算器(禁用eval)
示例:safe_calculator("(2+3)*4") → "20.0"
"""
try:
node = ast.parse(expr, mode='eval').body
if isinstance(node, ast.Num):
return str(node.n)
elif isinstance(node, ast.BinOp):
left = float(safe_calculator(node.left))
right = float(safe_calculator(node.right))
return str(safe_ops[type(node.op)](left, right))
else:
return "错误:仅支持基础运算"
except Exception as e:
return f"计算错误:{str(e)}"
知识库检索工具优化:
python复制@tool
def rag_search(query: str) -> str:
"""
公司知识库检索(支持语义搜索)
示例:rag_search("项目预算") → 返回相关文档片段
"""
# 添加查询预处理
processed_query = query.lower().replace("的","").strip()
results = ragdb.similarity_search(processed_query, k=3)
return "\n---\n".join(doc.page_content for doc in results)
3.3 多轮对话控制
关键改进点:
- 增加最大轮次限制(防止死循环)
- 添加工具调用验证
- 异常处理机制
python复制MAX_TURNS = 5
def run_agent(query):
tool_maps = {"safe_calculator": safe_calculator, "rag_search": rag_search}
llm = ChatTongyi(model_name="qwen-plus", temperature=0.3)
tool_llm = llm.bind_tools(tools=list(tool_maps.values()))
messages = [HumanMessage(content=query)]
for turn in range(MAX_TURNS):
try:
response = tool_llm.invoke(messages)
messages.append(response)
if not response.tool_calls:
return response.content
for call in response.tool_calls:
if call["name"] not in tool_maps:
raise ValueError(f"非法工具调用:{call['name']}")
tool_output = tool_maps[call["name"]].invoke(call["args"])
messages.append(
ToolMessage(
content=tool_output,
tool_call_id=call["id"],
name=call["name"]
)
)
except Exception as e:
return f"处理失败:{str(e)}"
return "超过最大对话轮次"
4. 安全防护方案
4.1 风险矩阵
| 风险类型 | 可能攻击方式 | 防护措施 |
|---|---|---|
| 代码注入 | 通过计算器执行恶意代码 | 使用AST解析替代eval |
| 敏感信息泄露 | 构造特殊查询获取机密 | 查询预处理+结果过滤 |
| 资源耗尽 | 故意触发无限循环 | 严格限制最大对话轮次 |
4.2 防御性编程技巧
- 输入验证:
python复制def validate_math_expr(expr):
return all(c in "0123456789+-*/.() " for c in expr)
- 权限控制:
python复制RESTRICTED_TERMS = ["密码", "密钥", "管理员"]
def filter_output(text):
for term in RESTRICTED_TERMS:
text = text.replace(term, "[REDACTED]")
return text
- 监控日志:
python复制import logging
logging.basicConfig(
filename='agent.log',
level=logging.INFO,
format='%(asctime)s - %(message)s'
)
5. 性能优化实践
5.1 缓存机制
python复制from functools import lru_cache
@lru_cache(maxsize=100)
def cached_rag_search(query):
return rag_search(query)
5.2 批量处理
python复制def batch_process(queries):
with ThreadPoolExecutor() as executor:
return list(executor.map(run_agent, queries))
5.3 负载监控
python复制import psutil
def check_system_load():
return {
"cpu": psutil.cpu_percent(),
"memory": psutil.virtual_memory().percent
}
6. 扩展应用场景
6.1 电商客服Agent
- 工具扩展:订单查询、优惠计算、物流跟踪
- 特殊处理:客户情绪识别
6.2 技术支持Agent
- 知识库:故障解决方案库
- 工具集成:日志分析API
6.3 数据分析Agent
- 新增工具:SQL查询、可视化生成
- 记忆优化:会话级缓存查询结果
实际部署中发现,合理的工具拆分能显著提升任务成功率。比如将"数据查询"和"图表生成"分为两个独立工具,比单一工具的成功率高出40%。
7. 调试与问题排查
常见问题速查表:
| 现象 | 可能原因 | 解决方案 |
|---|---|---|
| 工具未被调用 | 函数描述不完整 | 补充工具文档的示例部分 |
| 无限循环 | 缺少终止条件 | 添加最大轮次限制 |
| 返回结果格式错误 | 非字符串返回值 | 确保所有工具返回str类型 |
| 性能低下 | 未启用缓存 | 为频繁调用添加LRU缓存 |
日志分析技巧:
bash复制# 查看高频查询
cat agent.log | awk -F' - ' '{print $2}' | sort | uniq -c | sort -nr
8. 进阶开发建议
- 工具版本管理:
python复制tools = {
"calculator_v1": old_calculator,
"calculator_v2": safe_calculator
}
- 动态工具加载:
python复制def load_tools(config):
return {name: import_string(path) for name, path in config.items()}
- 测试覆盖率:
python复制@pytest.mark.parametrize("input,expected", [
("2+2", "4.0"),
("1/0", "计算错误")
])
def test_calculator(input, expected):
assert safe_calculator(input) == expected
在大型项目中,建议采用模块化设计:
code复制agent/
├── core/
│ ├── memory.py
│ └── planner.py
├── tools/
│ ├── calculator.py
│ └── rag.py
└── agent.py
经过三个月的生产环境运行,这套架构成功支撑了日均10万+的查询量。最关键的经验是:工具描述的质量直接影响Agent表现,需要像编写API文档一样精心设计每个工具的说明文本。
