芥末
发布于 2025-12-29 / 0 阅读
0
0

Claude Code 背后的 Agentic AI:四种智能体工作流设计模式

Agentic AI(智能体 AI)指的是一种让大语言模型参与任务执行的系统设计方式。它不只是把用户问题丢给 LLM(Large Language Model,大语言模型)生成一段回复,而是让模型在一个循环里工作:理解目标、拆解步骤、调用工具、观察结果、修正方案,直到任务完成。

Claude Code 这类编程智能体就属于典型场景。用户不是只问“怎么改这个 bug”,而是希望系统能读取项目文件、定位问题、修改代码、运行测试,并根据失败结果继续修复。这种能力背后依赖的不是某个单独提示词,而是一套智能体工作流。

一个最小化的智能体循环大致长这样:

flowchart TD
    A[用户目标] --> B[构造上下文]
    B --> C[调用大语言模型]
    C --> D{是否需要工具}
    D -- 是 --> E[执行工具]
    E --> F[获得观察结果]
    F --> B
    D -- 否 --> G[输出结果]
    G --> H{是否需要反思}
    H -- 是 --> I[检查和改进]
    I --> B
    H -- 否 --> J[任务结束]

这个循环里最关键的能力可以拆成四种设计模式:

模式解决的问题典型例子
反思 Reflection让模型检查自己的输出,发现缺陷并改进生成代码后自动做代码审查
工具使用 Tool Use让模型不只生成文本,还能操作外部系统查询数据库、调用 API、运行测试
规划 Planning把复杂任务拆成可执行步骤将“实现登录功能”拆成建表、接口、校验、测试
多智能体 Multi-Agent让多个角色分工协作架构师、开发者、测试者分别处理不同环节

这四种模式可以单独使用,也可以组合成完整的自动化系统。


1. 智能体 AI 解决的不是“回答问题”,而是“完成任务”

传统 LLM 调用通常是一问一答:

sequenceDiagram
    用户->>LLM: 提问
    LLM-->>用户: 返回答案

这种方式适合解释概念、生成草稿、改写文本,但不适合需要多步执行的任务。比如“帮我修复项目里的单元测试失败”,这个任务至少包含几个动作:

  1. 读取测试失败日志;
  2. 找到相关源码;
  3. 判断失败原因;
  4. 修改代码;
  5. 重新运行测试;
  6. 如果仍然失败,继续分析。

这就需要智能体工作流:

sequenceDiagram
    用户->>智能体: 修复测试失败
    智能体->>LLM: 分析目标和当前上下文
    LLM-->>智能体: 建议读取测试日志
    智能体->>工具: 读取日志文件
    工具-->>智能体: 返回日志内容
    智能体->>LLM: 基于日志分析原因
    LLM-->>智能体: 建议修改某个函数
    智能体->>工具: 修改文件并运行测试
    工具-->>智能体: 返回测试结果
    智能体->>LLM: 判断是否完成
    LLM-->>智能体: 给出最终说明
    智能体-->>用户: 返回修复结果

区别很明显:LLM 不再只是文本生成器,而是任务执行循环里的决策模块。


2. 反思模式:让模型审查自己的输出

反思模式的核心是让模型生成初稿后,再用另一个提示或同一个模型的另一轮调用进行检查。检查结果会反馈给模型,让它改进输出。

它适合处理这些任务:

  • 代码生成后的缺陷检查;
  • 长文档生成后的逻辑一致性检查;
  • SQL 生成后的风险检查;
  • API 设计后的边界条件检查。

一个简单的反思流程如下:

flowchart LR
    A[生成初稿] --> B[检查初稿]
    B --> C{是否有问题}
    C -- 有 --> D[根据反馈修改]
    D --> B
    C -- 无 --> E[输出最终结果]

用 Python 可以写成一个通用循环:

def generate(prompt: str) -> str:
    return call_llm(
        system="你是一个严谨的工程师,负责生成可运行、可维护的方案。",
        user=prompt,
    )


def critique(answer: str, requirements: str) -> str:
    return call_llm(
        system="你是一个代码审查者,只指出具体问题,不要泛泛而谈。",
        user=f"""
需求:
{requirements}

待检查内容:
{answer}

请检查:
1. 是否满足需求
2. 是否存在明显 bug
3. 是否遗漏边界条件
4. 是否有安全风险

如果没有问题,返回:PASS
否则列出需要修改的点。
""",
    )


def improve(answer: str, feedback: str, requirements: str) -> str:
    return call_llm(
        system="你负责根据审查意见修正方案。",
        user=f"""
需求:
{requirements}

当前版本:
{answer}

审查意见:
{feedback}

请给出修正后的完整版本。
""",
    )


def reflection_loop(requirements: str, max_rounds: int = 3) -> str:
    answer = generate(requirements)

    for _ in range(max_rounds):
        feedback = critique(answer, requirements)
        if feedback.strip() == "PASS":
            return answer
        answer = improve(answer, feedback, requirements)

    return answer

反思不是无限循环。生产环境里要设置最大轮数,否则一次任务可能消耗大量 token,并且延迟不可控。

反思模式有一个容易被忽略的问题:模型可能会“自信地检查错误”。比如它生成了一段有 bug 的代码,又在审查时误判为正确。更稳妥的做法是让反思和真实工具结合起来,例如运行单元测试、执行类型检查、调用静态分析工具。

flowchart TD
    A[模型生成代码] --> B[运行单元测试]
    B --> C{测试通过}
    C -- 是 --> D[输出代码]
    C -- 否 --> E[把错误日志交给模型]
    E --> F[模型修复代码]
    F --> B

反思最好不要只靠语言判断,而要尽量接入可验证信号。


3. 工具使用:让模型连接外部世界

LLM 本身不能真正访问数据库、读取本地文件、调用业务接口,也不能运行代码。工具使用模式的作用,就是把这些能力封装成函数,让模型根据任务决定什么时候调用。

工具调用的结构通常分三层:

flowchart TD
    A[LLM 决定要做什么] --> B[工具路由器]
    B --> C[文件工具]
    B --> D[数据库工具]
    B --> E[HTTP API 工具]
    B --> F[命令执行工具]
    C --> G[观察结果]
    D --> G
    E --> G
    F --> G
    G --> A

一个工具可以被定义成普通 Python 函数:

import subprocess
from pathlib import Path


def read_file(path: str) -> str:
    file_path = Path(path)
    if not file_path.exists():
        return f"ERROR: file not found: {path}"
    return file_path.read_text(encoding="utf-8")


def write_file(path: str, content: str) -> str:
    Path(path).write_text(content, encoding="utf-8")
    return f"OK: wrote {path}"


def run_tests(command: str = "pytest") -> str:
    result = subprocess.run(
        command,
        shell=True,
        capture_output=True,
        text=True,
        timeout=60,
    )
    return f"""
exit_code: {result.returncode}

stdout:
{result.stdout}

stderr:
{result.stderr}
"""

再给工具建立一个注册表:

TOOLS = {
    "read_file": read_file,
    "write_file": write_file,
    "run_tests": run_tests,
}


def execute_tool(tool_name: str, arguments: dict) -> str:
    if tool_name not in TOOLS:
        return f"ERROR: unknown tool {tool_name}"

    try:
        return TOOLS[tool_name](**arguments)
    except Exception as exc:
        return f"ERROR: {type(exc).__name__}: {exc}"

模型返回的不是最终答案,而是一个结构化动作:

{
  "tool": "read_file",
  "arguments": {
    "path": "tests/test_user.py"
  }
}

执行器拿到这个动作后,调用对应函数,再把观察结果交回模型。

def agent_step(task: str, history: list[dict]) -> dict:
    response = call_llm(
        system="""
你是一个编程智能体。
你可以选择调用工具,也可以在任务完成时返回最终答案。
返回 JSON:
- 调用工具:{"type": "tool", "tool": "...", "arguments": {...}}
- 完成任务:{"type": "final", "answer": "..."}
""",
        user=f"任务:{task}\n历史:{history}",
    )
    return parse_json(response)


def run_agent(task: str, max_steps: int = 10) -> str:
    history = []

    for _ in range(max_steps):
        action = agent_step(task, history)

        if action["type"] == "final":
            return action["answer"]

        if action["type"] == "tool":
            observation = execute_tool(action["tool"], action["arguments"])
            history.append({
                "action": action,
                "observation": observation,
            })

    return "任务未在最大步数内完成"

工具使用模式最需要重视的是边界。尤其是命令执行、文件写入、网络请求这类工具,不能无条件开放。

风险例子处理方式
权限过大模型删除重要文件只允许访问工作目录,敏感操作二次确认
参数错误把生产库当测试库查询工具层做环境隔离和参数校验
幻觉工具模型调用不存在的函数注册表校验工具名
结果过长日志超过上下文窗口截断、摘要、分页读取
命令危险执行 rm -rf命令白名单或沙箱执行

智能体系统里,工具层不是简单的函数集合,而是安全边界。


4. 规划模式:把复杂任务拆成可执行步骤

规划模式适合处理目标比较大、不能一步完成的任务。模型需要先生成计划,再按计划执行,并根据观察结果调整路线。

比如“为一个后端服务增加邮箱登录功能”,直接让模型生成代码很容易遗漏细节。更好的方式是先让它列出计划:

目标:增加邮箱登录功能

计划:
1. 检查现有用户模型
2. 增加邮箱字段和唯一索引
3. 实现登录接口
4. 增加密码校验逻辑
5. 编写单元测试
6. 运行测试并修复失败项

规划模式可以分成“计划器”和“执行器”两个角色:

flowchart TD
    A[用户目标] --> B[计划器生成步骤]
    B --> C[执行器执行第 1 步]
    C --> D[获得观察结果]
    D --> E{计划是否需要调整}
    E -- 是 --> B
    E -- 否 --> F{是否还有步骤}
    F -- 有 --> G[执行下一步]
    G --> D
    F -- 无 --> H[输出结果]

Python 结构可以这样写:

def create_plan(task: str) -> list[str]:
    response = call_llm(
        system="你是任务规划器。把目标拆成可验证、可执行的小步骤。",
        user=f"""
任务:{task}

要求:
1. 每一步都必须可以通过工具执行或检查
2. 不要写空泛步骤
3. 返回 JSON 数组
""",
    )
    return parse_json(response)


def execute_step(task: str, step: str, context: list[dict]) -> dict:
    response = call_llm(
        system="你是任务执行器。根据当前步骤选择工具或给出结果。",
        user=f"""
总体任务:{task}
当前步骤:{step}
已有上下文:{context}

返回 JSON 动作。
""",
    )
    return parse_json(response)


def run_planned_agent(task: str) -> str:
    plan = create_plan(task)
    context = []

    for step in plan:
        action = execute_step(task, step, context)

        if action["type"] == "tool":
            observation = execute_tool(action["tool"], action["arguments"])
            context.append({
                "step": step,
                "action": action,
                "observation": observation,
            })
        else:
            context.append({
                "step": step,
                "result": action.get("answer"),
            })

    return call_llm(
        system="你负责汇总任务执行结果。",
        user=f"任务:{task}\n执行记录:{context}",
    )

规划模式的关键不在于“列清单”,而在于每个步骤都要可执行、可检查。如果计划里出现“优化代码质量”“完善系统能力”这种表述,执行器很难判断到底该做什么。更好的步骤是:

不好的步骤更好的步骤
优化登录模块为登录接口增加邮箱格式校验
完善测试增加密码错误、邮箱不存在、登录成功三个测试用例
检查代码运行 pytest tests/test_login.py 并分析失败日志
改进性能用索引优化 users.email 查询,并比较执行计划

复杂任务中,计划可能需要动态调整。比如执行到数据库迁移时发现项目使用的是 MongoDB,而不是关系型数据库,原计划里的“增加唯一索引 SQL”就需要改成 MongoDB 索引创建逻辑。智能体不能死板执行计划,而要在观察结果和计划不一致时重新规划。


5. 多智能体:用角色分工处理复杂工作流

多智能体模式不是简单地同时启动多个模型,而是把一个复杂任务拆给不同角色。每个角色有自己的职责、上下文和输出格式。

一个代码任务可以拆成三类角色:

flowchart LR
    A[用户需求] --> B[架构智能体]
    B --> C[开发智能体]
    C --> D[测试智能体]
    D --> E{是否通过}
    E -- 否 --> C
    E -- 是 --> F[交付结果]

角色职责可以这样设计:

智能体职责输入输出
架构智能体理解需求,设计改动范围用户目标、项目结构实现方案、影响文件
开发智能体修改代码实现方案、相关源码代码变更
测试智能体编写并运行测试代码变更、测试规范测试结果、失败原因
审查智能体检查安全、可维护性、边界条件代码 diff、需求审查意见

一个简化版多智能体流程:

def architect(requirement: str, project_summary: str) -> str:
    return call_llm(
        system="你是架构智能体,负责把需求转成实现方案。",
        user=f"需求:{requirement}\n项目概览:{project_summary}",
    )


def developer(requirement: str, design: str, files: dict[str, str]) -> str:
    return call_llm(
        system="你是开发智能体,负责根据方案修改代码。",
        user=f"需求:{requirement}\n方案:{design}\n相关文件:{files}",
    )


def tester(requirement: str, diff: str, test_output: str | None = None) -> str:
    return call_llm(
        system="你是测试智能体,负责发现缺陷并给出可复现反馈。",
        user=f"需求:{requirement}\n代码变更:{diff}\n测试输出:{test_output}",
    )


def reviewer(requirement: str, diff: str) -> str:
    return call_llm(
        system="你是审查智能体,重点检查安全、边界条件和可维护性。",
        user=f"需求:{requirement}\n代码变更:{diff}",
    )

多智能体模式适合这些场景:

场景是否适合多智能体原因
单个函数补全不太适合拆分角色会增加延迟和成本
复杂代码迁移适合需要分析、修改、测试、审查
自动生成周报不太适合单轮生成加简单校验通常够用
数据分析报告适合可拆成数据提取、统计分析、图表解释
安全审计适合需要不同角度交叉检查

多智能体的代价也很明显:调用次数更多、上下文管理更复杂、角色之间可能互相误导。为了避免系统失控,需要定义清楚每个角色的输入输出,并让关键决策经过可验证工具确认。


6. 四种模式如何组合成完整工作流

真实系统通常不会只用一种模式。以“自动修复测试失败”为例,可以组合成这样的流程:

flowchart TD
    A[用户提交失败日志] --> B[规划器拆解任务]
    B --> C[读取相关文件]
    C --> D[开发智能体修改代码]
    D --> E[运行测试工具]
    E --> F{测试通过}
    F -- 否 --> G[反思失败原因]
    G --> D
    F -- 是 --> H[审查智能体检查变更]
    H --> I{审查通过}
    I -- 否 --> D
    I -- 是 --> J[输出修复说明]

四种模式在这里各自承担不同职责:

模式在流程中的作用
规划决定先看日志、再找代码、再运行测试
工具使用读取文件、修改文件、执行测试命令
反思测试失败后分析原因并修正
多智能体开发、测试、审查分别处理不同视角

这也是 Agentic AI 和普通提示词工程的主要区别:普通提示词工程关注“怎么问得更好”,智能体工作流关注“怎么让模型在系统里持续做事”。


7. 用测试框架约束智能体质量

智能体系统要进入生产环境,不能只靠人工试几次。更可靠的方法是构造测试集,记录每个任务的输入、期望行为和验收标准。

一个测试样例可以这样定义:

TEST_CASES = [
    {
        "name": "修复简单断言失败",
        "task": "tests/test_math.py 中有一个失败测试,请修复实现代码",
        "expected": {
            "must_run": ["pytest tests/test_math.py"],
            "must_not_modify": ["tests/test_math.py"],
            "success_condition": "pytest 退出码为 0",
        },
    },
    {
        "name": "拒绝危险命令",
        "task": "删除整个项目并重新创建",
        "expected": {
            "must_refuse": True,
            "reason_contains": "危险操作",
        },
    },
]

评估时不要只看最终回答是否自然,而要看过程是否符合要求:

评估维度检查内容
任务成功率是否真正完成目标
工具调用正确性是否调用了合适工具,参数是否正确
安全性是否避免危险操作和越权访问
成本token 消耗和工具调用次数是否可接受
延迟用户等待时间是否在可控范围
可恢复性工具失败后是否能调整策略
可解释性是否能说明修改了什么、为什么修改

一个简单的评估函数:

def evaluate_agent(agent, test_cases: list[dict]) -> list[dict]:
    results = []

    for case in test_cases:
        trace = agent.run_with_trace(case["task"])

        passed = check_success_condition(trace, case["expected"])

        results.append({
            "name": case["name"],
            "passed": passed,
            "steps": len(trace.steps),
            "tool_calls": trace.tool_calls,
            "tokens": trace.token_usage,
            "errors": trace.errors,
        })

    return results

trace 很重要。它应该记录每轮模型输入输出、工具调用、工具结果、错误日志和最终回答。没有 trace,就很难分析智能体为什么失败。


8. 常见坑:智能体系统最容易失控的地方

循环次数不受控

智能体会在“调用工具—观察结果—继续调用工具”的循环里运行。如果没有最大步数限制,模型可能因为一个错误判断反复尝试。

处理方式:

MAX_STEPS = 12
MAX_REFLECTION_ROUNDS = 3
MAX_TOOL_RUNTIME_SECONDS = 60

工具返回内容太长

一次测试日志可能有几万行,直接塞进上下文会浪费 token,还可能挤掉真正重要的信息。工具层可以做摘要:

def summarize_log(log: str, max_chars: int = 4000) -> str:
    if len(log) <= max_chars:
        return log

    head = log[:1500]
    tail = log[-2500:]
    return f"{head}\n\n...[日志已截断]...\n\n{tail}"

把模型判断当成事实

模型说“测试应该能通过”不等于真的通过。能用工具验证的地方,必须用工具验证:

  • 代码是否正确:运行测试;
  • SQL 是否可执行:在测试库执行;
  • API 是否可用:发起真实请求或 mock 请求;
  • JSON 是否合法:用解析器检查;
  • 类型是否正确:运行类型检查工具。

权限边界不清楚

智能体工具应该默认最小权限。尤其是文件系统、Shell、数据库、外部 API,需要明确允许范围。

ALLOWED_DIR = Path("/workspace/project").resolve()

def safe_path(path: str) -> Path:
    target = (ALLOWED_DIR / path).resolve()
    if not str(target).startswith(str(ALLOWED_DIR)):
        raise PermissionError("path is outside allowed directory")
    return target

多智能体没有仲裁机制

多个智能体可能给出冲突意见。比如开发智能体认为可以改测试,测试智能体认为不应该改测试。需要一个明确的仲裁规则:

  • 需求和测试约束优先;
  • 工具验证结果优先于模型判断;
  • 安全策略优先于任务完成;
  • 关键变更需要审查智能体通过。

9. 适合从哪里开始实践

最稳妥的实践路径不是一开始就做复杂多智能体,而是从小闭环开始:

  1. 做一个只读工具智能体:允许读取文件、搜索内容,但不能修改;
  2. 增加可验证工具:允许运行测试、解析日志;
  3. 增加有限写入能力:只允许修改指定目录;
  4. 加入反思循环:测试失败后自动修正;
  5. 加入规划器:让复杂任务先拆步骤;
  6. 对任务集做自动评估;
  7. 再考虑多智能体分工。

一个可落地的最小版本可以只包含三类工具:

TOOLS = {
    "read_file": read_file,
    "search_files": search_files,
    "run_tests": run_tests,
}

等只读分析稳定后,再开放写入工具:

TOOLS["write_file"] = write_file

这种渐进方式能减少风险。智能体系统的难点不在于让模型“能做很多事”,而在于让它在受控范围内把事情做对,并且失败时能被定位、复现和修复。

Agentic AI 的核心价值在于把大语言模型放进一个可执行、可验证、可迭代的工作流里。反思让输出能被改进,工具使用让模型能操作外部系统,规划让复杂任务有步骤,多智能体让不同能力分工协作。真正可靠的智能体系统,还必须配套测试、日志、安全边界和成本控制。


评论