一次 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_file、search、edit_file、execute 和 git_diff;研究 Agent 可能使用 web_search、fetch_url 和 summarize;业务 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 的核心不是一次生成答案,而是让模型在受控环境中持续获得反馈、采取行动并验证结果。