智能体(Agent)这个概念在AI领域已经存在多年,但直到大语言模型(LLM)的爆发才真正展现出其强大潜力。与普通的聊天机器人不同,一个真正的智能体更像是一个数字员工——它能理解复杂指令、规划任务步骤、记住上下文信息,还能调用各种工具完成任务。
我在实际开发中发现,构建一个实用的智能体需要考虑四个核心组件:
假设我们需要开发一个能处理企业内务查询的智能体,它需要具备两种核心能力:
这个场景非常典型——既需要理解自然语言查询,又要处理结构化数据运算。下面是我在实际项目中验证过的实现方案。
python复制@tool
def calculator(expression: str) -> str:
"""
安全计算数学表达式。仅支持基础四则运算和简单数学函数。
参数:
expression: 经过安全过滤的数学算式,如 "(2 + 2)*3"
返回:
str: 计算结果或错误信息
安全措施:
1. 使用ast.literal_eval替代eval
2. 运算符白名单校验
3. 最大长度限制
"""
import ast
import re
# 安全校验
if len(expression) > 100:
return "错误:表达式过长"
if not re.match(r'^[\d\s+\-*/().]+$', expression):
return "错误:包含非法字符"
try:
node = ast.parse(expression, mode='eval')
for n in ast.walk(node):
if not isinstance(n, (ast.Expression, ast.Num, ast.BinOp, ast.UnaryOp)):
return "错误:非法语法结构"
return str(eval(expression))
except Exception as e:
return f"计算错误: {e}"
关键点:绝对不要直接使用eval()!我吃过亏——曾经有测试人员通过注入"import('os').system('rm -rf /')"差点酿成事故。现在我都用ast模块做语法树分析+白名单校验。
python复制@tool
def rag_search(query: str) -> str:
"""
从企业向量数据库检索相关信息,支持语义搜索。
参数:
query: 自然语言查询,如"项目预算"
返回:
str: 格式化后的检索结果,包含相关文档片段
技术栈:
- 文本分块:RecursiveCharacterTextSplitter
- 向量化:DashScopeEmbeddings
- 存储:FAISS
"""
from langchain_community.vectorstores import FAISS
from langchain_text_splitters import RecursiveCharacterTextSplitter
# 模拟企业文档
corporate_docs = [
Document(page_content="项目A预算:100万元,周期:2023Q1-2024Q4"),
Document(page_content="项目B代号'北极星',负责人:张三")
]
# 智能分块(避免切断语义)
splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", "。", "!", "?"]
)
chunks = splitter.split_documents(corporate_docs)
# 构建向量库(实际项目应持久化)
embeddings = DashScopeEmbeddings(model="text-embedding-v1")
vector_db = FAISS.from_documents(chunks, embeddings)
# 语义搜索
results = vector_db.similarity_search(query, k=3)
return "\n---\n".join(doc.page_content for doc in results)
python复制def init_agent():
# 工具注册表
tools = {
"calculator": calculator,
"rag_search": rag_search
}
# 模型选择建议:
# - 简单任务:qwen-turbo(低成本)
# - 复杂推理:qwen-plus(高准确率)
# - 中文场景:建议通义千问系列
llm = ChatTongyi(
model_name="qwen-plus",
temperature=0.3 # 降低随机性
)
# 关键步骤:绑定工具
return llm.bind_tools(
tools=list(tools.values()),
tool_choice="auto" # 让模型自主决定是否调用工具
), tools
python复制def run_conversation(query: str, max_turns=5):
agent, tools = init_agent()
conversation = [HumanMessage(content=query)]
for turn in range(max_turns):
# 模型推理
response = agent.invoke(conversation)
conversation.append(response)
# 无工具调用时直接返回
if not response.tool_calls:
return response.content
# 处理工具调用
for call in response.tool_calls:
tool_name = call["name"]
if tool_name not in tools:
conversation.append(
ToolMessage(
content=f"错误:未知工具{tool_name}",
tool_call_id=call["id"]
)
)
continue
# 执行工具
try:
output = tools[tool_name].invoke(call["args"])
conversation.append(
ToolMessage(
content=output,
tool_call_id=call["id"],
name=tool_name
)
)
except Exception as e:
conversation.append(
ToolMessage(
content=f"工具执行错误:{str(e)}",
tool_call_id=call["id"]
)
)
return "超过最大对话轮次"
输入过滤层:
权限控制:
python复制TOOL_PERMISSIONS = {
"junior_staff": ["rag_search"],
"senior_staff": ["rag_search", "calculator"]
}
def check_permission(user_role, tool_name):
return tool_name in TOOL_PERMISSIONS.get(user_role, [])
审计日志:
python复制def log_tool_call(user, tool_name, args):
with open("audit.log", "a") as f:
f.write(f"{datetime.now()} {user} called {tool_name} with {args}\n")
向量库优化:
对话缓存:
python复制from langchain.cache import SQLiteCache
import langchain
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")
超时控制:
python复制from func_timeout import func_timeout, FunctionTimedOut
try:
output = func_timeout(3, tools[tool_name].invoke, args=(call["args"],))
except FunctionTimedOut:
output = "工具执行超时"
| 问题现象 | 可能原因 | 解决方案 |
|---|---|---|
| 工具不被调用 | 1. 函数描述不清晰 2. temperature值过高 |
1. 完善工具文档字符串 2. 调低temperature至0.3以下 |
| 中文处理异常 | 文本分块切分不合理 | 调整RecursiveCharacterTextSplitter的separators参数 |
| 计算错误 | 数字格式问题 | 在工具内统一转换为float处理 |
| 知识库检索不准 | 嵌入模型不匹配 | 尝试切换text-embedding-v2模型 |
我在实际部署时遇到过工具频繁误调用的问题,后来发现是因为函数描述中缺少示例。建议每个工具的描述都包含:
比如:
python复制@tool
def get_weather(city: str) -> str:
"""
获取指定城市天气信息
示例调用:
- 输入: "北京"
- 输出: "北京: 晴, 25℃"
支持城市:
- 中国大陆地级市
- 拼音或中文名称
"""
...
最后分享一个调试技巧:在开发阶段可以添加verbose日志:
python复制import logging
logging.basicConfig()
logging.getLogger("langchain").setLevel(logging.DEBUG)
这能完整显示智能体的决策过程,对理解模型行为非常有帮助。当遇到难以理解的工具调用决策时,这个日志往往能揭示根本原因。