做了大半年Agent相关的开发,踩了不少坑。这篇文章不聊概念,只聊工程落地时那些让人头疼的真实问题。
前言
2025年是Agent概念爆发的一年,2026年则是真正要把Agent"用起来"的一年。
如果你只是写个Demo,让大模型调两个API,那确实不难。但当你要把Agent部署到生产环境,面对真实用户、真实数据、真实的边界情况时,你会发现——Demo和Production之间隔着一整个工程化的深渊。
这篇文章从四个我实际遇到的关键问题展开:工具调用的可靠性、多步推理的错误处理、记忆与状态管理、安全边界控制。每个问题我都会给出我的思考和实践方案。
一、工具调用(Tool Calling)的可靠性
问题:模型"手滑"比你想象的频繁
Function Calling 是Agent的核心能力。但在实际使用中,大模型的工具调用远没有想象中可靠:
- 参数幻觉:模型编造不存在的参数值
- 工具选择错误:明明应该调搜索,却去调了日历
- 格式不合规:返回的JSON偶尔会多一个逗号、少一个引号
这些问题在Demo里可能一百次遇不到一次,但在日活过千的场景里,每天都会出现。
实践方案:三层防御
# 第一层:Schema校验(硬性拦截)
def validate_tool_call(tool_name, params, schema_registry):
schema = schema_registry.get(tool_name)
if not schema:
raise ValueError(f"Unknown tool: {tool_name}")
# 校验逻辑...
# 第二层:语义校验(逻辑兜底)
def semantic_check(tool_name, params, context):
# 检查参数合理性...
pass
# 第三层:执行隔离(沙箱化)
def execute_tool_sandboxed(tool_name, params, timeout=30):
# 带超时的执行...
pass
二、多步推理的错误处理
问题:链路越长,崩溃概率越高
一个典型的Agent任务可能是这样的:
用户:"帮我查一下上周的销售数据,生成报表,然后发给老板"
这背后是三步:查数据 → 生成报表 → 发邮件。每一步都可能失败。
最棘手的是中间态的处理——第二步失败了,第一步的结果要不要缓存?要不要从头重试?
实践方案:状态机 + 检查点
interface AgentTask {
id: string;
steps: StepDefinition[];
currentStep: number;
state: 'pending' | 'running' | 'paused' | 'failed' | 'completed';
checkpoints: Map<number, StepResult>;
retryCount: Map<number, number>;
}
async function executeTask(task: AgentTask) {
for (let i = task.currentStep; i < task.steps.length; i++) {
try {
// 检查是否有可用的checkpoint
if (task.checkpoints.has(i)) {
continue;
}
const result = await executeStep(step, task.checkpoints);
// 关键:校验步骤结果
if (!validateStepOutput(result, task.steps[i + 1])) {
throw new StepOutputError();
}
task.checkpoints.set(i, result);
await persistTask(task);
} catch (error) {
// 重试逻辑...
if (retries < maxRetries) {
i--; // 重试当前步骤
continue;
}
// 暂停任务,等待人工介入
task.state = 'paused';
await notifyUser(task, error);
}
}
}
三、Agent记忆与状态管理
问题:上下文窗口不是万能的
很多人一开始的做法是"把所有历史对话都塞进上下文"。这在对话轮数少的时候没问题,但当Agent需要处理持续数天、数十轮的复杂任务时,你会遇到:
- Token爆炸:上下文塞满后要么截断要么报错
- 注意力衰减:模型对远处信息的注意力会下降
- 成本失控:每轮对话都带上完整历史,API费用指数增长
实践方案:分层记忆架构
┌─────────────────────────────────────────┐
│ Working Memory(工作记忆) │
│ 当前对话的最近N轮 + 当前任务状态 │
│ 存储:内存 / 上下文窗口 │
│ 生命周期:单次会话 │
├─────────────────────────────────────────┤
│ Short-term Memory(短期记忆) │
│ 最近几次会话的摘要 + 关键决策点 │
│ 存储:Redis / 本地文件 │
│ 生命周期:小时~天 │
├─────────────────────────────────────────┤
│ Long-term Memory(长期记忆) │
│ 用户偏好、历史任务结果、学到的经验 │
│ 存储:向量数据库 + 结构化数据库 │
│ 生命周期:持久化 │
└─────────────────────────────────────────┘
四、安全边界控制
问题:Agent有了工具,就有了"手脚"
一旦Agent能调用外部工具(发邮件、操作数据库、执行代码),安全问题就不再是"可能会出错",而是"一定会被滥用"。
实际遇到过的安全问题:
- Prompt注入:用户在输入中嵌入指令
- 权限越界:Agent在多租户场景下访问了其他用户的数据
- 连锁破坏:一个错误的删除操作触发了一连串不可逆后果
实践方案:最小权限 + 人机协同
TOOL_RISK_LEVELS = {
"search": "low", # 只读,无副作用
"read_file": "low",
"send_message": "medium", # 有副作用但可撤回
"delete_file": "high", # 不可逆操作
"execute_sql": "critical", # 直接操作数据库
}
async def tool_gateway(tool_name, params, user_context):
risk = TOOL_RISK_LEVELS.get(tool_name, "high")
if risk == "low":
return await execute_tool(tool_name, params)
if risk == "medium":
await audit_log(tool_name, params, user_context)
return await execute_tool(tool_name, params)
if risk == "high":
# 需要用户确认
confirmed = await request_user_confirmation(...)
if not confirmed:
return {"status": "cancelled"}
return await execute_tool(tool_name, params)
if risk == "critical":
# 先在沙箱中dry-run
dry_run_result = await dry_run_tool(tool_name, params)
confirmed = await request_user_confirmation(...)
if not confirmed:
return {"status": "cancelled"}
return await execute_tool_with_rollback(tool_name, params)
写在最后:Agent工程化的未来
回顾这些问题,你会发现Agent工程化的核心挑战其实不在模型能力本身,而在如何在不确定性中构建可靠的系统。
几个我比较看好的方向:
- MCP等协议的成熟:工具调用的标准化会大幅降低集成成本
- Agent可观测性:类似传统微服务的链路追踪
- 人机协同模式的细化:根据风险等级和置信度动态调整人类介入的程度
- Agent-native的测试框架:能模拟多轮对话、工具调用链、异常场景的专用测试工具
Agent技术正在从"能跑Demo"走向"能扛生产"。这个过程没有捷径,但好在我们做工程的人,最不缺的就是把粗糙的东西打磨成可靠系统的耐心。
共勉。
本文首发于稀土掘金。如果对你有帮助,欢迎点赞收藏,也欢迎在评论区交流你在Agent开发中遇到的坑 🤝