Files
worldquant-alpha-system/.agents/skills/langchain-dev-guide/reference/contextseek-middleware.md
T
yuxuanhui 7797ff88df Add comprehensive documentation and templates for LangChain skill development
- Introduced a new reference document for streaming output issues, detailing the differences between streaming APIs and providing solutions for common problems.
- Created a structured output issues reference, outlining the use of `with_structured_output`, `response_format`, and schema enforcement strategies.
- Added a user query convention guide to standardize structured questions for skill authors, including block types for queries and data gathering.
- Implemented a template for a chat model, encapsulating API key management, request payload construction, and response handling.
- Established a symlink for the LangChain dev guide in the Claude skills directory for easier access.
- Initialized a skills lock file to manage dependencies and versions for the LangChain dev guide.
2026-09-07 16:19:11 +08:00

265 lines
16 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# ContextSeek Middleware — Use Case Scenarios
Every scenario below answers one question: **"I'm building X — is ContextSeekMiddleware the right fit, and how do I wire it in?"** Parameter-level configuration issues that arise after the decision to use the middleware are covered in [contextseek-params.md](contextseek-params.md).
---
## Issue 1: Agent loses context across sessions — personal assistant or support bot
**Keywords**: semantic memory, context loss, cross-session, agent forgets, StoreBackend alternative, vector retrieval, passive memory, persistent memory
You're building a **personal coding assistant** or a **customer support bot**. Users notice the agent "forgets" everything between sessions: an architecture decision made last week ("we chose OceanBase over Redis because of HTAP requirements") is unknown to the agent today; a support user who already explained their account type and preferred language has to explain it again on every new conversation.
The instinct is to reach for `memory=` and `StoreBackend` (from Deep Agents). That works — but only for knowledge the agent actively chooses to write down. It degrades when conversation history is long or when the agent doesn't "know" what's worth remembering. What's missing is a passive retrieval layer that automatically surfaces semantically relevant history before each model call without any agent involvement.
`ContextSeekMiddleware` sidecars the agent loop transparently:
1. **Before every model call**: retrieves the top-k semantically relevant items from the vector store and appends them to the system message as a `[Relevant Context]` block.
2. **After every final answer**: stores the Q+A pair for future retrieval (skips intermediate tool-call turns to avoid noise).
The agent is never aware of either step.
- **Install**:
```bash
# Inside the agentseek project
pip install "agentseek[context]"
# Standalone
pip install contextseek contextseek-bridges-langchain
```
- **Configure via env vars** (copy from `.env.example`, section "ContextSeek semantic context layer"). All constructor parameters are optional — the middleware reads from env when nothing is passed:
```bash
# seekdb = local persistent storage, built-in ONNX embedder (no API key needed)
AGENTSEEK_CTX_STORAGE_BACKEND=seekdb
# Optional LLM for richer L1 summaries
AGENTSEEK_CTX_LLM_PROVIDER=openai
AGENTSEEK_CTX_LLM_MODEL=gpt-4o-mini
```
The `AGENTSEEK_CTX_*` prefix is automatically aliased to the env vars contextseek reads internally — no credential duplication.
- **Minimum integration** (zero constructor arguments):
```python
from langchain.agents import create_agent
from contextseek.bridges.langchain.middleware import ContextSeekMiddleware
agent = create_agent(
model=model,
tools=[...],
middleware=[ContextSeekMiddleware()],
)
```
- **Sharing the agent's own model and embedder** (avoids a second model instantiation):
```python
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
model = ChatOpenAI(model="gpt-4o")
embedder = OpenAIEmbeddings(model="text-embedding-3-small")
agent = create_agent(
model=model,
tools=[...],
middleware=[ContextSeekMiddleware(model=model, embedder=embedder)],
)
```
- **Lessons learned**: `ContextSeekMiddleware` and `memory=` (StoreBackend) solve different problems and can coexist in the same agent. Use `memory=` when the agent needs to explicitly read and write structured notes it controls. Use `ContextSeekMiddleware` when you want the agent's accumulated conversation history to automatically inform future answers — no agent-side file management required.
**Common configuration problems for this scenario** → see [contextseek-params.md](contextseek-params.md): scope isolation (multi-user context bleeding), auto_compact throttling and graceful shutdown, retrieval_tags / min_score filtering noisy context, tool_arg_overrides for injecting runtime arguments.
---
## Issue 2: Multi-tool data-pipeline agent — auditing tool call decisions
**Keywords**: tool provenance, audit trail, tool call history, data pipeline agent, record_tool_calls, why did agent choose this query, compliance, tool decision tracing
You're building a **data analysis agent** that, on each task, calls a chain of tools: `query_db` → `run_sql` → `transform_data` → `generate_chart` — typically 5–10 tool invocations per turn. A compliance requirement asks: *"Why did the agent choose that particular SQL query? Were the tool arguments reasonable?"* Your team also needs to reproduce past analyses from stored tool traces.
`ContextSeekMiddleware` with `record_tool_calls=True` writes a structured record for each tool invocation into the vector store. Each record captures:
- `tool` — tool name
- `args` — arguments passed (after any `tool_arg_overrides` are applied)
- `result` — the ToolMessage content
- `rationale` — the AIMessage text that preceded the tool call (the model's stated reasoning)
- `task` — the originating user message
These records are retrievable by future agent turns: *"What SQL did we use for the Q3 revenue report last month?"* can return the exact query with its rationale.
- **Integration**:
```python
middleware = ContextSeekMiddleware(
model=model,
embedder=embedder,
auto_store=True, # store final Q+A pairs
record_tool_calls=True, # additionally store each tool invocation
)
agent = create_agent(model=model, tools=[query_db, run_sql, transform_data, generate_chart], middleware=[middleware])
```
- **Retrieval-only mode** — read historical tool traces without writing new ones (e.g. a read-only audit agent):
```python
ContextSeekMiddleware(
model=model,
embedder=embedder,
auto_store=False,
record_tool_calls=False, # retrieval only; no writes
)
```
- **Lessons learned**: `record_tool_calls=True` multiplies write volume by the average number of tool calls per turn. Each call triggers the full summarizer + embedding + DB write pipeline. For a 5-tool agent, that is 5× the LLM cost per turn compared to `auto_store=True` alone. Only enable it when you actually need per-tool provenance.
**Common configuration problems for this scenario** → see [contextseek-params.md](contextseek-params.md): auto_store / record_tool_calls write volume and cost spikes.
---
## Issue 3: Research agent accumulates raw notes — cross-topic pattern discovery
**Keywords**: dream, cross-topic pattern, consolidation, divergence, knowledge synthesis, idle-time, hypothesis generation, dreaming, research agent, implicit connections
You're building a **literature research agent** that writes dozens of raw research summaries per day across multiple topics. After a week the store holds 200+ items but the agent can only retrieve "known" content — it misses implicit cross-topic connections: "gene editing for Alzheimer's treatment" and "mRNA vaccine T-cell activation" both involve immune regulation mechanisms, but no item makes that bridge explicit. You need the system to synthesize new insights from accumulated material without re-running the full agent.
`ctx.dream()` runs offline in two phases:
1. **Consolidation** — scans recently active, un-dreamed items. Within a similarity window `(0.35, 0.72)` — related but not duplicates — it synthesizes new `extracted`-stage items that surface implicit patterns. Tagged `dreamed`, `consolidation`.
2. **Divergence** — generates cross-cluster hypothesis items bridging dissimilar topic clusters. Tagged `dreamed`, `divergence`. Confidence is lower (×0.85 multiplier) — these are hypotheses, not facts.
Dream items have `stability=transient` and decay fast. They are "use it or lose it": `ctx.feedback(ref, score=1.0)` on a valuable hypothesis promotes it to a stable item; otherwise it fades within days.
- **Trigger dream manually after a research session**:
```python
from contextseek import ContextSeek
ctx = ContextSeek.from_settings()
report = ctx.dream(scope="research/immunology")
consolidation_count = len(report.consolidation.items)
divergence_count = len(report.divergence.items) if report.divergence else 0
print(f"Consolidations: {consolidation_count}, Divergences: {divergence_count}")
```
- **Review and promote valuable dream items**:
```python
scope = "research/immunology"
# ctx.overview() returns stage distribution counts; dreamed items are filtered by tag separately
all_items = ctx.items(scope=scope)
dreamed = [item for item in all_items if "dreamed" in (item.tags or [])]
for item in dreamed:
if human_review_approves(item):
# feedback() requires a full URI ref, not a bare item id
ref = ctx.resolver.ref_for(scope, item.id)
ctx.feedback(ref, scope=scope, score=1.0, reason="confirmed cross-domain insight")
```
- **Enable LLM-enhanced dream** (richer synthesis):
```bash
DREAM_LLM_ENABLED=true
# uses the same LLM configured for the agent; no separate key needed
```
- **Lessons learned**: By default `ContextSeekMiddleware` does **not** trigger `dream()` — it only handles `compact()`. For research workflows, call `ctx.dream()` explicitly after bulk ingestion sessions, or schedule it via cron / the contextseek daemon lifecycle. If you want automatic dream triggering inside the agent loop, set `auto_dream=True` on the middleware (see [contextseek-params.md](contextseek-params.md) Issue 11 for the dual-gate trigger mechanics). Dream without LLM falls back to keyword-overlap heuristics; results are coarser but still useful for surface-level clustering.
**Common configuration problems for this scenario** → see [contextseek-params.md](contextseek-params.md): dream trigger conditions not met (min_items, cooldown), dream-generated items disappearing due to transient stability.
---
## Issue 4: SRE incident postmortem agent — tracing knowledge confidence and conflicts
**Keywords**: evidence_chain, provenance, confidence propagation, conflict detection, SRE, postmortem, audit, upstream, chain_confidence, broken links, knowledge trustworthiness
You're building an **incident postmortem agent** that accumulates observations during a live incident: "Alert A fired at 14:03", "Log B shows connection timeouts", "Analysis: timeouts caused by DB connection pool exhaustion", "Recommendation: lower `connection_timeout` to 500ms". Each item is derived from the previous. A week later, during a recurring-incident review, an engineer asks: *"Is this recommendation actually well-supported? Were there any conflicting signals? How confident should we be in this diagnosis?"*
`ctx.evidence_chain()` constructs a full DAG starting from any item, traversing `derived_from`, `supported_by`, `merged_from` (positive) and `refuted_by` (negative) links. It returns:
- `overall_confidence` — propagated confidence score (Noisy-OR for supports, penalty for refutations)
- `critical_path` — `list[str]` of item ids forming the highest-weight inference path
- `conflicts` — `list[ConflictReport]`; each has `item_id`, `refuter_id`, `refutation_strength`
- `broken_links` — `list[str]` of item ids that no longer exist in the store
- `nodes` / `edges` — full DAG for visualization
- `needs_reverification` — `bool`; `True` when `overall_confidence < 0.4`
- **Write items with explicit derivation links**:
```python
from contextseek import ContextSeek
from contextseek.domain.links import Link, LinkType
ctx = ContextSeek.from_settings()
scope = "incidents/2026-06-01"
alert = ctx.add("Alert A fired at 14:03: p99 latency > 2s", scope=scope, source="pagerduty")
log = ctx.add("Log B: connection pool exhausted (pool_size=10, wait_timeout=30s)",
scope=scope, source="datadog",
links=[Link(target_id=alert.id, relation=LinkType.supported_by)])
analysis = ctx.add("Root cause: DB connection pool exhausted under 50-rps load",
scope=scope, source="agent_inference",
links=[Link(target_id=log.id, relation=LinkType.derived_from)])
rec = ctx.add("Recommendation: lower connection_timeout to 500ms",
scope=scope, source="agent_inference",
links=[Link(target_id=analysis.id, relation=LinkType.derived_from)])
```
- **Evaluate the recommendation's trustworthiness**:
```python
# evidence_chain and chain_confidence require a full URI ref, not a bare item id
ref = ctx.resolver.ref_for(scope, rec.id)
chain = ctx.evidence_chain(ref, scope=scope)
print(f"Confidence: {chain.overall_confidence:.2f}") # e.g. 0.71
if chain.conflicts:
# conflicts is list[ConflictReport]; each has item_id, refuter_id, refutation_strength
for c in chain.conflicts:
print(f"Conflicting evidence: {c.refuter_id} refutes {c.item_id} (strength={c.refutation_strength:.2f})")
if chain.overall_confidence < 0.4:
print("Low confidence — needs human review before applying")
```
- **Quick check without the full DAG**:
```python
ref = ctx.resolver.ref_for(scope, rec.id)
score = ctx.chain_confidence(ref, scope=scope)
# returns a float; same traversal as evidence_chain but no DAG construction overhead
```
- **Lessons learned**: the agent itself doesn't need to call `evidence_chain` — it's a postmortem / audit API. Wire it into your review pipeline or a separate audit agent that periodically evaluates low-confidence recommendations. Writing `links=` when calling `ctx.add()` is optional but unlocks this entire capability; without links, `evidence_chain` sees only isolated nodes.
**Common configuration problems for this scenario** → see [contextseek-params.md](contextseek-params.md): evidence_chain vs chain_confidence — when to use which.
---
## Issue 5: Enterprise knowledge migration — agent is retrieval-ready on day one
**Keywords**: pre-populate, cold start, bulk import, DataPlug, RAGPlug, PowerMemPlug, existing knowledge, seed context, initial corpus, plug, knowledge migration
You're migrating an enterprise knowledge base to a ContextSeek-backed agent. The existing data is 100 k+ FAQ entries, historical support tickets, and internal wiki pages — currently stored in a RAG vector store or PowerMem. The new agent cannot wait months for `auto_store` to accumulate conversation history; it needs to be able to retrieve from all of this on day one.
`ctx.plug()` consumes a `DataPlug` (a streaming iterator of `RawEvent` objects) and routes each event through the same full pipeline as `ctx.add()`: summarization, embedding, conflict detection, and persistence. Built-in plugs cover the most common sources:
| Plug class | Source |
|------------|--------|
| `RAGPlug` | Existing RAG / vector store chunks |
| `PowerMemPlug` | PowerMem memory store |
| `TracePlug` | Execution traces / agent logs |
| `MCPToolImporter` | MCP tool definitions → `skill` stage |
| `OpenAIFunctionImporter` | OpenAI function schemas → `skill` stage |
- **Import from an existing RAG store**:
```python
from contextseek import ContextSeek
from contextseek.plugs import RAGPlug
ctx = ContextSeek.from_settings()
# RAGPlug accepts a list of dicts; each dict needs at least "content" or "page_content"
docs = existing_vector_store.similarity_search("*", k=10000)
rag_plug = RAGPlug(
documents=[{"content": d.page_content, "metadata": d.metadata} for d in docs],
source_name="wiki-v2",
)
ctx.plug(rag_plug, scope="company/knowledge")
```
- **Import from PowerMem**:
```python
from contextseek.plugs import PowerMemPlug
# PowerMemPlug.from_records() accepts dicts from PowerMem get_all/search results
records = powermem_instance.get_all(user_id="shared") # yields list of dicts
plug = PowerMemPlug.from_records(records, source_prefix="powermem")
ctx.plug(plug, scope="company/support-history")
```
- **Run compact after bulk import** to consolidate and promote stages before the agent goes live:
```python
ctx.compact(scope="company/knowledge")
```
- **Combine pre-populated knowledge with live agent writes** — plug for the initial corpus, then deploy the agent with `ContextSeekMiddleware` writing new Q+A pairs into the same scope. The two pipelines are additive.
- **Lessons learned**: `plug()` is for one-time or scheduled batch ingestion outside the agent loop. `ContextSeekMiddleware` handles continuous, per-turn ingestion inside the agent loop. Use both: `plug()` to seed the store, middleware to keep it growing. Run `compact()` after large bulk imports before the first retrieval to maximize retrieval quality.
**Common configuration problems for this scenario** → see [contextseek-params.md](contextseek-params.md): DataPlug vs manual ctx.add() — which to use for bulk import; plug() scope priority and stage inference.