Three-Layer Capability Decomposition
Analyze the production agent three-tier architecture: prompt policy, deterministic tools, and isolated sandbox execution.
In monolithic AI agent scripts, developers frequently embed raw operating system calls or database clients directly inside model Tool functions (e.g. importing pg within an execute_sql tool). When transitioning across environments (such as switching from a cloud PostgreSQL database to a local SQLite memory store), this tight coupling leads to brittle codebases.
DeepSeek Harness establishes the Three-Role Capability Design Pattern: decoupling every agent capability into Seam (Abstract Extension Seam), Provider (Underlying Actuator), and Tool (LLM Interaction Layer) across three distinct packages.
This tutorial uses an “Agent Database Query Capability” as a real-world case study to explain the end-to-end implementation of this pattern.
Architectural Perspective: Roles and Interaction Topology
┌─────────────────────────────────────────────────────────────┐
│ Three-Role Capability Topology │
├─────────────────────────────────────────────────────────────┤
│ Layer 3: Tool (LLM-Facing Actuator) │
│ • Package: dsh-tool-sql │
│ • Role: Exports defineTool, validates Schema, renders data │
│ • Dependency: Depends ONLY on Seam; zero driver binaries │
├──────────────────────────────▲──────────────────────────────┤
│ │ Consumes ctx.dbDriver │
├──────────────────────────────┴──────────────────────────────┤
│ Layer 1: Seam (Abstract Extension Seam) │
│ • Package: dsh-database-seam │
│ • Role: Defines IDatabaseDriver and module augmentation │
│ • Dependency: Pure TypeScript types; zero external deps │
├──────────────────────────────▲──────────────────────────────┤
│ │ Implements & mounts dbDriver │
├──────────────────────────────┴──────────────────────────────┤
│ Layer 2: Provider (Underlying Actuator Implementation) │
│ • Package: dsh-database-postgres (or dsh-database-sqlite) │
│ • Role: Imports native drivers & manages connection pools │
└─────────────────────────────────────────────────────────────┘
Step 1: Author the Seam Layer (dsh-database-seam)
The Seam package is the contract connecting Tool and Provider. It contains no execution binaries, only interface signatures and service declarations:
// packages/dsh-database-seam/src/index.ts
import { Service, type Context } from '@deepseek-ai/cordis';
// 1. Core Domain Types
export interface QueryResult {
columns: string[];
rows: Record<string, any>[];
rowCount: number;
}
export interface IDatabaseDriver {
query(sql: string, params?: any[]): Promise<QueryResult>;
ping(): Promise<boolean>;
}
// 2. TypeScript Module Augmentation
declare module '@deepseek-ai/cordis' {
interface Context {
dbDriver: IDatabaseDriver;
}
}
// 3. Abstract Service Base Class
export abstract class DatabaseDriverSeam extends Service implements IDatabaseDriver {
constructor(ctx: Context) {
super(ctx, 'dbDriver');
}
abstract query(sql: string, params?: any[]): Promise<QueryResult>;
abstract ping(): Promise<boolean>;
}
Step 2: Implement the Provider Layer (dsh-database-postgres)
The Provider implements the Seam contract and brings in native dependencies (e.g. pg):
// packages/dsh-database-postgres/src/index.ts
import type { Context } from '@deepseek-ai/cordis';
import { DatabaseDriverSeam, type QueryResult } from 'dsh-database-seam';
export const name = 'postgres-database-provider';
class PostgresDriverImpl extends DatabaseDriverSeam {
async query(sql: string, params: any[] = []): Promise<QueryResult> {
console.log(`[Postgres] Executing query: ${sql}`);
return {
columns: ['id', 'user_name', 'status'],
rows: [
{ id: 1, user_name: 'Alice', status: 'active' },
{ id: 2, user_name: 'Bob', status: 'pending' }
],
rowCount: 2
};
}
async ping(): Promise<boolean> {
return true;
}
}
export function apply(ctx: Context) {
ctx.plugin(PostgresDriverImpl);
}
Step 3: Author the Tool Layer (dsh-tool-sql)
The Tool interacts with the LLM, relying purely on ctx.dbDriver and formatting outputs into Markdown:
// packages/dsh-tool-sql/src/index.ts
import type { Context } from '@deepseek-ai/cordis';
import { defineTool } from '@deepseek-ai/dsh-tools';
import 'dsh-database-seam';
export const name = 'sql-query-tool';
export const inject = ['tools', 'dbDriver'];
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'execute_sql_query',
description: 'Executes a read-only SQL query and returns formatted rows. Use to inspect tables or data.',
parameters: {
sql: {
type: 'string',
required: true,
description: 'Valid read-only SELECT SQL statement'
}
},
output: {
schema: { type: 'object' },
render: (_args, result) => [
{
type: 'text',
text: `[SQL Query Result (${result.rowCount} rows)]
` +
`| ${result.columns.join(' | ')} |
` +
`| ${result.columns.map(() => '---').join(' | ')} |
` +
result.rows.map((r: any) => `| ${Object.values(r).join(' | ')} |`).join('
')
}
]
},
async execute(args) {
// Interacts purely with the abstract Seam interface
return await ctx.dbDriver.query(args.sql);
}
}));
}
Three Core Engineering Benefits
- Provider Swappability: Switch from local SQLite to cloud PostgreSQL without modifying Tool code;
- Dependency Decoupling: Tool packages remain lightweight without dragging in heavy C++ native binaries;
- Independent Evolution: Driver internal refactors do not disrupt model prompt templates.
Frequently Asked Questions (FAQ)
Q1: Why not combine Provider and Tool inside a single npm package?
Answer: Merging them creates tight coupling. When switching execution backends (e.g. from local shell to a remote Docker daemon), the entire tool would require rewriting; it also forces unnecessary driver dependencies into lightweight runtimes.
Q2: What happens if Tool mounts without a matching Provider?
Answer: Because Tool declares inject = ['dbDriver'], the microkernel keeps the Tool in the PENDING state until a Provider supplying dbDriver is loaded, preventing unhandled runtime exceptions.
Q3: How does the official Bash tool implement the three-role design?
Answer: Official Bash execution is split into: dsh-shell (Seam contract), dsh-bash-local (local Provider), and dsh-tool-bash (LLM-facing Tool).
Q4: How can multiple Providers register under the same Seam?
Answer: Implement a manager registry pattern in the Seam (e.g. ctx.dbManager.register('prod', driver)), allowing routing strategies to select targets dynamically.