用 Amazon Bedrock AgentCore 搭建高精度 AI 代码审查 Agent——Baz 的实战拆解

2026-06-02 28 预计阅读时间:1 分钟
来源:aws.amazon.com AI 摘要 原文链接

免责声明:本文为 AI 摘要整理,建议结合原文阅读。摘要可能省略上下文、版本差异或边界条件,不作为官方说明。

预计阅读时间:13 分钟

代码审查是工程团队最耗时又最不可省略的环节。Baz 团队面对的问题是:Spec Review(规格审查)需要逐条比对需求文档与代码实现,人工做这件事既慢又容易漏项。他们用 Amazon Bedrock 搭配新发布的 AgentCore 框架,构建了一个 Spec Review Agent,把审查准确率拉到了可投产的水平。这篇文章拆解他们的架构选择、实现细节和踩坑经验。

问题本质:不是"读代码",而是"比对意图"

普通 LLM 代码审查容易陷入表面风格检查——变量命名、缺少注释之类。Baz 的核心需求不同:他们要验证代码是否完整覆盖了 spec 中每一条要求。这意味着 Agent 必须:

  1. 解析 spec 文档,提取可验证的条目清单;
  2. 逐条在代码中寻找对应实现或缺失;
  3. 输出结构化审查报告,而非泛泛的"建议改进"。

这天然是一个多步骤、有状态、需要工具调用的任务——正好落在 AgentCore 的能力范围内。

架构选择:为什么是 AgentCore 而不是裸 Bedrock Invoke

Baz 最初尝试直接调用 Bedrock 的 Converse API,把整个 spec 和代码塞进一个 prompt。结果有两个硬伤:

  • 长上下文丢细节:spec + 代码动辄上万 token,模型对尾部条目的召回率明显下降。
  • 无法分步验证:一次性输出让模型跳过了"逐条比对"的严谨过程,容易给出笼统结论。

切换到 AgentCore 后,他们获得了三个关键能力:

能力 解决的问题
多步行动规划(ReAct loop) 拆解"提取条目→逐条比对→汇总报告"为独立步骤
工具调用(Code Access) Agent 可按需拉取特定文件、函数,而非一次性喂入全部代码
会话状态管理 每条 spec 条目的验证结果持久化,避免重复计算

架构简图:

Spec Document ──► AgentCore Orchestrator
                      │
                      ├── Step 1: SpecParser Tool → 提取条目清单
                      ├── Step 2: CodeFetcher Tool → 按条目拉取相关代码片段
                      ├── Step 3: ClauseValidator Tool → 逐条比对并记录结果
                      └── Step 4: ReportGenerator → 汇总结构化报告
                      │
                 Amazon Bedrock (Claude 3.5 Sonnet)

核心设计原则:让 Agent 每一步只处理一小块信息,但通过状态串联保证全局一致性

实现细节:Agent 与工具的定义

Baz 用 AgentCore 的 Python SDK 定义了 Agent 和三个自定义工具。下面是一个可改造运行的简化示例,展示核心结构:

# agent_spec_review.py
# 前置依赖:pip install boto3 agentcore-sdk
# 需要 AWS 凭证配置(~/.aws/credentials 或环境变量)
# 需要 Amazon Bedrock 模型访问权限(Claude 3.5 Sonnet)

import boto3
from agentcore_sdk import Agent, Tool, ToolInput

# ---- 工具定义 ----

class SpecParserInput(ToolInput):
    spec_text: str  # 需求文档原文

@Tool(name="SpecParser", description="从需求文档中提取可验证的条目清单")
def parse_spec(input: SpecParserInput) -> dict:
    """
    实际生产中这里会调用 Bedrock 做结构化提取,
    示例用简化逻辑演示流程。
    """
    # 将 spec 拆分为条目列表(生产环境用 LLM 提取)
    clauses = [
        {"id": "C1", "text": "用户登录必须使用 OAuth2.0"},
        {"id": "C2", "text": "密码长度不少于 12 位"},
        {"id": "C3", "text": "登录失败需记录审计日志"},
    ]
    return {"clauses": clauses, "total": len(clauses)}


class CodeFetcherInput(ToolInput):
    clause_id: str       # 要验证的条目 ID
    repo_path: str       # 代码仓库本地路径

@Tool(name="CodeFetcher", description="根据条目关键词拉取相关代码片段")
def fetch_code(input: CodeFetcherInput) -> dict:
    """
    生产环境会做语义搜索(如向量检索),
    示例用关键词匹配简化。
    """
    # 模拟:根据 clause_id 返回相关代码片段
    code_snippets = {
        "C1": "auth/oauth_login.py: def oauth2_login(client_id, redirect_uri): ...",
        "C2": "auth/password_policy.py: MIN_PASSWORD_LENGTH = 12",
        "C3": "auth/audit_logger.py: def log_failed_attempt(user_id, timestamp): ...",
    }
    return {
        "clause_id": input.clause_id,
        "snippet": code_snippets.get(input.clause_id, "未找到相关代码"),
    }


class ClauseValidatorInput(ToolInput):
    clause: dict     # 条目详情
    code_snippet: str  # 对应代码片段

@Tool(name="ClauseValidator", description="比对单条需求与代码实现,判定覆盖状态")
def validate_clause(input: ClauseValidatorInput) -> dict:
    """
    生产环境调用 Bedrock 做语义比对,
    示例用关键词包含判断简化。
    """
    clause_text = input.clause["text"]
    snippet = input.code_snippet

    # 简化判定逻辑(实际应由 LLM 语义判断)
    keywords = clause_text.lower().split()
    matched = any(kw in snippet.lower() for kw in keywords if len(kw) > 3)

    status = "COVERED" if matched else "MISSING"
    return {
        "clause_id": input.clause["id"],
        "clause_text": clause_text,
        "status": status,
        "evidence": snippet,
    }


# ---- Agent 定义 ----

bedrock_client = boto3.client("bedrock-runtime", region_name="us-east-1")

spec_review_agent = Agent(
    name="SpecReviewAgent",
    model_id="anthropic.claude-3-5-sonnet-20241022-v2:0",
    instruction=(
        "你是一个代码规格审查 Agent。工作流程:\n"
        "1. 调用 SpecParser 从需求文档提取条目清单\n"
        "2. 对每条条目,调用 CodeFetcher 拉取相关代码\n"
        "3. 对每条条目,调用 ClauseValidator 比对验证\n"
        "4. 汇总所有条目结果,输出结构化审查报告\n"
        "报告格式:每条列出 id、状态(COVERED/MISSING/PARTIAL)、证据代码位置。\n"
        "最后给出覆盖率百分比和缺失条目清单。"
    ),
    tools=[parse_spec, fetch_code, validate_clause],
    bedrock_client=bedrock_client,
)

# ---- 运行 ----

if __name__ == "__main__":
    spec_text = """
    需求文档 v2.3:
    - 用户登录必须使用 OAuth2.0
    - 密码长度不少于 12 位
    - 登录失败需记录审计日志
    """

    result = spec_review_agent.run(
        input_text=f"请审查以下需求文档与代码的覆盖情况:\n{spec_text}",
        # 传入代码仓库路径供 CodeFetcher 使用
        context={"repo_path": "/path/to/your/repo"},
    )

    print(result.output)

运行前需要修改的地方:

  1. repo_path 改为你的实际代码目录;
  2. 确保 AWS 账号已开通 Bedrock Claude 3.5 Sonnet 模型访问;
  3. 生产环境中三个 Tool 的内部逻辑应替换为真实的 LLM 调用或向量检索。

关键优化:准确率是怎么拉上去的

Baz 从初版到最终版做了几轮迭代,准确率提升的核心动作:

分步比对替代一次性判断。 这是最根本的改变。AgentCore 的 ReAct 循环让模型先提取条目、再逐条验证,每步的 prompt 短且聚焦,大幅减少了遗漏。Baz 的数据:条目召回率从单次调用的 68% 提升到分步的 94%。

工具粒度控制信息量。 CodeFetcher 不是把整个仓库喂给模型,而是根据条目关键词只拉取相关文件片段。这把每步的上下文控制在 2000 token 以内,模型对细节的注意力显著增强。

结果持久化防重复与遗漏。 AgentCore 的会话状态让每条条目的验证结果被记录。即使某步出错重试,已完成条目不会丢失,也不会被跳过。

模型选择:Claude 3.5 Sonnet。 Baz 测试了多个模型,Sonnet 在"严格比对而非自由发挥"这类任务上表现最稳定——它更倾向于说"未找到对应实现",而非编造一个看似合理的覆盖判断。

部署与运维考量

Baz 的生产部署架构:

# cloudformation-snippet.yaml
# AgentCore Agent 的基础设施定义(简化版)

Resources:
  SpecReviewAgent:
    Type: AWS::Bedrock::Agent
    Properties:
      AgentName: spec-review-agent
      FoundationModelId: anthropic.claude-3-5-sonnet-20241022-v2:0
      Instruction: !Ref AgentInstructionText  # 上述 instruction 内容
      AgentResourceRoleArn: !GetAtt AgentRole.Arn

  SpecParserAction:
    Type: AWS::Bedrock::AgentActionGroup
    Properties:
      AgentId: !Ref SpecReviewAgent
      ActionGroupName: SpecParser
      ActionGroupExecutor:
        Lambda: !GetAtt SpecParserLambda.Arn
      FunctionSchema:
        Functions:
          - Name: parseSpec
            Description: 从需求文档提取条目清单
            Parameters:
              specText:
                Type: string
                Required: true
                Description: 需求文档原文

  AgentRole:
    Type: AWS::IAM::Role
    Properties:
      AssumeRolePolicyDocument:
        Version: "2012-10-17"
        Statement:
          - Effect: Allow
            Principal:
              Service: bedrock.amazonaws.com
            Action: sts:AssumeRole
      ManagedPolicyArns:
        - arn:aws:iam::aws:policy/AmazonBedrockFullAccess

几个运维要点:

  • 成本控制:每次审查涉及 N 次工具调用 + N 次 Bedrock 调用(N = 条目数)。Baz 用 Prompt Caching 减少了重复 spec 文本的 token 计费,单次审查成本控制在 $0.3–0.8。
  • 超时处理:大型仓库审查可能超过 AgentCore 默认超时。Baz 设置了 15 分钟上限,并对超时条目标记为 REVIEW_INCOMPLETE,由人工补审。
  • 幻觉兜底:ClauseValidator 的结果经过一轮"反查"——用条目 ID 回查代码位置,确认模型声称的证据确实存在。这步用简单的字符串匹配即可,成本极低但有效拦截了约 5% 的幻觉输出。

落地建议与取舍

如果你也在考虑用 Agent 搞代码审查,几个判断标准:

适合用 Agent 的场景: - 审查对象是结构化 spec 与代码的覆盖关系; - 仓库规模大,人工逐条比对不可行; - 需要可追溯的审查记录(每条有证据、有状态)。

不适合的场景: - 纯风格/命名审查——单次 LLM 调用就够了,Agent 的多步开销不值得; - spec 本身模糊不可拆条——Agent 的分步优势建立在条目可结构化提取的前提上; - 极小仓库(< 10 文件)——直接把全量代码塞进一次调用更简单。

上手路径: 1. 先用裸 Bedrock Converse API 做一次端到端审查,记录遗漏率; 2. 把遗漏最多的条目类型抽出来,用 AgentCore 拆成独立验证步骤; 3. 逐步把剩余条目也迁入 Agent 流程,对比准确率变化; 4. 加上反查兜底和 Prompt Caching 后再投产。

Baz 的经验说明:AI 代码审查的准确率瓶颈往往不是模型能力,而是任务拆解方式和信息供给粒度。AgentCore 提供的正是这个拆解框架——让模型每步只做一件小事,但串联起来完成大任务。


相关推荐