大模型Agent从原型验证走向生产服务,需要解决状态管理、工具安全、异步编排、可观测性和成本控制等一系列工程问题。本文基于三个已落地的生产级Agent项目(客服自动化、数据分析助手、多步RAG问答系统),系统梳理Agent系统的工程化架构设计与实现要点。所有代码示例均可在Python 3.11+环境下复现。
在评估多个开源框架后,我们采用了LangGraph + 自研工具层的组合方案。LangGraph提供基于状态图的显式控制流,将Agent推理过程建模为有向图,每个节点代表一个计算步骤(如调用LLM、执行工具),每条边定义状态流转条件。这种设计使开发者能够精确编排"思考-行动-观察"循环,支持条件分支、并行执行和循环重试等复杂控制模式。
相较于高层封装框架,LangGraph的核心优势在于控制流的透明性——开发者可以明确知道每个状态节点的输入输出,便于调试和性能优化。工具执行层则完全自研,以降低对第三方库的依赖,确保核心逻辑的稳定可控。
Agent状态是整个系统的核心数据结构,需要包含输入、历史对话、中间执行步骤和最终输出。以下为生产环境使用的状态类型定义:
from typing import TypedDict, List, Tuple, Any, Optional
from typing import Literal
class AgentState(TypedDict, total=False):
# 用户当前输入
user_input: str
# 对话历史(符合OpenAI消息格式)
chat_history: List[dict]
# 执行轨迹:[("action_type", "action_name", result), ...]
intermediate_steps: List[Tuple[str, str, Any]]
# 最终输出
final_output: Optional[str]
# 重试计数
retry_count: int
# 当前步骤编号
step_number: int基于状态定义,构建包含"LLM推理节点"和"工具执行节点"的循环图:
from langgraph.graph import StateGraph, END
from langgraph.prebuilt import ToolExecutor
from langchain_openai import ChatOpenAI
# 初始化LLM和工具集
llm = ChatOpenAI(model="gpt-4o", temperature=0.2)
tools = [web_search_tool, code_interpreter_tool, database_query_tool]
tool_executor = ToolExecutor(tools)
def build_messages(state: AgentState) -> List[dict]:
"""将状态转换为LLM消息列表"""
messages = [{"role": "system", "content": system_prompt}]
messages.extend(state.get("chat_history", []))
return messages
def agent_node(state: AgentState) -> dict:
"""LLM推理节点:决定下一步行动或直接回答"""
messages = build_messages(state)
response = llm.bind_tools(tools).invoke(messages)
if hasattr(response, "tool_calls") and response.tool_calls:
return {
"intermediate_steps": [("llm_decision", response.tool_calls[0]["name"], response.tool_calls[0])]
}
return {"final_output": response.content}
def tool_node(state: AgentState) -> dict:
"""工具执行节点:调用具体工具并返回结果"""
last_step = state["intermediate_steps"][-1]
tool_call = last_step[2]
try:
result = tool_executor.invoke(tool_call)
except Exception as e:
result = {"error": f"工具执行异常: {str(e)}"}
return {
"intermediate_steps": [("tool_result", tool_call["name"], result)],
"retry_count": state.get("retry_count", 0) + 1
}
def route_after_agent(state: AgentState) -> Literal["tools", "end"]:
"""条件路由:是否需要调用工具"""
if state.get("final_output"):
return "end"
if state.get("intermediate_steps", []) and state["intermediate_steps"][-1][0] == "llm_decision":
return "tools"
return "end"
# 构建工作流图
workflow = StateGraph(AgentState)
workflow.add_node("agent", agent_node)
workflow.add_node("tools", tool_node)
workflow.set_entry_point("agent")
workflow.add_conditional_edges("agent", route_after_agent, {"tools": "tools", "end": END})
workflow.add_edge("tools", "agent") # 工具执行后返回推理节点
app = workflow.compile()此图执行流程为:agent → 判断是否需调用工具 → 若是则执行tools → 返回agent继续推理,直至LLM直接输出最终答案。LangGraph内置的检查点机制可持久化状态,支持断点续跑和人工介入。
工具调用是生产Agent最易出现故障的环节,需在多个维度建立防护机制。
使用asyncio.wait_for控制单个工具执行时间上限,配合指数退避重试应对瞬时故障:
import asyncio
from tenacity import retry, stop_after_attempt, wait_exponential
def with_timeout(seconds: int = 10):
def decorator(func):
async def wrapper(*args, **kwargs):
try:
return await asyncio.wait_for(func(*args, **kwargs), timeout=seconds)
except asyncio.TimeoutError:
return {"status": "timeout", "error": f"执行超时(>{seconds}s)"}
return wrapper
return decorator
@retry(stop=stop_after_attempt(3), wait=wait_exponential(multiplier=1, min=1, max=5))
@with_timeout(15)
async def call_external_api(params: dict) -> dict:
# 外部API调用实现
pass若Agent需要支持动态代码执行(如数据分析场景),必须在受限环境中运行,防止恶意代码危害宿主系统。采用进程级隔离方案,通过resource模块限制CPU时间和内存:
import resource
import subprocess
import tempfile
import os
import signal
def run_in_sandbox(code: str, timeout_sec: int = 5, memory_mb: int = 256) -> str:
with tempfile.NamedTemporaryFile(mode="w", suffix=".py", delete=False) as f:
f.write(code)
f.flush()
script_path = f.name
try:
# 设置资源限制
resource.setrlimit(resource.RLIMIT_CPU, (timeout_sec, timeout_sec + 1))
resource.setrlimit(resource.RLIMIT_AS, (memory_mb * 1024 * 1024, memory_mb * 1024 * 1024))
result = subprocess.run(
["python3", script_path],
capture_output=True,
text=True,
timeout=timeout_sec,
preexec_fn=lambda: signal.alarm(timeout_sec)
)
return result.stdout if result.returncode == 0 else result.stderr
finally:
os.unlink(script_path)对于更高安全要求的场景,建议采用Docker容器或gVisor等隔离技术。
Agent多实例并发调用外部API时,需控制请求速率避免触发服务端限流:
from aiolimiter import AsyncLimiter
import asyncio
# 每秒最多5次请求
rate_limiter = AsyncLimiter(max_rate=5, time_period=1)
async def rate_limited_invoke(func, *args, **kwargs):
async with rate_limiter:
return await func(*args, **kwargs)为提升问答准确率,Agent需具备访问私有知识库的能力。采用"先检索后决策"的集成模式:在LLM推理前,根据用户输入从向量数据库中检索相关文档片段,将其注入系统提示词作为上下文参考。
from chromadb import PersistentClient
from sentence_transformers import SentenceTransformer
from typing import List
class VectorRetriever:
def __init__(self, persist_path: str, collection_name: str, model_name: str = "BAAI/bge-large-zh-v1.5"):
self.encoder = SentenceTransformer(model_name)
self.client = PersistentClient(path=persist_path)
self.collection = self.client.get_or_create_collection(collection_name)
def retrieve(self, query: str, top_k: int = 3) -> List[str]:
emb = self.encoder.encode(query).tolist()
results = self.collection.query(query_embeddings=[emb], n_results=top_k)
return results["documents"][0] if results else []在build_messages函数中,将检索结果拼接为系统提示词的一部分。检索过程应异步执行,并可配置阈值开关——对于无需外部知识的问题(如简单问候),跳过检索以节省计算资源。
生产Agent难以调试的核心原因是内部决策过程不透明。需要建立三层可观测性机制。
import logging
import time
from functools import wraps
from datetime import datetime
def trace_step(step_name: str):
def decorator(func):
@wraps(func)
async def wrapper(state: dict, *args, **kwargs):
start = time.perf_counter()
logging.info(json.dumps({
"event": "step_start",
"step": step_name,
"input": state.get("user_input"),
"timestamp": datetime.utcnow().isoformat()
}))
try:
result = await func(state, *args, **kwargs)
elapsed = time.perf_counter() - start
logging.info(json.dumps({
"event": "step_end",
"step": step_name,
"elapsed_ms": elapsed * 1000,
"output_preview": str(result)[:200]
}))
return result
except Exception as e:
logging.error(f"Step {step_name} failed: {e}", exc_info=True)
raise
return wrapper
return decorator将所有交互记录持久化至数据库,表结构包含:session_id、step_sequence、llm_prompt、llm_response(截断)、tool_name、tool_input、tool_output、token_usage、latency_ms、success_flag。配合Grafana构建监控看板,实时跟踪成功率、平均延迟、Token消耗趋势。
大模型API调用成本是生产系统必须考虑的约束条件。
使用轻量级分类器(如text-embedding-3-small + 逻辑回归)对用户输入进行意图预判。简单意图(天气查询、常识问答)路由至gpt-3.5-turbo,复杂推理任务路由至gpt-4o。分类器准确率在实测场景中达到92%,成本节省约40%。
使用tiktoken库在每次LLM调用前统计token总数,当超过阈值(如8000 tokens)时触发历史摘要压缩——调用gpt-3.5-turbo对对话历史生成摘要,用摘要替换原始历史。
import tiktoken
def count_tokens(text: str, model: str = "gpt-4") -> int:
enc = tiktoken.encoding_for_model(model)
return len(enc.encode(text))
def should_compress(state: AgentState, max_tokens: int = 8000) -> bool:
total = sum(count_tokens(msg["content"]) for msg in state["chat_history"])
return total > max_tokens相同输入参数的工具调用结果在TTL(如5分钟)内复用,使用Redis作为缓存后端,Key设计为tool_name:hash(arguments)。
复杂业务场景可通过多Agent分工协作处理。LangGraph支持子图嵌套,实现模块化组合。
以"主管-工人"模式为例:主管Agent负责任务拆解和路由,将子任务分发给专业Agent(检索Agent、代码Agent、报告Agent)。每个子图独立维护状态,通过主管节点的路由函数决定流转方向。
# 子图示例
search_subgraph = create_search_agent_graph()
code_subgraph = create_code_agent_graph()
supervisor = StateGraph(OverallState)
supervisor.add_node("router", route_by_intent)
supervisor.add_node("search_worker", search_subgraph.compile())
supervisor.add_node("code_worker", code_subgraph.compile())
supervisor.add_conditional_edges("router", ...)各子图可独立测试和版本迭代,提升系统的可维护性。
Agent输出的非结构化特性使得传统自动化测试难以覆盖。我们采用两层评估体系:
gpt-4作为评判模型,对Agent输出在相关性、完整性和安全性三个维度打分,与基准版本对比。当综合得分下降超过5%时阻断发布。async def evaluate_agent(test_set: List[Tuple[str, str]]) -> float:
total_score = 0.0
for query, gold in test_set:
result = await app.ainvoke({"user_input": query})
score = await judge_with_llm(query, gold, result["final_output"])
total_score += score
return total_score / len(test_set)模块 | 推荐技术方案 | 关键考量 |
|---|---|---|
控制流 | LangGraph状态图 | 显式状态管理,便于调试 |
工具执行 | 自研层 + 超时/重试/沙箱 | 安全与稳定性优先 |
RAG集成 | Chroma + BGE Embedding | 异步检索,按需开关 |
可观测性 | 结构化日志 + 时序数据库 + Grafana | 全链路追踪,异常快速定位 |
成本控制 | 模型路由 + Token压缩 + 缓存 | 在效果与成本间取得平衡 |
评估体系 | 单元测试 + LLM-as-Judge端到端测试 | 量化驱动迭代 |
Agent系统的工程化是一个持续迭代的过程。从第一个可运行的Demo到稳定承载生产流量,需要逐步补全上述各个维度的工程能力。本文梳理的架构方案已在多个项目中得到验证,希望能为相关实践者提供可参考的实现路径。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。