Skip to content
🔗 分享本题
查看我的学习进度 →

23 模块 Q14 教学图:多 Agent 系统的分布式追踪:TraceID 传递与关联

🧠 图解记忆:Trace 上下文既要穿过同步 HTTP,也要随消息元数据跨越异步边界;点击图片可查看原图。

💡 答案要点

核心挑战:

单 Agent:TraceID 在单个进程内传递
多 Agent:TraceID 跨越多个进程/服务,需要手动传播

问题:Agent A 调用 Agent B,trace_id 断了吗?

传播机制对比:

方式原理适用场景局限性
HTTP Headertraceparent 自动传播HTTP 调用需要所有服务支持 OTEL
消息队列手动注入 baggage异步消息需要修改消息格式
共享内存Redis 存储上下文同机器多进程延迟增加
数据库Task 表存储 trace_id持久化任务需要事务支持

生产级实现(HTTP + 消息队列双模式):

展开 Python 代码示例(58 行)
python
from opentelemetry import propagate, trace
from opentelemetry.propagate import inject, extract
from opentelemetry.trace.propagation.tracecontext import TraceContextTextMapPropagator

class MultiAgentTracer:
    def __init__(self):
        self.propagator = TraceContextTextMapPropagator()
    
    # 模式1:HTTP 调用(自动传播)
    async def call_agent_http(self, agent_name: str, task: dict, url: str):
        headers = {}
        inject(headers)  # 自动从当前 context 注入 traceparent
        
        response = await httpx.AsyncClient().post(
            url,
            json=task,
            headers={**headers, "X-Agent-Name": agent_name}
        )
        
        span_context = extract(response.headers).get(
            trace_span_context_key, None
        )
        return response.json()
    
    # 模式2:消息队列调用(手动传播)
    async def call_agent_mq(self, agent_name: str, task: dict, mq_client):
        current_span = trace.get_current_span()
        span_context = current_span.get_span_context()
        
        traceparent = f"00-{span_context.trace_id:032x}-{span_context.span_id:016x}-01"
        
        message = {
            **task,
            "_trace_context": {
                "traceparent": traceparent,
                "tracestate": current_span.get_span_context().trace_state,
                "agent_name": agent_name,
                "parent_span_id": span_context.span_id
            }
        }
        
        await mq_client.publish(
            exchange="agent_exchange",
            routing_key=agent_name,
            body=json.dumps(message)
        )

# 跨 Agent 轨迹聚合查询
async def get_full_trace(task_id: str):
    """获取同一任务的所有 Agent 轨迹"""
    trace_id = await redis.hget(f"task:{task_id}", "trace_id")
    
    spans = await jaeger_client.query(
        service=["agent-orchestrator", "agent-search", "agent-executor"],
        trace_id=trace_id
    )
    
    return sorted(spans, key=lambda s: s.start_time)

数据库 Schema 设计:

sql
-- 任务表存储 trace_id 关联
CREATE TABLE agent_tasks (
    id UUID PRIMARY KEY,
    trace_id VARCHAR(64) NOT NULL,
    parent_span_id VARCHAR(32) NOT NULL,
    status VARCHAR(20),
    created_at TIMESTAMP,
    completed_at TIMESTAMP,
    INDEX idx_trace_id (trace_id),
    INDEX idx_status (status)
);

-- 子 Agent 任务关联表
CREATE TABLE agent_task_children (
    parent_task_id UUID REFERENCES agent_tasks(id),
    child_agent_name VARCHAR(50),
    child_trace_id VARCHAR(64),
    child_span_id VARCHAR(32),
    status VARCHAR(20),
    PRIMARY KEY (parent_task_id, child_agent_name)
);

面试话术:

"多Agent追踪的核心是trace_id的传播方式。HTTP调用自动传播(OTel标准),消息队列需要手动注入traceparent到消息体。生产中我用过两种模式:同步HTTP用自动传播,异步MQ用手工传播(把traceparent放消息头)。每个任务在数据库存trace_id,调试时用Jaeger按trace_id查询,能看到OrchestratorAgent→SearchAgent→ExecutorAgent的完整时序。"

📚 参考:OpenTelemetry:Distributed Tracing(TraceID 传播)