如何进行 AI Agent 的上下文工程管理?

作者:🧑‍🚀 deadmau5v 发布于 2025/10/17

如何进行 AI Agent 的上下文工程管理?

最近AI Agent很火啊。但真正动手做过的人都知道。最大的坑不是模型不够聪明,而是Agent运行时间一长,上下文就爆了,然后性能急剧下降。

今天聊聊在构建生产级AI Agent时,那些看似简单实则要命的工程决策。

上下文爆炸

先说问题的本质。Agent和普通的ChatLLM最大的区别是什么?自主性。它会在一个循环里不断调用工具,每次调用都会产生结果,这些结果都要塞回上下文。

举个例子:一个研究型Agent,比如Deepsearch、Manus等,可能需要:

  • 搜索20次
  • 读取10个网页
  • 分析5个文档
  • 生成3个图表

每个操作的结果少则几百tokens,多则上万。50次工具调用后,你的上下文可能已经突破100K tokens。然后模型开始”犯傻”:重复操作、忘记之前的结果、甚至开始胡言乱语。

Anthropic把这个现象叫”Context Rot”(上下文腐烂),很形象。

搜索结果:存还是不存?这是个问题

我们用网络搜索这个最常用,token较大的案例作为演示。

这是我见过争议最大的问题之一。搜索工具返回了10个网页的摘要,每个1000 tokens,你怎么办?

方案一:全部保留

# 反面教材
search_results = search_tool.search(query)
messages.append({
    "role": "tool",
    "content": search_results  # 10000 tokens进入上下文
})

优点:信息无损,Agent随时可以引用
缺点:上下文快速膨胀,3-5次搜索就爆了

方案二:激进压缩

# 只保留标题和URL
compressed = [{"title": r.title, "url": r.url} for r in results]
messages.append({
    "role": "tool",
    "content": json.dumps(compressed)  # 只有200 tokens
})

优点:极度节省空间
缺点:Agent看不到内容,可能需要重复读取

方案三:混合策略(推荐)

全部保留 vs 激进压缩
# 方案一:全部塞进上下文
messages.append({
    "content": search_results  # 10000 tokens
})

# 方案二:只保留标题
messages.append({
    "content": json.dumps(titles_only)  # 200 tokens
})

问题:要么浪费上下文,要么丢失信息

混合策略
# 完整结果写文件,上下文只放摘要
file_path = f"search_{timestamp}.json"
save_to_file(search_results, file_path)

summary = summarize(search_results, max_tokens=500)
messages.append({
    "role": "tool",
    "content": f"搜索完成,结果已保存至 {file_path}\n摘要:{summary}"
})

精髓:让Agent既能看到关键信息,又能在需要时读取完整内容

智能体间通信

多Agent协作听起来很美好,实际上是个大坑。核心问题:信息同步。

反模式:全量共享

# 错误示范:每个子Agent都带着完整历史
def spawn_sub_agent(full_context):
    sub_agent = Agent(
        context=full_context,  # 包含所有历史消息
        task=specific_task
    )
    return sub_agent.run()

问题:

  1. 预填充成本极高
  2. 子Agent被无关信息干扰
  3. KV缓存无法复用

正确姿势:按需传递

# 方式一:任务导向的最小上下文
def delegate_task(task_description, required_files=[]):
    context = {
        "task": task_description,
        "files": required_files,
        "output_schema": {...}  # 约定输出格式
    }
    return sub_agent.execute(context)

# 方式二:共享存储,独立上下文
def parallel_research(topics):
    shared_workspace = create_workspace()
    agents = []
    for topic in topics:
        agent = Agent(
            task=f"研究{topic},结果写入workspace",
            workspace=shared_workspace,
            context=[]  # 空白上下文
        )
        agents.append(agent)
    return run_parallel(agents)

关键原则

  1. 能通过文件共享的,不要放上下文
  2. 定义清晰的输入输出契约
  3. 子任务要高度独立

延伸阅读:如果你在构建多Agent系统,可以关注一下 Google A2A 协议Anthropic MCP。A2A 解决 Agent 间的标准化通信,MCP 解决 Agent 与工具的标准化连接。不过说实话,除非你在做平台级产品,否则直接写代码可能更快。

“智能体即工具”设计模式

核心思想:把复杂的子任务封装成一个工具,但背后是另一个Agent,类似于Google的A2A协议。

class AgentTool:
    def __init__(self, agent_config):
        self.agent = Agent(agent_config)

    def execute(self, instruction):
        # 对外表现为简单工具
        result = self.agent.run(instruction)
        # 只返回结构化结果
        return result.to_dict()

# 主Agent眼中就是一个普通工具
tools = [
    search_tool,
    file_tool,
    AgentTool(deep_research_config),  # 实际是个Agent
    AgentTool(code_review_config)      # 这也是
]

优势

  1. 主Agent的上下文保持简洁
  2. 复杂逻辑被封装,可独立迭代
  3. 可以用不同的模型(省钱)

适用场景

  • 深度研究某个特定问题
  • 多步骤的数据处理
  • 需要特殊prompt的任务

数据格式:Plain Text vs Structured

这是个被忽视但很重要的细节。

原则一:优先使用基于行的格式

# 好的格式
logs = """
2024-01-15 10:00:00 | 开始搜索
2024-01-15 10:00:05 | 找到10个结果
2024-01-15 10:00:10 | 开始分析第1个结果
"""

# 不好的格式
logs = {
    "entries": [
        {"time": "10:00:00", "action": "search"},
        {"time": "10:00:05", "results": 10}
    ]
}

为什么?因为Agent可以用grep、sed等工具快速定位,而JSON需要解析。

原则二:结构化数据用Schema约束

# 定义明确的Schema
SUMMARY_SCHEMA = {
    "files_modified": List[str],
    "key_findings": List[str],
    "next_steps": List[str],
    "blockers": Optional[List[str]]
}

# 强制Agent按格式输出
def summarize_with_schema(content, schema):
    prompt = f"按以下格式总结:{schema}"
    return structured_output(prompt, content)

原则三:大数据不进上下文

# 反面教材
df = pd.read_csv("huge_file.csv")  # 100MB
context.append(f"数据:{df.to_string()}")  # 疯了

# 正确做法
stats = df.describe()
sample = df.head(10)
context.append(f"""
数据统计:{stats}
样本预览:{sample}
完整数据:huge_file.csv ({len(df)} rows)
""")

架构设计的几个关键决策

1. 工具数量控制

  • 不要超过30个工具(经验值:10-20个最佳)
  • 用代码执行来扩展能力,而不是无限增加工具
  • 原子化 + 可组合 > 大而全

2. 缓存策略

  • 利用好模型商的KV缓存(能省很多钱)
  • 稳定的系统提示词放最前面
  • 频繁变化的内容放后面

3. 压缩时机

  • 设置”腐烂阈值”(通常100-200K tokens)
  • 先压缩,后摘要
  • 保留最近N轮完整对话

4. 简化原则

  • 能用文件系统解决的,不要用数据库
  • 能用grep的,不要用向量检索
  • 能单Agent搞定的,不要多Agent

最重要的教训

做了这么多Agent项目,最大的感悟是:大部分时候,你不需要那些花哨的技术。

几个反直觉的事实:

  1. 模型升级带来的提升,往往大于你所有的工程优化
  2. 简单粗暴的方案,往往比精巧的设计更稳定
  3. 过早优化真的是万恶之源

写在最后

上下文工程本质上是个资源管理问题。就像操作系统管理内存一样,我们在管理Agent的”认知资源”。

记住最重要的原则:保持简单。工程师的价值在于选择合适的技术,而不是追逐最新的技术。

能跑起来的糙代码,胜过设计完美但跑不起来的架构。

标签:AIAgent上下文工程LLM架构设计

评论

发表评论

加载评论中...