CAUTION
构建 AI 智能体(AI Agents)已经不是什么新鲜事了,但真正上手做过项目的人都知道:从 demo 到生产环境,中间隔着无数坑。 我见过太多团队兴致勃勃地搭起第一个 Agent,结果上线第一天就崩了——或者更隐蔽的问题是:看起来运行正常,但实际上逻辑漏洞百出,响应质量一言难尽。
这篇文章源自一个 Reddit 高赞帖 [来源1],作者是一位有实战经验的工程师,总结了 构建 AI 智能体最值得告诉新人的 10 件事。我结合自己的踩坑经历,做了进一步的展开和补充。
这不是理论文章,全是实打实的工程细节。读完你带走的,是可以直接复用的经验和代码模式。
一、先让你的第一个 Agent 跑起来,再谈优化
很多新手一上来就想要“完美的智能体”——多层规划、记忆增强、工具链编排……结果代码写了一周,Agent 还是调不通。
核心建议:从最简单的单 Agent 开始。
## 最基础的 Agent 骨架(可直接运行)from langchain.agents import AgentExecutor, create_react_docstore_agentfrom langchain_openai import ChatOpenAIfrom langchain_community.tools import WikipediaQueryRun, WikipediaAPIWrapper## 1. 定义工具tools = [WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())]## 2. 创建 Agentllm = ChatOpenAI(model="gpt-4", temperature=0)agent = create_react_docstore_agent(llm, tools)## 3. 执行executor = AgentExecutor(agent=agent, tools=tools)result = executor.invoke({"input": "谁是爱因斯坦?"})print(result["output"])
这个 Agent 能做什么?接收问题 → 调用 Wikipedia 查询 → 返回答案。就这么简单。
TIP
先把"问题→工具→答案"这条路跑通,再加复杂逻辑。
二、工具调用(Tool Calling)是核心,别轻视
Agent 的能力边界,很大程度上由它能调用多少工具决定。但工具调用本身就有很多坑。
2.1 工具描述要清晰
工具的描述(description)是 LLM 判断"什么时候该调用"的依据。描述模糊,Agent 就会乱调用。
## ❌ 错误示例:描述太模糊tools = [ {"name": "search", "description": "搜索工具", "parameters": {...}}]## ✅ 正确示例:描述清楚输入输出和适用场景tools = [ { "name": "search_code", "description": "在代码库中搜索函数或变量定义。输入是自然语言查询,返回匹配的文件路径和行号。适用于:找某个功能在哪里实现。", "parameters": { "type": "object", "properties": { "query": { "type": "string", "description": "要搜索的关键词,如函数名、变量名、或功能描述" } }, "required": ["query"] } }]
2.2 工具返回要结构化
Agent 解析工具返回结果的能力有限,返回内容太乱会导致后续步骤出错。
## ❌ 返回一坨无结构文本def search_code(query): results = do_search(query) return str(results) # 大概率出错## ✅ 返回结构化信息,让 Agent 容易理解def search_code(query): results = do_search(query) return json.dumps({ "count": len(results), "files": [{"path": r.path, "line": r.line, "preview": r.preview} for r in results] }, ensure_ascii=False)
三、记忆管理:别让 Agent "失忆"
Agent 处理长对话时,经常"忘记"之前说过什么。这是因为 LLM 有上下文窗口限制,也因为你没有设计好记忆机制。
3.1 短期记忆:保留最近 N 轮对话
from collections import dequeclass ConversationBuffer: def __init__(self, max_turns=10): self.buffer = deque(maxlen=max_turns) # 只保留最近10轮 def add(self, role, content): self.buffer.append({"role": role, "content": content}) def get_history(self): return list(self.buffer) def clear(self): self.buffer.clear()## 使用示例memory = ConversationBuffer(max_turns=10)memory.add("user", "帮我查下北京今天的天气")memory.add("assistant", "今天北京晴,气温 15-25 度")memory.add("user", "那上海呢?")## Agent 可以看到前两轮,但更早的会被自动清除
3.2 长期记忆:摘要+向量检索
简单对话用 buffer 够用,但涉及大量历史信息时,需要更复杂的记忆策略:
from langchain.retrievers import VectorStoreRetrieverfrom langchain.text_splitter import CharacterTextSplitterclass AgentMemory: def __init__(self): self.short_term = [] # 最近对话 self.summaries = [] # 摘要归档 self.vector_store = None def add_interaction(self, user_input, assistant_output): self.short_term.append({ "user": user_input, "assistant": assistant_output }) # 超过阈值时,生成摘要并存入长期记忆 if len(self.short_term) > 5: summary = self._generate_summary(self.short_term) self.summaries.append(summary) self.short_term = [] def get_context(self, current_query): # 检索相关记忆片段 relevant = self.vector_store.similarity_search(current_query, k=3) return "\n".join([s.content for s in relevant])
四、错误处理:Agent 犯错时怎么办
Agent 调用工具时,失败是常态。你需要提前设计好容错策略。
WARNING
上线前必须设计好容错策略,否则 Agent 出错时就是"裸奔"状态。
4.1 重试机制
import timefrom functools import wrapsdef retry_on_failure(max_attempts=3, delay=1.0): def decorator(func): @wraps(func) def wrapper(*args, **kwargs): for attempt in range(max_attempts): try: return func(*args, **kwargs) except Exception as e: if attempt == max_attempts - 1: raise time.sleep(delay * (attempt + 1)) # 指数退避 return wrapper return decorator## 使用@retry_on_failure(max_attempts=3)def call_api(tool_name, params): # API 调用逻辑 pass
4.2 优雅降级
def execute_with_fallback(tool_name, params, fallback_result="无法完成该操作"): try: result = call_tool(tool_name, params) return {"status": "success", "data": result} except ToolNotFoundError: return {"status": "fallback", "message": "工具不可用,请尝试其他方式"} except RateLimitError: time.sleep(5) return execute_with_fallback(tool_name, params) # 递归重试 except Exception as e: return {"status": "error", "message": fallback_result}
五、评估测试:别只靠"感觉"
开发 Agent 最容易犯的错误:用几个例子测了测,感觉 OK,就上线了。结果上线后用户说什么它都听不懂。
5.1 建立评估数据集
## 评估数据集示例EVAL_DATASET = [ { "input": "帮我把这份报告翻译成英文", "expected_tools": ["document_loader", "translator"], "expected_keywords": ["翻译", "英文"] }, { "input": "今天周三,提醒我下午三点开会", "expected_tools": ["calendar_check", "reminder_set"], "expected_keywords": ["周三", "下午三点", "开会"] }, # ... 至少准备 30-50 条,覆盖主要场景]def evaluate_agent(agent, dataset): results = [] for case in dataset: response = agent.run(case["input"]) score = calculate_similarity(response, case["expected"]) results.append({ "input": case["input"], "score": score, "passed": score > 0.7 }) # 输出统计 passed = sum(1 for r in results if r["passed"]) print(f"通过率: {passed}/{len(results)} = {passed/len(results)*100:.1f}%") return results
5.2 关键指标
NOTE
评估 Agent 时需要关注的核心指标:
• 工具调用准确率:Agent 是否在正确时机调用正确工具
• 任务完成率:用户意图是否被满足
• 响应延迟:P95 延迟是否在可接受范围(一般 < 5s)
• 成本控制:每次任务消耗的 token 数
六、成本控制:LLM 调用很贵
很多团队做 demo 时忽略了成本,上线后收到账单才发现:一天烧了几百块。
6.1 善用小模型
不是所有任务都需要 GPT-4。
def route_to_model(task_type, query): # 简单查询用小模型,省钱 if task_type == "simple_qa": return ChatOpenAI(model="gpt-3.5-turbo") # 复杂推理用大模型 elif task_type == "complex_reasoning": return ChatOpenAI(model="gpt-4") # 批量处理用更便宜的 elif task_type == "batch_summary": return ChatOpenAI(model="gpt-3.5-turbo-16k")def classify_task(query): # 用小模型做分类,决定用什么模型处理 classifier = ChatOpenAI(model="gpt-3.5-turbo") classification_prompt = f"""判断以下查询的复杂度: - simple: 简单问答,不需要推理 - complex: 需要多步推理或复杂理解 查询:{query}""" result = classifier.invoke(classification_prompt) return "simple" if "simple" in result.content else "complex"
6.2 缓存重复请求
from functools import lru_cache@lru_cache(maxsize=1000)def cached_llm_call(prompt_hash, model): # 相同语义的问题,命中缓存直接返回 # 需要配合语义缓存,不能只靠字符串 hash pass## 更精确的语义缓存from langchain.cache import SemanticCachefrom langchain.embeddings import OpenAIEmbeddingsSemanticCache.from_llm( llm=ChatOpenAI(model="gpt-3.5-turbo"), embedding=OpenAIEmbeddings())
七、迭代优化:从 MVP 到生产
构建 Agent 不是一蹴而就的,需要分阶段迭代。
八、这些坑,你可能会遇到
坑 1:工具调用死循环
Agent 反复调用同一个工具,拿到的结果相似但不满足,然后继续调用……
解决:设置最大调用次数,超限后强制返回或转人工。
executor = AgentExecutor( agent=agent, tools=tools, max_iterations=10, # 最多调用 10 次工具 max_execution_time=30 # 或最多执行 30 秒)
坑 2:Prompt 注入
恶意用户试图通过 Prompt 注入让 Agent 执行未授权操作。
解决:在工具层做权限校验,不要完全信任 LLM 的输出。
def execute_tool(tool_name, params, user_id): # 检查用户权限 if not has_permission(user_id, tool_name): return {"error": "权限不足"} # 参数校验 if not validate_params(params, tool_name): return {"error": "参数格式错误"} return actual_execute(tool_name, params)
坑 3:上下文溢出
长对话导致 token 超限,Agent "失忆"。
解决:定期总结 + 限制上下文长度。
九、总结:核心 Takeaway
1. 从简单开始:先跑通最小闭环,不要一开始就设计复杂架构
2. 工具描述决定上限:工具是 Agent 能力的边界,描述要清晰、结构化
3. 记忆要有层次:短期 + 摘要 + 长期,按需选择
4. 错误处理是底线:重试 + 降级 + 监控,上线必备
5. 评估驱动迭代:没有量化评估,就没有优化方向
6. 成本要可控:小任务用小模型,做好缓存
7. 迭代是常态:MVP → 扩展 → 稳定 → 上线
IMPORTANT
构建 AI 智能体,本质是构建一个"会犯错但能快速纠正"的系统。不要追求一步到位,要追求持续优化。
十、参考资料
[来源1] Reddit - r/AI_Agents: "10 things I'd tell anyone starting to build AI agents" (来源)
夜雨聆风