LangGraph + 多Agent系统实战:上下文工程才是智能协作的核心基建 原创

发布于 2025-9-8 07:17
浏览
0收藏

随着大语言模型(LLMs)和多智能体系统(Multi-Agent Systems, MAS)的快速发展,我们不再满足于单个“聪明”的AI,而是追求多个AI代理(Agent)之间的高效协作、动态推理与上下文感知。而这一切的核心,正是我们今天要探讨的主题 —— Multi-Agent。 Context Engineering(多智能体上下文工程)

什么是 Multi-Agent Context Engineering?

Context Engineering(上下文工程) 是指通过设计、管理和优化上下文信息,使AI系统能够更准确地理解当前任务、环境与用户意图,从而做出更合理、更连贯的响应。

而在多智能体系统中,每个Agent都有自己的目标、记忆、工具和推理能力。Multi-Agent Context Engineering 就是:

在多个Agent之间,动态构建、共享、演化和优化上下文信息,以支持协同决策、任务分解、角色分配与冲突解决。

简单来说:让每个Agent都知道“现在在干嘛、谁在干、为什么干、怎么干”,并能根据全局上下文动态调整自己的行为。

为什么需要 Multi-Agent Context Engineering?

想象一个由3个Agent组成的团队:

  • Planner Agent:负责拆解任务
  • Researcher Agent:负责搜索信息
  • Writer Agent:负责撰写报告

如果没有良好的上下文工程:

  • Researcher 不知道 Planner 最关心哪部分数据
  • Writer 拿到的信息可能是过时的或与目标无关
  • Planner 无法评估任务是否真正完成

结果:效率低下、重复劳动、输出不一致。

而通过 Context Engineering:

  • 每个Agent的输入输出都携带结构化上下文
  • 上下文随任务推进动态更新
  • Agent 之间共享“情境记忆”与“意图图谱”
  • 系统具备“情境感知”能力,能自动协调冲突或填补信息缺口

核心组件与设计模式

全局上下文存储(Global Context Store)

类似一个“黑板系统”(Blackboard Architecture),所有Agent都可以读写一个共享的上下文空间。这个空间可以是:

  • 一个结构化的 JSON 对象
  • 一个向量数据库(用于语义检索)
  • 一个图数据库(用于关系建模)

# state.py
from typing import TypedDict, List, Dict, Optional, Any, Annotated
from langgraph.graph.message import add_messages
from datetime import datetime

class AgentState(TypedDict):
    agent_id: str
    role: str
    last_action: str
    last_updated: str
    status: str  # "idle", "working", "blocked", "done"
    output: Optional[Any]

class SharedContext(TypedDict):
    # 任务元信息
    session_id: str
    task_id: str
    task_goal: str
    task_constraints: List[str]
    current_phase: str
    phase_history: List[Dict[str, Any]]

    # 共享数据区
    shared_memory: Dict[str, Any]

    # Agent 状态池
    agent_states: Dict[str, AgentState]

    # 上下文控制
    context_version: Annotated[int, lambda x, y: x + 1]  # 自动递增版本号
    event_queue: Annotated[List[Dict], add_messages]      # 事件队列(LangGraph 原生支持)

    # 时间戳
    created_at: str
    updated_at: str

Annotated[int, lambda x, y: x + 1]:每次状态更新自动递增版本号 Annotated[List, add_messages]:利用 LangGraph 内置的 add_messages 实现事件队列累加

上下文协议(Context Protocol)

定义Agent之间交换上下文的格式与语义。例如:

  • 使用自定义 Schema 描述上下文字段
  • 定义“上下文变更事件”(Context Update Event)的发布/订阅机制
  • 支持增量更新(Delta Update)而非全量覆盖

当 Researcher 找到新数据时,它发布一个 ​​context.update​​​ 事件,附带 ​​data_snippet​​​ 和 ​​confidence_score​​,Writer 订阅该事件并决定是否采纳。

[Researcher Agent]
       │
       ▼
[更新 state.event_queue] → LangGraph 内部流转(Writer 下一步消费)
       │
       ▼
[发布到 Redis "agent_events"] → 外部系统实时响应
       │
       ├→ [前端仪表盘] 实时显示“研究员已获取数据”
       ├→ [日志服务] 写入结构化日志
       └→ [告警服务] 检测异常事件(如 confidence < 0.5)

# agents/researcher.py
from event_bus.redis_adapter import RedisEventBus

# 全局 EventBus 实例(生产环境建议用依赖注入或 Singleton)
redis_bus = RedisEventBus()

def researcher_agent(state: SharedContext) -> Dict[str, Any]:
    data_snippet = "AI市场规模2025年预计达$3000亿"
    confidence_score = 0.92

    # 写入 LangGraph 内部 event_queue(用于状态机流转)
    internal_event = {
        "event_id": str(uuid.uuid4()),
        "event_type": "context.update",
        "source": "researcher",
        "target": "writer",
        "payload": {
            "key": "market_data",
            "value": data_snippet,
            "confidence": confidence_score
        }
    }

    # 同时发布到 Redis(用于外部系统监听)
    redis_bus.publish("agent_events", {
        "event_type": "researcher.data_fetched",
        "source_agent": "researcher",
        "payload": {
            "data_snippet": data_snippet,
            "confidence": confidence_score,
            "task_id": state["task_id"]
        },
        "context_version": state["context_version"]
    })

    return {
        "shared_memory": {**state["shared_memory"], "latest_data": data_snippet},
        "event_queue": [internal_event],  # 供 LangGraph 内部消费
        "agent_states": { ... }
    }

# monitor/realtime_logger.py
import json
from event_bus.redis_adapter import RedisEventBus

def on_agent_event(message):
    data = json.loads(message['data'])
    event_type = data["event_type"]

    if event_type == "researcher.data_fetched":
        print(f" 实时监控: 研究员获取数据: {data['payload']['data_snippet']}")
    elif event_type == "report.draft_ready":
        print(f" 报告已生成,长度: {data['payload']['length']} 字符")

# 启动监听
bus = RedisEventBus()
bus.subscribe("agent_events", on_agent_event)

我们也可以在状态更新时自动发布快照事件。

# checkpoint/event_trigger.py
from langgraph.checkpoint.base import BaseCheckpointSaver

class EventEmittingCheckpointer(BaseCheckpointSaver):
    def put(self, config, checkpoint, metadata):
        # 先调用原生保存逻辑
        super().put(config, checkpoint, metadata)

        # 发布“状态已保存”事件到 EventBus
        redis_bus.publish("system_events", {
            "event_type": "checkpoint.saved",
            "thread_id": config["configurable"]["thread_id"],
            "version": checkpoint["channel_versions"]["__root__"],
            "timestamp": metadata["write_timestamp"]
        })

注意:LangGraph 的 ​​BaseCheckpointSaver​​ 是底层接口,自定义要谨慎。更推荐在节点返回后手动发布。

角色感知上下文(Role-Aware Context)

不同角色的Agent关注的上下文维度不同:

  • Planner 关注任务结构与依赖关系
  • Researcher 关注查询意图与数据新鲜度

因此,上下文引擎应支持:

  • 视图隔离(每个Agent看到自己需要的上下文子集)
  • 权限控制(某些上下文字段只读/可写)
  • 个性化摘要(自动为每个Agent生成“上下文简报”)

我们在节点内实现“视图生成器”:

def get_role_view(state: SharedContext, role: str) -> Dict[str, Any]:
    view_templates = {
        "planner": ["task_goal", "current_phase", "agent_states"],
        "researcher": ["task_goal", "shared_memory.queries", "task_constraints"],
        "writer": ["shared_memory.latest_data", "task_goal"]
    }

    view = {}
    for key in view_templates.get(role, []):
        if '.' in key:
            parts = key.split('.')
            val = state
            for part in parts:
                val = val.get(part, {}) if isinstance(val, dict) else {}
            view[key] = val
        else:
            view[key] = state.get(key)
    return view

# 在 researcher_agent 中调用
view = get_role_view(state, "researcher")
print(f"[Researcher View] {view}")

上下文演化与版本控制

上下文不是静态的 —— 它随时间推移、任务推进、外部输入而演化。我们需要:

  • 记录上下文变更历史(类似 Git)
  • 支持“上下文快照”用于回滚或对比
  • 检测上下文漂移(Context Drift)并触发重校准

例如:当用户中途修改了报告目标,系统应自动通知所有Agent,并触发 Planner 重新规划。

LangGraph 的 ​​Checkpointer​​ 是天然解决方案。

# graph_builder.py
from langgraph.graph import StateGraph
from langgraph.checkpoint.sqlite import SqliteSaver
from .state import SharedContext
from .agents import planner_agent, researcher_agent, writer_agent

# 初始化检查点(持久化 + 版本历史)
memory = SqliteSaver.from_conn_string("checkpoints.db")

def build_graph():
    workflow = StateGraph(SharedContext)

    workflow.add_node("planner", planner_agent)
    workflow.add_node("researcher", researcher_agent)
    workflow.add_node("writer", writer_agent)

    workflow.set_entry_point("planner")
    workflow.add_edge("planner", "researcher")
    workflow.add_edge("researcher", "writer")

    # 编译时绑定检查点
    return workflow.compile(checkpointer=memory)

手动触发快照(当关键字段变更时)

# agents/planner.py
def planner_agent(state: SharedContext) -> Dict[str, Any]:
    new_phase = "data_collection"

    # 当 phase 改变时,强制创建快照(用于回滚)
    if state["current_phase"] != new_phase:
        # 在返回值中不直接支持“创建快照”,但可通过外部机制或自定义 reducer 实现
        # 这里我们通过 event 通知外部系统创建快照
        snapshot_event = {
            "event_id": str(uuid.uuid4()),
            "event_type": "snapshot.request",
            "reason": f"phase_changed: {state['current_phase']} → {new_phase}",
            "manual_trigger": True
        }

        return {
            "current_phase": new_phase,
            "event_queue": [snapshot_event],
            # ... 其他字段
        }

回滚到历史版本(外部调用)

# rollback_manager.py
from .graph_builder import app

def rollback_to_version(thread_id: str, target_version: int):
    """回滚到指定版本"""
    # 获取所有历史状态
    history = list(app.get_state_history({"configurable": {"thread_id": thread_id}}))

    for snapshot in reversed(history):
        if snapshot.values["context_version"] == target_version:
            # 执行回滚
            app.update_state(
                {"configurable": {"thread_id": thread_id}},
                snapshot.values,  # 完整状态覆盖
                as_node="system_rollback"  # 虚拟节点名
            )
            print(f"已回滚到版本 {target_version}")
            return True
    return False

检测上下文漂移(例如用户修改目标)

def detect_context_drift(current_state: SharedContext, baseline: Dict) -> bool:
    """检测关键字段是否被外部修改"""
    drift_fields = ["task_goal", "task_constraints"]
    for field in drift_fields:
        if current_state.get(field) != baseline.get(field):
            return True
    return False


if detect_context_drift(latest_state, original_baseline):
    # 触发 Planner 重新规划
    app.update_state(
        {"configurable": {"thread_id": thread_id}},
        {"event_queue": [{
            "event_id": "drift_detected",
            "event_type": "system.reset",
            "payload": {"reason": "task_goal_modified"}
        }]},
        as_node="planner"  # 强制在 planner 上下文中处理
    )

最佳实践

上下文压缩与摘要

当上下文过长时(如超过 LLM 的上下文窗口),使用:

  • Map-Reduce 摘要
  • 关键信息提取(Key-Value Extraction)
  • 向量化 + 相似度检索(只注入最相关的上下文)

上下文反思(Context Reflection)

定期让一个“Meta-Agent”或“Critic Agent”审查当前上下文:

  • 是否偏离原始目标?
  • 是否存在矛盾信息?
  • 是否需要人类介入?

外部上下文注入

从外部系统(数据库、API、用户反馈)动态注入上下文

context["user_feedback"] = get_latest_feedback()
context["market_data"] = fetch_real_time_data()

最近建了langgraph & langgraph 智能体开发交流群,感兴趣的朋友可以点赞关注后入群交流

本文转载自​AI 博物院​ 作者:longyunfeigu

©著作权归作者所有,如需转载,请注明出处,否则将追究法律责任
已于2025-9-8 10:10:26修改
收藏
回复
举报
回复
相关推荐