一次代码 Agent 请求发生了什么

一次 Agent 请求发生了什么

给一个 Agent 一个任务:

修复用户登录失败的 bug,定位原因、修改代码并运行测试验证。

表面上是一次对话,实际执行过程通常是:

理解问题和已有信息
    -> 搜索相关代码和日志
    -> 读取调用链
    -> 定位问题原因
    -> 修改代码
    -> 运行测试
    -> 根据失败结果继续修复
    -> 检查改动并总结

同样的链路也适用于代码修改、数据分析、客服处理和业务自动化:

LLM API
    -> Agent Loop
        -> Reasoning / Planning
            -> Tool Use
                -> Observation
                    -> Context / Memory
                        -> 下一轮模型请求

Skills、MCP、Subagent 和 Multi-Agent 都是在这条链路上扩展能力;Prompt Engineering、Context Engineering 和 Harness Engineering 则分别负责指令、信息和运行环境。

LLM API:模型如何参与执行

Agent 首先通过 LLM API 请求模型。请求中不只有用户问题,还包括历史消息、系统指令和可用工具定义:

{
  "model": "agent-model",
  "messages": [
    {"role": "system", "content": "Use available tools and provide evidence."},
    {"role": "user", "content": "Fix the login failure, run tests, and explain the cause."}
  ],
  "tools": [
    {
      "type": "function",
      "function": {
        "name": "web_search",
        "description": "Search external information",
        "parameters": {
          "type": "object",
          "properties": {
            "query": {"type": "string"},
            "max_results": {"type": "integer"}
          },
          "required": ["query"]
        }
      }
    }
  ],
  "tool_choice": "auto",
  "stream": true
}

模型的返回可能是普通文本,也可能是工具调用:

{
  "role": "assistant",
  "tool_calls": [
    {
      "id": "call_001",
      "type": "function",
      "function": {
        "name": "web_search",
        "arguments": "{\"query\":\"topic overview and latest information\",\"max_results\":5}"
      }
    }
  ]
}

流式模式下,文本和工具调用可能被拆成多个 delta。宿主程序需要把这些片段重新组装成完整消息,再决定是否执行工具。

Agent Loop:把请求持续运行起来

Agent Loop 是运行时的主循环:

接收任务
    -> 请求模型
    -> 检查是否有工具调用
    -> 执行工具
    -> 写回工具结果
    -> 再次请求模型
    -> 没有工具调用时结束

最小实现:

messages = [system_message, user_task]

while true:
    response = model(messages, tools)
    messages.append(response)

    if response.tool_calls is empty:
        return response.text

    for call in response.tool_calls:
        approve_if_needed(call)
        result = dispatch(call)
        messages.append(tool_result(call.id, result))

Agent Loop 由几个部分组成:

Model     生成下一步决策
Tools     读取或改变外部环境
State     保存消息和任务状态
Policy    控制权限、审批和停止条件
Events    展示过程并记录执行轨迹

Tool Use:模型如何调用工具

模型不会直接访问互联网、数据库或本地文件。模型只返回结构化调用,宿主程序负责执行。

工具调用的分发流程:

读取 tool_call.function.name
    -> 在工具注册表中查找工具
    -> 解析 function.arguments
    -> 校验参数和访问范围
    -> 检查工具风险并请求审批
    -> 执行工具
    -> 得到结果或错误
function dispatch(call):
    tool = registry.find(call.function.name)
    if tool is null:
        return error("unknown tool")

    args = parse_json(call.function.arguments)
    validate(args, tool.schema)

    if tool.is_mutating:
        require_approval(call)

    return tool.execute(args)

以搜索为例,执行结果必须带着原来的调用 ID 回到上下文:

{
  "role": "tool",
  "tool_call_id": "call_001",
  "content": "Result 1: ...\nResult 2: ...\nSources: ..."
}

之后重新请求模型:

messages = [
    system_message,
    user_message,
    assistant_tool_call,
    tool_result
]

next_response = model(messages, tools)

工具调用只描述一次动作:

Model -> Tool

Agent 的完整闭环是:

Model -> Tool -> Result -> Model -> Tool -> Result

代码 Agent 只是一个场景

Claude Code、Codex 和 Grok Code 把同一套机制用于代码任务:

读取项目结构
    -> 搜索符号和调用关系
    -> 读取相关文件
    -> 修改文件
    -> 执行测试或构建
    -> 读取错误
    -> 再次修改和验证

代码 Agent 的工具通常是 read_filesearchedit_fileexecutegit_diff;研究 Agent 可能使用 web_searchfetch_urlsummarize;业务 Agent 可能使用数据库、CRM 或工单系统。

变化的是工具和权限,核心仍然是:

模型决策 -> 调用工具 -> 获取结果 -> 再次决策

Reasoning:下一步为什么这样选

在 Agent 中,Reasoning 主要体现在动作选择上:

搜索结果来自多个来源
    -> 先比较来源和发布时间

资料之间存在冲突
    -> 查询更权威的来源

关键事实缺少依据
    -> 补充查询并保留证据

不需要把模型的完整内部思考过程暴露给用户。对系统更重要的是:

  • 当前状态是否完整
  • 工具结果是否可靠
  • 下一步动作是否基于结果
  • 是否能在错误后改变路径

Planning:把任务拆成可跟踪步骤

ReAct 的每一步可以临时决定;Planning 则把任务拆成显式计划:

plan = [
    "明确问题范围",
    "收集相关资料",
    "筛选可靠来源",
    "交叉验证",
    "整理结论"
]

执行过程中计划也可能变化:

来源不足
    -> 增加“查询官方资料”步骤

不同来源结论冲突
    -> 增加“对比发布时间和来源”步骤

Planning 解决“任务有哪些阶段”;Agent Loop 解决“当前这一刻执行什么”。

Prompt Engineering、Context Engineering、Harness Engineering

Prompt Engineering

Prompt Engineering 关注如何告诉模型应该做什么:

先确认问题范围,再调用工具;
结论必须有对应证据;
不要把未验证的信息当成事实。

它主要解决指令、角色、约束和工具描述。

Context Engineering

Context Engineering 关注每一轮应该给模型哪些信息:

system prompt
    + 用户目标
    + 历史消息
    + 当前工具定义
    + 工具执行结果
    + 搜索结果和来源
    + 相关文档和数据

上下文不是越多越好。Agent 需要在相关性、完整性、长度和新鲜度之间做取舍,包括:

  • 选择相关资料而不是读取全部结果
  • 截断过长的工具输出
  • 压缩旧对话
  • 将中间结果写入文件、数据库或其他存储
  • 在下一轮保留真正影响决策的信息

Harness Engineering

Harness Engineering 关注模型之外的运行系统:

  • 工具注册和参数校验
  • 工作区、沙箱和权限
  • 审批、重试和取消
  • 状态保存、回滚和恢复
  • 流式事件和日志
  • 最大步数和重复调用保护
  • 测试、评估和结果验证

模型决定“做什么”,Harness 决定“能不能做、怎么做、做完如何继续”。

KV Cache:为什么上下文管理会影响成本

多轮 Agent 会反复携带系统指令、工具定义和历史上下文。模型推理通常可以复用相同前缀的 Key-Value 表示,这就是 KV Cache 的作用。

第 1 轮:system + tools + user + response
第 2 轮:system + tools + user + tool_result + response
                         ^^^^^^^^^^^^^
                         可复用的前缀

KV Cache 可以减少重复计算和首 token 延迟,但它不是 Agent Memory:

  • KV Cache 是推理过程中的计算缓存
  • Memory 是跨回合或跨会话保存的信息
  • 上下文裁剪或前缀变化可能使缓存失效

因此,稳定的系统指令、工具定义和上下文组织不仅影响模型理解,也影响推理效率。

Skills、MCP 和 Memory

Skills

Skill 是可复用的能力包,通常包含指令、流程、工具使用约束和相关资源:

skill = {
    name: "research_topic",
    instructions: "检索可靠来源并保留证据",
    tools: [web_search, fetch_url, summarize],
    output: "返回结论、来源和不确定性"
}

Skill 解决的是“遇到某类任务时,加载哪套专业行为”。它不是新的模型,也不是新的 Agent Loop。

Skill 通常在任务进入执行循环前,或执行过程中识别到任务类型时加载:

用户任务
    -> 匹配任务类型
    -> 加载 Skill 指令、工具和资源
    -> 注入当前上下文
    -> Agent Loop 开始决策

例如,任务涉及安全审查时加载 review_security;任务涉及生成文档时加载 write_document。Skill 加载后会影响后续的 Prompt、工具选择和输出格式,但它本身通常不是一次工具调用。

MCP

MCP 是连接 Agent 和外部工具、资源的协议层:

Agent Host
    -> MCP Client
        -> MCP Server
            -> 搜索、数据库、文件、文档或业务系统

MCP 负责发现和调用外部能力,Agent Loop 负责决定什么时候调用。两者的关系类似:

MCP       = 工具连接协议
Tool Use  = 调用工具的行为
Agent Loop = 组织调用的运行机制

MCP 有两个时间点:

Agent 启动
    -> MCP Client 连接 MCP Server
    -> 发现 tools / resources / prompts
    -> 把可用能力注册给 Agent

Agent Loop 运行
    -> 模型选择某个 MCP tool
    -> MCP Client 转发调用
    -> MCP Server 访问外部系统
    -> 结果回到当前上下文

MCP Server 不会自动执行所有工具。只有模型在某一轮生成对应的工具调用,Agent Host 才会真正调用它。

一次 MCP 工具调用可以抽象成:

response = model(messages, discovered_tools)
call = response.tool_call("query_database")
result = mcp_client.call(
    server="analytics",
    tool="query_database",
    arguments=call.arguments
)
messages.append(tool_result(call.id, result))

Memory

Memory 解决“过去的信息如何在未来被使用”:

短期记忆:当前对话和工具结果
工作记忆:当前任务的计划、文件和中间产物
长期记忆:跨会话的用户偏好、项目约定和历史结论

Memory 的基本过程是:

提取重要信息
    -> 存储
    -> 下一次任务检索
    -> 注入当前上下文

记住更多信息不一定更好。Memory 需要处理过期、冲突、权限和相关性。

Subagent 和 Multi-Agent

Subagent

Subagent 是由主 Agent 委派的独立任务执行单元:

主 Agent:分析任务并分工
    -> Subagent A:收集资料
    -> Subagent B:核对来源
    -> Subagent C:整理结果
主 Agent:汇总结果并决定下一步

Subagent 的价值主要是隔离上下文和分担任务:

  • 每个子任务只携带相关信息
  • 大量中间过程不会全部塞进主 Agent 上下文
  • 不同子任务可以使用不同工具或模型

主 Agent 和 Subagent 的协同不是共享同一个完整对话,而是通过任务和结果传递:

主 Agent
    -> 生成子任务描述
    -> 选择 Subagent、模型和工具
    -> 传入必要上下文
    -> 等待或并行等待结果
    -> 将结果摘要写回主上下文
    -> 决定继续、重派或结束

Subagent 内部仍然运行自己的 Agent Loop:

function run_subagent(task, context, tools):
    state = [subagent_prompt, task, context]
    while true:
        response = model(state, tools)
        if response.has_no_tool_call:
            return response.text
        result = execute_tools(response.tool_calls)
        state.append(result)

Multi-Agent

Multi-Agent 是多个 Agent 之间的协作系统:

Coordinator
    -> Researcher
    -> Coder
    -> Tester
    -> Reviewer
Coordinator -> 汇总并决定是否继续

协同方式取决于任务依赖关系:

独立任务:并行执行
Researcher A ─┐
Researcher B ─┼-> Coordinator 汇总
Researcher C ─┘

有依赖任务:串行传递
Research -> Analyst -> Writer -> Reviewer

一个协调器的伪代码:

function coordinator(goal):
    plan = model("拆分任务", goal)

    if plan.tasks_are_independent:
        results = parallel_map(
            task -> run_subagent(task, relevant_context(task), tools_for(task)),
            plan.tasks
        )
    else:
        results = []
        for task in plan.tasks:
            result = run_subagent(task, results, tools_for(task))
            results.append(result)

    return model("汇总子任务结果", results)

它适合任务可以拆分、角色边界清晰、子任务之间相对独立的场景。代价是通信、状态同步、重复工作和错误协调都会增加。

Subagent 更关注一次委派和上下文隔离;Multi-Agent 更关注多个 Agent 的长期协作和协调。

LangGraph、DeepAgents 和自定义实现

自定义 Agent Loop

while not finished:
    response = model(state)
    result = execute(response.tool_call)
    state.append(result)

适合理解模型、工具和上下文之间的基本闭环。

LangGraph

LangGraph 把 Agent Loop 表示成状态图:

START -> call_model
call_model -- 有工具调用 --> run_tools
run_tools -> call_model
call_model -- 没有工具调用 --> END

它主要解决状态持久化、分支、循环、重试、人工介入、暂停恢复和执行追踪。

DeepAgents

DeepAgents 在 Agent Loop 之上提供规划、文件系统、上下文管理、子 Agent 和人工审批等默认能力:

agent = create_deep_agent(
    model,
    tools,
    planning,
    filesystem,
    subagents,
    human_approval
)

agent.run(user_task)

三者关系:

ReAct       = 行为模式
Agent Loop  = 运行机制
LangGraph   = 编排运行时
DeepAgents  = 高层 Agent Harness

一次执行的完整分解

用户任务
    -> Prompt Engineering:定义目标和约束
    -> Context Engineering:准备本轮信息
    -> LLM API:请求模型
    -> Reasoning / Planning:选择下一步
    -> Tool Use:调用工具
    -> Harness:校验、审批、执行和记录
    -> Memory:保存需要复用的信息
    -> Agent Loop:继续或结束

任务规模扩大后,可以再加入:

Skills:加载专业能力
MCP:连接外部工具和资源
Subagent:隔离并委派子任务
Multi-Agent:协调多个角色

LLM Agent 的核心不是一次生成答案,而是让模型在受控环境中持续获得反馈、采取行动并验证结果。