Hindsight 监控指南:Prometheus 指标、健康探针与 OpenTelemetry 分布式追踪
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
Hindsight(Agent Memory That Learns)通过三条互补的通道提供完整的可观测性:Prometheus 指标(/metrics端点)、三个语义不同的健康端点(/health/live、/health/ready、/health)以及遵循 GenAI 语义约定(v1.37+)的 OpenTelemetry 分布式追踪。本文以 monitoring.md 为主线,结合仓库中的 metrics.py、tracing.py、liveness.py 以及 monitoring 启动脚本 等实现,系统讲解如何在本地一键拉起 Grafana LGTM 可观测栈、正确配置健康探针(避免把数据库抖动升级成停机事故)、理解每一类指标与标签的语义,并利用现成的 Grafana 仪表盘与示例 PromQL 查询完成生产级告警。
本地可观测栈:一条命令启动 Grafana LGTM
Hindsight 为本地开发提供了开箱即用的 Grafana LGTM(Loki、Grafana、Tempo、Mimir)一体化栈,一条命令即可启动:
./scripts/dev/start-monitoring.sh该脚本是 start.sh 的便捷包装,后者会先探测 API 是否已在运行(curl http://localhost:8888/metrics),未检测到时给出警告并提示先通过./scripts/dev/start-api.sh启动 API,随后调用docker-compose up拉起整个栈。从 docker-compose.yaml 可以看到,它基于grafana/otel-lgtm:latest镜像启动一个名为hindsight-monitoring的容器,并做了三处关键配置:
- 端口映射:
3000(Grafana UI)、4317(OTLP gRPC 追踪)、4318(OTLP HTTP 追踪); - 匿名管理员登录:
GF_AUTH_ANONYMOUS_ENABLED=true、GF_AUTH_ANONYMOUS_ORG_ROLE=Admin、GF_AUTH_DISABLE_LOGIN_FORM=true,本地开发无需登录; - 挂载 Prometheus 配置与 Hindsight 仪表盘:用 prometheus.yml 覆盖 LGTM 默认抓取配置,并把三个 Hindsight 仪表盘 JSON 挂载进容器,通过 grafana-dashboards.yaml 自动 provisioning。
启动后即可获得:
| 能力 | 地址 / 说明 |
|---|---|
| Grafana UI | http://localhost:3000(匿名 admin 访问) |
| 追踪(Tempo) | OTLP 端点 http://localhost:4318(HTTP)与 http://localhost:4317(gRPC) |
| 指标(Prometheus/Mimir) | 自动抓取 http://localhost:8888/metrics |
| 日志(Loki) | 日志聚合 |
| 预置仪表盘 | Hindsight Operations、LLM Metrics、API Service |
值得注意的细节是 prometheus.yml 中的抓取配置:它同时声明了hindsight-api(host.docker.internal:8888)和hindsight-worker(host.docker.internal:8889)两个 job,配合extra_hosts: "host.docker.internal:host-gateway"让容器能访问宿主机上的服务;scrape_interval: 5s、evaluation_interval: 5s,并开启了scrape_native_histograms: true以支持原生直方图。注释明确说明 worker 的 job 在宿主机没有运行 worker 时是"无害的空操作"——这为后续生产环境抓取独立 worker 指标提供了现成模板。
在 API 中启用追踪:
export HINDSIGHT_API_OTEL_TRACES_ENABLED=true export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318:::note 生产环境部署 本地监控栈仅用于开发。生产环境应单独部署 Grafana LGTM,或使用商业平台(Grafana Cloud、DataDog、New Relic 等)。 :::
健康端点:区分存活与就绪,避免把数据库抖动变成停机
API 服务端(端口 8888)与每个 worker(端口 8889)都暴露同样的三个端点。它们回答两个不同的问题,把探针指错位置,就是"平稳度过数据库抖动"与"把抖动升级成一次停机事故"的区别:
| 端点 | 是否检查数据库 | 用途 |
|---|---|---|
/health/live | 否 | Liveness 探针 |
/health/ready | 是 | Readiness 探针 |
/health | 是 | Readiness——/health/ready的别名,为兼容性保留 |
/health/live只要进程还能处理请求就返回 200,且不产生任何 I/O:
{ "status": "alive", "version": "0.4.0", "uptime_seconds": 812.4 }能应答本身就是检查。Hindsight 在单个事件循环上运行请求处理器与任务工作,因此被阻塞调用卡死的事件循环无法在探针超时内应答——而这正是重启能修复的故障类型。其实现位于 liveness.py:模块文档开宗明义地写道"liveness 只回答一个问题:这个进程是否已经卡死到只能靠重启修复?它绝不能触碰数据库",因为"运行SELECT 1的 liveness 探针会把数据库降级变成停机事故"。
worker 的载荷额外增加了worker_id、is_shutdown和seconds_since_last_poll(距上次完成 claim 周期的时长,第一次之前为null)。最后一个字段是为了告警而设计的:它从不改变状态码,因为卡在饱和数据库后面的 poller 恰恰是"重启会雪上加霜"的情形——正如 WorkerLivenessResponse 的文档所写:"报告它只是为了告警——无论它多陈旧端点都保持 200,这样饱和的数据库永远不可能触发重启。"
/health与/health/ready获取一个池化连接并执行SELECT 1,数据库可达时返回 200,不可达时返回 503。载荷区分了两种出错方式——db_acquire_ms与db_pool_waiting指向连接池耗尽,而慢查询指向数据库本身:
{ "status": "healthy", "database": "connected", "db_acquire_ms": 0.4, "db_pool_waiting": 0 }:::warning 绝不要把 liveness 探针指向依赖检查 liveness 失败意味着"重启这个进程"。如果 liveness 探针检查数据库,那么数据库变慢会让所有 Pod 同时重启:在途请求被丢弃,已认领的异步操作以递增的retry_count重新排队,一步步走向永久失败悬崖;每个重启的 Pod 还会在一个已经不堪重负的数据库上重新连接、重新预热连接池。就绪失败才是正确响应——它把 Pod 从 Service 中摘除,待数据库恢复后再放回。
仓库自带的 Helm chart 已经如此配置(livenessProbe→/health/live,readinessProbe→/health)。如果你基于旧版本手写了 manifest,请把 liveness 路径改过来。 :::
这条警告在源码与 Helm 配置中都能得到印证:api-deployment.yaml 将探针模板化为 values 中的值,而 values.yaml 中 API 的livenessProbe使用httpGet: path: /health/live, port: 8888(initialDelaySeconds: 30, periodSeconds: 10, timeoutSeconds: 5, failureThreshold: 3),readinessProbe使用path: /health(initialDelaySeconds: 10, periodSeconds: 5, timeoutSeconds: 3, failureThreshold: 3)。注释还特别说明 liveness 需要 chart 的 appVersion 或更新版本的镜像——旧镜像只提供/health,会以 404 使探针失败。worker(端口 8889)的探针配置同样遵循这一模式。
指标端点与指标体系概览
Hindsight 在/metrics暴露 Prometheus 指标:
curl http://localhost:8888/metrics指标基于 OpenTelemetry SDK 构建,由 metrics.py 中的initialize_metrics()完成初始化:创建带service.name/service.version资源的MeterProvider,通过PrometheusMetricReader导出,并为三个时长直方图注册自定义分桶的 View(见下文"直方图分桶")。MetricsCollector类通过create_histogram/create_counter/create_up_down_counter/create_observable_gauge逐一注册了本文列出的全部指标;当指标未启用时,NoOpMetricsCollector提供同接口的空实现,保证调用方无需判空。
指标体系中还有几个仓库实现中值得一提的细节:
- 租户标签:所有核心指标都通过
_get_tenant()附带tenant标签(来自当前 schema),多租户部署可按租户拆分查询(原文档表格未列出,但源码确认存在); - 端点基数控制:normalize_http_endpoint() 会把路径中的
/banks/<id>段、UUID 和纯数字 ID 分别模板化为{bank_id}、{id},保证endpoint标签基数有界; - 指标开关:
metrics_include_bank_id配置项控制是否附加bank_id标签(避免高基数); - 同步抓取路径:backlog / 队列深度等 gauge 由后台任务每 30 秒刷新缓存,
/metrics抓取路径保持同步(见 metrics.py)。
指标详解
操作指标(Operation Metrics)
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.operation.duration | Histogram | operation, bank_id, source, budget, max_tokens, success | 操作时长(秒) |
hindsight.operation.total | Counter | operation, bank_id, source, budget, max_tokens, success | 已执行操作总数 |
标签:
operation:操作类型(retain、recall、reflect,以及consolidation等异步 worker 任务类型)bank_id:记忆银行标识source:操作触发来源(api、reflect、internal、worker)budget:若指定则为预算等级(low、mid、high)max_tokens:若指定则为最大 token 数success:操作是否成功(true、false)
source标签用于区分:
api:客户端直接 API 调用reflect:reflect 操作期间发起的内部 recall 调用internal:其他内部操作worker:异步 worker 在认领任务达到终态时记录的完成
对于source="worker",success标签是完成吞吐信号:false表示任务在重试耗尽或发生意外错误后向 poller 抛出。在 executor 内部处理并正常返回的失败在此仍记录success="true";要获取权威的异步操作失败状态,请使用hindsight_async_operations{status="failed"}。
从源码看,操作指标还隐藏了一个语义细节:在 record_operation 的上下文管理器中,客户端断连导致的取消(OperationCancelledError,由 HTTP 层以HTTPException(499)重新抛出)既不算成功也不算失败,会被完全排除在指标之外,避免膨胀成功率或失败率——这是通过遍历__cause__链匹配取消异常实现的(见_is_client_cancellation)。
Retain 指标
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.retain.documents.total | Counter | outcome, bank_id | retain 处理的文档数,按抽取结果分桶 |
标签:
outcome:文档在 retain 后拥有记忆单元时为facts,没有则为no_factsbank_id:记忆银行标识
outcome="no_facts"是"文档已存储但未产生任何记忆"的信号。这些文档对recall和reflect不可见,直到被重新处理——没有其他指标会报告它们,因为 retain 本身成功了。该占比上升通常意味着 retain mission 排除了超出预期的内容:
sum(rate(hindsight_retain_documents_total{outcome="no_facts"}[15m])) / sum(rate(hindsight_retain_documents_total[15m]))这一指标的动机在 metrics.py 的注释中有明确记录:"只有 memory_units 携带嵌入,文档零事实意味着它在 recall/reflect 中不可达,而系统内没有任何其他东西报告它——操作仍然成功完成。对它告警可以捕捉到 retain_mission 静默排除过多内容(issue #3040)。"
LLM 指标
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.llm.duration | Histogram | provider, model, scope, success | LLM API 调用时长(秒) |
hindsight.llm.calls.total | Counter | provider, model, scope, success | LLM API 调用总数 |
hindsight.llm.tokens.input | Counter | provider, model, scope, success, token_bucket | LLM 调用输入 token |
hindsight.llm.tokens.output | Counter | provider, model, scope, success, token_bucket | LLM 调用输出 token |
标签:
provider:LLM 提供商(openai、anthropic、gemini、groq、ollama、lmstudio、bedrock、litellm)model:模型名(如gpt-4、claude-3-sonnet)scope:LLM 调用的用途(memory、reflect、consolidation、answer)success:调用是否成功(true、false)token_bucket:用于基数控制的 token 数分桶(0-100、100-500、500-1k、1k-5k、5k-10k、10k-50k、50k+)
token_bucket的实现就是 get_token_bucket():把连续 token 数映射到 7 个离散桶,避免每个调用产生一个唯一标签值而引发高基数问题。
此外,源码中还注册了两个原文档未列入表格的补充计数器,用于更诚实的成本归属:
hindsight.llm.tokens.cached_input:按缓存费率计费的输入 token 子集,用于独立追踪提示缓存命中率;hindsight.llm.tokens.thoughts:推理/思考 token(Gemini 2.5+ 系列),按输出费率计费但不出现在 candidates 中——"按输出量看起来便宜、实际因长推理链而昂贵"的工作负载正是靠它暴露的。
合并指标(Consolidation Metrics)
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.consolidation.batch_failures | Counter | failure_class, error_type | 失败的合并 LLM 批量调用,包括其事实后来被恢复的 |
标签:
failure_class:调用的处理方式(fail_fast——模型返回了响应 schema 拒绝的内容,重发相同载荷无济于事;retry——传输层形态的问题,原样重发可能成功;propagate——不属于批量失败,重新抛给任务处理器)error_type:异常类名(如ValidationError、JSONDecodeError)
这与 bank stats 中的failed_consolidation字段不是同一个信号。该字段统计的是失败后仍在等待合并的事实;当批量调用失败时,合并会把批次减半重试,因此一个在批次大小 8 时失败、大小 1 时成功的调用会让failed_consolidation停留在 0。但失败响应请求的一切都会被丢弃——包括它想删除的 observations——所以一个银行可以看起来完全健康,而其超驰清理却什么都没做。
对 schema 拒绝率告警,这是其他指标无法暴露的:
sum(rate(hindsight_consolidation_batch_failures_total{failure_class="fail_fast"}[15m]))持续非零的速率通常意味着合并模型不能稳定地为响应 schema 输出合法 JSON。切换到结构化输出更强的模型,或在提供商支持语法强制的 schema 时启用HINDSIGHT_API_LLM_STRICT_SCHEMA_CONSOLIDATION,是常见的修复方式。
同一计数还会按运行周期报告为合并操作结果中的llm_batch_failures,合并日志摘要会在其非零时打印警告行。两者统计的都是尝试次数——一个重试三次的批量调用贡献三次——因此不受运行中批次数量上限的约束。
源码注释(metrics.py)进一步阐明了设计动机:"failed_consolidation是对携带consolidation_failed_at的行的 gauge,因此它只报告卡住的事实——绝不会报告失败后其事实被调用方的自适应二分法救回的调用。一次运行可以烧掉几十次 schema 非法的调用、丢弃它们携带的全部删除,却仍以该 gauge 为 0 和observations_deleted为 0 结束,与健康运行无法区分(#4151、#4152)。这个计数器正是缺失的信号:它统计调用,而非卡住的行。"
HTTP 请求指标
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.http.duration | Histogram | method, endpoint, status_code, status_class | HTTP 请求时长(秒) |
hindsight.http.requests.total | Counter | method, endpoint, status_code, status_class | HTTP 请求总数 |
hindsight.http.requests.in_progress | UpDownCounter | method, endpoint | 正在处理的 HTTP 请求数 |
标签:
method:HTTP 方法(GET、POST、PUT、DELETE)endpoint:请求路径(已归一化以降低基数——UUID 替换为{id})status_code:HTTP 状态码(200、400、500等)status_class:状态码类别(2xx、4xx、5xx)
HTTP 指标由 observability.py 中的纯 ASGI 中间件HttpObservabilityMiddleware记录。该文件的文档字符串解释了一个重要的性能背景:它取代了两个基于@app.middleware("http")的 StarletteBaseHTTPMiddleware处理器——后者每次请求都会派生子任务并通过 anyio 内存流管道传输响应,在 2-CPU 容器、32 并发客户端的实测中,移除它们使/health/live从约 1500 rps 提升到约 5100 rps(p99 从 159ms 降到 33ms),同时 CPU 从饱和的单核降到约 65%——成本来自调度跳转而非计算。纯 ASGI 中间件则是在同一任务中的一次await,包裹send以观察响应,同时负责把未知参数注入X-Ignored-Params响应头。
数据库连接池指标
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.db.pool.size | Gauge | - | 连接池当前连接数 |
hindsight.db.pool.idle | Gauge | - | 连接池空闲连接数 |
hindsight.db.pool.min | Gauge | - | 连接池最小大小 |
hindsight.db.pool.max | Gauge | - | 连接池最大大小 |
(源码中还存在hindsight.db.pool.acquire_wait直方图,用于记录等待获取池化数据库连接的时间——即连接池耗尽信号;以及hindsight.event_loop.stalls/hindsight.event_loop.stall_duration,用于检测超过 watchdog 阈值的事件循环停滞,来自 loop_watchdog.py。)
进程指标
| 指标 | 类型 | 标签 | 说明 |
|---|---|---|---|
hindsight.process.cpu.seconds | Gauge | type | 进程 CPU 时间(秒) |
hindsight.process.memory.bytes | Gauge | type | 进程内存使用(字节) |
hindsight.process.open_fds | Gauge | - | 打开的文件描述符数 |
hindsight.process.threads | Gauge | - | 活跃线程数 |
标签:
type(CPU):user或systemtype(内存):rss_max(最大常驻集大小)
进程指标以可观测 gauge 实现,在抓取时通过回调采集:CPU 时间来自getrusage的ru_utime/ru_stime,内存使用ru_maxrss(源码注释提醒:Linux 上该值以 KB 计,需乘以 1024 转字节,而 macOS 上本身就是字节);open_fds在 Linux 上通过枚举/proc/self/fd计数,否则回退到资源软限制;threads使用threading.active_count()(见 metrics.py)。
直方图分桶
为获得更准确的分位数,配置了自定义桶边界:
操作时长分桶(秒):
0.1, 0.25, 0.5, 0.75, 1.0, 2.0, 3.0, 5.0, 7.5, 10.0, 15.0, 20.0, 30.0, 60.0, 120.0LLM 时长分桶(秒):
0.1, 0.25, 0.5, 1.0, 2.0, 3.0, 5.0, 10.0, 15.0, 30.0, 60.0, 120.0HTTP 时长分桶(秒):
0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, 30.0这三组分桶常量直接定义在 metrics.py,并通过ExplicitBucketHistogramAggregation视图在初始化时注册:操作桶在 0–30 秒区间(大多数操作完成的范围)提供细粒度,LLM 桶针对更快的调用细化,HTTP 桶则细化到毫秒级以覆盖快速端点。
Prometheus 抓取配置
scrape_configs: - job_name: 'hindsight' static_configs: - targets: ['localhost:8888']生产抓取时,参考 scripts/dev/monitoring/prometheus.yml 中现成的写法:metrics_path: '/metrics'、5 秒抓取间隔、单独为 worker 增加一个 job(目标端口 8889),并按需开启scrape_native_histograms。
示例 PromQL 查询
按类型统计平均操作延迟
rate(hindsight_operation_duration_sum[5m]) / rate(hindsight_operation_duration_count[5m])各提供商每分钟 LLM 调用数
rate(hindsight_llm_calls_total[1m]) * 60P95 LLM 延迟
histogram_quantile(0.95, rate(hindsight_llm_duration_bucket[5m]))按模型统计总 token 消耗
sum by (model) (hindsight_llm_tokens_input_total + hindsight_llm_tokens_output_total)内部 vs API recall 操作
sum by (source) (rate(hindsight_operation_total{operation="recall"}[5m]))按端点统计每秒 HTTP 请求数
sum by (endpoint) (rate(hindsight_http_requests_total[1m]))HTTP 错误率(5xx)
sum(rate(hindsight_http_requests_total{status_class="5xx"}[5m])) / sum(rate(hindsight_http_requests_total[5m]))P95 HTTP 延迟
histogram_quantile(0.95, sum by (le) (rate(hindsight_http_duration_seconds_bucket[5m])))数据库连接池利用率
hindsight_db_pool_size / hindsight_db_pool_max活跃数据库连接数
hindsight_db_pool_size - hindsight_db_pool_idleCPU 使用率
rate(hindsight_process_cpu_seconds{type="user"}[1m])Grafana 仪表盘
预置仪表盘位于 monitoring/grafana/dashboards/,包含三个 JSON 文件:hindsight-operations.json、hindsight-llm.json、hindsight-api-service.json。将它们导入 Grafana 实例即可:
| 仪表盘 | 说明 |
|---|---|
| Hindsight Operations | 操作速率、延迟分位数、按银行统计的指标 |
| Hindsight LLM Metrics | LLM 调用、token 使用、按 scope/提供商统计的延迟 |
| Hindsight API Service | HTTP 请求、错误率、数据库连接池、进程指标 |
使用监控栈脚本时,仪表盘会通过 grafana-dashboards.yaml 自动 provisioning——该文件先声明 LGTM 自带的 RED Metrics(classic 与 native 直方图)与 JVM Metrics 仪表盘,随后声明三个 Hindsight 仪表盘(foldersFromFilesStructure: false,全部放在根目录)。
分布式追踪
Hindsight 支持针对记忆操作与 LLM 调用的 OpenTelemetry 分布式追踪,遵循 GenAI 语义约定 v1.37+。追踪由 tracing.py 实现:默认关闭(与DEFAULT_OTEL_TRACES_ENABLED = False对应,见 config.py),启用时通过 OTLP HTTP 导出器导出到任意兼容后端。
配置
环境变量见 Configuration - OpenTelemetry Tracing(表格摘录):
| 环境变量 | 说明 | 默认值 |
|---|---|---|
HINDSIGHT_API_OTEL_TRACES_ENABLED | 为 LLM 调用启用分布式追踪 | false |
HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT | OTLP 端点 URL(如 Grafana LGTM、Langfuse 等) | - |
HINDSIGHT_API_OTEL_EXPORTER_OTLP_HEADERS | OTLP 导出器请求头(格式:"key1=value1,key2=value2") | - |
HINDSIGHT_API_OTEL_SERVICE_NAME | 追踪的服务名。适用于 API 与独立 worker——未设置时 worker 默认上报为hindsight-worker | hindsight-api |
HINDSIGHT_API_OTEL_DEPLOYMENT_ENVIRONMENT | 部署环境名(如 development、staging、production) | development |
快速开始:
# 启用追踪 export HINDSIGHT_API_OTEL_TRACES_ENABLED=true export HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318 # 使用 Grafana LGTM 查看追踪(本地开发) ./scripts/dev/start-monitoring.sh # 打开 http://localhost:3000 → Explore → Tempo支持任何 OTLP 兼容后端(Grafana LGTM、Langfuse、OpenLIT、DataDog、New Relic、Honeycomb、Pydantic Logfire 等)。
从实现细节看,initialize_tracing() 会自动为不以/v1/traces结尾的端点补上该路径后缀,解析"key1=value1,key2=value2"格式的请求头,并使用BatchSpanProcessor批量导出;initialize_tracing_from_config()保证 API(FastAPI 生命周期)与独立 worker 两个进程入口走同一套初始化逻辑,让设置了HINDSIGHT_API_OTEL_*的部署能从每个干活的进程拿到追踪(issue #3614),且初始化失败只记录日志、绝不让进程无法启动——"tracing 绝不能阻碍进程启动"。shutdown_tracing()会强制冲刷BatchSpanProcessor,避免进程退出时丢弃仍在队列中的 span——这对单条合并 span 可能长达数分钟的 worker 尤其重要。
Span 层级
父 Span(操作):
hindsight.retain- 记忆摄取hindsight.recall- 记忆检索hindsight.recall_embedding- 查询嵌入hindsight.recall_retrieval- 并行搜索(语义、BM25、图、时间)hindsight.recall_fusion- 倒数排名融合(RRF)hindsight.recall_rerank- 交叉编码器重排
hindsight.reflect- 智能体推理hindsight.reflect_tool_call- 工具执行(recall、lookup 等)
hindsight.consolidation- 观察综合hindsight.mental_model_refresh- 心智模型更新
子 Span(LLM 调用):
- 按 scope 命名(如
hindsight.memory、hindsight.reflect) - 以事件形式包含完整提示词/补全
- 属性遵循 GenAI 语义约定
父操作 span 由 create_operation_span() 创建,命名规则为hindsight.{operation}并设置hindsight.operation与hindsight.bank_id属性。LLM 子 span 由LLMSpanRecorder.record_llm_call()在调用完成后以显式时间戳创建(end_on_exit=False+ 手动span.end()),以适配现有的同步指标记录模式。
Span 属性
操作 Span:
hindsight.operation- 操作类型hindsight.bank_id- 记忆银行 IDhindsight.query- 查询文本(截断到 100 字符)hindsight.fact_types- recall 的事实类型hindsight.thinking_budget- 预算分配hindsight.max_tokens- token 上限
LLM Span(GenAI 语义约定):
gen_ai.operation.name- 恒为"chat"gen_ai.provider.name- 提供商(openai、anthropic、google等)gen_ai.request.model- 模型名gen_ai.usage.input_tokens- 输入 tokengen_ai.usage.output_tokens- 输出 tokenhindsight.scope- LLM 调用用途(memory、reflect、consolidation等)
事件:
gen_ai.client.inference.operation.details- 完整提示词与补全
在实现中,提供商名通过PROVIDER_NAME_MAPPING映射为 GenAI 语义约定的值(如gemini→google、openai-codex→openai),消息以 JSON 数组写入事件属性,超长内容截断到MAX_CONTENT_LENGTH = 100_000字符并附截断说明;工具调用会记录gen_ai.tool_calls.count/gen_ai.tool_calls.names属性与逐条gen_ai.tool_call.{i}事件;出错时设置错误状态、error.type属性并调用record_exception。此外,CompositeSpanRecorder会把同一 LLM 调用扇出给所有注册的 recorder(如 OpenTelemetry span 导出器与按银行落库的 DB tracer),单个 recorder 失败绝不影响其他 recorder 与 LLM 调用本身。
追踪上下文传播
Hindsight 会融入你已有的追踪,而不是另起一套并行的追踪。当调用方发送 W3C 追踪上下文(标准的traceparent头,大多数 OpenTelemetry HTTP 客户端插桩都会自动添加)时,Hindsight 会继续该追踪:API 请求作为调用方下的一个 server span 出现,其触发的每个记忆操作与 LLM 调用都嵌套在其下。
没有携带追踪上下文的请求仍然会开启自己的追踪,因此未插桩的调用方行为不变。
健康与指标端点被排除在追踪之外,这样探针流量不会淹没真正的工作。可通过OTEL_PYTHON_FASTAPI_EXCLUDED_URLS设置为逗号分隔的 URL 模式列表来覆盖此行为(默认值为health,metrics,见 configuration.md)。
值得一提的实现细节是,异步任务队列与追踪的打通:inject_task_trace_context()会把当前 W3C traceparent 写入任务 payload(键为_traceparent),worker 侧再由extract_task_trace_context()重建出队时请求的追踪上下文并包裹任务执行——否则 API 的 enqueue span 与 worker 的hindsight.retainspan 就是两条互不相干的追踪,API 那半条里只有入队动作。
Worker 进程
独立 worker 进程遵循与 API 相同的HINDSIGHT_API_OTEL_*变量并导出自己的 span。这对部署了专用 worker 的环境很重要,因为合并、后台 retain 与心智模型刷新——大部分长时工作与 token 开销——都发生在那里。
为 worker 设置独立的HINDSIGHT_API_OTEL_SERVICE_NAME,以便在追踪后端中将其与 API 区分。未设置时,worker 默认上报为hindsight-worker。
worker 的 span 目前是独立的追踪:后台操作不会链接到将其入队的请求,因为它在该请求返回后很久才执行。
小结
把本文的要点压缩成一份可操作的核对清单:
- 本地开发:
./scripts/dev/start-monitoring.sh一条命令拉起 Grafana LGTM,通过HINDSIGHT_API_OTEL_TRACES_ENABLED=true+HINDSIGHT_API_OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318启用追踪; - 健康探针:liveness 永远指向
/health/live(零 I/O),readiness 指向/health(SELECT 1检查数据库);seconds_since_last_poll只用于告警、永不改变状态码; - 指标告警:
no_facts占比告警监控 retain mission 过度排除;failure_class="fail_fast"告警捕获 schema 拒绝导致的静默清理失效;hindsight.operation.total{source="worker",success="false"}与hindsight_async_operations{status="failed"}组合使用以获得权威的异步失败; - 追踪:API 与 worker 共用一套
HINDSIGHT_API_OTEL_*配置,为 worker 单独命名服务以区分;生产环境务必调用shutdown_tracing()冲刷 span,避免丢失长 span。
仓库中的三个仪表盘 JSON、prometheus.yml抓取模板与 Helm 探针配置都可以直接作为生产部署的起点。
【免费下载链接】hindsightHindsight: Agent Memory That Learns项目地址: https://gitcode.com/GitHub_Trending/hindsight2/hindsight
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考