DeepSeek Harness: Context, Memory & Knowledge Architecture Deep Dive

This article dissects DeepSeek Harness's unified Session Log architecture where context management, memory compaction, and knowledge acquisition collaborate through layered context assembly, structure-preserving compaction, and tool-mediated retrieval — all traceable and replayable.

Architecture and Beyond
Architecture and Beyond
Architecture and Beyond
DeepSeek Harness: Context, Memory & Knowledge Architecture Deep Dive

Overview

DeepSeek Harness does not treat context, memory, and knowledge as three independent heavyweight systems. Instead, they collaborate around a single Session Log . A core constraint governs the design: everything the model sees must have a traceable source and be reconstructible from the session record. This constraint shapes the entire data flow: context cannot be arbitrarily concatenated by the controller, tool results cannot stay only in process memory, compaction cannot overwrite original events, and retrieval results cannot bypass the session log.

The end-to-end chain is: SystemPrompt.assemble() assembles the current context → ReactLoopAgent.preStep() decides whether dynamic content enters the session → Session.append() persists events → Session.deriveMessages() derives model-ready history → buildRequest() builds the final request → llm.stream() completes the model call.

Context Management

1. Layered Organization

Context is split into four categories, each owned by a distinct module:

System Rules : role, behavior constraints, tool usage specs (managed by SystemPrompt)

Dynamic State : working directory, runtime environment, terminal state (managed by Runtime Context)

Session History : user messages, model replies, tool results (managed by Session)

Tool Definitions : currently available tools and parameter schemas (managed by ToolRuntime)

The AgentLoop only controls execution flow; it does not concatenate prompts, manage history, or register tools.

2. Dynamic Per-Step Assembly

At each step start, ReactLoopAgent.preStep() calls SystemPrompt.assemble() to collect: sections — relatively stable system prompts contexts — environment info that changes with runtime state tools — current tool descriptions variables — variables needed for prompt rendering

Plugins register their own contributions; SystemPrompt produces a per-step snapshot. When plugins start/stop, the working directory changes, or tool permissions shift, the next model call automatically receives the latest state without extra business logic in the AgentLoop.

3. Avoiding Duplicate Dynamic State Injection

RuntimeContextProjection

compares the current snapshot with the previous one:

If unchanged, the existing context is reused.

If changed, a new context message is written to the Session.

This reduces token waste (e.g., a stable working directory across many steps) and preserves a change trajectory. Projection state can be restored from an existing Session, so service restarts do not cause duplicate writes.

4. Unified History Derivation

User messages, model replies, and tool results are first written to the Session Log, then Session.deriveMessages() derives the protocol message list. This creates three layers:

Log : complete append-only raw events

Surface : current view of events participating in model context

Messages : final structured messages sent to the model

For example, a tool result is written as tool/result; next round deriveMessages() converts it to a model-readable message. Compaction only adjusts Surface, never deletes raw Log events.

5. Building and Recording the Final Request

buildRequest()

produces: system: system prompt messages: session history + dynamic context tools: current tool definitions sessionId: session identifier signal: control signals

It also records request/header and request/context, enabling answers to: what history the model saw, which system prompt version was used, which tools were available, whether the dynamic environment had changed, and whether an anomaly stems from model reasoning or input context.

Memory Compaction (Session Memory)

DeepSeek Harness currently provides memory via Compaction , which folds old history into structured checkpoints for the current session. Cross-session preferences, project experience, and long-term facts are not yet a complete system; the author terms this "session memory."

1. Compaction Target

Session has three layers: Log (raw append-only events), Surface (current derivation view), Messages (protocol messages). Compaction modifies Surface only — raw events stay in Log, while the model's visible history is replaced by summary nodes. This is far safer than deleting the first N messages, because debugging retains the original record and can audit compaction scope and summary provenance.

2. Trigger Conditions

BasicCompactionEngine

uses tokenMeter.measure(session) to estimate token pressure against the model's contextWindow. Two trigger entry points:

Regular pressure check at agent/pre-step Retry handling after a model context-overflow error selectCompactableRange() picks the region, preferring older history while preserving recent context (current edit progress, latest tool results, next steps). It also protects tool-call/result pairs via validateSurfaceRegion() to avoid breaking semantic pairing — some model adapters reject split pairs.

3. Summary Generation

After region selection, buildSummarizationInput() restores the corresponding model messages and retrieves the system prompt and tools at that time. summarizeWithLlm() then calls an LLM with a fixed template requiring:

Primary Request and Intent
Files and Code
Errors and Fixes
Next Step
Critical Context

The structured template prevents summaries from degrading into generic dialogue overviews. Coding-agent history concentrates useful signal in: original problem, files read/modified, commands run, failures and fixes, incomplete steps, and inviolable environment constraints. A free-form "summarize this conversation" prompt often drops filenames, error details, and failed attempts.

4. Replacement Transaction

The summary is wrapped in <compacted-summary>, written as a new user/message event, and applied via surfaceOp: replace over the old region. The reference implementation logs:

// compaction-basic/region.ts logic simplified
session.append('compaction/start', { ... })

const summary = await summarizeWithLlm(ctx, config, input, agent, signal)
const framed = frameSummary(summary.summary)

const summaryEvent = session.append('compaction/summary', { ... })

session.append(
  'user/message',
  createUserMessage({
    content: framed,
    source: compactCheckpointSource(...),
  }),
  {
    surfaceOp: { op: 'replace', start, end },
    sourceEventSeqs: [startEvent.seq, summaryEvent.seq, ...shadowedSeqs],
  },
)

session.append('compaction/end', { ... })

Recorded: compaction start, summary content, replacement message, covered event sequences, compaction end. sourceEventSeqs links the checkpoint to original history for traceability. Session.deriveMessages() detects replaceGeneration changes and rebuilds the message cache; subsequent models see the new checkpoint plus retained recent history. The transaction guarantees integrity: on failure the old Surface remains usable; only after successful commit does the model view change.

5. Lossy Risk

Compaction is inherently lossy. If a constraint (e.g., "test env requires specific env var, skip if empty") is omitted from the summary, the agent may later execute a wrong command. The raw event remains in Log but is no longer in the model's hot context. Session Log integrity solves auditability, not full information recovery. The author plans a history recall layer: summaries retain key event references, and the model can call a recall_history tool with structured parameters (turn range, step range, event seq, tool call id, file path, compaction id). This leverages the Session's existing event order and provenance rather than relying solely on semantic search. Recall results are written as tool/result so the system knows what history was restored and how it influenced later decisions.

6. Scheduling Latency

Compaction currently invokes an LLM synchronously on the main path. Short sessions tolerate this; long sessions with large summarization inputs cause noticeable pauses, especially when compaction blocks the next step during multi-tool execution. A two-level watermark is proposed:

Near window limit → background candidate checkpoint generation

Hard limit reached → commit available checkpoint

Candidate expires → discard

No candidate → fall back to synchronous compaction

Consistency is the main challenge: the Session continues appending events during background generation; before commit, the corresponding Surface generation must be validated to ensure no conflicts (new tool results or user inputs). Expired checkpoints cannot be forced. Existing re-entry protection, Surface generation, and transaction events provide a foundation; the author recommends staying synchronous initially and introducing background checkpoints only as long-task proportion grows.

Knowledge Acquisition

DeepSeek Harness has no traditional unified vector knowledge base . Knowledge enters via three paths:

File path references

Web search & fetch

Tool results written to Session

This suits coding agents: repositories have path, symbol, definition, and reference structure. Chunking everything into a vector DB loses structural information and incurs index maintenance cost.

1. File References

file-reference-local

provides workspace file/directory candidates. It creates a WorkspaceFileSearch rooted at session.header.cwd, scanning the workspace and returning only path and kind. The user selects a file; the frontend inserts @path or @"path with spaces" into the input. File content does not enter the model at this stage. The system prompt tells the model that @ denotes a workspace path; to read content, the model must call the read tool.

This separation handles two concerns: file-reference-local finds paths, read tool fetches content. Auto-inlining entire files on selection would cause: (1) large files exhausting context, (2) user unaware of how much content was expanded, (3) no independent record of the read action, (4) bypassing tool guards for permissions/scope, (5) inability to know which file version the model saw after modifications. Path references preserve user intent; tool calls preserve actual read behavior; both enter Session for a complete audit trail. WorkspaceFileSearch also enforces directory boundaries, exclude patterns, max entries, candidate limits, and rejects .. or symlinks escaping the workspace — security boundaries because paths come from user input or model generation and cannot be trusted by default.

2. Precise Retrieval (LSP)

Path completion only solves "roughly know where the file is." Large repos need: interface definition location, symbol references, dependents, impact of a change, compiler diagnostics linked to symbols. These suit LSP (Language Server Protocol). The project already has packages/lsp/ for extension. Priority capabilities:

Go to Definition

Find References

Workspace Symbols

Diagnostics

Call hierarchy

Vector retrieval relies on semantic similarity; similar names/comments don't prove dependency. LSP returns compiler-maintained definition/reference graphs, fitting code modification. The author proposes a four-layer retrieval strategy:

Known path → direct file read

Known text → grep Known symbol → LSP

Only conceptual description → semantic search

This prioritizes deterministic information. Repos already provide path and symbol structure; no need to convert to vector similarity first. LSP has runtime costs (server startup/warmup, multi-language processes, slow global queries in large monorepos), so it should be an on-demand tool, not a resident context. Query results still go through ToolRuntime into Session so replay can show which definitions/references the model read.

3. Web Retrieval

External knowledge enters via web_search and web_fetch in three layers: WebRuntime defines unified capability

search/fetch providers handle concrete requests tool-web registers capabilities as model-visible tools

This decouples tool schema from providers. The model only understands web_search and web_fetch; the backend can use DeepSeek, Exa, Perplexity, or an HTTP fetch provider. Provider selection is strict: configured provider ID → use it; unconfigured → require exactly one available provider; multiple available → error. This prevents plugin load order from affecting production behavior.

Search results are formatted as model-visible text with "external content untrusted" and source URL notices. web_fetch converts HTML to Markdown; plain text passes through. Prompts can only influence model behavior; network boundaries must be enforced by the provider. The HTTP fetch provider already implements: HTTPS only, reject URL credentials, reject private/non-public addresses, control same-origin redirects, limit redirect count, limit response bytes, limit body length, fixed validated DNS addresses, timeout via deployment config. Model-generated URLs are also untrusted input; prompt injection in web pages could induce requests to internal metadata endpoints or credentialed URLs. Network policy must execute outside the model.

4. Tool Ingestion

File reads, web searches, and web fetches all feed the model through the same tool chain: ToolRuntime registers tool schemas into SystemPrompt Model returns

tool-call
AgentLoop

calls executeToolCalls() Session writes

tool/call
ToolRuntime

executes the tool

Session writes tool/result Next round deriveMessages() projects results to the model

Reference call path:

// Model returns tool-call
const toolCalls = message.content.filter(block => block.type === 'tool-call')

// Agent executes tools
const { concluded } = await executeToolCalls(
  this.loopCtx,
  turn,
  step,
  toolCalls,
  signal,
  context => this.inbox.splice('next-step', this.inbox.nextStep.length, 0, [context]),
)

// Tool results enter session log; next step deriveMessages() projects to model

Tool results never stay only in the current execution stack or mutate a temporary messages array. This is critical for web info: search results and page content change over time. If only search parameters were recorded, replay would re-fetch and potentially yield different content. Same for file reads: the agent may modify a file after reading; historical reasoning needs the exact content sent to the model then. Engineering trade-off: store the rendered result the model actually saw, plus metadata (truncated?, original length, source path/URL, content type, tool call params, provider info). Content the model never saw need not be disguised as context history; replay cares about the model's input at that moment.

Relationship of the Three Modules

Context, memory, and knowledge control three phases of model input:

Context decides current input : SystemPrompt gathers system constraints, dynamic state, tool schemas. preStep() checks dynamic content changes; buildRequest() builds the final request. Solves "what to carry this round."

Memory controls history volume : Compaction watches token pressure, selects old regions, generates structured summaries, replaces Surface. Solves "what to keep when history is too long."

Knowledge supplements missing info : File references locate paths, read tools fetch repo content, web tools get external info, future LSP for symbol-level queries. Solves "where to get info the session lacks."

All three converge on the Session:

Dynamic context enters via user/message Tool info enters via tool/result Compaction rewrites Surface via compaction/* events and replacement messages

The data spine: assemble context → record events → derive messages → call model → execute tools → write results → fold Surface when needed . Plugins may add new context sources, tools, or compaction strategies but cannot bypass this spine. Directly stuffing GenerateOptions.messages creates unreplayable shadow context. Retrieval bypassing ToolRuntime loses permissions, audit, and result recording. Compaction overwriting raw events breaks historical tracing. These boundaries matter more for maintainability than the choice of model, search service, or vector DB.

Summary

Context management assembles current input.

Compaction compresses current session history.

File and web tools supplement missing information.

Shared Session Log invariants:

Model-visible dynamic state must enter the log

Model-obtained tool results must enter the log

Compaction only adjusts Surface; raw events persist

Subsequent model inputs are uniformly derived by Session.deriveMessages() Strengths: replayability, auditability, clean module boundaries. Concrete gaps: Compaction incurs semantic loss; compaction calls block the main thread; file path retrieval lacks symbol-level capability; cross-session long-term memory is not yet a standalone system. Session Log stores facts; Surface controls what the model currently sees; tools fetch on demand. The three modules collaborate along this data chain, keeping the system controllable across context window limits, latency, and information integrity.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

Tool UseAgent ArchitectureMemory Compactioncontext managementReplayabilityDeepSeek HarnessSession LogLSP Integration
Architecture and Beyond
Written by

Architecture and Beyond

Focused on AIGC SaaS technical architecture and tech team management, sharing insights on architecture, development efficiency, team leadership, startup technology choices, large‑scale website design, and high‑performance, highly‑available, scalable solutions.

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.