-
Notifications
You must be signed in to change notification settings - Fork 17
Expand file tree
/
Copy path43-data-security-pipeline.ts
More file actions
152 lines (138 loc) · 4.82 KB
/
Copy path43-data-security-pipeline.ts
File metadata and controls
152 lines (138 loc) · 4.82 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
/**
* 43 - Data Security Pipeline
*
* Demonstrates a sequential pipeline with data flow control where
* sensitive information is collected, redacted, and then presented safely:
*
* collector >> validator >> responder
*
* - collector: Fetches raw user data using tools (includes PII).
* - validator: Redacts sensitive fields (SSN, balances, email) using tools.
* - responder: Presents the safe, redacted data to the user.
*
* This pattern enforces a security boundary between data access and
* user-facing responses, ensuring PII never reaches the final output.
*
* Requirements:
* - Conductor server with LLM support
* - AGENTSPAN_SERVER_URL=http://localhost:8080/api as environment variable
* - AGENTSPAN_LLM_MODEL=openai/gpt-4o-mini as environment variable
*/
import { Agent, AgentRuntime, tool } from '@io-orkes/conductor-javascript/agents';
import { llmModel } from './settings';
// -- Data tools ---------------------------------------------------------------
const fetchUserData = tool(
async (args: { userId: string }) => {
const users: Record<string, Record<string, unknown>> = {
U001: {
name: 'Alice Johnson',
email: 'alice@example.com',
role: 'admin',
ssn_last4: '1234',
account_balance: 15000.0,
},
U002: {
name: 'Bob Smith',
email: 'bob@example.com',
role: 'user',
ssn_last4: '5678',
account_balance: 3200.0,
},
};
return users[args.userId] ?? { error: `User ${args.userId} not found` };
},
{
name: 'fetch_user_data',
description: 'Fetch user data from the database.',
inputSchema: {
type: 'object',
properties: {
userId: { type: 'string', description: 'The user\'s identifier' },
},
required: ['userId'],
},
},
);
// -- Redaction tools ----------------------------------------------------------
const redactSensitiveFields = tool(
async (args: { data: string }) => {
let parsed: Record<string, unknown>;
try {
parsed = JSON.parse(args.data) as Record<string, unknown>;
} catch {
return { error: 'Could not parse data for redaction' };
}
const sensitiveKeys = new Set(['ssn_last4', 'account_balance', 'email']);
const redacted: Record<string, unknown> = {};
for (const [k, v] of Object.entries(parsed)) {
redacted[k] = sensitiveKeys.has(k) ? '***REDACTED***' : v;
}
return { redacted_data: redacted };
},
{
name: 'redact_sensitive_fields',
description: 'Redact sensitive fields from data before responding to users.',
inputSchema: {
type: 'object',
properties: {
data: { type: 'string', description: 'JSON string of user data to redact' },
},
required: ['data'],
},
},
);
// -- Pipeline agents ----------------------------------------------------------
// Data collector fetches raw user data
export const collector = new Agent({
name: 'data_collector',
model: llmModel,
instructions:
'You are a data collection agent. When asked about a user, ' +
'call fetch_user_data with their ID. Pass the raw data along ' +
'to the next agent for security review.',
tools: [fetchUserData],
});
// Validator enforces data security policy
export const validator = new Agent({
name: 'security_validator',
model: llmModel,
instructions:
'You are a security validator. Review data for sensitive information ' +
'(SSN, account balances, email addresses). Use the redact_sensitive_fields ' +
'tool to redact any sensitive data before passing it along. ' +
'Only pass redacted data to the next agent.',
tools: [redactSensitiveFields],
});
// Responder formats the final answer
export const responder = new Agent({
name: 'responder',
model: llmModel,
instructions:
'You are a customer service agent. Use the validated, redacted data ' +
'to answer the user\'s question. NEVER reveal redacted information. ' +
'If data shows ***REDACTED***, explain that the information is ' +
'restricted for security reasons.',
});
// Sequential pipeline enforces data flow: collect -> validate -> respond
const pipeline = collector.pipe(validator).pipe(responder);
async function main() {
const runtime = new AgentRuntime();
try {
const result = await runtime.run(
pipeline,
'Tell me everything about user U001 including their financial details.',
);
result.printResult();
// Production pattern:
// 1. Deploy once during CI/CD (optional -- serve() below also deploys):
// await runtime.deploy(pipeline);
// CLI alternative:
// agentspan deploy --package sdk/typescript/examples --agents data_collector
//
// 2. In a separate long-lived worker process (deploys + registers workers + starts polling):
// await runtime.serve(pipeline);
} finally {
await runtime.shutdown();
}
}
main().catch(console.error);