1. 技术文章/

Agent 开发前置基础:从 LLM 调用到可控 Agent 的完整知识框架

·2043 字·5 分钟

如果你要从零开始理解 Agent 开发,这篇文章是索引 #

过去三周,我通过 SecurityClaw 这个开源 SOC Agent 框架,把 Agent 开发的 18 个核心主题走了一遍。这篇文章不是教程——它是一个知识地图,告诉你每个概念在哪个文件里,以及它们之间的调用关系。

如果你想按章节系统读完整个专题,直接看这里:SecurityClaw 学习笔记总纲

Agent 开发知识框架


第一层:LLM 是引擎,不是大脑 #

很多人入门 Agent 的第一个误区是把 LLM 当成决策中枢。实际上,LLM 只负责一件事:给定状态,输出下一步动作建议。所有"能不能执行"、“执行到一半怎么办”、“状态怎么恢复”——这些是代码的责任。

# Agent 的核心不是 LLM,是这个循环体
class AgentLoop:
    def run(self, state: AgentState) -> FinalOutput:
        while not self.should_stop(state):
            action = self.llm.plan(state)      # LLM 建议下一步
            result = self.execute(action)       # 代码验证 + 执行
            state = self.reflect(state, result) # 评估结果,更新状态
        return state.final_output

这个模式在 SecurityClaw 的多个文件中反复出现:core/runner.py(编排层)、core/chat_router/logic.py(LangGraph 循环)、skills/ 下的各个技能执行器。


第二层:六个必须理解的概念 #

1. 状态管理(State) #

Agent 的"记忆"是一个显式的数据结构,不是隐式的对话历史:

class AgentState(TypedDict):
    messages: list[Message]           # LLM 对话记录
    task_plan: Optional[Plan]         # 当前执行计划
    execution_history: list[Step]     # 已完成步骤(含成功/失败)
    context: dict[str, Any]           # 注入的环境(DB、LLM、配置)
    error_count: int                  # 连续失败计数(防止死循环)
    final_output: Optional[str]       # 最终输出

为什么用 TypedDict 而不是 Pydantic? 在 LangGraph 的节点间传递状态时,不可变字典操作({**state, key: val})比 Pydantic 模型的 .copy(update=...) 更高效,且与 LangGraph 的 checkpoint 机制兼容。

2. 工具调用(Tool Calling) #

def dispatch(tool_name: str, args: dict, runtime: Runtime) -> Observation:
    # 模型只建议 tool_name + args,代码负责五步验证
    if tool_name not in runtime.whitelist:       return error("not allowed")
    if not runtime.validator.check(args):        return error("invalid args")
    if not runtime.authorizer.check(tool_name):  return error("permission denied")
    if runtime.budget.remaining < cost:          return error("budget exceeded")
    return runtime.execute(tool_name, args)      # 终于执行

关键设计:工具调用的每一步失败都返回一个结构化的 Observation 对象(含 statuserror),而不是抛异常。这让 LLM 能在下一轮规划时看到"上次为什么失败",决定是否重试。

3. 规划与循环(Planning & Loop) #

flowchart TD
    Input[用户输入] --> Plan[LLM 规划<br/>生成 Action 列表]
    Plan --> Execute[执行 Action]
    Execute --> Eval{评估结果}
    Eval -->|成功| Next{还有 Action?}
    Eval -->|失败| Retry{重试次数 < 3?}
    Retry -->|是| Plan
    Retry -->|否| Final[强制输出]
    Next -->|是| Execute
    Next -->|否| Final

error_count >= 3 强制停止是这个循环里最重要的安全阀。没有它,LLM 可能在"执行失败→重新规划→再次失败"的循环中消耗所有 token budget。

4. RAG 与上下文工程 #

# 一个好的 build_context 不只是拼接文本
def build_context(query: str, state: AgentState) -> str:
    # 1. 语义检索
    docs = rag.retrieve(query, k=5)
    # 2. 按 relevance score 过滤(> 0.7 才保留)
    relevant = [d for d in docs if d.score > 0.7]
    # 3. 格式化:源 + 时间 + 内容
    return "\n".join(f"[{d.source}] {d.timestamp}: {d.text}" for d in relevant)

SecurityClaw 的 RAG 引擎(core/rag_engine.py)有三个核心方法:store()(嵌入→存向量库)、retrieve()(kNN 搜索+keyword fallback)、build_context()(格式化→注入 prompt)。

5. 安全护栏(Guardrails) #

Agent 的权限检查必须发生在执行前,不是在规划阶段:

# 这是 Agent 的安全边界——每条权限规则都是一个显式的 deny
class PermissionRule:
    tool: str           # 哪个工具
    require_role: str   # 需要什么角色
    max_args_len: int   # 参数长度限制
    rate_limit: int     # 每分钟最大调用次数

SecurityClaw 把权限收敛到 AgentRunner.execute_step() 这一个函数里,而不是分散在各个 skill 中。这样不会出现"某个 skill 忘了校验权限"的 bug。

6. 可观测性(Observability) #

没有可观测性的 Agent 就是一个黑盒。SecurityClaw 在 core/agent_state.pyexecution_history 字段中记录了每一步的完整信息:

class StepResult:
    status: Literal["SUCCESS", "ERROR", "TIMEOUT", "DENIED"]
    action: Action           # 哪个动作
    tool_name: str           # 调了哪个工具
    duration_ms: int         # 执行耗时
    error: Optional[str]     # 失败原因
    data: Optional[dict]     # 返回数据

有了 execution_history,你可以回溯任何一个 Agent 决策:“为什么它选了那个工具?执行了多久?为什么失败了?”


知识框架总览 #

层级概念SecurityClaw 对应文件核心问题
1LLM 调用core/llm_provider.py怎么让 LLM 输出结构化方案?
2状态管理core/agent_state.py多步之间如何不丢上下文?
3工具调用core/runner.py:dispatch()谁负责验证和执行?
4规划循环core/chat_router/logic.py执行失败后如何重试?
5RAG 检索core/rag_engine.py如何注入外部知识?
6安全护栏core/runner.py:execute_step()权限在哪里检查?
7技能系统skill_loader.py + skills/如何动态加载新能力?
8可观测性execution_history出错了怎么回溯?

读完这套系列,你应该能回答这三个问题 #

  1. Agent 和"能调工具的 LLM"有什么区别? — Agent 有显式的状态管理、执行前验证、失败恢复机制。LLM 只管规划,代码负责所有执行边界。

  2. 为什么用 LangGraph 而不是手写 while 循环? — LangGraph 提供了检查点(中断+恢复)、条件路由、状态隔离,手写循环要实现这些得自己搞序列化。

  3. 最容易被忽略的设计是什么? — 安全护栏和可观测性。大多数 Agent demo 只关心"能不能跑通",但生产环境里,“跑错了怎么办"和"为什么跑错了"比"能不能跑"重要十倍。


基于 SecurityClaw 源码分析的 18 篇学习笔记系列总结