关于 add-lens
add-lens 是一项 Agent 技能,指导将 redis-iris 中的 Lens 跟踪接入 Agent 应用,涵盖轮次边界、身份与会话 ID、模型仪器化及遥测缺口处理。
主要功能
- 01定位 Agent 轮次和启动边界
- 02使用 Agent 身份初始化 Lens
- 03为轮次添加会话上下文
- 04记录检索与提示词组装
- 05跨进程传递跟踪上下文
- 06验证遥测和现有 Provider
适用场景
- 01为 Agent 应用接入 Lens 跟踪
- 02记录检索和提示词组装
- 03向工作进程传递跟踪上下文
MCP 相关
该技能指导用户通过可用的文档 MCP 查询当前框架和 Redis Lens 信息;它本身不提供 MCP 工具。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ redis/ agent-skills --skill add-lens安装后在 Claude Code 中输入/add-lens确认已加载。
详细介绍
添加 Lens
每轮 Agent 调用对应一条追踪记录,可在 Lens UI 中按 Agent 的 agent_id 查看。Lens 运行在 redis-iris 内;无需预配服务,也没有 CLI。启动时添加两行代码,每条消息对应一轮调用,然后补齐 Agent 尚未覆盖的部分。Lens 可以与任何其他 Iris 产品搭配使用,也可以单独使用。
完整参考文档:Lens(redis-iris 中的 docs/core/lens.md)。
1. 了解项目
阅读项目代码:
- 调用轮次:一条用户消息如何变成一条回答(
app.invoke、Runner.run、runner.run_async、agent.run、kickoff,或手写循环外围的处理器)。 - ID:Agent 自身的名称定义在哪里,以及每个请求的会话或线程 ID、用户 ID 定义在哪里。始终设置
agent_id和session_id。Lens UI 中的所有视图都按agent_id筛选,因此没有该字段的追踪记录不会显示。调用轮次按会话分组显示,因此没有会话 ID 的调用轮次也不会显示(即将推出按无会话 ID 显示调用轮次的功能)。 - 启动:第一个调用轮次开始前运行的唯一位置(应用工厂、
main、工作进程启动)。 - 模型客户端:最终调用模型的库,无论它是否位于某个框架之下。Lens 会为四种库添加插桩:
openai、anthropic、botocore(Bedrock)和langchain_core。许多框架会在底层调用其中一种;请在第 2 步确认。其他客户端(例如google-genai或litellm)不会记录 LLM span 或令牌,除非第 6 步为其添加插桩。 - 现有遥测:
opentelemetry、TracerProvider、opentelemetry-instrument、OTEL_*变量,或其他 LLM 插桩。确认由谁安装全局 provider,以及安装时机。 - 第 5 步中的缺口:
.client调用、自定义检索、线程池、进程切换、短生命周期进程。
然后匹配相应模式。这决定由谁开启调用轮次:
| Agent | 开启调用轮次的位置 | 需要添加的内容 |
|---|---|---|
redis_iris.integrations.langgraph.attach(hooks, graph) | attach,在每次 invoke / stream 及其异步对应方法中开启,会话来自 configurable.thread_id,用户来自 configurable.user_id | 仅需 lens.init() |
workflows.turn(...) / workflows.aturn(...) | workflows.turn,取自 session= 和 owner= | 仅需 lens.init() |
未使用 attach 的 LangGraph | 由你开启 | 在 app.invoke / app.ainvoke 外围使用 lens.turn(),线程 ID 作为会话 ID |
Google ADK,包括 redis_iris.integrations.adk.attach | 由你开启:ADK 的 attach 不会开启调用轮次,因此 before 和 after 会生成两条追踪记录 | 在 runner.run_async 外围使用 lens.turn() |
| OpenAI Agents SDK | 由你开启 | 在 Runner.run 外围使用 lens.turn() |
| Pydantic AI、CrewAI 或其他框架 | 由你开启 | 在 agent.run / kickoff 外围使用 lens.turn() |
普通循环(openai、anthropic 或其他客户端) | 由你开启 | 在处理单条消息的处理器外围使用 lens.turn(),不要围绕单次模型调用 |
同一进程中有两个循环时,每个循环都在各自的入口开启调用轮次。已开启的调用轮次会保持不变;在其中调用 attach 和 workflows.turn 不会再开启新的调用轮次。
2. 从 MCP 获取当前信息
编写代码前先完成这一步。切勿凭记忆猜测框架 API。
- 框架文档:通过可用的文档 MCP(Context7 或框架自带的 MCP)查询已安装版本的运行入口、底层调用的模型客户端、框架本身是否会生成 OpenTelemetry span,以及是否会在线程或其他进程中运行任务。
- Redis 文档:如果已连接 Redis 文档 MCP,请用它查询 Lens 概念。
- 没有可用的 MCP:阅读虚拟环境中已安装的软件包源码,并在报告中说明。
3. 安装和配置
始终安装额外依赖。它会引入 lens.init() 为 OpenAI、Anthropic、Bedrock 和 LangChain 启动的埋点,以及 mcp 和 threading;没有它就不会有大模型跨度,也不会统计令牌总数。
- 存在
uv.lock或[tool.uv]:uv add 'redis-iris[llm-tracing]'。 - 使用 Poetry:
poetry add 'redis-iris[llm-tracing]'。 - 其他情况:将
redis-iris[llm-tracing]添加到项目的 requirements 文件中。
端点和 Agent 密钥来自 Lens UI。没有 CLI 命令可以生成它们,因此请告知对方:打开 Lens UI,复制 Lens 端点,创建 Agent 密钥,然后按下方所示将两者放入 .env。切勿在聊天中索要密钥、在命令行中传递密钥,或回显、使用 cat 或 grep 查看环境文件。
IRIS_LENS_ENDPOINT= # the Iris host, such as https://iris.example.com
IRIS_LENS_API_KEY= # the Lens agent key
在 .env.example 中添加相同的两个名称,并将值留空。切勿提交 .env。在集群内的本地部署环境中,只有设置了 IRIS_LENS_ALLOW_INSECURE=true,端点才可以使用普通 http(http://lens-dataplane:9000)。
保留内容捕获的默认设置。不要询问此事。
4. 接入
启动时,在首次对话轮次之前,且在应用自身的 OpenTelemetry 设置之后(步骤 6),调用一次:
import logging
from redis_iris import lens
lens.init(agent_id="support-bot") # always: every Lens UI view is filtered by agent_id
state = lens.instrumentation()
if state.get("lens") != "on":
logging.warning("Lens is not recording: %s", state)
始终传入 agent_id,即使函数签名没有强制要求:没有它的跟踪记录永远不会显示在 Lens UI 中。根据 Agent 自身的名称选择它,并与对方确认。lens.init() 会从环境中读取端点和密钥,且永远不会抛出异常。它的返回值只表示已设置端点且 Lens 未关闭;即使端点被拒绝或缺少额外依赖,返回值仍会是 True,因此请检查 lens.instrumentation()。
然后,在步骤 1 中写着“你”的位置,包装一个对话轮次:
with lens.turn(thread_id, user_id=user_id) as t:
answer = await Runner.run(agent, message) # the framework's run call, unchanged
t.set_outcome("completed")
不需要使用 async with:lens.turn() 是普通上下文管理器,可以在 await 周围使用。第一个参数是会话 ID。始终传入请求的真实线程或会话 ID,绝不要使用常量或 None:Lens UI 按会话显示对话轮次,没有会话 ID 的轮次目前不会显示。接入期间设置 IRIS_LENS_STRICT=true,这样缺少会话 ID 时会抛出异常,而不是填入哨兵值。t.trace_id 是该轮在 Lens 中的 ID,值得与请求 ID 一并记录日志。
5. 补齐缺口
只处理步骤 1 中发现的缺口。
.client 调用。 memory.client、playbook.client 和其他 .client 属性都是生成的 SDK;Lens 不会为它们添加补丁,因此轮次中会出现缺口。优先使用目标对象的方法。否则,请包装调用:
with lens.span("agent_memory.get_long_term_memory", memory_id=memory_id):
raw = memory.client.get_long_term_memory(memory_id=memory_id)
自定义检索。 向提示词提供内容的向量存储、搜索 API 或数据库读取:记录检索结果以及最终进入提示词的内容,这样跟踪记录就会显示“检索到 5 条,使用了 3 条”。
with lens.retrieval(query=message, data_source="kb") as r:
docs = index.search(message, k=5)
r.results([(d.id, d.score, d.text) for d in docs])
with lens.prompt_assembly() as p:
p.fetched([(d.id, d.score, d.text) for d in docs])
p.used([(d.id, d.score, d.text) for d in docs[:3]]) # whatever survived the token limit
Agent 从自身存储读取的记忆也应使用 lens.memory_recall(query=, store_id=),方式相同。Iris hooks() 和 disclosure() 会记录各自的 prompt_assembly;不要为它们的代码块额外添加一条。
线程池。 ThreadPoolExecutor 中的任务只有在 lens.init() 下启用了 [llm-tracing] 中的 threading 埋点时,才会保留当前轮次。asyncio 任务和 asyncio.to_thread 无论如何都会保留。如果设置了 IRIS_LENS_INSTRUMENT_THREADS=false,请将其移除。
进程间传递。 对话轮次交接给队列、Celery 任务、子进程或工作进程时,除非上下文也一同传递,否则会开始新的跟踪:
payload["lens"] = lens.inject() # sender, inside the turn
with lens.continue_from(payload.get("lens")): # receiver, around the work
handle(payload)
仅将载体发送给你自己的服务,绝不要发送给第三方或 MCP 调用的 meta;也绝不要将由不可信调用方的请求头构建的载体传给 continue_from。接收进程使用相同的 agent_id 调用 lens.init()。已由 OpenTelemetry 埋点的 HTTP 调用会自行携带 traceparent。
短生命周期进程。 脚本、CLI、批处理作业或测试运行可能会在导出器刷新之前退出。请在最末尾调用一次 lens.shutdown(timeout_millis=5_000)。它会关闭 Lens 在该进程中的后续使用,因此绝不要按轮次、按请求或按每次预热的 Lambda 调用执行此操作。
6. 与现有遥测共存
Lens 不会替换任何提供程序。如果应用已有 OpenTelemetry SDK TracerProvider,Lens 会在第一个轮次中向其添加处理器;如果没有,Lens 会安装一个。
| 应用已有 | 操作 |
|---|---|
自己的 OpenTelemetry SDK 设置,或 opentelemetry-instrument | 确保它在第一个轮次之前运行。如果应用较晚才配置 OpenTelemetry,请设置 IRIS_LENS_OWN_PROVIDER=false,让 Lens 等待它,而不是先安装自己的提供程序。应用自己的导出器也会收到轮次内记录的跨度,包括内容;每个轮次都是独立的跟踪,并与请求跨度关联。 |
| 已封装 Lens 的四个客户端之一的插桩 | Lens 不会处理该客户端(other_instrumentation)。如果它使用同一个全局提供程序进行记录,其跨度会在轮次内到达 Lens;请在 lens.init() 和第一个轮次之前完成设置。 |
| Lens 未插桩的模型客户端 | 为该客户端添加 OpenTelemetry 插桩,或启用框架自身的 OpenTelemetry 跟踪功能(步骤 2 会说明是否支持),并使用全局提供程序记录。轮次内的跨度会发送到 Lens;如果跨度包含 gen_ai.usage.input_tokens / output_tokens(或 prompt_tokens / completion_tokens)名称,轮次的 Token 总数也会统计这些跨度。添加依赖前请先征求对方同意。 |
| 非 OpenTelemetry SDK 的跟踪提供程序 | Lens 无法添加处理器,并会记录 iris.lens: could not attach, telemetry disabled。请报告此问题;不要更改应用的跟踪设置。 |
| 采样率低于 100% 的采样器 | 默认情况下,Lens 会强制采样轮次。IRIS_LENS_SAMPLE_TURNS=inherit 可让应用的采样器丢弃这些轮次。 |
请勿为 Lens 设置任何 OTEL_EXPORTER_OTLP_* 变量:其导出器不会读取任何 OTEL_EXPORTER_OTLP_* 变量。它会遵循 HTTPS_PROXY、NO_PROXY 和 SSL_CERT_FILE。
7. 验证
-
运行 Agent 的启动流程,并在导入大模型客户端后打印
lens.instrumentation()。预期'lens': 'on';当 Agent 使用 OpenAI、Anthropic、Bedrock 或 LangChain 时,providers下的该客户端应为instrumented或other_instrumentation。其他客户端不会出现在此处;请在步骤 4 中检查其自身 instrumentor 的 spans。 -
某个值不正确?从
lens.instrumentation()中查看原因:值 原因 修复方法 lens: off进程中没有 IRIS_LENS_ENDPOINT、IRIS_LENS_ENABLED=false,或缺少opentelemetry-sdk(使用了--no-deps安装)在 lens.init()前加载.env;取消设置该开关;重新安装依赖lens: endpoint_refused端点既不是 https,也不是环回地址 使用 https,或在本地部署环境中设置 IRIS_LENS_ALLOW_INSECURE=truelens: shut_down进程中较早调用了 lens.shutdown()将它移到进程结束时调用 providers.<client>: instrumentor_missing或needs_wrapt此环境中未安装 [llm-tracing]在 Agent 运行的环境中安装这个 extra providers.<client>: needs_init环境变量已启用 Lens,但从未运行 lens.init()在启动时调用它 providers.<client>: disabledIRIS_LENS_AUTO_INSTRUMENT=false取消设置它 providers.<client>: failed未能应用补丁 查看 iris.lens: could not auto-instrument警告。它会在lens.init()时记录;如果应用之后才首次导入客户端,则会在导入时记录 -
使用真实的 session id 运行一轮,并记录
t.trace_id(或在运行期间调用lens.current_trace_id())。 -
打开 Lens UI,按
agent_id筛选,然后找到对应的 session 和该轮运行。应能看到大模型调用、token,以及本轮运行产生的所有 Iris 产品、工具和检索 span。 -
UI 为空,但
lens: on:检查iris.lens警告(导出时出现 401 或 403 表示密钥错误或已撤销),确认本轮运行在进程结束前已完成(步骤 5,短生命周期进程),并检查lens.stats():iris.lens.spans_exported应持续增长,iris.lens.export_failures应保持为 0,iris.lens.identity_unassigned应保持为 0(非零表示开启运行时未提供 session id,因此不会显示在 UI 中)。
8. 报告
列出 Agent 的结构以及由谁开启运行、agent_id、session 和 user id 的来源、大模型客户端及 Lens 是否会对其进行检测、你填补的缺口、Lens 如何与现有遥测共存、lens.instrumentation() 的输出、你在 Lens UI 中找到的 trace id,以及任何无法通过 MCP 确认的内容。
规则
- 保留用户的框架、图和提示词。尽量减少差异。
- 只能使用
redis_iris.lens中的 API。切勿为 Lens 调用trace.set_tracer_provider()或trace.get_tracer()。 - Lens 不会向 Agent 抛出异常(
IRIS_LENS_STRICT启用时除外),因此无需使用 try/except。 - 切勿在聊天中索要密钥。切勿提交
.env。 - 其他 Iris 产品:
add-redis-iris。