1. SecurityClaw 学习笔记/

09 Agent 代码模板:从通用骨架映射到 SecurityClaw

结论先说:通用 Agent 模板没有价值,真正有价值的是“模板如何落地到真实系统” #

你在网上能看到无数 Agent 模板,核心都差不多:

输入 -> 规划 -> 执行 -> 评估 -> 继续或结束

问题不在这四个词对不对,而在:

  1. 这些词对应代码里的什么结构
  2. 状态字段怎么拆
  3. 技能和工具怎么接进循环
  4. 停止条件和恢复路径怎么定义

SecurityClaw 的价值就在这里:它不是讲概念,而是把这套模板落成了可运行的骨架。


一、先看最小骨架:AgentState 是整个模板的地基 #

class AgentState(TypedDict):
    question: str
    plan: list[dict]
    skill_results: list[dict]
    step_count: int
    max_steps: int
    evaluation: str
    trace: list[dict]

这 7 个字段就够搭起一个多步 Agent。把它们重新分类,会更容易理解:

1.1 输入层 #

  • question

1.2 规划层 #

  • plan
  • step_count
  • max_steps

1.3 执行层 #

  • skill_results

1.4 评估与记忆层 #

  • evaluation
  • trace

你会发现,一个通用 Agent 模板真正需要的不是“很多能力”,而是少量但边界清晰的状态字段


二、通用模板如何映射到真实节点 #

在 SecurityClaw 里,这套模板被拆成图中的几个节点:

flowchart TD
    Start((开始)) --> Decide[decide_node]
    Decide --> Execute[execute_node]
    Execute --> Evaluate[evaluate_node]
    Evaluate --> ShouldLoop{should_loop}
    ShouldLoop -- 继续 --> Decide
    ShouldLoop -- 结束 --> Format[format_response_node]
    Format --> End((结束))

这张图里,每个节点都不是“业务功能”,而是“模板的一部分”。

2.1 decide_node #

模板里的“规划”

2.2 execute_node #

模板里的“执行”

2.3 evaluate_node #

模板里的“判断结果是否足够”

2.4 should_loop #

模板里的“继续还是停止”

2.5 format_response_node #

模板里的“给用户一个最终回答”

这意味着一件很重要的事:

模板不是抽象 PPT,而是能一一对应到真实函数边界。


三、decide_node 真正负责的不是“思考”,而是产出可验证计划 #

def decide_node(state: AgentState) -> dict:
    if state["step_count"] == 0:
        plan = llm_parse_plan(state["question"])
    else:
        plan = llm_replan(state["question"], state["trace"], state["evaluation"])
    validated_plan = validate_plan(plan)
    return {
        "plan": validated_plan,
        "step_count": state["step_count"] + 1,
    }

这里最重要的不是 LLM 生成 plan,而是:

validated_plan = validate_plan(plan)

也就是说,模板落地到真实项目后,规划不是“模型说了算”,而是:

模型产出计划
  -> 代码验证计划
  -> 只有通过验证的计划才能进入执行层

这是通用 Agent 模板和真实工程之间最核心的差别。


四、execute_node 把“工具调用”变成“技能调用” #

通用模板通常写成:

result = tool(action)

但在 SecurityClaw 里,执行层更像:

def execute_node(state: AgentState) -> dict:
    results = []
    for step in state["plan"]:
        skill_func = registry.get(step["skill"])
        ctx = build_context(state)
        output = skill_func.run(ctx, **step["parameters"])
        results.append({
            "skill": step["skill"],
            "output": output,
            "error": None,
        })
    return {"skill_results": results}

这说明了一个关键映射 #

通用模板里的 tool #

在这里变成了 skill.run(context, **kwargs)

通用模板里的“环境” #

在这里变成了 Context

通用模板里的“结果” #

在这里变成了结构化的 skill_results

所以你可以把 SecurityClaw 理解成:

用技能系统,把通用 Agent 模板中的“工具调用位”填成了真正可扩展的能力层。


五、为什么 trace 比很多人想象的重要 #

很多 Agent 模板只保留:

  • 当前问题
  • 当前计划
  • 当前结果

但 SecurityClaw 多保留了一个 trace

trace: list[dict]

它的意义不是“方便打印日志”,而是让系统有机会:

  1. 做再规划
  2. 做恢复
  3. 输出失败解释
  4. 写入 checkpoint
  5. 做后续 RAG 检索

也就是说,模板里的 trace 不是装饰字段,而是把一次执行过程变成可重复利用的证据链

这一步是很多网上 Agent demo 没做的,所以它们看起来能跑,但一失败就只会说: “抱歉,我没有完成。”

而不是: “我尝试了哪些路径、为什么失败、还能不能继续。”


六、停止条件才是模板真正的灵魂 #

模板最容易被写浅的地方,就是最后的 if done(): break

在真实系统里,“结束”至少有三种完全不同的含义:

  1. 成功结束:答案已经足够
  2. 预算结束:步数或 token 用尽
  3. 失败结束:没有可恢复路径了
def should_loop(state: AgentState) -> Literal["decide", "finish"]:
    if state["evaluation"] == "SUCCESS":
        return "finish"
    if state["step_count"] >= state["max_steps"]:
        return "finish"
    if state["evaluation"] == "ERROR" and no_new_paths(state):
        return "finish"
    return "decide"

你看,这里的“结束”不是一个布尔值,而是一个状态判断系统

这就是为什么我说:

Agent 模板最有价值的部分,不是 Planning,而是 Stopping。


七、通用模板在真实项目里要补上的四件事 #

如果你把一个抽象 Agent 模板真正落地,至少要补四层工程约束:

7.1 计划验证 #

模型说的不一定能执行

7.2 运行时上下文注入 #

技能不能自己乱连依赖

7.3 结构化结果记录 #

执行结果不能只是一段自然语言

7.4 停止与恢复 #

失败时不能只有“重试”这一种手段

没有这四层,模板只是“会循环的 prompt”。


八、这一篇真正要你记住的框架 #

把通用 Agent 模板映射到真实工程,可以记成这张表:

抽象模板SecurityClaw 中的落点真正解决的问题
输入question用户意图进入系统
规划decide_node + plan把问题变成可执行步骤
执行execute_node + skill.run()调用真实能力
评估evaluate_node + evaluation判断结果是否够用
记忆trace让失败、恢复和解释成为可能
停止should_loop控制何时继续、何时结束

如果你能把这张表讲清楚,你就不是“知道 Agent 模板长什么样”,而是真的理解它怎么落地。


九、验证命令 #

python -m pytest tests/test_core/test_chat_router/test_logic.py -v --tb=short

如果你只想看循环是否正确终止:

python -m pytest tests/test_core/test_chat_router/test_logic.py -k "loop or finish or evaluate" -v