Prompt 与 Agent 工作流上线前:一份不漏项的验收清单
本文围绕“从原型到生产的验收清单”梳理可执行的工程取舍与检查重点。文中的配置、阈值和示例用于说明设计方法;接入实际项目时,应根据业务场景、监控数据和依赖能力完成验证。
将一个 Prompt 原型转化为生产级可用功能,本质上是一场从“凭感觉调参”到“严密工程验收”的跨越。
生产化转型的四大死角与防范手段
在原型验证阶段,我们倾向于假设模型总是听话的。但在生产环境中,必须假设模型随时可能“不听话”。
flowchart TD
A[原型阶段:单一 Prompt 调试] --> B(生产阻碍: 结构偏移/死循环/幻觉/超时)
B --> C[构建结构化 Schema 强制校验]
B --> D[引入 Agent 状态机终止条件]
B --> E[设计 Prompt 版本对比与回归集]
C --> F[上线前 10 维验收清单]
D --> F
E --> F
F --> G[生产级 Agent 稳定运行]
针对原型走向生产的常见死角,防范手段需要前置到代码架构中:
- 死角一:结构化输出撕裂。模型偶尔会在 JSON 前后包裹多余的说明文字,或漏掉某个关键字段。需要引入 Pydantic 或 JSON Schema 进行严格的强类型校验,校验失败触发结构自愈重试。
- 死角二:Agent 工具调用的无限递归。当 Agent 发现某个工具返回空结果时,它可能会换个参数重新调用,导致步数瞬间达到上限。必须在代码层强制注入最大步数计数器与死循环检测器。
- 死角三:语气风格的漂移。提示词中简短的一句“语气要温柔”,在面对用户抱怨时可能演化为敷衍。需要将语气约束拆解为明确的正反例范本(Few-Shot Shots)。
生产级 Agent 验收的 10 维检查清单
在将 Agent 功能合并到主干分支前,建立一套客观的验收清单至关重要。这套清单不依赖主观感觉,而是用明确的代码指标说话:
- Schema 容错度:是否具备 JSON 代码块自动提取与缺失字段默认填充能力?
- 最大步数限制:单次任务 Agent 最多允许调用几次工具?(建议上限 5 步)
- 幻觉拦截:当检索结果为空时,模型是否能坦诚告知而非编造答案?
- 敏感词与安全性:输入与输出是否挂载了安全过滤钩子?
- Prompt 版本跟踪:当前提示词是否有 Git 版本的明确映射?
- 响应延时分布:P95 延时是否被控制在用户可接受的阈值内?
- 降级策略:模型 API 全线故障时,是否有离线模版兜底?
- Token 消耗上限:单次 Agent 执行总 Token 是否设置硬性 Limit?
- 日志与链路追踪:是否记录了 Prompt 的输入、输出及中间工具调用链?
- 自动化测试覆盖:是否拥有至少 20 个边界 Case 的回归测试集?
完整工程实现:带 Schema 校验与死循环检测的 Agent 框架
下面是基于 Python 编写的 Agent 执行引擎,实现了结构化输出校验、工具调用步数限制、死循环防护以及自动化验收测试驱动。
import json
import logging
import re
from typing import Dict, Any, List, Callable, Optional
from pydantic import BaseModel, Field, ValidationError
logging.basicConfig(level=logging.INFO, format="%(asctime)s - [%(levelname)s] - %(message)s")
logger = logging.getLogger("AgentProductionEngine")
# 1. 定义生产级输出数据结构
class AgentActionSchema(BaseModel):
thought: str = Field(description="Agent 的思考过程")
action_name: str = Field(description="调用的工具名称,若完成则为 'finish'")
action_input: Dict[str, Any] = Field(default_factory=dict, description="工具入参")
final_response: Optional[str] = Field(default=None, description="最终呈现给用户的温馨回答")
class ToolRegistry:
"""Agent 可用工具注册表"""
def __init__(self):
self._tools: Dict[str, Callable] = {}
def register(self, name: str, func: Callable):
self._tools[name] = func
def execute(self, name: str, params: Dict[str, Any]) -> str:
if name not in self._tools:
return f"Error: Tool '{name}' is not registered."
try:
return self._tools[name](**params)
except Exception as e:
return f"Error executing tool '{name}': {str(e)}"
class ProductionAgentRunner:
"""具备生产验收标准的 Agent 执行引擎"""
def __init__(
self,
tool_registry: ToolRegistry,
max_steps: int = 4,
prompt_version: str = "v1.2.0"
):
self.tools = tool_registry
self.max_steps = max_steps
self.prompt_version = prompt_version
def _extract_json(self, raw_output: str) -> str:
"""从模型输出中顽强剥离 JSON 内容"""
match = re.search(r"\{.*\}", raw_output, re.DOTALL)
if match:
return match.group(0)
return raw_output.strip()
def parse_model_output(self, raw_text: str) -> AgentActionSchema:
"""结构化解析与 Schema 容错校验"""
clean_str = self._extract_json(raw_text)
try:
data = json.loads(clean_str)
return AgentActionSchema(**data)
except (json.JSONDecodeError, ValidationError) as err:
logger.warning(f"结构解析失败 ({err}),尝试构造纠错 Schema")
# 降级:将原始文本包装为 finish 操作
return AgentActionSchema(
thought="解析模型输出失败,触发结构降级",
action_name="finish",
action_input={},
final_response=raw_text.strip()
)
async def run(self, user_goal: str, llm_simulator: Callable) -> Dict[str, Any]:
"""执行 Agent 迭代循环,内置防死循环机制"""
history_trace = []
visited_actions = [] # 用于死循环检测
current_step = 0
logger.info(f"开启 Agent 任务 [Prompt 对应版本: {self.prompt_version}] | 目标: '{user_goal}'")
while current_step < self.max_steps:
current_step += 1
logger.info(f"--- 执行第 {current_step}/{self.max_steps} 步 ---")
# 调用 LLM 模拟器或真实 API
raw_response = await llm_simulator(user_goal, history_trace)
action_obj = self.parse_model_output(raw_response)
history_trace.append({"step": current_step, "thought": action_obj.thought, "action": action_obj.action_name})
# 检查终止条件
if action_obj.action_name == "finish":
logger.info("Agent 顺利完成任务目标。")
return {
"status": "success",
"total_steps": current_step,
"final_response": action_obj.final_response,
"trace": history_trace
}
# 死循环检测:如果连续两次调用完全相同的工具和入参
action_signature = f"{action_obj.action_name}:{json.dumps(action_obj.action_input)}"
if visited_actions.count(action_signature) >= 2:
logger.error(f"检测到死循环重复调用: {action_signature},强制挂起")
return {
"status": "loop_detected",
"total_steps": current_step,
"final_response": "抱歉,正在重新梳理思绪,请稍后再试一次吧。",
"trace": history_trace
}
visited_actions.append(action_signature)
# 执行工具
logger.info(f"调用工具: {action_obj.action_name} | 参数: {action_obj.action_input}")
tool_result = self.tools.execute(action_obj.action_name, action_obj.action_input)
history_trace.append({"step": current_step, "tool_result": tool_result})
# 超过最大步数拦截
logger.warning(f"任务超出最大步数上限 ({self.max_steps}),启动强制降级")
return {
"status": "max_steps_exceeded",
"total_steps": current_step,
"final_response": "整理信息花的时间比预想稍长了一些,这是目前为你总结好的要点。",
"trace": history_trace
}
# 自动化验证与测试
def mock_weather_search(city: str) -> str:
return f"{city} 今天晴朗,温度 22℃,微风,非常适合出门散步。"
async def mock_llm_logic(goal: str, trace: List[Dict]) -> str:
"""模拟 LLM 的多步响应行为"""
if len(trace) == 0:
return json.dumps({
"thought": "用户想了解天气,我需要先调用 weather_search 工具",
"action_name": "weather_search",
"action_input": {"city": "杭州"}
})
else:
return json.dumps({
"thought": "已获取到天气信息,生成最终温馨回复",
"action_name": "finish",
"action_input": {},
"final_response": "杭州今天阳光明媚,22℃ 的天气刚刚好。忙完手头的事,不妨去窗边晒晒太阳。"
})
async def main():
registry = ToolRegistry()
registry.register("weather_search", mock_weather_search)
runner = ProductionAgentRunner(tool_registry=registry, max_steps=3)
result = await runner.run("帮我查查杭州的天气并给点建议", mock_llm_logic)
print("\n=== Agent 执行结果汇总 ===")
print(f"执行状态: {result['status']}")
print(f"耗费步数: {result['total_steps']}")
print(f"最终输出: {result['final_response']}")
if __name__ == "__main__":
asyncio.run(main())
建立可持续的测试集:用真实数据压测 Prompt
在原型阶段,我们习惯手动在网页界面里敲几句话测试。但上线后,任何一次对 System Prompt 的修改,都可能导致原本正常的场景出现退化。
一套可落地的生产回归机制应当包含:
- 边界 Case 积累库:将用户线上曾经触发过报错的输入、极端长文本输入、拼写错误的语句整理成 JSONL 格式的测试集。
- 自动化得分比对:每次调整提示词后,跑一遍脚本,检查结构化解析成功率、敏感词拦截率以及平均耗时。
- 灰度发布与观察期:将新版本的 Prompt 先切入 5% 的流量,观察日志中的重试率和用户主动重发的比例,确认稳定后再全量覆盖。
把 Prompt 从随手写的草稿,打磨成带有严格类型约束、异常熔断和自动化测试保障的生产功能,这才是让 AI 应用稳健落地的工程力量。

257

被折叠的 条评论
为什么被折叠?



