让 Agent 不只是"查资料",而是能真正"操作世界"
系列一解决了"怎么搭一个 Agent",系列二从工具开始深入。工具是 Agent 的手和脚------没有好工具,Agent 再聪明也只是个聊天机器人。
这篇讲三类最实用的高级工具:代码解释器、数据库查询、工作流触发,重点放在"怎么安全地用"。
一、代码解释器:让 Agent 会算、会画图、会处理文件
很多 Agent 框架直接用 exec() 跑 LLM 生成的代码,这是极度危险 的。LLM 生成的代码可能包含 os.system("rm -rf /")、网络请求、无限循环。
安全做法:沙箱隔离 + 白名单限制。
方案 A:用 E2B 沙箱(推荐)
E2B 提供隔离的云端代码执行环境,自带资源限制和网络隔离。
python
from e2b_code_interpreter import Sandbox
def run_code_in_sandbox(code: str, timeout: int = 30) -> dict:
"""在沙箱中执行 Python 代码"""
sandbox = Sandbox()
try:
result = sandbox.run_code(code, timeout=timeout)
return {
"stdout": result.logs.stdout,
"stderr": result.logs.stderr,
"files": sandbox.files.list("/"), # 可以读取生成的文件
}
finally:
sandbox.kill()
# Agent 工具定义
code_interpreter_tool = {
"name": "code_interpreter",
"description": "执行 Python 代码进行计算、数据处理、绘图。支持 matplotlib、pandas、numpy。",
"parameters": {
"type": "object",
"properties": {
"code": {
"type": "string",
"description": "要执行的 Python 代码"
}
},
"required": ["code"]
}
}
方案 B:本地轻量沙箱(适合离线场景)
python
import subprocess
import tempfile
import os
SAFE_MODULES = {"math", "statistics", "decimal", "fractions", "json", "csv"}
DANGEROUS_PATTERNS = [
"import os", "import sys", "import subprocess", "import socket",
"import requests", "import urllib", "__import__", "exec(", "eval(",
"open(", "input(", "compile(", "globals(", "locals("
]
def safe_code_execute(code: str) -> dict:
"""本地安全代码执行:模块白名单 + 危险模式检测"""
# 1. 危险模式检测
for pattern in DANGEROUS_PATTERNS:
if pattern in code:
return {"error": f"禁止使用危险操作: {pattern}"}
# 2. 写入临时文件
with tempfile.NamedTemporaryFile(mode='w', suffix='.py', delete=False) as f:
f.write(code)
tmp_path = f.name
try:
# 3. 在受限环境中执行(无网络、超时限制)
result = subprocess.run(
["python3", tmp_path],
capture_output=True,
text=True,
timeout=10,
env={**os.environ, "PYTHONPATH": ""} # 清空 PYTHONPATH
)
return {
"stdout": result.stdout[:5000], # 限制输出长度
"stderr": result.stderr[:2000],
"returncode": result.returncode
}
except subprocess.TimeoutExpired:
return {"error": "代码执行超时"}
finally:
os.unlink(tmp_path)
典型应用场景:
- "帮我算一下这个数据集的平均值和标准差"
- "把这份 CSV 数据画成折线图"
- "把下面这段 JSON 转成表格格式"
二、数据库查询:让 Agent 会查数据
核心原则:Agent 只读不写,且查询必须经过 SQL 注入防护。
python
import sqlite3
import re
class SafeDBQuery:
"""安全的数据库查询工具"""
def __init__(self, db_path: str):
self.db_path = db_path
def execute_query(self, sql: str) -> list[dict]:
# 1. 只允许 SELECT
sql_upper = sql.strip().upper()
if not sql_upper.startswith("SELECT"):
raise ValueError("只允许 SELECT 查询")
# 2. 禁止危险操作
dangerous = ["DROP", "DELETE", "UPDATE", "INSERT", "ALTER", "CREATE"]
for keyword in dangerous:
if keyword in sql_upper:
raise ValueError(f"禁止使用 {keyword} 操作")
# 3. 限制返回行数
if "LIMIT" not in sql_upper:
sql = sql.rstrip(";") + " LIMIT 100"
# 4. 执行查询
conn = sqlite3.connect(self.db_path)
conn.row_factory = sqlite3.Row
try:
cursor = conn.execute(sql)
rows = [dict(row) for row in cursor.fetchall()]
return rows
finally:
conn.close()
# 工具定义
db_tool = {
"name": "query_database",
"description": "查询业务数据库。只支持 SELECT,自动限制 100 行。",
"parameters": {
"type": "object",
"properties": {
"sql": {
"type": "string",
"description": "SQL 查询语句(仅 SELECT)"
}
},
"required": ["sql"]
}
}
# 使用示例
db = SafeDBQuery("business.db")
result = db.execute_query("SELECT name, revenue FROM companies ORDER BY revenue DESC")
进阶技巧 :给 Agent 提供表结构描述而不是让它猜字段名。
ini
SCHEMA_DESCRIPTION = """
数据库表结构:
- companies: id, name, industry, revenue, employee_count, founded_year
- orders: id, company_id, amount, order_date, status
- products: id, name, category, price, stock
"""
# 在 System Prompt 中注入
system_prompt = f"""你是一个数据分析助手。{SCHEMA_DESCRIPTION}
查询时请使用上述字段名。"""
三、工作流触发:让 Agent 能"启动一个流程"
Agent 不只是回答问题,还可以触发业务流程。关键是参数校验 和异步执行。
python
import asyncio
from enum import Enum
class WorkflowType(str, Enum):
CREATE_TICKET = "create_ticket"
SEND_EMAIL = "send_email"
GENERATE_REPORT = "generate_report"
APPROVE_REQUEST = "approve_request"
# 工作流注册表
WORKFLOW_REGISTRY = {
WorkflowType.CREATE_TICKET: {
"description": "创建工单",
"params": ["title", "description", "priority", "assignee"],
"handler": "ticket_service.create",
},
WorkflowType.SEND_EMAIL: {
"description": "发送邮件",
"params": ["to", "subject", "body"],
"handler": "email_service.send",
},
}
def trigger_workflow(workflow_type: str, params: dict) -> dict:
"""触发一个工作流"""
# 1. 校验工作流类型
if workflow_type not in WORKFLOW_REGISTRY:
return {"error": f"未知工作流: {workflow_type}"}
# 2. 校验必填参数
required = WORKFLOW_REGISTRY[WorkflowType(workflow_type)]["params"]
missing = [p for p in required if p not in params]
if missing:
return {"error": f"缺少参数: {', '.join(missing)}"}
# 3. 异步执行(不阻塞 Agent)
handler_path = WORKFLOW_REGISTRY[WorkflowType(workflow_type)]["handler"]
# 实际项目中通过消息队列或 Celery 异步执行
task_id = f"{workflow_type}_{id(params)}"
return {
"status": "accepted",
"task_id": task_id,
"message": f"工作流 {workflow_type} 已触发,任务ID: {task_id}"
}
# 工具定义
workflow_tool = {
"name": "trigger_workflow",
"description": "触发一个业务流程。可用类型: create_ticket, send_email, generate_report, approve_request",
"parameters": {
"type": "object",
"properties": {
"workflow_type": {
"type": "string",
"enum": list(WorkflowType),
"description": "工作流类型"
},
"params": {
"type": "object",
"description": "工作流参数"
}
},
"required": ["workflow_type", "params"]
}
}
Agent 使用示例:
csharp
用户:"客户投诉服务器宕机,优先级很高,派给运维组"
Agent 思考:需要创建工单 → 调用 trigger_workflow
workflow_type: "create_ticket"
params: {
"title": "客户投诉:服务器宕机",
"description": "客户反映服务器宕机,需紧急处理",
"priority": "high",
"assignee": "ops_team"
}
四、工具设计的安全清单
| 风险 | 防护措施 |
|---|---|
| 代码注入 | 沙箱隔离 / 白名单模块 / 危险模式检测 |
| SQL 注入 | 只允许 SELECT / 参数化查询 / 表结构白名单 |
| 工具滥用 | 频率限制 / 配额管理 / 人工审批节点 |
| 数据泄露 | 脱敏中间结果 / 不返回完整记录 / 审计日志 |
| 无限循环 | 超时限制 / 最大步数限制 / 死循环检测 |
| 资源耗尽 | 内存限制 / CPU 限制 / 输出长度限制 |