ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

基于OpenTelemetry与Elastic Stack构建Hermes智能体可观测性体系

基于OpenTelemetry与Elastic Stack构建Hermes智能体可观测性体系 1. 项目概述构建Hermes智能体的可观测性体系最近在折腾Hermes智能体这玩意儿确实好用能帮你处理各种自动化任务从数据查询到流程编排效率提升不是一点半点。但用久了就发现一个问题当你的智能体开始处理复杂任务或者部署在服务器上7x24小时运行时你根本不知道它“脑子”里在想什么。它执行成功了吗卡在哪一步了调用外部API花了多长时间内存和CPU占用是不是爆了出了问题日志散落在各处查起来像大海捞针。这就是可观测性Observability要解决的问题。它不是简单的监控Monitoring监控是告诉你系统“是否”坏了而可观测性是让你能理解系统“为什么”会这样运行让你能像调试本地代码一样洞察分布式、异步执行的智能体内部状态。对于Hermes这类基于大语言模型LLM的智能体来说其决策过程具有不确定性和复杂性可观测性更是至关重要。我选择的方案是OTel Elastic。OTel全称 OpenTelemetry是目前云原生领域可观测性数据采集的事实标准它提供了一套与厂商无关的API、SDK和工具用来收集、生成遥测数据包括链路追踪-Traces、指标-Metrics、日志-Logs。Elastic这里主要指 Elastic Stack尤其是 Elasticsearch 和 Kibana它是一个强大的搜索和分析引擎用来存储、索引和可视化 OTel 收集上来的海量数据。简单来说OTel 负责从 Hermes 智能体中“探针”采集它运行时的所有关键信号Elastic 则负责提供一个“作战指挥室”把这些信号存储起来并用丰富的图表和仪表盘展示出来让你一目了然。这套组合拳打下来你就能对 Hermes 智能体的健康状况、性能瓶颈和错误根因了如指掌。2. 核心需求与方案选型解析2.1 为什么Hermes智能体需要专门的可观测性传统的应用监控关注的是HTTP请求响应时间、服务器负载、数据库连接池这些。但智能体尤其是像Hermes这样能执行复杂技能Skill、具有自主规划能力的智能体其运行范式完全不同。异步与长周期任务一个智能体任务可能涉及多轮对话、调用多个工具、等待外部API响应持续几分钟甚至几小时。你需要一个贯穿始终的“故事线”Trace来跟踪整个流程。LLM调用的不确定性每次调用大模型生成内容其耗时、消耗的Token数、甚至结果都可能波动。你需要量化这些调用比如统计每次对话的平均Token消耗、生成延迟的P99值。技能Skill执行的细粒度洞察Hermes的核心能力在于其技能库。你需要知道每个技能被调用的频率、成功率、执行耗时从而优化技能设计或发现冷门技能。工具Tool使用与外部集成智能体经常调用搜索引擎、数据库、API等外部工具。这些外部调用的性能和稳定性直接影响智能体体验需要被单独监控。成本与资源管理如果使用按Token付费的云端LLM可观测性数据能帮你准确核算每个任务、每个用户的成本。同时智能体进程本身的资源使用内存、CPU也需要关注。没有可观测性智能体就是一个黑盒。出了问题你只能看到“任务失败”这个结果而对中间过程一无所知排查效率极低。2.2 为什么选择OTel Elastic这套组合市面上可观测性方案很多比如直接使用云厂商的监控服务或者用Jaeger做链路追踪Prometheus做指标Loki做日志。我选择OTelElastic主要基于以下几点考量OTel的厂商中立性与未来兼容性OTel是CNCF毕业项目它定义了一套标准的数据模型和采集接口。这意味着你今天用OTel Instrumentation插桩了你的Hermes代码未来后端存储无论是换成Jaeger、Prometheus还是其他任何支持OTel协议的系统你的代码都无需改动。这避免了供应商锁定技术选型更加灵活。“三大支柱”的统一采集OTel从一开始就设计为统一采集Traces, Metrics, Logs。你只需要在代码中集成一次OTel SDK配置好导出器Exporter就能同时上报三种遥测数据并且它们之间通过Trace ID、Span ID实现自动关联。这比维护三套独立的采集客户端要简单、可靠得多。Elasticsearch的强大分析与可视化能力Elasticsearch不仅仅是搜索引擎它对时序数据、结构化/非结构化日志的索引和聚合分析能力极其强大。Kibana提供的可视化组件和仪表盘定制功能非常灵活能够轻松构建出贴合智能体监控需求的视图例如将技能执行耗时Metric与对应的错误日志Log在同一个时间轴上关联展示。成熟的生态与集成Elastic官方提供了对OTel协议的原生支持OpenTelemetry integration可以直接接收OTel数据。部署上你可以使用Elastic Cloud服务也可以在本地用Docker快速搭建一套Elastic Stack。社区资源和解决方案都很丰富。对复杂查询和关联分析的支持当你想分析“所有使用了‘网络搜索’技能且最终失败的任务它们的共同特征是什么”这类复杂问题时Elasticsearch的DSL查询语言和Kibana的Lens功能能够提供强大的支持这是很多单纯指标监控系统所欠缺的。注意如果你的环境资源极其有限或者只需要非常基础的指标监控单独使用Prometheus可能更轻量。但考虑到智能体监控对链路追踪和日志关联的强需求OTelElastic提供的是一套更完整、更面向未来的企业级解决方案。3. 环境准备与核心组件部署3.1 Elastic Stack 部署与配置我们首先搭建数据后端。这里以使用Docker Compose在本地或测试环境快速部署为例。生产环境建议考虑Elastic Cloud或基于Kubernetes的集群化部署。创建docker-compose.yml文件version: 3.8 services: elasticsearch: image: docker.elastic.co/elasticsearch/elasticsearch:8.13.0 container_name: elasticsearch environment: - discovery.typesingle-node - xpack.security.enabledfalse # 测试环境可关闭安全认证生产环境必须开启并配置密码 - ES_JAVA_OPTS-Xms512m -Xmx512m ulimits: memlock: soft: -1 hard: -1 volumes: - es-data:/usr/share/elasticsearch/data ports: - 9200:9200 networks: - elastic kibana: image: docker.elastic.co/kibana/kibana:8.13.0 container_name: kibana environment: - ELASTICSEARCH_HOSTShttp://elasticsearch:9200 ports: - 5601:5601 depends_on: - elasticsearch networks: - elastic volumes: es-data: driver: local networks: elastic: driver: bridge这个配置启动了一个单节点的Elasticsearch和Kibana并禁用了安全认证以简化流程。ES_JAVA_OPTS设置了JVM堆内存请根据机器资源调整。启动服务docker-compose up -d等待几分钟访问http://localhost:5601应该能看到Kibana界面。访问http://localhost:9200会返回Elasticsearch的JSON欢迎信息。在Kibana中配置OTel数据流登录Kibana进入左侧菜单Management Integrations。搜索并选择“OpenTelemetry”集成点击“Add OpenTelemetry”。在配置页面你会看到如何向Elastic发送OTel数据的指南最重要的是OTLP Endpoint的地址。对于本例如果OTel Collector或客户端与Elastic在同一网络地址通常是http://your-host:8200Elastic OTLP ingest服务默认端口。但更常见的做法是使用OpenTelemetry Collector作为中介。3.2 OpenTelemetry Collector 部署与配置虽然OTel SDK可以直接将数据发送到Elasticsearch通过OTLP HTTP/gRPC但在生产环境中强烈建议使用OpenTelemetry Collector。它是一个独立的进程负责接收、处理、批量和导出遥测数据。这样做的好处是解耦应用端无需关心后端存储的地址和认证变更只需固定发送给Collector。统一处理可以在Collector层对数据进行过滤、采样、添加属性、转换格式等操作。多路导出可以同时将数据导出到多个后端如开发环境用Jaeger生产环境用Elastic。创建Collector配置文件otel-collector-config.yamlreceivers: otlp: protocols: grpc: endpoint: 0.0.0.0:4317 http: endpoint: 0.0.0.0:4318 processors: batch: # 批量处理提高传输效率 timeout: 1s send_batch_size: 1024 memory_limiter: # 防止内存溢出 check_interval: 1s limit_mib: 512 spike_limit_mib: 256 exporters: logging: verbosity: detailed otlp/elastic: endpoint: elasticsearch:9200 # 假设Collector与ES在同一Docker网络。否则用主机IP:端口 tls: insecure: true # 如果ES开启了安全认证这里需要配置证书 headers: # 如果ES开启了安全认证可能需要添加认证头例如 # Authorization: Bearer your-token service: pipelines: traces: receivers: [otlp] processors: [memory_limiter, batch] exporters: [otlp/elastic] metrics: receivers: [otlp] processors: [memory_limiter, batch] exporters: [otlp/elastic] logs: receivers: [otlp] processors: [memory_limiter, batch] exporters: [otlp/elastic]这个配置让Collector在4317gRPC和4318HTTP端口上接收OTel数据经过批处理和内存限制后通过OTLP协议导出到Elasticsearch。logging导出器用于调试可以在控制台看到接收到的数据。使用Docker运行Collectordocker run -p 4317:4317 -p 4318:4318 \ -v $(pwd)/otel-collector-config.yaml:/etc/otelcol/config.yaml \ otel/opentelemetry-collector-contrib:latest现在你的数据管道就准备好了Hermes (OTel SDK) - OTel Collector - Elasticsearch。4. Hermes智能体的OTel插桩实战这是最核心的一步在你的Hermes智能体代码中集成OTel SDK在关键位置埋点。这里以Python版本的Hermes智能体为例其他语言原理类似。4.1 基础依赖安装首先在你的Hermes项目环境中安装必要的Python包pip install opentelemetry-api opentelemetry-sdk opentelemetry-exporter-otlp-proto-http # 如果你使用特定的框架可能需要对应的instrumentation包例如 # pip install opentelemetry-instrumentation-fastapi opentelemetry-instrumentation-requestsopentelemetry-api和opentelemetry-sdk是核心。opentelemetry-exporter-otlp-proto-http让我们能通过HTTP协议将数据发送到Collector。4.2 初始化OTel并创建Tracer在你的Hermes应用启动入口例如main.py或app.py进行初始化。重要这段初始化代码应该在应用生命周期中最早执行。import logging from opentelemetry import trace from opentelemetry.sdk.trace import TracerProvider from opentelemetry.sdk.trace.export import BatchSpanProcessor from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter from opentelemetry.sdk.resources import Resource, SERVICE_NAME, DEPLOYMENT_ENVIRONMENT # 1. 创建TracerProvider它是Tracer的工厂 trace.set_tracer_provider(TracerProvider( resourceResource.create({ SERVICE_NAME: hermes-intelligent-agent, # 服务名在Kibana中用于筛选 DEPLOYMENT_ENVIRONMENT: development, # 环境标识如 dev/staging/prod hermes.agent.version: 1.0.0, # 自定义属性 }) )) # 2. 创建OTLP导出器指向我们部署的Collector otlp_exporter OTLPSpanExporter( endpointhttp://localhost:4318/v1/traces, # Collector的OTLP HTTP端点 # 可选添加headers进行认证 # headers{Authorization: Bearer xxx} ) # 3. 创建批处理Span处理器并添加到TracerProvider span_processor BatchSpanProcessor(otlp_exporter) trace.get_tracer_provider().add_span_processor(span_processor) # 4. 获取一个Tracer实例用于创建Span tracer trace.get_tracer(__name__)现在你可以在代码中使用tracer来记录关键操作了。4.3 对关键操作进行埋点假设你的Hermes智能体有一个核心的process_query函数它接收用户查询规划步骤调用技能。from opentelemetry.trace import Status, StatusCode def process_query(user_query: str, session_id: str): 处理用户查询的核心函数。 # 为整个查询处理创建一个根Span with tracer.start_as_current_span(process_query) as root_span: # 为当前Span设置一些有用的属性Attributes root_span.set_attribute(user.query, user_query) root_span.set_attribute(session.id, session_id) root_span.set_attribute(component, query_processor) try: # 1. 意图识别与规划 (LLM调用) with tracer.start_as_current_span(intent_planning) as planning_span: planning_span.set_attribute(llm.provider, openai) planning_span.set_attribute(llm.model, gpt-4) # 这里模拟调用LLM plan call_llm_for_planning(user_query) planning_span.set_attribute(plan.steps, len(plan)) # 记录一个事件Event表示规划完成 planning_span.add_event(planning_completed, attributes{plan: str(plan)}) # 2. 按计划执行技能 for i, step in enumerate(plan): skill_name step.get(skill) # 为每个技能执行创建一个子Span with tracer.start_as_current_span(fexecute_skill.{skill_name}) as skill_span: skill_span.set_attribute(skill.name, skill_name) skill_span.set_attribute(step.index, i) skill_span.set_attribute(step.parameters, str(step.get(params))) # 执行具体的技能 result execute_skill(skill_name, step.get(params)) skill_span.set_attribute(skill.result.success, result[success]) if not result[success]: # 如果技能执行失败记录错误状态和异常信息 skill_span.set_status(Status(StatusCode.ERROR, result.get(error))) skill_span.record_exception(Exception(result.get(error)))) else: skill_span.set_attribute(skill.result.data_summary, str(result[data])[:100]) # 记录摘要 # 3. 结果汇总与回复生成 (可能再次调用LLM) with tracer.start_as_current_span(summarize_response) as summary_span: final_response generate_final_response(plan, collected_results) root_span.set_attribute(response.length, len(final_response)) root_span.add_event(query_processed_successfully) except Exception as e: # 捕获整个流程的未处理异常 root_span.set_status(Status(StatusCode.ERROR, str(e))) root_span.record_exception(e) logging.error(fFailed to process query: {user_query}, exc_infoe) raise代码解读与关键点start_as_current_span创建一个Span跨度代表一个逻辑操作单元。with语句确保Span会自动开始和结束并记录耗时。Span层级process_query是根Spanintent_planning、execute_skill.xxx、summarize_response是其子Span形成了清晰的调用树。属性Attributes使用set_attribute记录与业务相关的上下文信息如查询内容、技能名、LLM模型等。这些是后期筛选和聚合分析的关键维度。事件Events使用add_event在Span的时间线上标记一个特定时刻发生的事如“规划完成”。状态Status使用set_status标记Span的成功或失败。这对于错误告警和成功率统计至关重要。异常记录record_exception会将异常的堆栈信息记录到Span中在Kibana中可以直接查看。4.4 对LLM调用和外部工具进行自动埋点手动埋点工作量大且易遗漏。OTel提供了自动插桩Auto-Instrumentation功能可以对常见的库如requests,openai,sqlalchemy进行无侵入式的埋点。安装自动插桩包pip install opentelemetry-instrumentation-openai opentelemetry-instrumentation-requests在应用启动时启用自动插桩from opentelemetry.instrumentation.openai import OpenAIInstrumentor from opentelemetry.instrumentation.requests import RequestsInstrumentor # 在初始化TracerProvider之后调用 OpenAIInstrumentor().instrument() RequestsInstrumentor().instrument()这样所有通过openai库发起的API调用以及通过requests库发起的HTTP请求都会自动创建Span并记录URL、方法、状态码、耗时等信息。这极大地简化了对第三方依赖的监控。4.5 集成日志与指标Metrics为了构建完整的可观测性我们还需要日志和指标。日志关联 使用OTel的日志SDK或者配置你的日志框架如Python的logging将日志发送到OTel Collector并注入当前的Trace ID和Span ID。这样在Kibana中你就能轻松地通过Trace ID找到该次请求对应的所有日志。import logging from opentelemetry import trace from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler from opentelemetry.sdk._logs.export import BatchLogRecordProcessor from opentelemetry.exporter.otlp.proto.http._log_exporter import OTLPLogExporter # 设置LoggerProvider logger_provider LoggerProvider(resourceResource.create(...)) log_exporter OTLPLogExporter(endpointhttp://localhost:4318/v1/logs) logger_provider.add_log_record_processor(BatchLogRecordProcessor(log_exporter)) # 创建一个Handler将其添加到你的logger中 handler LoggingHandler(levellogging.INFO, logger_providerlogger_provider) logging.getLogger().addHandler(handler) # 现在使用logging记录的日志会自动包含Trace上下文 logging.info(This log will be associated with the current trace.)指标采集 使用OTel Metrics API记录业务指标例如“每秒处理的查询数”、“技能执行平均耗时”、“LLM调用Token消耗总量”。from opentelemetry import metrics from opentelemetry.sdk.metrics import MeterProvider from opentelemetry.sdk.metrics.export import PeriodicExportingMetricReader from opentelemetry.exporter.otlp.proto.http.metric_exporter import OTLPMetricExporter meter_provider MeterProvider(...) metrics.set_meter_provider(meter_provider) meter metrics.get_meter(__name__) # 创建一个计数器 query_counter meter.create_counter( namehermes.queries.processed, descriptionTotal number of queries processed, unit1 ) # 在process_query函数中递增计数器 query_counter.add(1, {query.type: general})5. 在Kibana中可视化与分析数据数据上报后我们进入Kibana让数据说话。5.1 探索追踪数据Traces进入Observability Traces。在查询栏中你可以通过service.name: “hermes-intelligent-agent”或attributes.hermes.agent.version: “1.0.0”来筛选数据。点击任意一个Trace你会看到一个完整的瀑布图Waterfall清晰地展示了process_query这个根Span下intent_planning、各个execute_skill以及summarize_response子Span的耗时和层级关系。点击某个Span比如一个失败的技能执行在详情面板中可以看到我们设置的所有Attributes技能名、参数、错误信息以及记录的Events和Exceptions。这是排查问题的第一现场。5.2 创建指标仪表盘Dashboards进入Analytics Dashboard创建新的仪表盘。使用Lens可视化编辑器。技能执行成功率创建一个“指标”可视化使用span.name过滤出execute_skill.*然后使用status.code字段进行拆分计算ERROR状态与总次数的比率。技能执行耗时分布创建一个“时序图”或“柱状图”使用span.duration字段按attributes.skill.name进行拆分可以直观看到哪个技能最慢。LLM调用延迟与Token消耗如果你在Span属性中记录了llm.response.time和llm.usage.total_tokens可以创建相应的时序图表监控模型性能与成本。查询吞吐量使用我们记录的hermes.queries.processed指标创建一个“时序图”来展示QPS每秒查询数。5.3 关联日志与追踪进入Observability Logs。在查询栏输入trace.id: “YOUR_TRACE_ID”就能直接过滤出与该次请求相关的所有日志。这实现了“从链路追踪一键跳转到日志”的完美闭环极大提升了排障效率。5.4 设置告警Alerting基于我们收集的指标和日志可以设置主动告警。进入Management Rules and Connectors。创建一条新的规则。例如规则类型指标阈值。指标选择我们定义的hermes.queries.processed设置“在最近5分钟内总和小于1”即服务无响应。条件 1。动作发送邮件、Slack消息或调用Webhook。同样可以设置“技能执行错误率超过5%”或“LLM平均响应时间超过10秒”等业务相关的告警。6. 常见问题与排查技巧实录在实际部署和运行这套可观测性体系时我踩过不少坑这里总结几个典型问题和解决方法。6.1 数据没有在Kibana中显示检查Collector日志首先查看OTel Collector容器的日志 (docker logs collector-container-id)。看是否有错误信息比如连接Elasticsearch失败、认证失败等。验证数据接收在Collector配置中启用loggingexporter并设置verbosity: detailed。然后在你的Hermes应用中触发一个请求查看Collector控制台是否打印出接收到的Span信息。这是验证数据是否成功从应用发送到Collector的最直接方法。检查Kibana索引模式进入Kibana的Management Stack Management Index Patterns。确保存在以traces-*,metrics-*,logs-*开头的索引模式。如果没有可能是Elasticsearch没有正确接收到OTLP数据或者数据格式不对。验证网络连通性确保你的Hermes应用、OTel Collector、Elasticsearch三者之间的网络是通的。特别是在Docker或K8s环境中注意容器网络和主机网络的差异。6.2 Span数量过多导致存储和查询压力大智能体处理一个查询可能产生数十个Span尤其是每个工具调用都生成一个。在高频场景下数据量会激增。启用采样Sampling在OTel SDK或Collector中配置采样策略。例如使用“头部采样”Head-based Sampling只对一部分请求如10%进行全量Trace采集。对于错误请求可以提高采样率以确保问题能被捕获。在TracerProvider中配置from opentelemetry.sdk.trace.sampling import TraceIdRatioBased # 采样率50% sampler TraceIdRatioBased(0.5) trace.set_tracer_provider(TracerProvider(samplersampler, ...))在Collector中过滤可以在Collector的processors中添加filter处理器过滤掉一些不重要的Span例如耗时极短的内部检查Span。调整Span粒度审视你的埋点是否每个细小的操作都需要一个独立的Span有时可以进行适当的合并。6.3 属性Attributes设计不当影响查询效率随意添加大量或值域极广的属性如把完整的用户查询文本作为属性会导致索引膨胀查询变慢。遵循最佳实践键名使用小写蛇形命名如user.id,http.status_code。值为有限枚举对于状态、类型等字段尽量使用预定义的枚举值而不是自由文本。避免高基数High Cardinality值像用户ID、会话ID这种唯一性很高的值虽然有时需要但要意识到它们对性能的影响。可以考虑将它们作为单独的标签Tag处理或者在查询时谨慎使用。敏感信息脱敏绝对不要将密码、密钥、个人身份信息PII记录为属性。可以在Collector中添加attributes处理器进行脱敏。6.4 如何监控智能体的“思考”过程对于基于LLM的智能体我们不仅想知道它做了什么还想知道它“为什么”这么做。标准的OTel Span属性可能不够。记录LLM的输入与输出在调用LLM的Span中将关键的Prompt和Completion的摘要注意脱敏和截断作为属性或事件记录下来。例如llm.prompt_template: “总结以下内容{text}”,llm.completion_first_50_chars: “...”。利用事件Events记录决策点在智能体进行规划、选择技能、评估结果的关键决策时刻使用span.add_event记录一个事件并附上决策的依据如评分、置信度。自定义指标记录Token使用创建计数器Counter或直方图Histogram指标在每次LLM调用后累加使用的Prompt Token和Completion Token数量并打上模型名称的标签。这为成本核算提供了精确数据。6.5 在分布式部署中传递Trace上下文如果你的Hermes智能体需要调用其他微服务或者被其他服务调用需要确保Trace上下文Trace ID, Span ID在服务间传递。对于HTTP调用OTel的自动插桩如opentelemetry-instrumentation-requests和opentelemetry-instrumentation-fastapi会自动处理traceparent等标准HTTP头的注入和提取。对于消息队列如Redis, Kafka你需要手动或使用相应的Instrumentation库将上下文信息编码到消息的Header中在消费端再提取出来以保持链路的连续性。对于gRPC调用OTel也提供了相应的gRPC插桩库可以自动完成上下文传播。部署完这套可观测性系统后最大的感受是从“盲人摸象”变成了“全局透视”。以前智能体出问题需要翻看零散的日志文件靠猜来重建现场。现在在Kibana中输入一个失败的Trace ID整个请求的生命周期、每一步的耗时、LLM的交互内容、技能执行的输入输出、以及关联的错误日志全部呈现在一个界面上。定位问题的速度从小时级缩短到了分钟级。更重要的是通过长期观察指标仪表盘你能发现性能瓶颈的规律比如每天下午某个技能调用变慢从而有针对性地进行优化让智能体运行得更稳健、更高效。
返回列表