要分清一点:"解析失败、字段缺失、字段格式错误"虽然表现不同,但都属于 Structured Output 数据契约没有满足。处理上不要一律重试,而应该"分类 → 修复/重试 → 最终兜底"。
结合你的 DBA Agent,推荐下面这套机制。
| 问题 | 典型情况 | 首选处理 | 最终兜底 |
|---|---|---|---|
| 解析失败 | 不是合法 JSON、JSON 截断、夹杂文本 | Parser/Structured Output Retry | JSON Extract/Repair → Fallback |
| 字段缺失 | 少 cluster、sql |
Schema Validation → LLM重试 | 默认值/补全/人工 |
| 字段格式错误 | need_explain:"yes"、risk_level:"VERY_HIGH" |
Pydantic Validation → LLM重试 | 类型转换/Repair → Fallback |
1. 解析失败:先修复结构,再重试
例如模型返回:
text
分析结果如下:
{
"cluster": "mysql-prod-01",
"database": "order",
"sql": "SELECT * FROM orders"
缺 }。
这时候还没有进入 Pydantic,因为:
python
json.loads(raw)
已经失败。
推荐:
text
LLM
↓
Parser
↓
解析失败
↓
Repair
↓
Parser
↓
成功?
如果是 LangChain Structured Output,可以让框架/模型重新生成结构化结果;如果你走的是自由文本解析路线,可以做:
text
JSON Extract
↓
JSON Repair
↓
Pydantic
例如:
python
try:
data = json.loads(raw)
except JSONDecodeError:
data = repair_json(raw)
但这里不建议自己大量写正则修 JSON。
JSON Repair 可以作为兼容层,但不要把它当核心方案。
更推荐:
text
Structured Output
+
Pydantic
+
有限 Retry
2. 字段缺失:让 Schema 告诉 LLM"缺了什么"
例如 Schema:
python
class SQLAnalysis(BaseModel):
cluster: str
database: str
sql: str
risk_level: Literal["LOW", "MEDIUM", "HIGH"]
need_explain: bool
模型返回:
json
{
"cluster": "mysql-prod-01",
"database": "order",
"sql": "SELECT * FROM orders"
}
缺:
text
risk_level
need_explain
Pydantic:
text
ValidationError
↓
missing field
这时候不要直接让整个 Agent Loop 结束。
应该:
text
LLM
↓
Structured Output
↓
Pydantic
↓
❌ missing field
↓
Error Handler
↓
把校验错误反馈给 LLM
↓
Retry
↓
Pydantic
↓
OK
例如给模型:
text
Structured output validation failed.
Missing required fields:
- risk_level
- need_explain
Please regenerate the complete structured output.
模型重新生成:
json
{
"cluster": "mysql-prod-01",
"database": "order",
"sql": "SELECT * FROM orders",
"risk_level": "MEDIUM",
"need_explain": true
}
通过。
3. 字段格式错误:Pydantic 最适合处理
例如:
json
{
"cluster": "mysql-prod-01",
"database": "order",
"sql": "SELECT ...",
"risk_level": "HIGH",
"need_explain": "yes"
}
Schema:
python
need_explain: bool
但是:
text
"yes" ≠ boolean
Pydantic 校验失败。
处理:
text
Pydantic
↓
ValidationError
↓
Error Handler
↓
Retry
↓
LLM重新生成
再比如:
json
{
"risk_level": "VERY_HIGH"
}
Schema:
python
Literal["LOW", "MEDIUM", "HIGH"]
同样失败。
4. 三种错误实际上应该统一成一个 Error Handler
不要在每个 Node 里面自己处理。
建议:
text
Agent
│
Structured Output
│
Pydantic Validate
│
┌────────┴────────┐
│ │
OK Error
│ │
↓ ↓
Next Node Error Handler
│
┌─────────────┼─────────────┐
↓ ↓ ↓
Parse Missing Type Error
│ │ │
└─────────────┼─────────────┘
↓
Retry
↓
LLM
这样整个 Agent 的错误处理机制是一致的。
5. Retry 一定要有限制
例如:
python
MAX_RETRY = 2
流程:
text
第一次
↓
失败
↓
Retry 1
↓
失败
↓
Retry 2
↓
失败
↓
Fallback
不要:
python
while True:
retry()
否则:
text
LLM
↓
错误
↓
LLM
↓
错误
↓
LLM
↓
错误
↓
无限循环
这就是你最开始说的:
数据格式问题导致 loop 中断
真正工程化的处理不是让它"永远不失败",而是:
失败可恢复,恢复失败后可控退出。
6. DBA Agent 还应该区分"能自动修复"和"不能自动修复"
例如:
A. 可以自动修复
text
JSON少一个 }
→ Repair
text
字段缺失
→ Retry
text
boolean/string类型错误
→ Retry
text
risk_level拼错
→ Retry
这些都属于:
text
格式性错误
可以自动恢复。
B. 不应该自动修复
例如:
json
{
"cluster": "mysql-prod-01",
"sql": "DROP TABLE orders"
}
Schema 完全正确。
但:
text
SQL危险
这不是 Structured Output Error。
应该进入:
text
Business Validator
↓
Security Validator
↓
HITL
而不是:
text
LLM Retry
这是 DBA Agent 特别重要的区别。
7. 最终推荐的完整链路
你的 DBA Agent 可以做成:
text
LLM
↓
Structured Output
↓
Pydantic
↓
┌─────────────────────────────┐
│ Validation │
│ │
│ 解析失败 │
│ 字段缺失 │
│ 字段类型错误 │
└──────────────┬──────────────┘
↓
Error Handler
↓
Retry / Repair
↓
LLM
↓
Pydantic
↓
成功
↓
Business Validator
↓
SQL AST / Rule
↓
Security / ACL
↓
Tool
↓
ToolResult
↓
LangGraph State
最后如果连续失败:
text
Retry 1
↓
Retry 2
↓
仍然失败
↓
Fallback
↓
END / HITL
可以统一返回:
python
class AgentError(BaseModel):
error_type: Literal[
"PARSE_ERROR",
"MISSING_FIELD",
"INVALID_TYPE",
"BUSINESS_ERROR",
"PERMISSION_ERROR",
"TOOL_ERROR"
]
message: str
retryable: bool
retry_count: int
这样 LangGraph 就可以根据:
python
error.retryable
决定走:
text
Retry
还是:
text
Fallback / HITL / END
所以三种错误的核心处理原则是:解析失败 → Repair/Retry;字段缺失 → Schema Validation + Retry;字段格式错误 → Pydantic Validation + Retry。三者最终统一进入 Error Handler,并设置最大重试次数;超过次数必须 Fallback,不能让 Agent Loop 无限跑。