add-lens
Skill174 starsChanged 3 months agoMITView on GitHub
Add Redis Iris Lens tracing to an existing AI agent through redis-iris, so every agent turn is one trace in the Lens UI with its LLM calls, tokens, tool calls, memory and document retrievals, and which of them entered the prompt. Detects how the agent runs (LangGraph or ADK attach, workflows.turn, another framework, or a plain loop), installs redis-iris[llm-tracing], calls lens.init(agent_id=...), opens one lens.turn() per turn where Iris does not open it already, and covers coexistence with an OpenTelemetry setup or LLM instrumentation the app already has. Use when the user asks to add Lens, tracing, observability, or per-turn traces to their agent, or asks why their Lens traces are missing.
- Reads credentials
1 other project uses a version of this.
Adopted by
Most recently taken up by
- aleemont1/TheCounciltheir own version, first seen Oct 13, 2026
Other versions1 project uses a version of this.
Word for word · 0
None found.
Edited · 0
None found.
Wrote their own version · 1
- aleemont1/TheCouncil★ 1same name
How matches are found. Word for word: the exact same text. Edited: shares passages with this file (“99% same” means 99% of its five-word phrases also appear here; 10% or more counts). Their own version: similar meaning, or, weakest, the same name. We can’t tell which project had it first. Projects are counted among the 200 most-starred candidates, and mentions are links to the repository we found, up to 50 per site, so both counts are a floor.
In this repository14 other files it gives its agents.
- AGENTS.mdAGENTS.md
- add-agent-memorySkill
- add-agent-playbooksSkill
- add-context-gatewaySkill
- add-intent-routerSkill
- add-redis-irisSkill
- redis-clusteringSkill
- redis-connectionsSkill
- redis-coreSkill
- redis-docs-mcpSkill
- redis-observabilitySkill
- redis-searchSkill
- redis-securitySkill
- redis-semantic-cacheSkill
The file
We split this skill into the rules it gives an agent
10 rules. About 0 are common, 5 are distinctive, and 5 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈5
Distinctive · estimate
This author's own choices. Worth a look.
5
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- could be automatic: a setting could enforce it
- Distinctive: line 43
Do this before writing code.
- Distinctive: line 47
No MCP available: read the installed package source in the virtualenv.
- Very specific: line 51
Always install the extra. It brings the instrumentation lens.init() starts for OpenAI, Anthropic, Bedrock and LangChain, mcp and threading.
- Very specific: line 64
Add the same two names to .env.example with empty values.
- Distinctive: line 66
Leave content capture at its default.
- Distinctive: line 97
Only the ones step 1 found.
- Very specific: line 164
Run one real turn with a real session id and log t.trace_id (or lens.current_trace_id() from inside the run).
- Distinctive: line 174
Keep the user's framework, graph, and prompts.
- Very specific: line 175
Only use APIs in redis_iris.lens.
- Very specific: line 177
Never ask for a secret in chat.
≈ “Common” is an estimate: at least 1 in 5 of the 350,495 skill files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
Add Lens
One trace per agent turn, visible in the Lens UI under the agent's agent_id. Lens runs inside redis-iris; it has no service to provision and no CLI. Two lines at start-up, one turn per message, then close the gaps the agent has. Lens sits next to any other Iris product, or none.
Full reference: Lens (docs/core/lens.md in redis-iris).
1. Discover
Read the project:
- The turn: where one user message becomes one answer (
app.invoke,Runner.run,runner.run_async,agent.run,kickoff, or the handler around a hand-written loop). - The ids: where the agent's own name lives, and where each request's session or thread id and user id live. Always set both
agent_idandsession_id. Every Lens UI view is filtered byagent_id, so a trace without one never shows. Turns are shown grouped by session, so a turn without a session id does not show either (showing turns without a session is an upcoming feature). - Start-up: the one place that runs before the first turn (app factory,
main, worker boot). - The model client: the library that finally calls the model, under any framework. Lens instruments four:
openai,anthropic,botocore(Bedrock) andlangchain_core. Many frameworks call one of these underneath; confirm in step 2. Any other client (for examplegoogle-genaiorlitellm) records no LLM span and no tokens unless step 6 adds an instrumentor for it. - Existing telemetry:
opentelemetry,TracerProvider,opentelemetry-instrument,OTEL_*variables, or another LLM instrumentation. Note who installs the global provider and when. - The gaps in step 5:
.clientcalls, custom retrieval, thread pools, process hops, short-lived processes.
Then match the shape. It decides who opens the turn:
| The agent | Turn opened by | You add |
redis_iris.integrations.langgraph.attach(hooks, graph) | attach, on every invoke / stream and async twin, session from configurable.thread_id, user from configurable.user_id | lens.init() only |
workflows.turn(...) / workflows.aturn(...) | workflows.turn, from session= and owner= | lens.init() only |
LangGraph without attach | you | lens.turn() around app.invoke / app.ainvoke, thread id as the session |
Google ADK, including redis_iris.integrations.adk.attach | you: ADK attach does not open one, so before and after would be two traces | lens.turn() around runner.run_async |
| OpenAI Agents SDK | you | lens.turn() around Runner.run |
| Pydantic AI, CrewAI, any other framework | you | lens.turn() around agent.run / kickoff |
Plain loop (openai, anthropic, another client) | you | lens.turn() around the handler for one message, not around one model call |
Two loops in one process: each opens its own turns at its own seam. A turn already open is kept; attach and workflows.turn add none inside it.
2. Get current facts from MCP
Do this before writing code. Never guess framework APIs from memory.
- Framework docs: query an available docs MCP (Context7, or the framework's own) for the installed version's run entry point, which model client it calls underneath, whether it emits OpenTelemetry spans of its own, and whether it runs work on threads or other processes.
- Redis docs: if the Redis docs MCP is connected, use it for Lens concepts.
- No MCP available: read the installed package source in the virtualenv. Say so in the report.
3. Install and configure
Always install the extra. It brings the instrumentation lens.init() starts for OpenAI, Anthropic, Bedrock and LangChain, mcp and threading; without it there are no LLM spans and no token totals.
uv.lockor[tool.uv]present:uv add 'redis-iris[llm-tracing]'.- Poetry:
poetry add 'redis-iris[llm-tracing]'. - Otherwise add
redis-iris[llm-tracing]to the project's requirements file.
The endpoint and the agent key come from the Lens UI. There is no CLI command that mints them, so hand off: tell the person to open the Lens UI, copy the Lens endpoint, create an agent key, and put both in .env as below. Never ask for the key in chat, pass it on a command line, or echo, cat or grep an env file.
IRIS_LENS_ENDPOINT= # the Iris host, such as https://iris.example.com
IRIS_LENS_API_KEY= # the Lens agent keyAdd the same two names to .env.example with empty values. Never commit .env. On-prem inside the cluster, the endpoint can be plain http (http://lens-dataplane:9000) only with IRIS_LENS_ALLOW_INSECURE=true.
Leave content capture at its default. Do not ask about it.
4. Wire
At start-up, once, before the first turn and after the app's own OpenTelemetry setup (step 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)Always pass agent_id, even though the signature does not force it: a trace without one never shows in the Lens UI. Pick it from the agent's own name and confirm it with the person. lens.init() reads the endpoint and key from the environment and never raises. Its return value only says an endpoint is set and Lens is not switched off; it is still True for a refused endpoint or a missing extra, so check lens.instrumentation() instead.
Then, where step 1 says "you", wrap one turn:
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 is not needed: lens.turn() is a plain context manager and works around await. The first argument is the session id. Always pass the request's real thread or session id, never a constant and never None: the Lens UI shows turns by session, and a turn without one does not show yet. Set IRIS_LENS_STRICT=true while wiring so a missing session id raises instead of being stamped with a sentinel. t.trace_id is the turn's id in Lens, worth logging next to the request id.
5. Close the gaps
Only the ones step 1 found.
.client calls. memory.client, playbook.client and the other .client properties are the generated SDK; Lens does not patch them, so the turn shows a gap. Prefer the goal-object method. Otherwise wrap the call:
with lens.span("agent_memory.get_long_term_memory", memory_id=memory_id):
raw = memory.client.get_long_term_memory(memory_id=memory_id)Custom retrieval. A vector store, search API or database read that feeds the prompt: record what came back, then what entered the prompt, so the trace shows "5 retrieved, 3 used".
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 limitMemory the agent reads from its own store uses lens.memory_recall(query=, store_id=) the same way. Iris hooks() and disclosure() record their own prompt_assembly; do not add one for their block.
Thread pools. ThreadPoolExecutor work keeps the turn only with the threading instrumentation from [llm-tracing] under lens.init(). asyncio tasks and asyncio.to_thread keep it anyway. If IRIS_LENS_INSTRUMENT_THREADS=false is set, remove it.
Process hops. A queue, Celery task, subprocess or worker the turn hands off to starts a new trace unless the context travels with it:
payload["lens"] = lens.inject() # sender, inside the turn
with lens.continue_from(payload.get("lens")): # receiver, around the work
handle(payload)Send the carrier only to your own services, never to a third party or an MCP call's meta, and never pass continue_from a carrier built from an untrusted caller's headers. The receiving process calls lens.init() with the same agent_id. HTTP calls already instrumented by OpenTelemetry carry traceparent themselves.
Short-lived processes. A script, CLI, batch job or test run can exit before the exporter flushes. Call lens.shutdown(timeout_millis=5_000) once, at the very end. It turns Lens off for the rest of the process, so never call it per turn, per request, or per warm Lambda invocation.
6. Live with existing telemetry
Lens never replaces a provider. If the app has an OpenTelemetry SDK TracerProvider, Lens adds its processor to it at the first turn; if there is none, Lens installs one.
| The app already has | Do |
Its own OpenTelemetry SDK setup, or opentelemetry-instrument | Make sure it runs before the first turn. If the app configures OpenTelemetry late, set IRIS_LENS_OWN_PROVIDER=false so Lens waits for it instead of installing one first. The app's own exporters then also receive the spans recorded inside a turn, content included, and a turn is its own trace, linked to the request span. |
| An instrumentation that already wraps one of Lens's four clients | Lens leaves that client alone (other_instrumentation). Its spans reach Lens inside a turn when it records on the same global provider; set it up before lens.init() and the first turn. |
| A model client Lens does not instrument | Add an OpenTelemetry instrumentor for that client, or turn on the framework's own OpenTelemetry tracing (step 2 says whether it has one), recording on the global provider. Its spans inside a turn go to Lens, and the turn's token totals count them when they carry gen_ai.usage.input_tokens / output_tokens (or the prompt_tokens / completion_tokens names). Ask the person before adding a dependency. |
| A tracer provider that is not the OpenTelemetry SDK's | Lens cannot add its processor and logs iris.lens: could not attach, telemetry disabled. Report it; do not change the app's tracing. |
| A sampler below 100% | Lens force-samples turns by default. IRIS_LENS_SAMPLE_TURNS=inherit lets the app's sampler drop them. |
Set nothing in OTEL_EXPORTER_OTLP_* for Lens: its exporter reads no OTEL_EXPORTER_OTLP_* variable. It does honour HTTPS_PROXY, NO_PROXY and SSL_CERT_FILE.
7. Verify
- Run the agent's start-up and print
lens.instrumentation()after the LLM client is imported. Expect'lens': 'on'and, when the agent uses OpenAI, Anthropic, Bedrock or LangChain, that client underprovidersasinstrumentedorother_instrumentation. Any other client has no entry there; check its own instrumentor's spans in step 4 instead. A value is wrong? Read the cause from
lens.instrumentation():Value Cause Fix lens: offNo IRIS_LENS_ENDPOINTin the process,IRIS_LENS_ENABLED=false, oropentelemetry-sdkis missing (a--no-depsinstall)Load .envbeforelens.init(); unset the switch; reinstall with dependencieslens: endpoint_refusedThe endpoint is not https and not loopback https, or IRIS_LENS_ALLOW_INSECURE=trueon-premlens: shut_downlens.shutdown()ran earlier in the processMove it to the end of the process providers.<client>: instrumentor_missingorneeds_wrapt[llm-tracing]is not installed in this environmentInstall the extra in the environment the agent runs in providers.<client>: needs_initLens is on from the environment, but lens.init()never ranCall it at start-up providers.<client>: disabledIRIS_LENS_AUTO_INSTRUMENT=falseUnset it providers.<client>: failedPatching did not apply Read the iris.lens: could not auto-instrumentwarning. It is logged atlens.init(), or later, when the app first imports the client- Run one real turn with a real session id and log
t.trace_id(orlens.current_trace_id()from inside the run). - Open the Lens UI, filter by the
agent_id, and find the session and that turn. It should show the LLM call, its tokens, and every Iris product, tool and retrieval span the turn made. - Empty UI with
lens: on: check theiris.lenswarnings (a 401 or 403 on export means a wrong or revoked key), that the turn ended before the process did (step 5, short-lived processes), andlens.stats():iris.lens.spans_exportedshould grow,iris.lens.export_failuresstay 0, andiris.lens.identity_unassignedstay 0 (non-zero means a turn was opened without a session id, so it does not show in the UI).
8. Report
List the agent shape and who opens the turn, the agent_id, where the session and user ids come from, the model client and whether Lens instruments it, the gaps you closed, how Lens coexists with the existing telemetry, the lens.instrumentation() output, the trace id you found in the Lens UI, and anything you could not confirm over MCP.
Rules
- Keep the user's framework, graph, and prompts. Smallest diff.
- Only use APIs in
redis_iris.lens. Never calltrace.set_tracer_provider()ortrace.get_tracer()for Lens. - Lens never raises into the agent (except under
IRIS_LENS_STRICT), so it needs no try/except. - Never ask for a secret in chat. Never commit
.env. - Other Iris products:
add-redis-iris.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

