LangGraph教程之后:硬边、模式与安全层
通过类型检查、模式刷新、成本路由及多层验证,将可运行的 SQL 代理转变为具备防御能力的版本。
教程完成后显示绿色对勾
LangGraph教程旨在让图结构能够正常运行。当有人询问为何某个决策是安全的时,评估便开始了。此次重写记录了将一个可运行的SQL分析员风格智能体转变为具备防御能力的系统的整个过程:这些系统无法绕过安全检查,具有结构化的输出、最新的模式结构、成本意识,以及在编写过程中发现的错误。
情境与任务
教程提供的是代码路径,而非不变规则。任务是在确保项目正常运行的同时,让每个分支在审查时都能被解释清楚——尤其是那些执行SQL的操作。
行动1 —— 两种架构确保无法绕过安全检查
安全机制必须是硬性约束,而非提示建议。结构化的判断应体现在类型化的模式结构中:
class JudgeAgentSchema(BaseModel):
answer: Literal["yes", "no"] = Field(
description="Return 'yes' if the SQL query ONLY retrieves data (like SELECT). "
"Return 'no' if it modifies data (like INSERT, UPDATE, DELETE, DROP)."
)
comments: str = Field(default="", description="Reasoning behind the verdict")
llm_judge = llm.with_structured_output(schema=JudgeAgentSchema)
带有明确条件的路径:
def is_safe_sql_condition(state: AgentSchema) -> str:
if state.is_safe.lower() == "yes":
return "Execute_SQL"
return "Cancel_SQL_if_Not_Safe"
当模型偏离预设词汇表时,就会出现验证错误:
ValidationError: Input should be 'yes' or 'no'
[type=literal_error, input_value='No', input_type=str]
@field_validator('answer', mode='before')
@classmethod
def normalize_answer(cls, v):
if isinstance(v, str):
v = v.strip().lower()
return v if v in ("yes", "no") else "no" # fail closed
ValidationError: Input should be 'yes' or 'no'
[type=literal_error, input_value='', input_type=str]
需谨慎设置枚举值和默认值:
is_safe: Literal["yes", "no"] = Field(default="no") # fail closed
generated_sql_query: str = Field(default="")
messages: Annotated[list, add] = Field(default_factory=list) # not default=[]
PydanticJsonSchemaWarning: Default value (...) is not JSON serializable
comments: str = (
Field(..., description="..."), # ← trailing comma makes this a tuple
)
def prompt_query_context(state: AgentSchema) -> AgentSchema:
database_object = Database(connection_details)
schema_info = database_object.get_schema_details("public")
行动2——将结构化输出视为可编程组件
一旦安全答案变为固定值,边界条件就会转化为代码。模型是一个具有明确契约的组件,而非控制流的描述者。
行动3——每次运行时重新获取架构
提示词中使用的过时架构会导致生成错误的SQL语句。刷新架构会消耗令牌,而使用过时架构则可能引发问题。除非明确指定了版本号,否则对于不断变化的数据库,建议每次运行都重新获取架构。
行动4——智能体成本≠处理流程成本
智能体会循环执行任务。在规划预算时,需考虑递归限制以及用于路由的更便宜的模型:
def pick_llm(model_level: str) -> ChatAnthropic:
if normalized_level == "basic":
return ChatAnthropic(model="claude-haiku-4-5", temperature=0)
elif normalized_level == "advanced":
return ChatAnthropic(model="claude-sonnet-4-6", temperature=0)
elif normalized_level == "premium":
return ChatAnthropic(model="claude-opus-4-6", temperature=0)
暴露出的编码错误
还原器与节点都在追加数据
messages: Annotated[list, add] = Field(default_factory=list)
state.messages = state.messages + [response] # full list: old + new
return state # reducer adds it AGAIN
def sql_node(state: DataAgentSchema):
response = sql_analyst.invoke({...})
return {"messages": [AIMessage(content=response["final_answer"])]}
请选择一种架构元素:reducer或node,不可同时选择。
将整个子代理状态向上传递
response = sql_analyst.invoke({...}) # returns the full AgentSchema dict
state.messages = state.messages + [response]
仅传递父级所需的字段。
单层概率安全机制
数据库凭证与SQL解析必须为模型判断提供支撑:
POSTGRES_USER: agent_user
POSTGRES_PASSWORD: agent_pass
import sqlparse
def is_read_only(sql: str) -> bool:
statements = sqlparse.parse(sql)
if len(statements) != 1: # blocks stacked queries
return False
return statements[0].get_type() == "SELECT"
CREATE ROLE agent_readonly LOGIN PASSWORD '...';
GRANT CONNECT ON DATABASE agent_db TO agent_readonly;
GRANT USAGE ON SCHEMA public TO agent_readonly;
GRANT SELECT ON ALL TABLES IN SCHEMA public TO agent_readonly;
多层防御机制:模型门控 + 解析器 + 最小权限数据库用户。
结果与上限
该图结构已具备可审查性:边节点确保安全,模式验证严格封闭,成本设置合理,且各层之间存在重叠。其上限在于持续进行的评估与混沌测试——而非又一篇教程章节。
应坚持的实践方法
为架构分支编写ADR。测试不符合条件的路径。记录模式版本。分离工具的IAM权限。每次状态变更后重新检查reducer函数。教程到代码运行为止,而实际生产则从经过深思熟虑的决策开始。
有效的数值直觉
假设某个目标步骤的耗时为10毫秒,而某个方案提议使用5个token,其中3个token的平均接受率为60%。只要接受率保持良好,即使考虑到方案带来的额外开销,每个被接受的token的实际成本仍低于传统的单个token步骤。但如果接受率降至约1个token,该方案就会失败。正是这种敏感性使得仪表板比个人经验更可靠。
质量回归协议
在全局启用之前,需在事实问答、编程及拒绝处理模块中测试固定提示词的效果。当设置为精确匹配分布时,比较令牌完全相同的比例,同时检查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优势。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测扩展量的变化。糟糕的调度策略会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中更改相关设置。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比口头描述更有效。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中运行固定提示词。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测性扩展带来的变化。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比口头描述更有效。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中测试固定提示词的效果。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测扩展量的不确定性。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能面临工具调用、数据检索或客户端 Markdown 处理的延迟。需对整个流程进行端到端追踪。在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
供审核者理解的叙述框架
在 Pull Request 中需解释为何存在两种架构:其中一种路径能够证明由于边界条件缺失,跳过相关保护机制是不可能的。这样的图表正是审计人员所需要的。同时应附上那些试图在缺乏有效安全校验值的情况下调用 SQL 节点的失败测试案例。
在质量监控面板旁解释成本监控面板,避免出现“直接使用最大模型”这种做法。通过一个关于列名被修改的实例来说明模式刷新的流程。故事比单纯的检查清单更具说服力,但检查清单也应保留。
经过验证的数值直觉
假设某个目标步骤的成本为10毫秒,而草案建议使用5个标记符,其中3个标记符的平均接受率为60%。只要接受率保持良好,即使考虑到草案带来的额外开销,每个被接受的标记符的实际成本仍会低于传统使用单个标记符的方案。但如果接受率降至约1个标记符,该方案就会失败。正是这种敏感性使得监控面板比个人经验更有效。
质量退化检测机制
在全局启用之前,需在事实问答、编程及拒绝处理模块中测试固定提示词的效果。当设置为精确匹配分布时,比较令牌完全相同的比例,同时检查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优势。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测扩展量的变化。糟糕的调度策略会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中更改相关设置。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比口头描述更有效。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中测试固定提示词的效果。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测扩展带来的变化。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比口头描述更有效。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中运行固定提示词。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测性扩展带来的变化。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比口头描述更有效。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中测试固定提示词的效果。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测扩展带来的变化。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能需等待工具调用、数据检索或客户端端的 Markdown 处理。应进行端到端追踪,否则在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在可测量的条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些机制:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。
基于数据的直观理解
假设每个目标处理步骤需要10毫秒时间,而某种方案先生成5个标记符,其中3个的平均接受率为60%。只要接受率保持良好,即使考虑到方案生成带来的额外开销,每个被接受的标记符的实际成本仍低于传统的单个标记符处理方式。但如果接受率降至约1个标记符,该方案就会失去优势。正是由于这种敏感性,数据看板才比零散的案例更有价值。
质量退化检测方案
在全局启用之前,需在实际问答、编程及拒绝处理场景中运行固定提示词。当设置为精确分布匹配时,比较完全相同的token比例,排查是否存在系统性偏差。如需提前终止测试,可跟踪分级任务中的胜率以及现有条件下的人类偏好数据。
硬件部署
尽可能将草稿模型与目标模型部署在同一节点上。跨主机运行草稿模型会增加网络抖动,从而抵消已取得的优化效果。注意内存占用:两个模型加上KV缓存可能会让原本能够容纳一个模型的服务器出现内存不足。
交互调度
连续批处理服务器必须考虑推测性扩展带来的变化。糟糕的调度机制会导致批次碎片化,降低资源利用率。应与服务维护人员协调,切勿仅在应用代码中修改相关参数。
剩余的瓶颈问题
尽管解码效率有所提升,用户仍可能面临工具调用、数据检索或客户端 Markdown 处理的延迟。需对整个流程进行端到端追踪。在错误的部分进行优化只会浪费工程时间。
总结回顾
顺序解码是自回归结构带来的固有开销。在特定条件下,推测性解码与提前终止机制能够降低这一开销。应像处理任何生产级功能一样严格管理这些技术:设定指标、设置标志、准备回滚方案,并明确服务平台团队中的责任人员。