做普通聊天机器人时,我们很容易产生一种错觉:
用户发一句话,系统把这句话交给大模型,然后大模型返回答案。
但到了 Agent 里,真正发送给模型的内容通常远远不止用户刚刚输入的那句话。
一个 Agent 在每一轮调用 LLM 之前,都要重新回答一个问题:
这一轮,模型到底需要看到什么?
用户目标、系统规则、可用工具、前几轮工具执行结果、当前任务状态、RAG 检索出来的资料、Memory,甚至 Skill,都可能需要进入模型的上下文。
把这些东西挑出来、裁剪、排序,再组装成最终请求的过程,就是 Agent 里非常重要的一层:
Context Assembly / Context Engineering。
从一次最简单的 LLM 调用开始
普通的大模型请求大概长这样:
{
"messages": [
{
"role": "system",
"content": "你是一个助手"
},
{
"role": "user",
"content": "帮我查一下最近的 AI 新闻"
}
]
}
模型看到的东西很简单:
System Prompt
+
User Message
↓
LLM
↓
Assistant Response
但如果这是一个 Agent,情况就不一样了。
因为模型不只是要“回答”,它还要判断:
要不要搜索?
调用哪个工具?
工具需要什么参数?
之前已经做过什么?
当前还有多少预算?
搜索结果是否足够?
所以 Agent Runtime 需要提供更多信息。
一个更完整的调用可能变成:
System Prompt
+
User Goal
+
Conversation History
+
Current State
+
Relevant Memory
+
RAG Evidence
+
Skill Instructions
+
Tool Schemas
+
Previous Tool Results
↓
LLM
这里才开始进入 Agent 真正的 Prompt 组装问题。
Prompt 不是一整段字符串
实际开发 Agent 时,最好不要把 Prompt 理解成一大段字符串。
更准确地说,我们是在组装一个 Context。
它可能由几种完全不同的信息组成。
比如:
System
→ 你是谁、有哪些规则
User
→ 用户到底想干什么
State
→ 当前任务已经进行到哪里
Memory
→ 过去有哪些值得重新拿出来的信息
RAG
→ 当前问题需要哪些外部知识
Skill
→ 这类任务应该按照什么方法完成
Tools
→ 现在允许模型执行什么动作
Observation
→ 前面的工具真正返回了什么
最终 Runtime 再把这些内容转换成模型 API 所需要的 messages、tools 等结构。
所以 Prompt Assembly 更像:
各种信息源
↓
Context Builder
↓
选择 + 裁剪 + 格式化
↓
Messages + Tool Schemas
↓
LLM
真正关键的不是“Prompt 写得长不长”。
而是:
什么信息应该在这一轮进入模型。
System Prompt:定义 Agent 的长期规则
最上面通常是 System Prompt。
它负责描述这个 Agent 最稳定的部分,比如:
你的角色是什么
你能做什么
你不能做什么
如何使用工具
什么时候停止
证据要求
安全规则
最终输出格式
例如一个研究 Agent:
你是一个市场研究 Agent。
你的目标是基于可验证来源完成用户的研究任务。
如果信息不足,应继续调用搜索工具。
不要根据未经工具验证的信息声称事实。
所有重要结论必须关联有效 evidence_id。
这些内容通常不会随着某一次 Tool Calling 大幅变化。
因此它属于相对稳定的 Context。
但 System Prompt 并不是安全边界本身。
比如写一句:
不要调用没有权限的工具
并不能真正阻止模型尝试调用。
真正的权限控制仍然应该发生在 Runner 和 Tool Layer。
Prompt 负责告诉模型规则,代码负责真正执行规则。
User Goal:这一轮任务到底是什么
用户输入也不一定直接原封不动地塞给模型。
例如用户说:
帮我看看东南亚储能市场最近有没有什么机会。
Agent 内部可能会把它整理成更加明确的 Goal:
Goal:
研究近期东南亚储能市场的主要增长机会。
要求:
- 优先使用近期资料
- 给出关键市场变化
- 给出来源
- 如果证据不足,明确说明
这里并不是一定要再调用一个模型重写用户问题。
重点是 Runtime 要让模型明确:
当前任务的目标是什么。
否则随着 Agent Loop 越跑越长,模型很容易逐渐偏离最初目标。
Tool Schema:告诉模型它能做什么
Tool Calling 场景里,还有一类非常重要的信息:
Tool Schema。
比如:
{
"name": "search_web",
"description": "搜索公开网页信息",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string"
}
},
"required": ["query"]
}
}
这部分本质上也是 Context。
模型只有看到:
search_web 是什么
get_report 是什么
create_monitor 是什么
每个工具需要什么参数
它才能产生正确的 Tool Call。
所以一个 Agent 的 Prompt 质量,很大程度上也受 Tool Description 影响。
比如一个模糊的工具:
search()
搜索东西
模型很难知道什么时候该用。
而一个清晰的定义:
search_web(query)
用于检索当前公开互联网信息。
当用户问题涉及近期事件、市场变化或外部实时信息时使用。
模型就更容易作出正确选择。
Tool Result:Agent Loop 最关键的回流
Agent 真正形成闭环的关键,不是模型第一次调用了工具。
而是:
工具结果必须重新回到模型。
例如模型输出:
{
"tool_call_id": "call_001",
"name": "search_web",
"arguments": {
"query": "2026 Southeast Asia energy storage market"
}
}
Runner 执行真正的搜索。
得到:
{
"ok": true,
"results": [
{
"title": "...",
"url": "...",
"snippet": "..."
}
]
}
然后 Runtime 会把这份结果重新包装为 Tool Message:
{
"role": "tool",
"tool_call_id": "call_001",
"content": "..."
}
于是下一轮模型看到的 Messages 可能已经变成:
system
↓
user
↓
assistant: tool_call(search_web)
↓
tool: search result
↓
LLM
模型才能根据真实搜索结果判断:
信息够了吗?
↓
不够
↓
换一个 Query 再搜索
或者
信息够了
↓
生成 Final Answer
这就是最基本的 Agent Loop。
LLM
↓
Action
↓
Tool
↓
Observation
↓
LLM
如果 Tool Result 没有重新进入模型,那么严格来说,这个 Tool Calling 还没有形成完整的 Agent 闭环。
State 和 Prompt 不是一回事
这里很容易混淆两个概念:
State 和 Context。
State 是系统掌握的事实。
例如:
{
"run_id": "123",
"phase": "searching",
"step_count": 5,
"retry_count": 1,
"evidence_ids": [12, 18, 23],
"token_usage": 18340,
"lease_owner": "worker-3"
}
但这些东西没必要全部交给 LLM。
比如:
lease_owner
heartbeat
数据库主键
内部重试锁
它们对 Runtime 很重要,但对模型下一步决策没有价值。
所以中间需要一个 Context Builder:
State Store
↓
Context Builder
↓
只选择模型当前需要的信息
↓
Prompt / Messages
比如这一轮只告诉模型:
当前处于 searching 阶段
已经获得 3 个 evidence
还缺少市场规模方面的资料
剩余搜索预算为 4 次
而不是把整个数据库 State Dump 给模型。
可以记成一句话:
State 是系统知道的全部事实,Context 是这一轮选择给模型看的事实。
History 不能无限往里塞
最简单的 Agent 实现通常是:
messages.append(new_message)
每轮不断 append。
刚开始没问题。
但 Agent 一旦运行几十轮:
用户输入
+
十几次模型输出
+
几十次 Tool Call
+
几十份搜索结果
+
网页全文
+
RAG 内容
Context 很快就会膨胀。
这时候继续把全部历史发送给 LLM,会出现几个问题:
Token 成本越来越高,响应越来越慢,模型注意力被旧信息分散,甚至直接超过 Context Window。
因此真正的 Agent Runtime 通常还需要做 Context Management。
例如:
完整历史
↓
保留最近 N 轮
+
旧历史 Summary
+
当前相关 Evidence
+
当前 State
↓
LLM
重点不是“删掉旧消息”。
而是判断:
哪些旧信息对这一轮还有价值。
Memory 是怎样进入 Prompt 的
Memory 也不是模型脑子里真的多了一块永久记忆。
绝大多数情况下,Memory 最终还是要重新进入 Context。
例如系统保存了一条长期 Memory:
用户更喜欢简短的技术回答。
下一轮 Runtime 判断这条 Memory 和当前任务有关,于是加入:
Relevant Memory:
用户倾向于简洁、工程化的回答。
最终模型才“记得”。
所以实际链路是:
Memory Store
↓
Memory Retrieval
↓
Relevant Memory
↓
Context Builder
↓
LLM
模型不是永久记住了。
是系统在合适的时候又把这件事告诉了它。
RAG 也是 Prompt Assembly 的一部分
RAG 的 Retrieval 最终目的也是:
给这一轮 Context 找到需要的知识。
比如:
User Query
↓
Retriever
↓
Top-K Chunks
↓
Reranker
↓
Top-N Evidence
↓
Context Builder
↓
LLM
因此从 Agent Runtime 的角度看:
RAG 不只是“知识库功能”。
它其实也是 Context Builder 的一个数据源。
可以把它和 Memory 放在一起理解:
State
Memory
RAG
History
Skills
Tool Results
↓
Context Builder
↓
LLM Context
Skills 也是动态 Context
如果 Agent 支持 Skills,那么 Skill 也是一样的。
Agent 启动时可能只知道:
pdf-processing
code-review
research
spreadsheet-analysis
以及每个 Skill 的 Description。
当用户任务和 research 匹配时:
User Goal
↓
Skill Selection
↓
读取 research/SKILL.md
↓
把相关方法加入 Context
于是模型这一轮除了知道:
我要研究这个市场
还知道:
研究任务应该按照哪些步骤、标准和规则执行。
所以 Skill 本质上也是 Prompt Assembly 的动态来源之一。
一次完整的 Prompt 是怎么组装出来的
把前面的东西全部放在一起,一个比较完整的 Agent Runtime 可能是:
User Request
↓
读取 Run State
↓
选择 Relevant Memory
↓
执行 RAG Retrieval
↓
匹配 Relevant Skill
↓
选择最近 Conversation History
↓
加入 Previous Tool Results
↓
确定当前可用 Tool Schemas
↓
Context Builder
↓
Token Budget / Trim / Summary
↓
组装 Messages + Tools
↓
LLM
模型返回:
Final Answer
或者:
Tool Call
如果是 Tool Call:
Tool Call
↓
Runner 校验
↓
Tool 执行
↓
Structured Result
↓
更新 State
↓
写入 Tool Message
↓
重新 Context Assembly
↓
下一轮 LLM
注意最后一点。
Agent 每一轮都可能重新组装 Context。
不是第一次组好 Prompt,以后一直用。
因为 State、Tool Result、Evidence、Memory 和任务阶段一直在变化。
Context Builder 才是 Agent Runtime 的核心之一
做到这里会发现:
Agent 的重点已经不只是 Prompt Engineering。
传统 Prompt Engineering 更关注:
这一段 Prompt 怎么写得更好?
而 Agent Context Engineering 更关注:
这一轮模型应该看到哪些信息?
因此问题从:
Prompt 怎么写?
变成了:
哪些信息进入 Context?
什么时候进入?
以什么顺序进入?
进入多少?
哪些不应该进入?
过期信息怎么删除?
冲突信息怎么标记?
这其实比单纯写一个很长的 System Prompt 要复杂得多。
最后
一个 Agent 的运行过程可以简化成:
State
+
Memory
+
History
+
RAG
+
Skills
+
Tool Results
+
Tool Schemas
↓
Context Builder
↓
Messages
↓
LLM
↓
Action
↓
Environment
↓
Observation
↓
更新 State
↓
重新组装 Context
所以 Agent 并不是一个模型在后台不停“思考”。
真正持续运行的是 Runtime。
LLM 每次只是拿到 Runtime 为这一轮准备好的 Context,做一次推理,然后返回一个决策。
Runtime 再执行这个决策,获得新的事实,更新 State,然后重新准备下一轮 Context。
理解这一点之后,State、Memory、RAG、Skill、Tool Calling、Agent Loop 这些看起来分散的概念,就会开始连接起来。
它们最终都在解决同一个问题:
下一次调用 LLM 时,到底应该让模型看到什么。