A concise map of the moving pieces: GL SDK / GLAIP, GLAIR Connectors, and CATAPA Digital Employee.
flowchart TB
subgraph GL["GL SDK / GLAIP"]
A["Agent runtime<br/>LLM calls, memory, streaming<br/>PII masking, tracing"]
end
subgraph CATAPA_SDK["CATAPA SDK"]
C["catapa<br/>Public CATAPA API"]
CP["catapa_private<br/>Internal CATAPA API"]
end
subgraph CONN_SDK["GL Connectors SDK / Tools"]
CS["gl_connectors_sdk<br/>Direct connector API"]
CT["gl_connectors_tools<br/>Skill factory + MCPs"]
end
subgraph CONN_HOST["GLAIR Connectors host"]
H1["connectors.glair.ai<br/>Public API"]
H2["connectors.gdplabs.id<br/>Internal runtime"]
CON["Connectors Console"]
end
subgraph DE["CATAPA Digital Employee"]
D["LangChain/LangGraph agents<br/>tools + MCPs"]
end
A -->|calls| C
A -->|calls| CP
A -->|forwards| CS
A -->|uses| CT
CS -->|REST| H1
CS -->|REST| H2
CT -->|installs skills/MCPs| H2
A -->|gl_connectors_token| D
D -->|catapa SDK| C
D -->|catapa_private SDK| CP
D -->|sync connector methods| CS
H1 -->|OAuth callback| D
H2 -->|MCP server| D
CON -->|configure integrations| H1
CON -->|configure integrations| H2
Everything in the diagram is connected, but the arrow style matters:
- Solid arrows = direct code/API dependencies.
- Labeled arrows = token propagation or runtime wiring.
- GLAIP can talk to CATAPA data, GL Connectors API, and GL Connectors MCP servers.
- A Digital Employee reuses the same SDKs and connector runtime, but adds its own agent topology and CATAPA-specific guardrails.
GL SDK = GDP Labs Software Development Kit. GLAIP = the agent-interaction layer (SDK + runtime).
Key packages:
| Package | What it does |
|---|---|
glaip-sdk |
Agent runtime, Agent, run_agent, streaming, runtime_config, trace, MCP |
gl-connectors-sdk |
Direct connector API fluent interface |
gl-connectors-tools / gl-connectors-tools-binary |
Skill factory, install skills from GitHub |
catapa |
Public CATAPA API SDK |
catapa_private |
Internal/private CATAPA API SDK |
Common AIP patterns:
from glaip_sdk import Agent, MCP
agent = Agent(
name="github-agent",
instruction="Summarise PRs",
mcps=[MCP.from_native("github")],
mcp_configs={"github": {"allowed_tools": ["github_list_pull_requests"]}},
)Token propagation for connector auth:
client.run_agent(
agent_id="...",
message="...",
gl_connectors_token="<user-token>",
)gl_connectors_tokenis forwarded to/agents/{agent_id}/runand used for GL Connectors pre-checks.- During local
make runit may not reach custom tools; add env fallback (GL_CONNECTORS_TOKEN) in the tool.
GLAIR Connectors is an OAuth gateway and tool catalog for external services (Google, GitHub, Slack, SQL, etc.).
| Use when | Approach |
|---|---|
| One precise external operation | Direct API (gl_connectors_sdk) |
| Agent needs broad/uncertain access to a service | MCP server with allowed_tools |
| Repeatable workflow, formatting, rules | Agent Skill |
Base URLs:
https://connectors.glair.ai— common public APIhttps://connectors.gdplabs.id— internal/GL ecosystem runtime- Console:
https://connectors.glair.ai/console
Main endpoints:
POST /clients— create client (API key holder)GET /clients,PATCH /clients— list / updatePOST /users— create userPOST /auth/tokens— login, get user tokenDELETE /auth/tokens— logoutGET /users/meGET /connectors/{name}/auth-schema— auth config shapePOST /connectors/{name}/integrations— create integrationGET|POST|DELETE /connectors/{name}/integrations/{user_identifier}GET /connectors/{name}/success-authorize-callback— OAuth callbackGET /catalog/mcp— list MCP serversGET /catalog/tools— list native toolsGET /skills/list— curated skills registryGET /skills/openclaw— UGC skillsGET /api/skills— skill details listPOST /api/skills— register a new skillPOST /api/skills/tenants— attach skill to tenantGET /api/skills/tenants/{client_id}PUT|GET|DELETE /api/skills/{skill_id}
from gl_connectors_sdk.connector import GLConnectors
connector = GLConnectors(
api_base_url="https://connectors.glair.ai",
api_key=client_key,
)
response = (
connector.connect("google_drive")
.action("search_files")
.params({"query": "name contains 'wfo'"})
.token(user_token)
.run()
)https://connectors.gdplabs.id/<connector>/mcp
Examples:
- GitHub:
https://connectors.gdplabs.id/github/mcp - Google Mail:
https://connectors.gdplabs.id/google_mail/mcp - Slack:
https://connectors.gdplabs.id/slack/mcp
Auth: X-Api-Key header for client, Authorization: Bearer <user-token> for user. Optional X-Integration to pick a specific integration.
Always restrict tools:
mcp_configs={
"github": {"allowed_tools": ["github_list_pull_requests"]}
}A Digital Employee (DE) is an LLM-driven agent inside CATAPA that answers domain-specific requests and performs workflows.
- Single agent: one domain, few tools, no specialist boundary.
- Coordinator + sub-agents: multiple specialists, different tools per responsibility, coordinator routes work.
Default: prefer single agent unless multi-agent clearly reduces risk.
Source of truth repo: https://github.com/GDP-ADMIN/prompt-template/tree/main/project-wide/digital-employee
Steps:
- Prepare PRD.
- Read
de-build-de-catapa/README.mdandAGENTS.md. - Fill
<REPLACE: ...>placeholders in step prompts. step-1-scaffolding.shstep-2-architecture-specification.md- Review architecture (tools, MCPs, sub-agents, missing/unnecessary parts).
step-3-implementation-specification.md- Review spec (no invented endpoints, no invented query syntax, clear env vars, good errors).
step-4-implementation.md- Configure
.env. make run MESSAGE="...".step-5-validation-and-compliance.md.step-6-integration-test-verification.md(live tests when feasible).
prompt-template-single-agent-instructions.mdprompt-template-multi-agent-coordinator-instructions.mdprompt-template-multi-agent-subagent-instructions.md
These are instruction scaffolding, not runtime code.
Public import:
from catapa import CatapaPrivate/internal import:
from catapa_private import CatapaPrivateSDK version: minimum >=0.3.3; TMO pins 0.3.6. Pin to latest compatible per DE.
Use RSQL ?filter=:
?filter=companyId==zfrl;status==active
?filter=employeeId=in=(123,456)
?filter=createdAt=gt=2025-01-01
Operators: ==, =in=, =gt=, =ge=, =lt=, =le=, =contains=.
Combine with ; (AND) or , (OR within same field).
Legacy ?query= with : / > / < / , is deprecated and overloaded; avoid in new code.
Pagination: default size is small; use up to 50 per page from CATAPA-API OpenAPI spec. TMO uses internal RECAPITULATION_DETAIL_PAGE_SIZE = 400 for its own batching.
- Never guess CATAPA endpoints or query params.
- Enforce tenant isolation.
- No raw PII in logs/output.
- No
logger.info()on raw result objects. - Use HITL for risky writes.
- Register MCPs per-agent, not globally.
- Prefer built-in tools/MCPs over custom code.
- Keep business reasoning in prompts/instructions, not tool/client code.
| Need | Use |
|---|---|
| Single deterministic external call | gl_connectors_sdk API |
| Agent chooses among many service actions | MCP server with allowed_tools |
| Repeatable format/checklist/workflow | Agent Skill |
| CATAPA HRIS data | catapa / catapa_private SDK |
| Live CATAPA action | CATAPA SDK + correct tenant/user |
For Excel/PDF artifacts + email:
- Generate artifact, upload to object storage, get presigned URL.
- Call
google_mailsync method inside LangChain_run(async fails inside running event loop). - Build email body in tool with
_format_email_body()so markdown table/sections survive LLM collapse. - Use real presigned URL; never leave placeholder variables.
Use gogcli (gog gmail get/search) rather than browser automation. Faster and authoritative.
# Run a DE locally
make run MESSAGE="Run lateness frequency analysis for Finance last month"
# GL Connectors SDK install
uv add gl-connectors-sdk
# GL Connectors tools/skill install
uv add gl-connectors-tools-binary
# AIP SDK install
uv add glaip-sdk
# CATAPA SDK
uv add catapa catapa-private
# GitHub prompt-template source
gh api repos/GDP-ADMIN/prompt-template/contents/project-wide/digital-employee/de-build-de-catapa/README.mddigital-employee-core is the Python framework library every CATAPA DE is built on. It wraps glaip-sdk and adds DE-specific primitives.
uv add digital-employee-core
# or from CodeArtifact:
uv pip install --index-url https://aws:$CODEARTIFACT_AUTH_TOKEN@$CODEARTIFACT_REPOSITORY_ENDPOINT/simple/ digital-employee-corefrom digital_employee_core import (
DigitalEmployee,
DigitalEmployeeAgent,
DigitalEmployeeBuilder,
DigitalEmployeeConfig,
DigitalEmployeePipelineBuilder,
DigitalEmployeeState,
DigitalEmployeeIdentity,
DigitalEmployeeJob,
DigitalEmployeeSupervisor,
ConfigTemplateLoader,
)
from digital_employee_core.constants import (
DEFAULT_MODEL_NAME,
GPT_5_MODEL_NAME,
GPT_5_MINI_MODEL_NAME,
GPT_5_1_MODEL_NAME,
GPT_5_2_MODEL_NAME,
)| Concern | What digital-employee-core gives you |
|---|---|
| Identity | DigitalEmployeeIdentity, DigitalEmployeeJob, DigitalEmployeeSupervisor |
| Configuration | DigitalEmployeeConfig + ConfigTemplateLoader for YAML placeholder substitution |
| Runtime | DigitalEmployee.run(message=..., local=True) and .deploy() |
| MCPs | Pre-built connector MCP imports (google_mail_mcp, google_calendar_mcp, google_docs_mcp) |
| Tools | Utility tool imports and LangChain tool registration helpers |
| Scheduling | ScheduleItemConfig for recurring DE runs |
| Escalation | Escalation channels and supervisor wiring |
| BVT | DeploymentVerifier, BVTCheckResult, BVTStatus |
| Agents | DigitalEmployeeAgent / Agent for coordinator + sub-agents |
flowchart TB
subgraph DE["CATAPA Digital Employee"]
D["main.py / custom tools"]
end
subgraph DECore["digital-employee-core"]
Core["DigitalEmployee\nDigitalEmployeeIdentity\nMCP/tool wiring\nBVT / schedule / escalation"]
end
subgraph AIP["GL SDK / GLAIP"]
A["Agent, MCP, run_agent\nmemory, streaming, PTC, trace"]
end
D -->|imports| Core
Core -->|wraps| A
D -->|may also use directly| A
glaip-sdk is the lower-level agent runtime; digital-employee-core is the CATAPA DE abstraction on top. A DE imports from both: digital_employee_core.* for DE primitives and glaip_sdk.* when it needs direct Agent, MCP, PTC, or schedule access.
| DE / template | digital-employee-core version |
|---|---|
| Template default | 0.0.35 |
| Weekly Report | >=0.0.23 |
| Personnel Admin | >=0.0.33,<0.1.0 |
| Payroll Officer | ==0.0.31 |
| Dummy SQL | ==0.0.32 |
Pin per DE; newer isn't automatically safer because DEs depend on specific internal APIs.
- Local source:
~/Documents/github/CATAPA/CATAPA-SDK/main/python/digital-employee-core/ - Published to AWS CodeArtifact
catapa-digital-employee-corePyPI repo. - GitBook docs: same CATAPA Digital Employee section.
| Topic | Location |
|---|---|
| GL SDK docs | /llms-full.txt on SDK GitBook |
| GL Connectors API spec | /tmp/glair-openapi.json (or connectors.glair.ai/openapi.json) |
| GL Connectors SDK usage | skill_view:glair-connectors |
| GL Connectors HTTP endpoints | skill_view:glair-connectors-api |
| CATAPA DE GitBook | https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/ |
| CATAPA DE GitBook markdown trick | append .md to page URL |
| CATAPA DE build prompts | https://github.com/GDP-ADMIN/prompt-template/tree/main/project-wide/digital-employee |
| CATAPA DE repo | ~/Documents/github/CATAPA/digital-employee/main |
| CATAPA API/OpenAPI | ~/Documents/github/CATAPA/CATAPA-API |
| CATAPA SDK | ~/Documents/github/CATAPA/CATAPA-SDK |
Last updated: 2026-07-02. If a link breaks, prefer the local CATAPA clone or the SDK GitBook /llms-full.txt over web search snippets.
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Digital Employee
4|
5|- Digital Employee Architecture
6|- Tech Stack Overview
7|- Digital Employee Detailed Block Diagram
8|- Multi-tenant
9|- Install and Configure
10|- Getting Started Example
11|- Advanced Examples
12|- Extend
13|- Instantiation
14|- Digital Employee Supervisor
15|- Sub Agents Configuration
16|- Skills Configuration
17|- MCP Allowed Tools Configuration
18|- Escalation Configuration
19|- Scheduler Configuration
20|- Memory Configuration
21|- Programmatic Tool Calling (PTC) Configuration
22|- Build Verification Tests (BVT)
23|- User Information in Digital Employee Runs
24|- Run History
25|- Recommended Project Structure
26|
27|
28|---
29|
30|# Agent Instructions
31|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
32|
33|## Querying This Documentation
34|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
35|
36|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
37|
38| 39|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee.md?ask=<question>&goal=<endgoal> 40|
41|
42|ask is the immediate question: it should be specific, self-contained, and written in natural language.
43|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
44|
45|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
46|
47|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
48|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Advanced Examples
4|
5|- Extend
6|- Instantiation
7|- Digital Employee Supervisor
8|- Sub Agents Configuration
9|- Skills Configuration
10|- MCP Allowed Tools Configuration
11|- Escalation Configuration
12|- Scheduler Configuration
13|- Memory Configuration
14|- Programmatic Tool Calling (PTC) Configuration
15|- Build Verification Tests (BVT)
16|- User Information in Digital Employee Runs
17|- Run History
18|- Recommended Project Structure
19|
20|
21|---
22|
23|# Agent Instructions
24|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
25|
26|## Querying This Documentation
27|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
28|
29|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
30|
31| 32|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples.md?ask=<question>&goal=<endgoal> 33|
34|
35|ask is the immediate question: it should be specific, self-contained, and written in natural language.
36|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
37|
38|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
39|
40|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
41|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Build Verification Tests (BVT)
4|
5|### Overview
6|
7|Build Verification Tests (BVT) provide a pre-deployment validation step for DigitalEmployee. Before the underlying agent is deployed, BVT verifies that the resolved agent graph and its MCP integrations are usable.
8|
9|BVT is implemented by DeploymentVerifier in digital_employee_core.bvt.verifier and is executed automatically by DigitalEmployee.deploy() unless you explicitly disable it.
10|
11|At a high level, the default verifier checks:
12|
13|* The root agent and all nested sub-agents recursively
14|* Every MCP attached to each agent node
15|* Whether the MCP URL is present and well-formed
16|* Whether an MCP session can actually be created and initialized
17|
18|This helps catch deployment issues early, before they fail at runtime.
19|
20|### When BVT Runs
21|
22|BVT runs during deployment:
23|
24|python 25|digital_employee.deploy() 26|
27|
28|By default, deploy() does this sequence:
29|
30|1. Build the resolved glaip_sdk.Agent instance
31|2. Run self.verifier.verify(self.agent)
32|3. Raise BuildVerificationError if any check failed
33|4. Continue to agent.deploy() only when all checks passed
34|
35|The implementation also supports skipping verification:
36|
37|python 38|digital_employee.deploy(run_bvt=False) 39|
40|
41|Use run_bvt=False only when you intentionally want to bypass pre-deployment validation.
42|
43|### Key Concepts
44|
45|#### What the default verifier checks
46|
47|The built-in DeploymentVerifier performs MCP-focused checks on the fully resolved agent tree:
48|
49|* Recursive agent traversal: walks the top-level agent and every nested sub-agent
50|* URL validation: checks that each MCP config contains a URL with both scheme and host
51|* Session initialization: creates an MCP session and calls session.initialize()
52|* Authentication propagation check by execution: auth headers are built from the resolved config and used during session creation
53|
54|This means BVT validates the final resolved configuration, not just the original constructor inputs.
55|
56|#### Pass, fail, and skip semantics
57|
58|Each check produces a BVTCheckResult with one of three statuses:
59|
60|* passed: the check succeeded
61|* failed: the check failed and should block deployment
62|* skipped: the check was intentionally not run
63|
64|Important behavior:
65|
66|* BVTResults.all_passed only returns False when at least one check is failed
67|* skipped checks do not block deployment by themselves
68|
69|For example, if an MCP exists on the agent but no matching config is found, the default verifier marks it as skipped with the message No configuration found — skipped.
70|
71|#### What causes deployment to fail
72|
73|Deployment is blocked when any BVT check returns failed.
74|
75|Common failure conditions in the default verifier include:
76|
77|* MCP has no name
78|* MCP URL is missing or empty
79|* MCP URL is malformed
80|* MCP session initialization raises an exception
81|* Authentication is invalid
82|* The MCP endpoint is unreachable or times out
83|
84|### Default Verification Flow
85|
86|The built-in DeploymentVerifier follows this sequence:
87|
88|1. Reset previous results
89|2. Run _pre_checks(agent)
90|3. Traverse the agent tree depth-first
91|4. Run _check_agent_node(agent) for each visited node
92|5. Verify each MCP on that node
93|6. Run _post_checks(agent)
94|
95|Each MCP verification does:
96|
97|1. Resolve the MCP config from agent.mcp_configs
98|2. Extract the URL from config["config"]["url"]
99|3. Validate the URL format
100|4. Build an MCPConfiguration for create_session()
101|5. Attempt real session initialization
102|
103|This is not a schema-only validation. The verifier performs a real initialization attempt, which makes it useful for catching network, auth, and protocol issues.
104|
105|### Handling BVT Failures
106|
107|Catch BuildVerificationError if you want to inspect or log the failure before exiting:
108|
109|{% code lineNumbers="true" %}
110|
111|python 112|from digital_employee_core import BuildVerificationError 113| 114|try: 115| digital_employee.deploy() 116|except BuildVerificationError as exc: 117| print(exc) 118|
119|
120|{% endcode %}
121|
122|The exception message is generated from BVTResults.summary(), which includes:
123|
124|* Total passed checks
125|* Total failed checks
126|* Total skipped checks
127|* Names of failed checks
128|* Names of skipped checks
129|
130|### Custom Verifiers
131|
132|The verifier is injectable. DigitalEmployee accepts a verifier parameter:
133|
134|{% code lineNumbers="true" %}
135|
136|python 137|from digital_employee_core import DeploymentVerifier, DigitalEmployee 138| 139|verifier = DeploymentVerifier(timeout=15) 140| 141|digital_employee = DigitalEmployee( 142| identity=identity, 143| mcps=[google_mail_mcp], 144| configurations=configurations, 145| verifier=verifier, 146|) 147|
148|
149|{% endcode %}
150|
151|You can also subclass DeploymentVerifier to add organization-specific checks.
152|
153|#### Extension points
154|
155|The base class provides three hooks:
156|
157|* _pre_checks(agent): runs before MCP traversal
158|* _check_agent_node(agent): runs once per visited agent node
159|* _post_checks(agent): runs after traversal finishes
160|
161|Each hook should yield or return BVTCheckResult instances.
162|
163|#### Example: add pre-deployment policy checks
164|
165|The repository already includes a working example in examples/custom_deployment_verifier_example.py.
166|
167|This example defines StrictDeploymentVerifier, which adds:
168|
169|* Required environment variable checks
170|* A deployment policy check
171|* The standard MCP verification from the base verifier
172|
173|Example structure:
174|
175|{% code lineNumbers="true" %}
176|
177|python 178|from collections.abc import Iterable 179| 180|from glaip_sdk import Agent 181| 182|from digital_employee_core import BVTCheckResult, BVTStatus, DeploymentVerifier 183| 184| 185|class StrictDeploymentVerifier(DeploymentVerifier): 186| def _pre_checks(self, agent: Agent) -> Iterable[BVTCheckResult]: 187| yield BVTCheckResult( 188| name="deployment_policy", 189| item_type="policy", 190| status=BVTStatus.PASSED, 191| message="Custom policy passed", 192| ) 193|
194|
195|{% endcode %}
196|
197|This pattern is useful when you want to validate things such as:
198|
199|* Required environment variables
200|* Required configuration keys
201|* Service health checks
202|* Naming conventions
203|* Deployment environment policy
204|
205|#### BVTStatus
206|
207|BVTStatus is a StrEnum with these values:
208|
209|* BVTStatus.PASSED
210|* BVTStatus.FAILED
211|* BVTStatus.SKIPPED
212|
213|#### BVTCheckResult
214|
215|Represents a single check result:
216|
217|{% code lineNumbers="true" %}
218|
219|python 220|BVTCheckResult( 221| name="google_mail_mcp", 222| item_type="mcp", 223| status=BVTStatus.PASSED, 224| message="MCP session initialized successfully", 225| details={}, 226|) 227|
228|
229|{% endcode %}
230|
231|Fields:
232|
233|* name: item being checked
234|* item_type: category such as mcp, agent, env_var, or policy
235|* status: one of the BVTStatus values
236|* message: human-readable result description
237|* details: optional diagnostic metadata
238|
239|#### BVTResults
240|
241|Aggregates all check results and provides convenience properties:
242|
243|{% code lineNumbers="true" %}
244|
245|python 246|results = verifier.verify(agent) 247| 248|print(results.all_passed) 249|print(results.failed_checks) 250|print(results.skipped_checks) 251|print(results.summary()) 252|
253|
254|{% endcode %}
255|
256|Available helpers:
257|
258|* all_passed
259|* failed_checks
260|* skipped_checks
261|* summary()
262|
263|### Transport and Timeout Notes
264|
265|The default verifier supports MCP transports via the MCP definition on each agent node.
266|
267|Behavior worth knowing:
268|
269|* If an MCP does not define a transport, the verifier uses streamable_http
270|* For non-stdio transports, the generated session config includes timeout=self.timeout
271|* For stdio, the verifier applies asyncio.wait_for(..., timeout=self.timeout) around session.initialize()
272|
273|In practice, this means the timeout constructor parameter on DeploymentVerifier controls how long initialization is allowed to take before failing.
274|
275|Example:
276|
277|{% code lineNumbers="true" %}
278|
279|python 280|verifier = DeploymentVerifier(timeout=30) 281|
282|
283|{% endcode %}
284|
285|### Best Practices
286|
287|#### 1. Keep BVT enabled in normal deployments
288|
289|The default deploy() behavior is correct for most cases. Bypass it only when you have a deliberate operational reason.
290|
291|#### 2. Treat skipped checks as signals
292|
293|A skipped MCP check usually means the MCP exists on the agent but no resolved config was found. That may be intentional, but it is often a configuration gap worth reviewing.
294|
295|#### 3. Use custom verifiers for organization rules
296|
297|If your deployment process depends on environment flags, external services, or policy enforcement, encode those rules in a custom DeploymentVerifier subclass instead of checking them manually elsewhere.
298|
299|#### 4. Use the built agent as the source of truth
300|
301|BVT runs against the resolved agent returned by _build_agent_instance(). This is the correct layer for validation because it includes propagated and merged configs.
302|
303|#### 5. Log or surface results.summary()
304|
305|When deployment fails, the BVT summary provides a concise diagnosis that is suitable for CI logs or deployment output.
306|
307|### Troubleshooting
308|
309|#### BuildVerificationError during deploy
310|
311|Problem: digital_employee.deploy() raises BuildVerificationError.
312|
313|What it means: At least one BVT check returned failed.
314|
315|Solution:
316|
317|* Inspect the exception message
318|* Review the failed check names
319|* Confirm MCP URLs are present and valid
320|* Confirm authentication headers are correct
321|* Verify the MCP endpoint is reachable from the deployment environment
322|
323|#### MCP check is skipped
324|
325|Problem: A result is marked as skipped with a message like No configuration found — skipped.
326|
327|What it means: The MCP exists on the agent, but no matching config entry was found in agent.mcp_configs.
328|
329|Solution:
330|
331|* Confirm the MCP was configured through DigitalEmployeeConfiguration
332|* Confirm the configuration key matches the connector template
333|* Confirm the config was propagated to the final built agent
334|
335|#### URL format failure
336|
337|Problem: The result message indicates an invalid URL format.
338|
339|What it means: The resolved MCP URL is missing a scheme or host.
340|
341|Solution:
342|
343|* Use a full URL such as https://example.com/mcp
344|* Avoid bare hostnames like example.com/mcp
345|* Avoid empty values produced by missing environment variables
346|
347|#### Session initialization failure
348|
349|Problem: The URL looks valid, but session initialization still fails.
350|
351|What it usually means:
352|
353|* The endpoint is down
354|* Authentication is invalid
355|* The MCP server is not speaking the expected protocol
356|* Initialization timed out
357|
358|Solution:
359|
360|* Verify endpoint reachability
361|* Verify auth headers and tokens
362|* Increase verifier timeout if the service is slow to initialize
363|* Test the MCP independently if needed
364|
365|### API Summary
366|
367|Most users only need these exported symbols:
368|
369|{% code lineNumbers="true" %}
370|
371|python 372|from digital_employee_core import ( 373| BuildVerificationError, 374| BVTCheckResult, 375| BVTResults, 376| BVTStatus, 377| DeploymentVerifier, 378|) 379|
380|
381|{% endcode %}
382|
383|These cover:
384|
385|* Failure handling with BuildVerificationError
386|* Modeling individual and aggregate results
387|* Creating default or custom verifiers
388|
389|
390|---
391|
392|# Agent Instructions
393|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
394|
395|## Querying This Documentation
396|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
397|
398|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
399|
400| 401|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/build-verification-tests-bvt.md?ask=<question>&goal=<endgoal> 402|
403|
404|ask is the immediate question: it should be specific, self-contained, and written in natural language.
405|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
406|
407|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
408|
409|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
410|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Digital Employee Supervisor
4|
5|### Overview
6|
7|A Digital Employee Supervisor represents the human point-of-contact a Digital Employee can route decisions or blockers to.
8|
9|In Digital Employee Core, a supervisor is modeled on the identity:
10|
11|* DigitalEmployeeIdentity.supervisor
12|* DigitalEmployeeSupervisor(name=..., email=...)
13|
14|### Minimal Setup
15|
16|#### 1) Create a supervisor
17|
18|{% code lineNumbers="true" %}
19|
20|python 21|import os 22| 23|from digital_employee_core import DigitalEmployeeSupervisor 24| 25|supervisor = DigitalEmployeeSupervisor( 26| name=os.getenv("SUPERVISOR_NAME", ""), 27| email=os.getenv("SUPERVISOR_EMAIL", ""), 28|) 29|
30|
31|{% endcode %}
32|
33|#### 2) Attach the supervisor to the identity
34|
35|{% code lineNumbers="true" %}
36|
37|python 38|from digital_employee_core import DigitalEmployeeIdentity, DigitalEmployeeJob 39| 40|job = DigitalEmployeeJob( 41| title="Operations Assistant", 42| description="A digital employee that can escalate to a supervisor when blocked", 43| instruction=( 44| "You help with operational tasks. " 45| "If a tool fails or you encounter a critical blocker, follow the escalation protocol." 46| ), 47|) 48| 49|identity = DigitalEmployeeIdentity( 50| name="Ops Assistant - Example", 51| email="ops.assistant@example.com", 52| job=job, 53| supervisor=supervisor, 54|) 55|
56|
57|{% endcode %}
58|
59|### Where It Is Used
60|
61|* Escalation Configuration
62|
63|
64|---
65|
66|# Agent Instructions
67|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
68|
69|## Querying This Documentation
70|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
71|
72|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
73|
74| 75|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/digital-employee-supervisor.md?ask=<question>&goal=<endgoal> 76|
77|
78|ask is the immediate question: it should be specific, self-contained, and written in natural language.
79|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
80|
81|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
82|
83|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
84|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Escalation Configuration
4|
5|### Overview
6|
7|Escalation allows a Digital Employee to route a task to a human supervisor (see Digital Employee Supervisor) when it is blocked (e.g., tool failures, missing permissions, critical ambiguity). Escalation is opt-in and must be explicitly enabled.
8|
9|In this repository, see examples/escalation_example.py for a working end-to-end example.
10|
11|### Key Concepts
12|
13|#### Supervisor
14|
15|Escalation is designed to reach a supervisor configured on the Digital Employee identity:
16|
17|* DigitalEmployeeIdentity.supervisor
18|* DigitalEmployeeSupervisor(name=..., email=...)
19|
20|If escalation is enabled and a supervisor exists, the escalation protocol is included in the generated prompt.
21|
22|#### Escalation Channels
23|
24|Escalation is delivered via one or more channels (e.g., email).
25|
26|Channels can introduce additional runtime dependencies (MCP connectors/tools). This matters at deployment time.
27|
28|In this guide, we will use the GoogleMailMCPEscalationChannel as an example.
29|
30|### Example
31|
32|#### Step 1: Import dependencies
33|
34|{% code lineNumbers="true" %}
35|
36|python 37|import os 38| 39|from digital_employee_core import ( 40| DigitalEmployee, 41| DigitalEmployeeConfiguration, 42| DigitalEmployeeIdentity, 43| DigitalEmployeeJob, 44| DigitalEmployeeSupervisor, 45|) 46|from digital_employee_core.connectors.mcps import google_docs_mcp 47|from digital_employee_core.escalation.channels.google_mail_mcp_channel import ( 48| GoogleMailMCPEscalationChannel, 49|) 50|
51|
52|{% endcode %}
53|
54|#### Step 2: Define the job with escalation instructions
55|
56|{% code lineNumbers="true" %}
57|
58|python 59|job = DigitalEmployeeJob( 60| title="Operations Assistant", 61| description="A digital employee that can escalate to a supervisor when blocked", 62| instruction=( 63| "You help with operational tasks. " 64| "If a tool fails or you encounter a critical blocker, follow the escalation protocol." 65| ), 66|) 67|
68|
69|{% endcode %}
70|
71|{% hint style="warning" %}
72|Important: Use the term "escalation protocol" in the Digital Employee instructions whenever we need to refer to or trigger the escalation flow.
73|{% endhint %}
74|
75|#### Step 3: Configure the supervisor
76|
77|{% code lineNumbers="true" %}
78|
79|python 80|supervisor = DigitalEmployeeSupervisor( 81| name=os.getenv("SUPERVISOR_NAME", ""), 82| email=os.getenv("SUPERVISOR_EMAIL", ""), 83|) 84|
85|
86|{% endcode %}
87|
88|{% hint style="info" %}
89|Note: The following environment variables are required to enable escalation:
90|
91|* SUPERVISOR_NAME
92|* SUPERVISOR_EMAIL
93| {% endhint %}
94|
95|#### Step 4: Create the identity
96|
97|{% code lineNumbers="true" %}
98|
99|python 100|identity = DigitalEmployeeIdentity( 101| name="Ops Assistant - Example", 102| email="ops.assistant@example.com", 103| job=job, 104| supervisor=supervisor, 105|) 106|
107|
108|{% endcode %}
109|
110|#### Step 5: Set up configurations
111|
112|{% code lineNumbers="true" %}
113|
114|python 115|configurations = [ 116| DigitalEmployeeConfiguration(key="GOOGLE_MAIL_MCP_URL", value=os.getenv("GOOGLE_MAIL_MCP_URL", "")), 117| DigitalEmployeeConfiguration(key="GOOGLE_DOCS_MCP_URL", value=os.getenv("GOOGLE_DOCS_MCP_URL", "")), 118| DigitalEmployeeConfiguration(key="GOOGLE_MCP_X_API_KEY", value=os.getenv("GOOGLE_MCP_X_API_KEY", "")), 119|] 120|
121|
122|{% endcode %}
123|
124|{% hint style="info" %}
125|Note: The following environment variables are required to enable the Google Mail MCP escalation channel:
126|
127|* GOOGLE_MAIL_MCP_URL
128|* GOOGLE_MCP_X_API_KEY
129|
130|These values are typically passed into DigitalEmployeeConfiguration so they become part of the deployed agent configuration. Adjust the environment variables based on your escalation channel.
131|{% endhint %}
132|
133|#### Step 6: Initialize the Digital Employee
134|
135|{% code lineNumbers="true" %}
136|
137|python 138|digital_employee = DigitalEmployee( 139| identity=identity, 140| mcps=[google_docs_mcp], 141| configurations=configurations, 142|) 143|
144|
145|{% endcode %}
146|
147|#### Step 7: Enable escalation and add channels
148|
149|{% code lineNumbers="true" %}
150|
151|python 152|digital_employee.enable_escalation() 153|digital_employee.add_escalation_channel(GoogleMailMCPEscalationChannel()) 154|
155|
156|{% endcode %}
157|
158|#### Step 8: Deploy and run
159|
160|{% code lineNumbers="true" %}
161|
162|python 163|digital_employee.deploy() 164|result = digital_employee.run(message="Read google docs with document_id='123413'") 165|
166|
167|{% endcode %}
168|
169|#### Deployment Order Matters
170|
171|* Enable escalation and add channels before calling digital_employee.deploy().
172|* The deploy step will bundle any required MCP connectors/tools introduced by escalation channels.
173|
174|### Custom Escalation Channels
175|
176|Escalation channels are extensible. To create your own channel, implement EscalationChannel (see digital_employee_core/escalation/base_escalation_channel.py) and register it via digital_employee.add_escalation_channel(...).
177|
178|#### 1) Implement the channel
179|
180|Create a new class that extends EscalationChannel that defines:
181|
182|* get_required_mcps(): MCP connectors the channel needs at runtime.
183|* get_required_tools(): Additional tools (if any). Return [] if not needed.
184|* get_prompt_header(): Short title shown in the escalation protocol.
185|* get_prompt_body(supervisor, ...): Concrete instructions describing how the agent should escalate using your tools/MCP.
186|
187|Minimal skeleton:
188|
189|{% code lineNumbers="true" %}
190|
191|python 192|from typing import Any 193| 194|from glaip_sdk import MCP, Tool 195| 196|from digital_employee_core.escalation.base_escalation_channel import EscalationChannel 197|from digital_employee_core.identity.identity import DigitalEmployeeSupervisor 198| 199| 200|class CustomEscalationChannel(EscalationChannel): 201| def get_required_mcps(self) -> list[MCP]: 202| return [] 203| 204| def get_required_tools(self) -> list[Tool]: 205| return [] 206| 207| def get_prompt_header(self, **kwargs: Any) -> str: 208| return "Custom Escalation" 209| 210| def get_prompt_body(self, supervisor: DigitalEmployeeSupervisor, **kwargs: Any) -> str: 211| return ( 212| f"When blocked, notify {supervisor.name} ({supervisor.email}) using <YOUR_TOOL>. " 213| "Include: timestamp, failed action, what you tried, and what you need from the supervisor." 214| ) 215|
216|
217|{% endcode %}
218|
219|For a reference implementation, see digital_employee_core/escalation/channels/google_mail_mcp_channel.py.
220|
221|#### 2) Register the channel before deploy
222|
223|python 224|digital_employee.enable_escalation() 225|digital_employee.add_escalation_channel(CustomEscalationChannel()) 226|digital_employee.deploy() 227|
228|
229|#### Notes
230|
231|* One instance per channel type: EscalationChannel equality is based on class type, so adding the same channel type multiple times is treated as a duplicate.
232|* Tool restrictions: If your channel uses MCP tools, ensure those tools are allowed (if you are using whitelisted MCP tools, see MCP Allowed Tools Configuration).
233|
234|### Best Practices
235|
236|* Define clear escalation triggers in DigitalEmployeeJob.instruction (e.g., tool failures, permission issues, urgent blockers).
237|* Configure least-privilege tools for MCPs where possible (see MCP Allowed Tools Configuration).
238|* Validate supervisor identity (non-empty name/email) during development to avoid “silent” non-actionable escalations.
239|* Test with prompt preview (build_prompt()) before deploying.
240|
241|
242|---
243|
244|# Agent Instructions
245|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
246|
247|## Querying This Documentation
248|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
249|
250|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
251|
252| 253|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/escalation-configuration.md?ask=<question>&goal=<endgoal> 254|
255|
256|ask is the immediate question: it should be specific, self-contained, and written in natural language.
257|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
258|
259|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
260|
261|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
262|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Extend
4|
5|Some digital employee core components can be extended, for example, identity, connectors (tools & MCPs), and the digital employee object itself.
6|
7|## Extend Use Cases
8|
9|### Identity
10|
11|Digital employee core can be extended using this code example:
12|
13|{% code lineNumbers="true" %}
14|
15|python 16|from digital_employee_core import DigitalEmployeeIdentity 17| 18|class ExtendedDigitalEmployeeIdentity(DigitalEmployeeIdentity): 19| """Extended Digital Employee Identity with additional attributes.""" 20| 21| employee_id: str 22|
23|
24|{% endcode %}
25|
26|### Connectors
27|
28|#### Tools
29|
30|We can add our own tools in addition to those already provided by GL Connectors by extending the BaseTool.
31|
32|Here is the example:
33|
34|{% code lineNumbers="true" %}
35|
36|python 37|import calendar 38|from datetime import datetime, timedelta 39|from typing import Any 40| 41|from gllm_plugin.tools import tool_plugin 42|from langchain_core.runnables import RunnableConfig 43|from langchain_core.tools import BaseTool 44|from pydantic import BaseModel, Field 45| 46| 47|class InterviewDateInput(BaseModel): 48| """Input schema for interview date tool.""" 49| 50| reference_date: str = Field( 51| default=None, 52| description="Reference date in ISO format (YYYY-MM-DD, e.g., 2024-12-16). If not provided, uses current date.", 53| ) 54| 55| 56|class GenerateInterviewDateConfig(BaseModel): 57| """Configuration schema for interview date generation.""" 58| 59| days_to_add: int = Field( 60| default=7, 61| description="Number of days to add to the reference date before finding the next available weekday", 62| ) 63| excluded_weekdays: list[int] = Field( 64| default=[calendar.SATURDAY, calendar.SUNDAY], 65| description=( 66| "List of weekday numbers to exclude (0=Monday, 1=Tuesday, " 67| "..., 6=Sunday). Default excludes Saturday and Sunday." 68| ), 69| ) 70| 71| 72|@tool_plugin(version="1.0.0") 73|class ConfigurableGenerateInterviewDateTool(BaseTool): 74| """Generate an interview date with configurable days offset and excluded weekdays.""" 75| 76| name: str = "configurable_generate_interview_date" 77| description: str = ( 78| "Generate interview date: adds configurable days to reference date, " 79| "returns next available weekday excluding configured weekdays." 80| ) 81| args_schema: type[BaseModel] = InterviewDateInput 82| tool_config_schema: type[BaseModel] = GenerateInterviewDateConfig 83| 84| def _run( 85| self, 86| reference_date: str | None = None, 87| config: RunnableConfig = None, 88| **_kwargs: Any, 89| ) -> str: 90| """Generate interview date with configuration. 91| 92| Adds a configurable number of days to the reference date and returns 93| the next available weekday, excluding configured weekdays. 94| 95| Args: 96| reference_date (str | None, optional): Reference date in ISO format 97| (YYYY-MM-DD, e.g., 2024-12-16). If not provided, uses current 98| date. Defaults to None. 99| config (RunnableConfig, optional): Runnable configuration containing 100| tool settings. Defaults to None. 101| **_kwargs (Any): Additional keyword arguments (ignored). 102| 103| Returns: 104| str: Interview date in ISO format (YYYY-MM-DD) or error message if 105| date format is invalid. 106| """ 107| try: 108| # Get tool config 109| tool_config = self.get_tool_config(config) 110| except Exception as e: 111| return f"Error: Failed to retrieve tool configuration. " f"Details: {e}" 112| 113| try: 114| # Parse reference date 115| base_date = ( 116| datetime.strptime(reference_date, "%Y-%m-%d").date() if reference_date else datetime.now().date() 117| ) 118| except ValueError as e: 119| return f"Error: Invalid date format. Please use YYYY-MM-DD " f"(e.g., 2024-12-16). Details: {e}" 120| 121| # Add configured days to base date 122| target_date = base_date + timedelta(days=tool_config.days_to_add) 123| 124| # Find next available weekday (not in excluded list) 125| max_iterations = 7 # Prevent infinite loop 126| iterations = 0 127| while target_date.weekday() in tool_config.excluded_weekdays and iterations < max_iterations: 128| target_date += timedelta(days=1) 129| iterations += 1 130| 131| if iterations >= max_iterations: 132| return ( 133| f"Error: Could not find available weekday after " 134| f"{max_iterations} days. All weekdays may be excluded." 135| ) 136| 137| return f"Interview date: {target_date.isoformat()}" 138|
139|
140|{% endcode %}
141|
142|To configure the tools, we need to create a config_templates/tools_configs.yaml file. Here is an example of tools_configs.yaml :
143|
144|yaml 145|configurable_generate_interview_date: 146| days_to_add: ${INTERVIEW_DAYS_TO_ADD} 147| excluded_weekdays: ${INTERVIEW_EXCLUDED_WEEKDAYS} 148|
149|
150|To define default value for configs, create a config_templates/defaults.yaml file. Here is an example of defaults.yaml :
151|
152|yaml 153|INTERVIEW_DAYS_TO_ADD: 7 154|INTERVIEW_EXCLUDED_WEEKDAYS: 5,6 155|
156|
157|#### MCPs
158|
159|Here is how we can create our own MCP:
160|
161|{% code lineNumbers="true" %}
162|
163|python 164|from glaip_sdk.mcps import MCP 165| 166|new_google_calendar_mcp = MCP( 167| name="new_google_calendar_mcp", 168| description="MCP for Google Calendar Operation for DE", 169| transport="http", 170| config={"url": "https://default.com/google_calendar/mcp"}, 171|) 172|
173|
174|{% endcode %}
175|
176|We need to provide the config_templates/mcp_configs.yaml file to be able to use the MCP. Here is an example of mcp_configs.yaml:
177|
178|yaml 179|new_google_calendar_mcp: 180| config: 181| url: ${NEW_GOOGLE_CALENDAR_MCP_URL} 182| authentication: 183| type: api-key 184| key: X-API-Key 185| value: ${NEW_GOOGLE_MCP_X_API_KEY} 186|
187|
188|### Digital Employee
189|
190|{% code lineNumbers="true" %}
191|
192|python 193|from digital_employee_core import ( 194| DEFAULT_MODEL_NAME, 195| ConfigTemplateLoader, 196|) 197|from gllm_core.utils import LoggerManager 198|from typing import Any 199| 200|logger = LoggerManager().get_logger(__name__) 201| 202|class ExtendedDigitalEmployee(DigitalEmployee): 203| """Extended Digital Employee with extended tool and MCP configurations. 204| 205| This subclass demonstrates how to extend the base DigitalEmployee 206| with additional specific configurations. 207| """ 208| 209| def __init__( 210| self, 211| identity: DigitalEmployeeIdentity, 212| tools: list[Any] | None = None, 213| sub_agents: list[Any] | None = None, 214| mcps: list[Any] | None = None, 215| configurations: list[DigitalEmployeeConfiguration] | None = None, 216| model: str | None = DEFAULT_MODEL_NAME, 217| ): 218| """Initialize the Specific Digital Employee. 219| 220| Args: 221| identity (DigitalEmployeeIdentity): The Digital Employee's identity. 222| tools (list[Any] | None, optional): List of tools the Digital Employee can use. Defaults to None. 223| sub_agents (list[Any] | None, optional): List of sub-agents (for future use). Defaults to None. 224| mcps (list[Any] | None, optional): List of MCPs the Digital Employee can use. Defaults to None. 225| configurations (list[DigitalEmployeeConfiguration] | None, optional): List of configuration objects. 226| Defaults to None. 227| model (str | None, optional): Model identifier to use for the 228| agent. Defaults to DEFAULT_MODEL_NAME. 229| """ 230| super().__init__( 231| identity=identity, 232| tools=tools, 233| sub_agents=sub_agents, 234| mcps=mcps, 235| configurations=configurations, 236| model=model, 237| ) 238| 239| # Create a separate config loader for specific templates 240| # You can place tool_configs.yaml and mcp_configs.yaml in a separate directory 241| additional_config_dir = Path(__file__).parent / "config_templates" 242| additional_config_loader = ConfigTemplateLoader(template_dir=additional_config_dir) 243| # Simply add the additional config loader - the base class handles the rest! 244| # build_prompt() will use all loaders automatically 245| # build_tool_config() and build_mcp_config() will merge configs from all loaders 246| self.add_config_loader(additional_config_loader) 247| 248|
249|
250|{% endcode %}
251|
252|{% hint style="info" %}
253|To see more examples, please check the Digital Employee Example.
254|{% endhint %}
255|
256|
257|---
258|
259|# Agent Instructions
260|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
261|
262|## Querying This Documentation
263|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
264|
265|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
266|
267| 268|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/extend.md?ask=<question>&goal=<endgoal> 269|
270|
271|ask is the immediate question: it should be specific, self-contained, and written in natural language.
272|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
273|
274|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
275|
276|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
277|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Instantiation
4|
5|## Prerequisites
6|
7|For these examples, you will need to:
8|
9|* Complete the Extend section
10|
11|## Instantiate Digital Employee
12|
13|### Import the Package
14|
15|{% code lineNumbers="true" %}
16|
17|python 18|from extended_digital_employee.connectors.mcps import new_google_calendar_mcp # new MCP 19|from extended_digital_employee.connectors.tools import ConfigurableGenerateInterviewDateTool # new Tool 20|from extended_digital_employee.digital_employee import ExtendedDigitalEmployee 21|from extended_digital_employee.identity import ExtendedDigitalEmployeeIdentity 22|from glaip_sdk import Tool 23| 24|from digital_employee_core import ( 25| DigitalEmployeeConfiguration, 26| DigitalEmployeeJob, 27|) 28|
29|
30|{% endcode %}
31|
32|### Initialize the Extended Digital Employee
33|
34|{% code lineNumbers="true" %}
35|
36|python 37|job = DigitalEmployeeJob( 38| title="Recruitment Coordinator", 39| description="Coordinates technical interviews and manages candidate scheduling", 40| instruction="When candidates pass the initial screening, schedule their technical interview. Always confirm the date clearly and provide a warm and professional response.", 41|) 42|extended_identity = ExtendedDigitalEmployeeIdentity( 43| name="Alex Morgan", email="alex.morgan@example.com", job=job, employee_id="EMP-123" 44|) 45|configurations = [ 46| DigitalEmployeeConfiguration(key="INTERVIEW_DAYS_TO_ADD", value="10"), 47| DigitalEmployeeConfiguration(key="INTERVIEW_EXCLUDED_WEEKDAYS", value="5,6"), 48| DigitalEmployeeConfiguration(key="NEW_GOOGLE_CALENDAR_MCP_URL", value="https://api.bosa.id/google_calendar/mcp"), 49| DigitalEmployeeConfiguration(key="NEW_GOOGLE_MCP_X_API_KEY", value="[gl-connectors-x-api-key]"), 50|] 51| 52|# Initialize extended digital employee 53|extended_digital_employee = ExtendedDigitalEmployee( 54| identity=extended_identity, 55| mcps=[new_google_calendar_mcp], 56| tools=[Tool.from_langchain(ConfigurableGenerateInterviewDateTool)], 57| configurations=configurations, 58|) 59|extended_digital_employee.deploy() 60|
61|
62|{% endcode %}
63|
64|{% hint style="info" %}
65|In this example, we use new MCPs (mcps=[new_google_calendar_mcp]) and tools (tools=[Tool.from_langchain(ConfigurableGenerateInterviewDateTool)]) that are specifically created for the extended digital employee.
66|{% endhint %}
67|
68|{% hint style="info" %}
69|We also use INTERVIEW_DAYS_TO_ADD, INTERVIEW_EXCLUDED_WEEKDAYS, NEW_GOOGLE_CALENDAR_MCP_URL and NEW_GOOGLE_MCP_X_API_KEY configurations, which are defined in the config template of the new tool and MCP.
70|{% endhint %}
71|
72|### Run the Extended Digital Employee
73|
74|{% code lineNumbers="true" %}
75|
76|python 77|# Run the extended digital employee using a prompt 78|result = extended_digital_employee.run( 79| message="I need to schedule a technical interview for a software engineer candidate who just passed the screening round. Can you generate an interview date?", 80|) 81|
82|
83|{% endcode %}
84|
85|{% hint style="info" %}
86|Before running the sample code, replace the following placeholders:
87|
88|1. Replace [gl-connectors-x-api-key] with x-api-key from GL Connectors. See below for one way to do it.
89|2. (Optional) Replace NEW_GOOGLE_CALENDAR_MCP_URL if you are using a different GL Connectors server instance.
90|
91|
🔑 Get GL Connectors x-api-key
94| 95|1. Open https://api.bosa.id/console, then sign in. 96|2. In the Credentials section, expand the x-api-key panel and click Copy combined value button. Paste this value to replace[gl-connectors-x-api-key] .
97|
98|ask query parameter, and the optional goal query parameter:
126|
127| 128|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/instantiation.md?ask=<question>&goal=<endgoal> 129|
130|
131|ask is the immediate question: it should be specific, self-contained, and written in natural language.
132|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
133|
134|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
135|
136|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
137|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# MCP Allowed Tools Configuration
4|
5|### Overview
6|
7|The allowed_tools configuration enables you to restrict which tools a Digital Employee can access from Model Context Protocol (MCP) servers. This is useful for security, cost control, and ensuring your Digital Employee only uses approved functionality.
8|
9|### Key Concepts
10|
11|#### What are Allowed Tools?
12|
13|Allowed tools are a whitelist of specific tool names that an MCP connector is permitted to use. When configured, the Digital Employee will only have access to the specified tools from that MCP, even if the MCP server provides additional capabilities.
14|
15|#### Benefits
16|
17|* Security: Limit access to sensitive operations
18|* Cost Control: Prevent usage of expensive API calls
19|* Compliance: Ensure only approved tools are used
20|* Clarity: Make it explicit which capabilities are available
21|
22|### Configuration
23|
24|#### Basic Setup
25|
26|Allowed tools are configured using DigitalEmployeeConfiguration objects with specific key patterns:
27|
28|{% code lineNumbers="true" %}
29|
30|python 31|DigitalEmployeeConfiguration( 32| key="<MCP_NAME>_ALLOWED_TOOLS", 33| value="tool1,tool2,tool3" 34|) 35|
36|
37|{% endcode %}
38|
39|#### Key Pattern
40|
41|The configuration key follows this pattern:
42|
43|* <MCP_NAME>_ALLOWED_TOOLS
44|
45|Where <MCP_NAME> matches the MCP connector's configuration prefix (e.g., GOOGLE_MAIL_MCP, GOOGLE_CALENDAR_MCP).
46|
47|#### Value Format
48|
49|The value is a comma-separated string of tool names that will be automatically converted to a list:
50|
51|{% code lineNumbers="true" %}
52|
53|python 54|# This string... 55|value="google_mail_send_email,google_mail_get_email_details,google_mail_list_emails" 56| 57|# ...is automatically converted to this list: 58|['google_mail_send_email', 'google_mail_get_email_details', 'google_mail_list_emails'] 59|
60|
61|{% endcode %}
62|
63|### Complete Example
64|
65|#### Step 1: Import Required Components
66|
67|{% code lineNumbers="true" %}
68|
69|python 70|import os 71|from dotenv import load_dotenv 72|from digital_employee_core import ( 73| DigitalEmployee, 74| DigitalEmployeeConfiguration, 75| DigitalEmployeeIdentity, 76| DigitalEmployeeJob, 77|) 78|from digital_employee_core.connectors.mcps import google_calendar_mcp, google_mail_mcp 79| 80|load_dotenv() 81|
82|
83|{% endcode %}
84|
85|#### Step 2: Create Identity
86|
87|{% code lineNumbers="true" %}
88|
89|python 90|job = DigitalEmployeeJob( 91| title="Email Assistant", 92| description="Helps manage emails and calendars", 93| instruction="You are an email assistant that helps users manage their emails efficiently.", 94|) 95| 96|identity = DigitalEmployeeIdentity( 97| name="Email Bot", 98| email="emailbot@example.com", 99| job=job, 100|) 101|
102|
103|{% endcode %}
104|
105|#### Step 3: Configure MCP URLs and Allowed Tools
106|
107|{% code lineNumbers="true" %}
108|
109|python 110|configurations = [ 111| # MCP URLs 112| DigitalEmployeeConfiguration( 113| key="GOOGLE_MAIL_MCP_URL", 114| value=os.getenv("GOOGLE_MAIL_MCP_URL", ""), 115| ), 116| DigitalEmployeeConfiguration( 117| key="GOOGLE_MCP_X_API_KEY", 118| value=os.getenv("GOOGLE_MCP_X_API_KEY", ""), 119| ), 120| # Allowed tools - comma-separated strings 121| DigitalEmployeeConfiguration( 122| key="GOOGLE_MAIL_MCP_ALLOWED_TOOLS", 123| value="google_mail_send_email,google_mail_get_email_details,google_mail_list_emails", 124| ), 125|] 126|
127|
128|{% endcode %}
129|
130|{% hint style="info" %}
131|Note: Replace the example values above with your actual configuration:
132|
133|* GOOGLE_MAIL_MCP_URL: Your MCP server endpoints
134|* GOOGLE_MCP_X_API_KEY: Your actual API key (consider using environment variables)
135| {% endhint %}
136|
137|#### Step 4: Create and Deploy the Digital Employee
138|
139|{% code lineNumbers="true" %}
140|
141|python 142|# Create digital employee with MCPs 143|mcps = [google_mail_mcp] 144| 145|digital_employee = DigitalEmployee( 146| identity=identity, 147| mcps=mcps, 148| configurations=configurations, 149|) 150| 151|# Deploy applies the configurations 152|digital_employee.deploy() 153|
154|
155|{% endcode %}
156|
157|#### Step 5 (Optional): Verify Configuration
158|
159|If you want to verify the configuration was applied correctly, you can check the deployed MCP config:
160|
161|{% code lineNumbers="true" %}
162|
163|python 164|mail_mcp_config = digital_employee.agent.mcp_configs.get(google_mail_mcp.name).get("config") 165|print(f"Mail MCP allowed_tools in config: {mail_mcp_config.get('allowed_tools')}") 166|# Output: ['google_mail_send_email', 'google_mail_read_email', 'google_mail_search'] 167|
168|
169|{% endcode %}
170|
171|#### Step 6: Run the Digital Employee
172|
173|Now you can run the Digital Employee and it will only have access to the allowed tools:
174|
175|{% code lineNumbers="true" %}
176|
177|python 178|# The Digital Employee will only be able to use the allowed tools 179|response = digital_employee.run( 180| message="Read and summarize my latest email" 181|) 182|print(response) 183|
184|
185|{% endcode %}
186|
187|In this example:
188|
189|* The Digital Employee can use google_mail_get_email_details, google_mail_list_emails to find the latest email.
190|* It cannot use tools like google_mail_delete_email because they weren't in the allowed list.
191|
192|For the list of tools that are available via GLConnector, please refer to https://api.bosa.id/docs.
193|
194|### Common Use Cases
195|
196|#### Restricting Email Operations
197|
198|Only allow reading and searching emails, but not sending:
199|
200|{% code lineNumbers="true" %}
201|
202|python 203|DigitalEmployeeConfiguration( 204| key="GOOGLE_MAIL_MCP_ALLOWED_TOOLS", 205| value="google_mail_get_email_details,google_mail_list_emails", 206|) 207|
208|
209|{% endcode %}
210|
211|#### Read-Only Calendar Access
212|
213|Only allow listing events, but not creating or modifying:
214|
215|{% code lineNumbers="true" %}
216|
217|python 218|DigitalEmployeeConfiguration( 219| key="GOOGLE_CALENDAR_MCP_ALLOWED_TOOLS", 220| value="google_calendar_events_list", 221|) 222|
223|
224|{% endcode %}
225|
226|#### Multiple Tool Permissions
227|
228|Grant access to multiple related tools:
229|
230|{% code lineNumbers="true" %}
231|
232|python 233|DigitalEmployeeConfiguration( 234| key="GOOGLE_MAIL_MCP_ALLOWED_TOOLS", 235| value="google_mail_send_email,google_mail_get_email_details,google_mail_delete_email", 236|) 237|
238|
239|{% endcode %}
240|
241|### Best Practices
242|
243|#### 1. Principle of Least Privilege
244|
245|Only grant access to tools that are absolutely necessary for the Digital Employee's job:
246|
247|{% code lineNumbers="true" %}
248|
249|python 250|# Good: Specific tools for specific job 251|DigitalEmployeeConfiguration( 252| key="GOOGLE_MAIL_MCP_ALLOWED_TOOLS", 253| value="google_mail_get_email_details,google_mail_list_emails", 254|) 255| 256|# Avoid: Granting all available tools without restriction 257|
258|
259|{% endcode %}
260|
261|#### 2. Document Your Tool Choices
262|
263|Add comments explaining why specific tools are allowed:
264|
265|{% code lineNumbers="true" %}
266|
267|python 268|# Allow email reading and searching for customer support queries 269|DigitalEmployeeConfiguration( 270| key="GOOGLE_MAIL_MCP_ALLOWED_TOOLS", 271| value="google_mail_get_email_details,google_mail_list_emails", 272|) 273|
274|
275|{% endcode %}
276|
277|#### 3. Use Environment Variables for Sensitive Data
278|
279|Store API keys and URLs in environment variables:
280|
281|{% code lineNumbers="true" %}
282|
283|python 284|import os 285| 286|DigitalEmployeeConfiguration( 287| key="GOOGLE_MCP_X_API_KEY", 288| value=os.getenv("GOOGLE_MCP_API_KEY"), 289|) 290|
291|
292|{% endcode %}
293|
294|### Troubleshooting
295|
296|#### Tools Not Being Restricted
297|
298|Problem: All tools are still accessible despite configuration.
299|
300|Solution: Ensure the configuration key matches the MCP's expected pattern:
301|
302|* Check the MCP connector's documentation for the correct prefix
303|* Verify the key format: <MCP_PREFIX>_ALLOWED_TOOLS
304|
305|#### Tool Names Incorrect
306|
307|Problem: Tools are not recognized.
308|
309|Solution: Verify the exact tool names from the MCP server documentation. Tool names are case-sensitive and must match exactly.
310|
311|
312|---
313|
314|# Agent Instructions
315|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
316|
317|## Querying This Documentation
318|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
319|
320|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
321|
322| 323|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/mcp-allowed-tools-configuration.md?ask=<question>&goal=<endgoal> 324|
325|
326|ask is the immediate question: it should be specific, self-contained, and written in natural language.
327|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
328|
329|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
330|
331|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
332|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Memory Configuration
4|
5|### Overview
6|
7|Digital Employee Core supports user-scoped memory so an agent can remember facts and preferences across multiple calls. Memory is opt-in:
8|
9|* Enable a memory provider in agent_config.
10|* Pass a stable memory_user_id on every run() / arun() call.
11|
12|### Key Concepts
13|
14|#### What is memory_user_id?
15|
16|memory_user_id is the user identifier used to scope memory.
17|
18|* Same memory_user_id + same agent => the agent can recall previously stored facts.
19|* Different memory_user_id + same agent => isolated memory (no cross-user leakage).
20|* Same memory_user_id + different agent => isolated memory (each agent maintains its own memory scope).
21|
22|#### Memory Provider
23|
24|Memory is enabled by setting AgentConfigKeys.MEMORY to a provider (e.g. MemoryProvider.MEM0). Currently we utilize GL SDK Memory via AIP Memory.
25|
26|### Minimal Example
27|
28|#### 1) Create a memory-enabled Digital Employee
29|
30|{% code lineNumbers="true" %}
31|
32|python 33|from digital_employee_core import DigitalEmployee, DigitalEmployeeIdentity, DigitalEmployeeJob 34|from digital_employee_core.configuration.agent_configuration import AgentConfigKeys, MemoryProvider 35| 36|job = DigitalEmployeeJob( 37| title="Memory-Enabled Assistant", 38| description="A digital employee that can remember user-specific facts across calls", 39| instruction=( 40| "When the user tells you a personal preference or fact, remember it for future conversation." 41| ), 42|) 43| 44|identity = DigitalEmployeeIdentity(name="memory_assistant", email="memory.assistant@example.com", job=job) 45| 46|digital_employee = DigitalEmployee( 47| identity=identity, 48| agent_config={AgentConfigKeys.MEMORY: MemoryProvider.MEM0}, 49|) 50|
51|
52|{% endcode %}
53|
54|#### 2) Deploy, then call with a stable memory_user_id
55|
56|{% code lineNumbers="true" %}
57|
58|python 59|digital_employee.deploy() 60| 61|memory_user_id = "user-123" 62| 63|# Store a preference 64|result_1 = digital_employee.run( 65| message="My favorite color is beige. Please remember this for next time.", 66| memory_user_id=memory_user_id, 67|) 68| 69|# Recall later 70|result_2 = digital_employee.run( 71| message="What is my favorite color?", 72| memory_user_id=memory_user_id, 73|) 74|
75|
76|{% endcode %}
77|
78|{% hint style="info" %}
79|Note: After storing a memory, there may be a slight delay from the provider before it becomes available for retrieval, so you may need to wait a moment (manually or via Python's sleep() ) before querying for recently stored information.
80|{% endhint %}
81|
82|#### 3) Different user, different memory
83|
84|{% code lineNumbers="true" %}
85|
86|python 87|other_user_id = "user-456" 88| 89|result_3 = digital_employee.run( 90| message="What is my favorite color?", 91| memory_user_id=other_user_id, 92|) 93|
94|
95|{% endcode %}
96|
97|### Local Usage
98|
99|To use MemoryProvider.MEM0 locally, you must provide MEM0_API_KEY in your environment variables.
100|
101|bash 102|export MEM0_API_KEY="<your_api_key>" 103|
104|
105|If you load environment variables via .env, ensure your entrypoint calls load_dotenv().
106|
107|Then, run the digital employee with local=True to run it locally.
108|
109|{% code lineNumbers="true" %}
110|
111|python 112|result_3 = digital_employee.run( 113| message="What is my favorite color?", 114| memory_user_id=memory_user_id, 115| local=True, 116|) 117|
118|
119|{% endcode %}
120|
121|### Notes / Best Practices
122|
123|* memory_user_id is required when memory is enabled; the agent cannot store or recall memories without it.
124|* Use stable identifiers (e.g., internal user ID) and do not use PII (like emails) unless necessary.
125|* Write clear instructions in the job prompt indicating what to remember (preferences, long-term facts) vs what not to (secrets).
126|
127|
128|---
129|
130|# Agent Instructions
131|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
132|
133|## Querying This Documentation
134|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
135|
136|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
137|
138| 139|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/memory-configuration.md?ask=<question>&goal=<endgoal> 140|
141|
142|ask is the immediate question: it should be specific, self-contained, and written in natural language.
143|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
144|
145|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
146|
147|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
148|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Programmatic Tool Calling (PTC) Configuration
4|
5|### Overview
6|
7|Programmatic Tool Calling (PTC) lets a Digital Employee orchestrate tools through Python code executed in a sandboxed environment, rather than making individual API round-trips for each tool call.
8|
9|When PTC is enabled, the agent gets access to an execute_ptc_code tool. The agent can write Python code that calls its registered tools, processes intermediate results, and returns only the final output to its context window.
10|
11|> Note: PTC is currently only supported for local runs (run(..., local=True)).
12|
13|For the canonical guide, see the AIP PTC Guide.
14|
15|### Key Concepts
16|
17|#### Why use PTC?
18|
19|| Benefit | Description |
20|| ------------------------------ | --------------------------------------------------------------------------------------------------- |
21|| Context Window Protection | Intermediate results stay in the sandbox — only the final output is returned to the agent's context |
22|| Parallel Execution | The agent can call multiple tools concurrently within a single code block |
23|| Reduced Inference Overhead | One model pass writes the code; execution replaces multiple model-tool-model round-trips |
24|
25|#### How PTC works
26|
27|1. The agent receives a task requiring multiple tool calls.
28|2. The agent writes a Python script and invokes execute_ptc_code.
29|3. The script runs inside an E2B sandbox with all registered tools available.
30|4. Only the final printed output is returned to the agent's context.
31|
32|#### The PTC configuration object
33|
34|PTC is configured via the PTC class from glaip_sdk.ptc:
35|
36|{% code lineNumbers="true" %}
37|
38|python 39|from glaip_sdk.ptc import PTC 40| 41|ptc_config = PTC( 42| enabled=True, 43| sandbox_timeout=120.0, 44|) 45|
46|
47|{% endcode %}
48|
49|The instance is passed to DigitalEmployee via the ptc parameter. Tools registered on the Digital Employee are automatically made available in the sandbox — you do not need to configure them separately.
50|
51|#### Prerequisites
52|
53|* E2B_API_KEY must be set (get one at https://e2b.dev)
54|* OPENAI_API_KEY (or another supported model key) must be set
55|* glaip-sdk installed with [local] extras
56|
57|### Complete Example
58|
59|The file examples/ptc/ptc_example.py demonstrates the PTC workflow.
60|
61|#### Step 1: Import required components
62|
63|{% code lineNumbers="true" %}
64|
65|python 66|from dotenv import load_dotenv 67|from glaip_sdk import Tool 68|from glaip_sdk.ptc import PTC 69| 70|from digital_employee_core import ( 71| DigitalEmployee, 72| DigitalEmployeeIdentity, 73| DigitalEmployeeJob, 74|) 75|from digital_employee_core.connectors.tools.utility_tools import time_tool 76|from examples.ptc.tools.calculator_tool import CalculatorTool 77| 78|load_dotenv() 79|
80|
81|{% endcode %}
82|
83|#### Step 2: Create the Digital Employee identity
84|
85|{% code lineNumbers="true" %}
86|
87|python 88|job = DigitalEmployeeJob( 89| title="PTC Assistant", 90| description="A helpful assistant with Programmatic Tool Calling enabled", 91| instruction=( 92| "You are a helpful assistant with Programmatic Tool Calling (PTC) enabled. " 93| "When you need to orchestrate multiple tool calls or process data programmatically, " 94| "you can write Python code using the execute_ptc_code tool.\n\n" 95| "Use PTC when you need to:\n" 96| "1. Call multiple tools and process their results\n" 97| "2. Perform calculations or data transformations\n" 98| "3. Keep intermediate results out of context\n\n" 99| "Provide clear and helpful responses." 100| ), 101|) 102| 103|identity = DigitalEmployeeIdentity( 104| name="PTC Assistant", 105| email="ptc.assistant@example.com", 106| job=job, 107|) 108|
109|
110|{% endcode %}
111|
112|#### Step 3: Configure PTC and attach tools
113|
114|{% code lineNumbers="true" %}
115|
116|python 117|ptc_config = PTC( 118| enabled=True, 119| sandbox_timeout=120.0, # Maximum execution time in seconds, optional 120|) 121| 122|calculator_tool = Tool.from_langchain(CalculatorTool) 123| 124|digital_employee = DigitalEmployee( 125| identity=identity, 126| tools=[time_tool, calculator_tool], 127| ptc=ptc_config, 128|) 129|
130|
131|{% endcode %}
132|
133|All tools passed to DigitalEmployee are automatically available inside the PTC sandbox.
134|
135|#### Step 4: Run locally
136|
137|{% code lineNumbers="true" %}
138|
139|python 140|message = "Calculate what time it will be in 3.5 hours. Directly show me the time without any intermediate output." 141| 142|result = digital_employee.run(message=message, local=True) 143|print(result) 144|
145|
146|{% endcode %}
147|
148|> Note: Do not call deploy() for local PTC runs. Use run(..., local=True) directly.
149|
150|#### What this example shows
151|
152|* PTC is activated with PTC(enabled=True).
153|* Tools are registered on the Digital Employee — no separate sandbox configuration needed.
154|* The agent can orchestrate time_tool and calculator_tool in a single code block.
155|* Intermediate results (e.g., the raw time value) stay in the sandbox; only the final answer is returned to the model context.
156|
157|### Configuration Reference
158|
159|| Parameter | Type | Default | Description |
160|| ---------------------- | ------------------- | --------------------- | ---------------------------------------------------------------------------------------------------------------------------------------- |
161|| enabled | bool | False | Activates PTC. Must be True to use PTC. |
162|| sandbox_timeout | float | 120.0 | Maximum execution time (seconds) for a sandbox run. |
163|| default_tool_timeout | float | 60.0 | Per-tool call timeout (seconds) inside the sandbox. |
164|| sandbox_template | str \| None | "aip-agents-ptc-v1" | E2B sandbox template identifier. |
165|| prompt | dict \| None | None | Prompt configuration for the execute_ptc_code tool description. Accepts {"mode": "...", "include_example": bool}. |
166|| ptc_packages | list[str] \| None | None | Additional Python packages to install in the sandbox. None uses smart package selection; an explicit list is additive to the defaults. |
167|
168|> Not supported: custom_tools — tools are always auto-derived from the DigitalEmployee.tools list.
169|
170|### Common Use Cases
171|
172|#### Parallel tool calls
173|
174|The agent can call multiple tools concurrently in one code block, without extra round-trips:
175|
176|{% code lineNumbers="true" %}
177|
178|python 179|# Agent-generated code running inside the sandbox 180|result_a = time_tool() 181|result_b = calculator_tool(expression="365 * 24") 182|print(f"Time: {result_a}, Hours in a year: {result_b}") 183|
184|
185|{% endcode %}
186|
187|#### Installing extra sandbox packages
188|
189|{% code lineNumbers="true" %}
190|
191|python 192|ptc_config = PTC( 193| enabled=True, 194| ptc_packages=["pandas==2.2.0", "numpy"], 195|) 196|
197|
198|{% endcode %}
199|
200|#### Extending the sandbox timeout for long-running tasks
201|
202|{% code lineNumbers="true" %}
203|
204|python 205|ptc_config = PTC( 206| enabled=True, 207| sandbox_timeout=300.0, # 5 minutes 208| default_tool_timeout=90.0, 209|) 210|
211|
212|{% endcode %}
213|
214|### Best Practices
215|
216|#### 1. Mention execute_ptc_code in the job instruction
217|
218|Explicitly tell the agent when and how to use PTC in the instruction field:
219|
220|{% code lineNumbers="true" %}
221|
222|python 223|instruction=( 224| "When orchestrating multiple tool calls, use execute_ptc_code to run them " 225| "in a single Python block and return only the final result." 226|) 227|
228|
229|{% endcode %}
230|
231|Without this hint, the agent may fall back to individual tool calls.
232|
233|#### 2. Keep sandbox_timeout proportional to task complexity
234|
235|A short timeout is fine for quick calculations; increase it for tasks that involve many sequential tool calls or heavy data processing.
236|
237|#### 3. Use ptc_packages only when needed
238|
239|If ptc_packages is None (the default), aip-agents selects packages automatically based on which tools are registered. Only set it explicitly when you need a package that is not auto-detected.
240|
241|#### 4. Do not call deploy() for local PTC runs
242|
243|PTC is a local-only feature. Call run(..., local=True) directly — skip deploy().
244|
245|#### 5. Use a single DigitalEmployee instance per session
246|
247|Constructing a new DigitalEmployee on every request means a cold sandbox start for each run. Reuse the instance across calls to amortize sandbox startup time.
248|
249|### Troubleshooting
250|
251|#### E2B_API_KEY not set
252|
253|Problem: The sandbox fails to start with an authentication error.
254|
255|Solution: Obtain an API key from https://e2b.dev and add it to your .env file:
256|
257|{% code lineNumbers="true" %}
258|
259|bash 260|E2B_API_KEY=your_key_here 261|
262|
263|{% endcode %}
264|
265|#### glaip-sdk[local] not installed
266|
267|Problem: Import errors or missing sandbox dependencies.
268|
269|Solution: Install the local extras:
270|
271|{% code lineNumbers="true" %}
272|
273|bash 274|poetry add "glaip-sdk[local]" 275|
276|
277|{% endcode %}
278|
279|#### Agent not using execute_ptc_code
280|
281|Problem: The agent calls tools individually instead of using PTC.
282|
283|Solution: Add explicit PTC guidance to the job instruction. The model needs to be told when PTC is the preferred approach (see Best Practice 1 above).
284|
285|#### Sandbox timeout exceeded
286|
287|Problem: Execution is cut off mid-run with a timeout error.
288|
289|Solution: Increase sandbox_timeout and, if tools are slow, default_tool_timeout:
290|
291|{% code lineNumbers="true" %}
292|
293|python 294|ptc_config = PTC( 295| enabled=True, 296| sandbox_timeout=300.0, 297| default_tool_timeout=90.0, 298|) 299|
300|
301|{% endcode %}
302|
303|#### Tool not available inside the sandbox
304|
305|Problem: The agent's PTC code raises an import or NameError for a registered tool.
306|
307|Solution: Verify the tool is passed to DigitalEmployee(tools=[...]). Tools are auto-derived from this list — no additional sandbox configuration is required.
308|
309|
310|---
311|
312|# Agent Instructions
313|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
314|
315|## Querying This Documentation
316|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
317|
318|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
319|
320| 321|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/programmatic-tool-calling-ptc-configuration.md?ask=<question>&goal=<endgoal> 322|
323|
324|ask is the immediate question: it should be specific, self-contained, and written in natural language.
325|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
326|
327|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
328|
329|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
330|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Recommended Project Structure
4|
5|Here is the recommended project structure when building a digital employee.
6|
7| 8|digital-employee/ 9|├── agents/ 10|│ ├── agent_1.py 11|│ ├── agent_2.py 12|│ └── agent_3.py 13|├── config_templates/ 14|│ ├── defaults.yaml 15|│ ├── mcp_configs.yaml 16|│ └── tool_configs.yaml 17|├── connectors/ 18|│ ├── mcps/ 19|│ │ ├── mcp_1.py 20|│ │ ├── mcp_2.py 21|│ │ └── mcp_3.py 22|│ └── tools/ 23|│ ├── tool_1.py 24|│ ├── tool_2.py 25|│ └── tool_3.py 26|├── identity/ 27|│ └── identity.py 28|└── main.py 29|
30|
31|## Agents
32|
33|The agents folder contains a list of agents used by the digital employee to perform tasks. This is optional and depends on the digital employee's use cases.
34|
35|## Config Templates
36|
37|Config templates contain mcp_configs.yaml and tool_configs.yaml, which are configurations used when running the MCPs and tools. You can also add defaults.yaml to store your default values for each MCP and tool configuration.
38|
39|## Connectors
40|
41|The connectors folder contains MCPs and tools used by that specific digital employee. Please see the Extend section to learn how to create new MCPs and tools.
42|
43|## Identity
44|
45|Identity contains the extended identity of the digital employee if needed. This folder is optional if all the identity from the digital employee core is sufficient.
46|
47|
48|---
49|
50|# Agent Instructions
51|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
52|
53|## Querying This Documentation
54|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
55|
56|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
57|
58| 59|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/recommended-project-structure.md?ask=<question>&goal=<endgoal> 60|
61|
62|ask is the immediate question: it should be specific, self-contained, and written in natural language.
63|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
64|
65|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
66|
67|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
68|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Run History
4|
5|### Overview
6|
7|We use GLAIP Audit Trails to review a Digital Employee agent’s run history for auditing.
8|
9|### Retrieve Run History (via AIP CLI)
10|
11|#### 1) Start the AIP CLI
12|
13|Run:
14|
15|bash 16|aip 17|
18|
19|#### 2) Open the agents list
20|
21|In the CLI:
22|
23| 24|/agents 25|
26|
27|Find the agent by Digital Employee name, then select the matching agent entry.
28|
29|
36|/runs 37|
38|
39|
40|
41|#### 5) Review runs in the TUI
42|
43|The console will display a TUI list of runs (run history) for the selected agent.
44|
45|
46|
47|
48|---
49|
50|# Agent Instructions
51|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
52|
53|## Querying This Documentation
54|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
55|
56|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
57|
58| 59|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/run-history.md?ask=<question>&goal=<endgoal> 60|
61|
62|ask is the immediate question: it should be specific, self-contained, and written in natural language.
63|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
64|
65|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
66|
67|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
68|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Scheduler Configuration
4|
5|### Overview
6|
7|The Scheduler feature enables you to configure Digital Employees to execute tasks automatically at specified times using cron-based scheduling. This is ideal for recurring tasks, automated reports, periodic checks, and time-based workflows.
8|
9|Currently we utilize AIP Scheduled Run feature to manage these workflows.
10|
11|{% hint style="info" %}
12|Note: Schedules are only available for remote runs (deployed Digital Employees). Local runs do not support scheduled execution.
13|{% endhint %}
14|
15|### Key Concepts
16|
17|#### What is a Scheduler?
18|
19|A scheduler allows your Digital Employee to run tasks automatically based on time-based triggers. Each schedule consists of:
20|
21|* Schedule Configuration: Defines when the task should run (using cron syntax)
22|* Input: The message or instruction to execute when the schedule triggers
23|
24|#### Benefits
25|
26|* Automation: Execute tasks without manual intervention
27|* Consistency: Ensure tasks run at predictable times
28|* Efficiency: Free up human resources for higher-value work
29|* Reliability: Never miss scheduled tasks or reminders
30|
31|### Configuration
32|
33|#### Basic Components
34|
35|Schedules are configured using two main classes:
36|
37|1. ScheduleConfig: Defines the timing using cron-like parameters
38|2. ScheduleItemConfig: Combines the schedule with the input to execute
39|
40|#### Schedule Configuration Parameters
41|
42|The ScheduleConfig class uses cron-style parameters:
43|
44|{% code lineNumbers="true" %}
45|
46|python 47|from glaip_sdk.models.schedule import ScheduleConfig 48| 49|schedule = ScheduleConfig( 50| minute="0", # 0-59 or "*" for every minute 51| hour="8", # 0-23 or "*" for every hour 52| day_of_month="*", # 1-31 or "*" for every day 53| month="*", # 1-12 or "*" for every month 54| day_of_week="0-4", # 0-6 (0=Monday, 6=Sunday) or "*" for every day 55|) 56|
57|
58|{% endcode %}
59|
60|Reference to GLAIP Python SDK.
61|
62|#### Cron Syntax Guide
63|
64|| Field | Values | Special Characters | Examples |
65|| -------------- | ------ | ------------------------------------ | ---------------------- |
66|| minute | 0-59 | * (every), - (range), , (list) | 0, */15, 0,30 |
67|| hour | 0-23 | * (every), - (range), , (list) | 8, 9-17, 8,12,18 |
68|| day_of_month | 1-31 | * (every), - (range), , (list) | 1, 1-15, 1,15 |
69|| month | 1-12 | * (every), - (range), , (list) | *, 1-6, 1,7 |
70|| day_of_week | 0-6 | * (every), - (range), , (list) | 0-4, 0,6, * |
71|
72|Note: Day of week starts with Monday (0) and ends with Sunday (6).
73|
74|### Complete Example
75|
76|#### Step 1: Import Required Components
77|
78|{% code lineNumbers="true" %}
79|
80|python 81|from glaip_sdk.models.schedule import ScheduleConfig 82|from digital_employee_core import ( 83| DigitalEmployee, 84| DigitalEmployeeIdentity, 85| DigitalEmployeeJob, 86|) 87|from digital_employee_core.schedule import ScheduleItemConfig 88|
89|
90|{% endcode %}
91|
92|#### Step 2: Create Digital Employee Identity
93|
94|{% code lineNumbers="true" %}
95|
96|python 97|# Define the job 98|job = DigitalEmployeeJob( 99| title="Friendly Greeter", 100| description="A friendly digital employee that greets people at different times", 101| instruction="You are a friendly greeter. Always respond with a cheerful greeting!", 102|) 103| 104|# Create identity 105|identity = DigitalEmployeeIdentity( 106| name="Sunny", 107| email="sunny@example.com", 108| job=job, 109|) 110|
111|
112|{% endcode %}
113|
114|#### Step 3: Configure Schedule
115|
116|{% code lineNumbers="true" %}
117|
118|python 119|# Morning greeting - weekdays at 8 AM 120|morning_schedule = ScheduleConfig( 121| minute="0", 122| hour="8", 123| day_of_month="*", 124| month="*", 125| day_of_week="0-4", # Monday to Friday 126|) 127|
128|
129|{% endcode %}
130|
131|#### Step 4: Create Schedule Item
132|
133|{% code lineNumbers="true" %}
134|
135|python 136|# Create schedule item with input 137|morning_schedule_item = ScheduleItemConfig( 138| schedule_config=morning_schedule, 139| input="Morning greeting" 140|) 141|
142|
143|{% endcode %}
144|
145|#### Step 5: Create and Deploy Digital Employee
146|
147|{% code lineNumbers="true" %}
148|
149|python 150|# Create Digital Employee with schedule 151|digital_employee = DigitalEmployee( 152| identity=identity, 153| schedules=[morning_schedule_item], 154|) 155| 156|# Deploy the Digital Employee (this also creates the schedule) 157|digital_employee.deploy() 158|
159|
160|{% endcode %}
161|
162|#### Step 6: Verify Schedules (Optional)
163|
164|{% code lineNumbers="true" %}
165|
166|python 167|# Get schedules from the Digital Employee 168|schedules = digital_employee.get_schedule() 169|print(f"Number of schedules: {len(schedules)}") 170| 171|# Display schedule details 172|for i, schedule_item in enumerate(schedules, 1): 173| print(f"Schedule {i}:") 174| print(f" Input: {schedule_item.input}") 175| print(f" Cron: {schedule_item.schedule_config.to_cron_string()}") 176|
177|
178|{% endcode %}
179|
180|#### Step 7: Monitor Schedule Runs (Optional)
181|
182|{% code lineNumbers="true" %}
183|
184|python 185|from glaip_sdk import Client 186| 187|# Get the deployed agent 188|client = Client() 189|agent = client.get_agent_by_id(digital_employee.agent.id) 190| 191|# List all schedules 192|agent_schedules = agent.schedule.list() 193|print(f"Found {len(agent_schedules)} schedule(s)") 194| 195|# Check runs for a specific schedule 196|for schedule in agent_schedules: 197| runs = agent.schedule.list_runs(schedule.id) 198| print(f"\nSchedule: {schedule.input}") 199| print(f"Runs: {len(runs)}") 200| 201| # Display recent runs 202| for run in runs[-5:]: # Last 5 runs 203| print(f" - Run ID: {run.id}") 204| print(f" Status: {run.status}") 205| if run.status == "success": 206| result = run.get_result() 207| print(f" Result: {result}") 208|
209|
210|{% endcode %}
211|
212|### Common Use Cases
213|
214|#### Daily Morning Report
215|
216|Send a daily report every weekday at 9 AM:
217|
218|{% code lineNumbers="true" %}
219|
220|python 221|daily_report_schedule = ScheduleConfig( 222| minute="0", 223| hour="9", 224| day_of_month="*", 225| month="*", 226| day_of_week="0-4", # Monday to Friday 227|) 228| 229|report_item = ScheduleItemConfig( 230| schedule_config=daily_report_schedule, 231| input="Generate and send the daily morning report" 232|) 233|
234|
235|{% endcode %}
236|
237|#### Hourly Data Check
238|
239|Check data every hour during business hours:
240|
241|{% code lineNumbers="true" %}
242|
243|python 244|hourly_check_schedule = ScheduleConfig( 245| minute="0", 246| hour="9-17", # 9 AM to 5 PM 247| day_of_month="*", 248| month="*", 249| day_of_week="0-4", # Monday to Friday 250|) 251| 252|check_item = ScheduleItemConfig( 253| schedule_config=hourly_check_schedule, 254| input="Check system status and alert if issues found" 255|) 256|
257|
258|{% endcode %}
259|
260|#### Weekly Summary
261|
262|Generate a weekly summary every Friday at 5 PM:
263|
264|{% code lineNumbers="true" %}
265|
266|python 267|weekly_summary_schedule = ScheduleConfig( 268| minute="0", 269| hour="17", 270| day_of_month="*", 271| month="*", 272| day_of_week="4", # Friday 273|) 274| 275|summary_item = ScheduleItemConfig( 276| schedule_config=weekly_summary_schedule, 277| input="Generate weekly summary report" 278|) 279|
280|
281|{% endcode %}
282|
283|#### Monthly Reminder
284|
285|Send a reminder on the first day of each month:
286|
287|{% code lineNumbers="true" %}
288|
289|python 290|monthly_reminder_schedule = ScheduleConfig( 291| minute="0", 292| hour="9", 293| day_of_month="1", # First day of month 294| month="*", 295| day_of_week="*", 296|) 297| 298|reminder_item = ScheduleItemConfig( 299| schedule_config=monthly_reminder_schedule, 300| input="Send monthly reminder to review pending tasks" 301|) 302|
303|
304|{% endcode %}
305|
306|#### Every 15 Minutes
307|
308|Run a task every 15 minutes:
309|
310|{% code lineNumbers="true" %}
311|
312|python 313|frequent_check_schedule = ScheduleConfig( 314| minute="*/15", # Every 15 minutes 315| hour="*", 316| day_of_month="*", 317| month="*", 318| day_of_week="*", 319|) 320| 321|frequent_item = ScheduleItemConfig( 322| schedule_config=frequent_check_schedule, 323| input="Check for urgent notifications" 324|) 325|
326|
327|{% endcode %}
328|
329|### Best Practices
330|
331|#### 1. Use Descriptive Inputs
332|
333|Provide clear, actionable instructions in the schedule input:
334|
335|{% code lineNumbers="true" %}
336|
337|python 338|# Good: Specific and actionable 339|ScheduleItemConfig( 340| schedule_config=schedule, 341| input="Review unread emails from VIP customers and respond to urgent ones" 342|) 343| 344|# Avoid: Vague or unclear 345|ScheduleItemConfig( 346| schedule_config=schedule, 347| input="Check emails" 348|) 349|
350|
351|{% endcode %}
352|
353|#### 2. Consider Time Zones
354|
355|Be aware of the time zone used by your deployment:
356|
357|{% code lineNumbers="true" %}
358|
359|python 360|# Document the time zone in comments 361|# Schedule runs at 9 AM UTC 362|morning_schedule = ScheduleConfig( 363| minute="0", 364| hour="9", 365| day_of_month="*", 366| month="*", 367| day_of_week="0-4", 368|) 369|
370|
371|{% endcode %}
372|
373|#### 3. Avoid Overlapping Schedules
374|
375|Ensure schedules don't conflict or create excessive load:
376|
377|{% code lineNumbers="true" %}
378|
379|python 380|# Good: Staggered schedules 381|schedule_1 = ScheduleConfig(minute="0", hour="9", ...) # 9:00 AM 382|schedule_2 = ScheduleConfig(minute="30", hour="9", ...) # 9:30 AM 383| 384|# Avoid: Multiple schedules at the same time 385|schedule_1 = ScheduleConfig(minute="0", hour="9", ...) # 9:00 AM 386|schedule_2 = ScheduleConfig(minute="0", hour="9", ...) # 9:00 AM (conflict) 387|
388|
389|{% endcode %}
390|
391|#### 4. Test Schedule Timing
392|
393|Verify your cron expressions produce the expected schedule:
394|
395|{% code lineNumbers="true" %}
396|
397|python 398|schedule = ScheduleConfig( 399| minute="0", 400| hour="9", 401| day_of_month="*", 402| month="*", 403| day_of_week="0-4", 404|) 405| 406|# Check the cron string 407|cron_string = schedule.to_cron_string() 408|print(f"Cron expression: {cron_string}") 409|# Output: "0 9 * * 0-4" 410|
411|
412|{% endcode %}
413|
414|#### 5. Monitor Schedule Execution
415|
416|Regularly check schedule runs to ensure they're executing as expected:
417|
418|{% code lineNumbers="true" %}
419|
420|python 421|# Check recent failed runs 422|failed_runs = agent.schedule.list_runs(schedule_id, status="failed") 423| 424|if failed_runs: 425| print(f"Warning: {len(failed_runs)} failed runs detected") 426|
427|
428|{% endcode %}
429|
430|#### 6. Use Multiple Schedules Strategically
431|
432|Group related schedules in a single Digital Employee:
433|
434|{% code lineNumbers="true" %}
435|
436|python 437|# Good: Related schedules for a single purpose 438|digital_employee = DigitalEmployee( 439| identity=identity, 440| schedules=[ 441| morning_report_item, # Daily morning report 442| afternoon_check_item, # Afternoon status check 443| evening_summary_item, # Evening summary 444| ], 445|) 446| 447|# Avoid: Unrelated schedules in one employee 448|# Consider creating separate Digital Employees for different purposes 449|
450|
451|{% endcode %}
452|
453|### Advanced Configuration
454|
455|#### Dynamic Schedule Inputs
456|
457|Use detailed inputs to provide context:
458|
459|{% code lineNumbers="true" %}
460|
461|python 462|schedule_item = ScheduleItemConfig( 463| schedule_config=schedule, 464| input=""" 465| Generate a daily report including: 466| 1. Summary of completed tasks 467| 2. Pending items requiring attention 468| 3. Any system alerts or issues 469| 4. Send the report to the team channel 470| """ 471|) 472|
473|
474|{% endcode %}
475|
476|#### Combining with Other Features
477|
478|Schedules work seamlessly with other Digital Employee features:
479|
480|{% code lineNumbers="true" %}
481|
482|python 483|from digital_employee_core.connectors.mcps import google_mail_mcp 484| 485|# Digital Employee with schedules and MCP tools 486|digital_employee = DigitalEmployee( 487| identity=identity, 488| schedules=[email_check_schedule_item], 489| mcps=[google_mail_mcp], # Can use email tools in scheduled tasks 490| configurations=configurations, 491|) 492|
493|
494|{% endcode %}
495|
496|### Troubleshooting
497|
498|#### Schedule Not Triggering
499|
500|Problem: Schedule was created but tasks are not running.
501|
502|Solution:
503|
504|* Verify the Digital Employee is deployed: digital_employee.deploy()
505|* Check the cron expression is valid: schedule.to_cron_string()
506|* Confirm the schedule exists: agent.schedule.list()
507|* Check for failed runs: agent.schedule.list_runs(schedule_id)
508|
509|#### Incorrect Timing
510|
511|Problem: Schedule runs at unexpected times.
512|
513|Solution:
514|
515|* Verify time zone settings
516|* Double-check cron parameters (especially day_of_week where 0=Monday)
517|* Test with to_cron_string() to see the actual cron expression
518|* Review the next_run_time field in the schedule object
519|
520|#### Schedule Runs Failed
521|
522|Problem: Schedule triggers but execution fails.
523|
524|Solution:
525|
526|* Check run details: run.get_result() for error messages
527|* Verify the input instruction is clear and actionable
528|* Ensure required tools/MCPs are configured and accessible
529|* Review Digital Employee logs for detailed error information
530|
531|#### Multiple Schedules Conflict
532|
533|Problem: Multiple schedules running simultaneously cause issues.
534|
535|Solution:
536|
537|* Stagger schedule times by adjusting minute/hour values
538|* Consider if schedules can be combined into a single task
539|* Monitor system resources and adjust frequency if needed
540|
541|### API Reference
542|
543|#### ScheduleConfig
544|
545|{% code lineNumbers="true" %}
546|
547|python 548|from glaip_sdk.models.schedule import ScheduleConfig 549| 550|schedule = ScheduleConfig( 551| minute: str, # "0-59", "*", "*/15", "0,30" 552| hour: str, # "0-23", "*", "9-17", "8,12,18" 553| day_of_month: str, # "1-31", "*", "1,15" 554| month: str, # "1-12", "*", "1-6" 555| day_of_week: str, # "0-6", "*", "0-4", "0,6" 556|) 557| 558|# Convert to cron string 559|cron_string = schedule.to_cron_string() 560|
561|
562|{% endcode %}
563|
564|#### ScheduleItemConfig
565|
566|{% code lineNumbers="true" %}
567|
568|python 569|from digital_employee_core.schedule import ScheduleItemConfig 570| 571|schedule_item = ScheduleItemConfig( 572| schedule_config: ScheduleConfig, # The timing configuration 573| input: str, # The instruction to execute 574|) 575|
576|
577|{% endcode %}
578|
579|#### DigitalEmployee with Schedules
580|
581|{% code lineNumbers="true" %}
582|
583|python 584|from digital_employee_core import DigitalEmployee 585| 586|digital_employee = DigitalEmployee( 587| identity: DigitalEmployeeIdentity, 588| schedules: list[ScheduleItemConfig] = [], # Optional list of schedules 589| mcps: list = [], # Optional MCP connectors 590| configurations: list = [], # Optional configurations 591|) 592| 593|# Deploy to activate schedules 594|digital_employee.deploy() 595| 596|# Get configured schedules 597|schedules = digital_employee.get_schedule() 598|
599|
600|{% endcode %}
601|
602|#### Managing Schedules via Agent
603|
604|{% code lineNumbers="true" %}
605|
606|python 607|from glaip_sdk import Client 608| 609|client = Client() 610|agent = client.get_agent_by_id(agent_id) 611| 612|# List all schedules 613|schedules = agent.schedule.list() 614| 615|# Get runs for a schedule 616|runs = agent.schedule.list_runs(schedule_id) 617| 618|# Access run details 619|for run in runs: 620| print(f"Status: {run.status}") 621| print(f"ID: {run.id}") 622| if run.status == "success": 623| result = run.get_result() 624|
625|
626|{% endcode %}
627|
628|### Examples in Repository
629|
630|For complete working examples, see:
631|
632|* https://github.com/GDP-ADMIN/CATAPA-SDK/blob/main/python/digital-employee-core/examples/scheduler_usage.py - Comprehensive scheduler demonstration
633|
634|
635|---
636|
637|# Agent Instructions
638|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
639|
640|## Querying This Documentation
641|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
642|
643|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
644|
645| 646|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/scheduler-configuration.md?ask=<question>&goal=<endgoal> 647|
648|
649|ask is the immediate question: it should be specific, self-contained, and written in natural language.
650|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
651|
652|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
653|
654|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
655|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Skills Configuration
4|
5|### Overview
6|
7|Skills let a Digital Employee follow a reusable operating guide for a specific task or domain.
8|
9|This repository follows the AIP skills configuration model. For the canonical guide, see the AIP Skills Guide.
10|
11|There are two supported usage patterns demonstrated by the examples:
12|
13|* Remote GitHub-based skills
14|* Local path-based skills
15|
16|Use remote skills when you want a deployable Digital Employee that references skills stored in GitHub. Use local skills when you want to develop and test skill behavior locally without deploying.
17|
18|### Key Concepts
19|
20|#### What is a skill?
21|
22|A skill is an instruction package that gives the Digital Employee a narrowly scoped behavior. In practice, the examples in this repository show two ways to provide a skill:
23|
24|* A GitHub URL pointing to a skill directory
25|* A local filesystem path loaded with Skill.from_path(...)
26|
27|#### How skills are attached
28|
29|You attach skills when constructing DigitalEmployee:
30|
31|{% code lineNumbers="true" %}
32|
33|python 34|from digital_employee_core import DigitalEmployee 35| 36|DigitalEmployee( 37| identity=identity, 38| skills=skills, 39|) 40|
41|
42|{% endcode %}
43|
44|You can also add skills later using digital_employee.add_skills(...) before deployment or execution.
45|
46|#### Skill source types
47|
48|Remote GitHub skills
49|
50|In examples/skills_example.py, the skill source is a GitHub URL:
51|
52|{% code lineNumbers="true" %}
53|
54|python 55|canvas_design_skill = "https://github.com/anthropics/skills/tree/main/skills/canvas-design" 56| 57|return DigitalEmployee(identity=identity, skills=[canvas_design_skill]) 58|
59|
60|{% endcode %}
61|
62|This is the right choice when:
63|
64|* You want skills stored in a versioned repository
65|* You want the Digital Employee to be deployed and run remotely
66|* You want to share the same skill source across environments
67|
68|Local path-based skills
69|
70|In examples/local_skills/local_skills_example.py, the skill source is loaded from disk:
71|
72|{% code lineNumbers="true" %}
73|
74|python 75|from glaip_sdk.skills import Skill 76| 77|local_skill = Skill.from_path(str(skill_path)) 78|return DigitalEmployee(identity=identity, skills=[local_skill]) 79|
80|
81|{% endcode %}
82|
83|This is the right choice when:
84|
85|* You are developing a skill locally
86|* You want deterministic testing during development
87|* You do not want to deploy the skill first
88|
89|### Remote GitHub Skills Example
90|
91|The file examples/skills_example.py demonstrates the remote workflow.
92|
93|#### 1) Create the Digital Employee identity
94|
95|{% code lineNumbers="true" %}
96|
97|python 98|from digital_employee_core import DigitalEmployee, DigitalEmployeeIdentity, DigitalEmployeeJob 99| 100|job = DigitalEmployeeJob( 101| title="Skills-enabled Assistant - Example", 102| description="A digital employee using remote skills from GitHub", 103| instruction="You are a helpful assistant.", 104|) 105| 106|identity = DigitalEmployeeIdentity( 107| name="skills_github_assistant", 108| email="skills.github@example.com", 109| job=job, 110|) 111|
112|
113|{% endcode %}
114|
115|#### 2) Attach a GitHub-hosted skill
116|
117|{% code lineNumbers="true" %}
118|
119|python 120|canvas_design_skill = "https://github.com/anthropics/skills/tree/main/skills/canvas-design" 121| 122|github_de = DigitalEmployee(identity=identity, skills=[canvas_design_skill]) 123|
124|
125|{% endcode %}
126|
127|#### 3) Deploy before running
128|
129|{% code lineNumbers="true" %}
130|
131|python 132|github_de.deploy() 133| 134|message = ( 135| "Create a canvas design with a blue background and the text 'Hello, World!' " 136| "in white, do not ask any follow-up questions, just create the design." 137|) 138|result = github_de.run(message=message) 139|
140|
141|{% endcode %}
142|
143|#### What this example shows
144|
145|* The Digital Employee accepts a GitHub URL as a skill source
146|* Remote skills are used in the normal deployed workflow
147|* deploy() is called before run(...)
148|
149|#### Private GitHub repositories
150|
151|The example notes that public repositories work directly. For private repositories, you must provide one of these environment variables:
152|
153|* GITHUB_PERSONAL_ACCESS_TOKEN
154|* GITHUB_TOKEN
155|* GH_TOKEN
156|
157|### Local Skills Example
158|
159|The directory examples/local_skills/ demonstrates the local workflow.
160|
161|#### 1) Define the skill directory
162|
163|Make a file in ./.agents/skills/haiku-standup/SKILL.md with the following content:
164|
165|{% code lineNumbers="true" %}
166|
167|md 168|--- 169|name: haiku-standup 170|description: > 171| Format daily standup updates into the team's custom standup template. 172| Use when the user mentions "standup", "daily update", "what I did yesterday", 173| or asks to format their work status. 174|--- 175| 176|# Haiku Standup Formatter 177| 178|Format every standup update using the exact structure below. Never skip sections. 179|Never reorder them. Always generate the haiku — do not ask the user to write one. 180| 181|## Output Template 182| 183|```text 184|📋 STANDUP — [TODAY'S DATE in YYYY-MM-DD] 185| 186|🎋 [A haiku summarizing the update — must be valid 5-7-5 syllable structure] 187| 188|🔥 BLOCKER [PRIORITY-CODE] 189|[Blocker description — what it is and what it's blocking] 190|(If no blockers, write: "☁️ ALL CLEAR — No blockers today") 191| 192|✅ DONE 193|- [Completed item 1] 194|- [Completed item 2] 195| 196|🎯 TODAY 197|- [Planned item 1] 198|- [Planned item 2] 199| 200|Vibe Check: [MOON-RATING] ([N]/5) 201|``` 202|
203|
204|{% endcode %}
205|
206|#### 2) Load the local skill
207|
208|{% code lineNumbers="true" %}
209|
210|python 211|from pathlib import Path 212| 213|skill_path = Path(__file__).parent / ".agents/skills/haiku-standup" 214|
215|
216|{% endcode %}
217|
218|#### 3) Load the local skill
219|
220|{% code lineNumbers="true" %}
221|
222|python 223|from glaip_sdk.skills import Skill 224| 225|local_skill = Skill.from_path(str(skill_path)) 226|
227|
228|{% endcode %}
229|
230|#### 4) Attach the skill to a Digital Employee
231|
232|{% code lineNumbers="true" %}
233|
234|python 235|job = DigitalEmployeeJob( 236| title="Local Skills Assistant", 237| description="A digital employee using local skills for deterministic development", 238| instruction="You are a helpful assistant. Use the attached local skill as your operating guide.", 239|) 240| 241|identity = DigitalEmployeeIdentity( 242| name="skills_local_assistant", 243| email="skills.local@example.com", 244| job=job, 245|) 246| 247|local_de = DigitalEmployee(identity=identity, skills=[local_skill]) 248|
249|
250|{% endcode %}
251|
252|#### 5) Run locally without deployment
253|
254|{% code lineNumbers="true" %}
255|
256|python 257|message = ( 258| "Standup: Yesterday I migrated the user table to the new schema and pair-programmed with Alex on the search " 259| "feature. Today I'm writing tests for the migration. I'm stuck waiting on QA to finish their test plan." 260|) 261|result = local_de.run(message=message, local=True) 262|
263|
264|{% endcode %}
265|
266|#### What this example shows
267|
268|* A local skill is loaded from a filesystem path
269|* The skill folder is expected to exist before execution
270|* Path-based skills are intended for local execution only
271|* Local skills should be run with run(..., local=True)
272|* The example does not call deploy()
273|
274|### When to Use Which Approach
275|
276|#### Use remote GitHub skills when
277|
278|* You want a deployable Digital Employee
279|* Your skill definitions are stored in GitHub
280|* You want centralized version control for skill content
281|* You are building a remotely hosted assistant workflow
282|
283|#### Use local skills when
284|
285|* You are iterating on skill content locally
286|* You want quick testing without deployment
287|* You need local-only experimentation or deterministic development loops
288|* You already have a local skill folder with a valid SKILL.md
289|
290|### Best Practices
291|
292|#### 1. Keep skills narrowly scoped
293|
294|Write each skill for a specific job, such as copywriting or standup formatting, rather than combining many unrelated behaviors into one skill.
295|
296|#### 2. Use deploy() only for remote workflows
297|
298|Follow the examples:
299|
300|* Remote GitHub skill example: call deploy() before run(...)
301|* Local path-based skill example: skip deploy and call run(..., local=True)
302|
303|#### 3. Validate the local path before running
304|
305|The local example checks that the path exists before creating the employee. This is a good pattern when developing local skills.
306|
307|#### 4. Keep SKILL.md explicit
308|
309|A strong skill file should clearly define:
310|
311|* When the skill applies
312|* The expected output structure
313|* Rules and constraints
314|* What the model must infer versus what it must ask about
315|
316|### Troubleshooting
317|
318|#### Local skill is not being found
319|
320|Problem: The local skill does not load.
321|
322|Solution:
323|
324|* Verify the directory path is correct
325|* Verify SKILL.md exists at the root of the skill directory
326|* Verify you are calling Skill.from_path(str(skill_path))
327|
328|#### Local skill does not behave as expected
329|
330|Problem: The output ignores the intended format.
331|
332|Solution:
333|
334|* Make the instructions in SKILL.md more explicit
335|* Add stricter output templates and rules
336|* Ensure the employee instruction does not conflict with the skill behavior
337|
338|#### Remote GitHub skill cannot be accessed
339|
340|Problem: The Digital Employee cannot use the GitHub-based skill.
341|
342|Solution:
343|
344|* Verify the GitHub URL points to the correct skill directory
345|* If the repository is private, set GITHUB_PERSONAL_ACCESS_TOKEN, GITHUB_TOKEN, or GH_TOKEN
346|* Ensure you call deploy() before run(...)
347|
348|
349|---
350|
351|# Agent Instructions
352|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
353|
354|## Querying This Documentation
355|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
356|
357|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
358|
359| 360|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/skills-configuration.md?ask=<question>&goal=<endgoal> 361|
362|
363|ask is the immediate question: it should be specific, self-contained, and written in natural language.
364|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
365|
366|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
367|
368|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
369|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Sub Agents Configuration
4|
5|### Overview
6|
7|A Digital Employee can be composed of a coordinator agent plus one or more sub-agents. Sub-agents are attached as nested agents on the underlying glaip_sdk.Agent instance.
8|
9|In this repository, see examples/configuration_propagation_example.py for a working example (including config propagation).
10|
11|### Key Concepts
12|
13|#### What is a “sub-agent”?
14|
15|A sub-agent is a glaip_sdk.Agent instance attached under the coordinator agent. At deploy-time, DigitalEmployee builds a single root Agent and passes sub-agents into the Agent(..., agents=[...]) field.
16|
17|#### How sub-agents are attached
18|
19|You attach sub-agents by providing them to DigitalEmployee:
20|
21|* DigitalEmployee(sub_agents=[...]) (recommended)
22|* digital_employee.add_sub_agents([...])
23|
24|#### Configuration propagation (tools + MCPs)
25|
26|When the Digital Employee is deployed, it will process sub_agents and automatically propagate tool/MCP configs from the parent to sub-agents when:
27|
28|* The sub-agent uses a tool/MCP.
29|* The sub-agent does not already define a config for that tool/MCP.
30|
31|Notes:
32|
33|* Existing sub-agent configs take precedence over propagated configs.
34|* Propagation is recursive: nested sub-agents (sub-agents of sub-agents) are processed as well.
35|* Currently only glaip_sdk.Agent is supported as a sub-agent type; unsupported types are skipped with a warning.
36|
37|### Minimal Example
38|
39|#### 1) Create a coordinator Digital Employee
40|
41|python 42|from digital_employee_core import DigitalEmployeeIdentity, DigitalEmployeeJob 43| 44|job = DigitalEmployeeJob( 45| title="Coordinator", 46| description="Delegates tasks to specialists", 47| instruction="You are the coordinator. Delegate tasks to your sub-agents.", 48|) 49|identity = DigitalEmployeeIdentity(name="Coordinator", email="coordinator@example.com", job=job) 50|
51|
52|#### 2) Define one or more sub-agents
53|
54|python 55|from glaip_sdk import Agent 56| 57|reminder_agent = Agent( 58| name="ReminderAgent", 59| instruction="You are a reminder specialist.", 60|) 61|
62|
63|#### 3) Attach sub-agents and deploy
64|
65|python 66|from digital_employee_core import DigitalEmployee 67| 68|digital_employee = DigitalEmployee( 69| identity=identity, 70| sub_agents=[reminder_agent], 71|) 72| 73|digital_employee.deploy() 74|
75|
76|### Notes / Best Practices
77|
78|* Add or remove sub-agents before deploy; changes after deploy require re-deploying to update the agent graph.
79|* Prefer stable, unique Agent.name values (removal uses name matching).
80|* If sub-agents use MCPs/tools that require configs, prefer putting shared configs on the parent DigitalEmployee.configurations and rely on propagation; override only when a sub-agent needs different settings.
81|
82|
83|---
84|
85|# Agent Instructions
86|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
87|
88|## Querying This Documentation
89|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
90|
91|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
92|
93| 94|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/sub-agents-configuration.md?ask=<question>&goal=<endgoal> 95|
96|
97|ask is the immediate question: it should be specific, self-contained, and written in natural language.
98|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
99|
100|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
101|
102|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
103|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# User Information in Digital Employee Runs
4|
5|### Overview
6|
7|Digital Employee Core handles user information in two different ways:
8|
9|* User-scoped memory uses memory_user_id to isolate remembered facts per user.
10|* User-authenticated tools use delegated user tokens so external systems can authorize actions as the current user.
11|
12|These two mechanisms are related, but they solve different problems. memory_user_id controls memory isolation. Delegation tokens control what a tool is allowed to do on behalf of the user.
13|
14|For the GL AIP delegation flow, see:
15|
16|* Delegate to Agent for the delegation token object and scope structure.
17|* Validate Delegation Token for how receiving services validate delegation tokens and enforce scopes.
18|
19|For GL Connectors integration setup, see Integration Setup.
20|
21|### Key Concepts
22|
23|#### memory_user_id
24|
25|memory_user_id is the stable user identifier used by the memory provider.
26|
27|* Same memory_user_id + same agent => the agent can recall that user's previous facts.
28|* Different memory_user_id + same agent => memory stays isolated.
29|* It should be a stable internal user ID. Avoid PII such as email addresses unless required.
30|
31|See also: Memory Configuration.
32|
33|#### user_authentication
34|
35|user_authentication is an opt-in flag in a tool config. When it is enabled, GL AIP knows that the dependency needs a delegated user token.
36|
37|Use it for tools that call user-scoped APIs, such as calendars, mail, HR self-service, finance self-service, GL Connectors, or other systems where permissions depend on the current user.
38|
39|#### Delegated tokens
40|
41|Delegated tokens are passed at run time, not written into prompts. GL AIP validates the incoming delegation token, resolves which downstream token is needed for each user-authenticated dependency, then exposes that token to tools through runtime metadata.
42|
43|For example, a GL Connectors-enabled dependency receives gl_connectors_token in RunnableConfig.metadata. For local or manual runs, you can pass the token explicitly to run():
44|
45|python 46|result = digital_employee.run( 47| message="Show my pending requests", 48| gl_connectors_token=os.getenv("GL_CONNECTORS_TOKEN"), 49|) 50|
51|
52|The token name depends on the downstream integration. For GL Connectors, use gl_connectors_token.
53|
54|#### GL AIP handoff
55|
56|Tools should not parse the original GL IAM delegation token object directly. Treat GL AIP as the boundary that:
57|
58|1. Receives the delegation token object described in Delegate to Agent.
59|2. Validates the delegation token as described in Validate Delegation Token.
60|3. Looks up user-authenticated dependencies and attaches the appropriate integration token, such as gl_connectors_token, to tool runtime metadata.
61|
62|In custom tools, read only the integration-specific token from RunnableConfig.metadata and use it to authenticate with the downstream connector.
63|
64|When using GL Connectors, configure the connector integration first. See GL Connectors Integration Setup.
65|
66|### Runtime User Context in RunnableConfig
67|
68|Digital Employee tools can receive user and conversation context through RunnableConfig.metadata. This context can be used by the tools to act on behalf of human (OBOH).
69|
70|The runtime parameter contract is sent by GLChat to AIP from the AIP execution strategy.
71|
72|The main chat-message path populates this context from the Agent message processor. Pipeline-based runs may provide context through the Pipeline service.
73|
74|| Parameter | Location | Purpose |
75|| --------------------- | --------------------------------------------- | ----------------------------------------------- |
76|| gl_connectors_token | RunnableConfig.metadata.gl_connectors_token | Delegated connector token for the current user. |
77|| user_id | RunnableConfig.metadata.user_id | Current user identifier. |
78|| tenant_id | RunnableConfig.metadata.tenant_id | Current tenant context. |
79|| conversation_id | RunnableConfig.metadata.conversation_id | Conversation associated with the run. |
80|| message_id | RunnableConfig.metadata.message_id | User message associated with the run. |
81|| email | RunnableConfig.metadata.email | Current user email or username fallback. |
82|| organization_id | RunnableConfig.metadata.organization_id | Organization context. |
83|| chatbot_id | RunnableConfig.metadata.chatbot_id | Chatbot or assistant identifier. |
84|| agent | RunnableConfig.metadata.agent | Agent-specific metadata, when provided. |
85|
86|Example usage in a custom tool:
87|
88|python 89|def _run(self, config: RunnableConfig = None, **kwargs): 90| metadata = ((config or {}).get("metadata") or {}) 91| 92| gl_connectors_token = metadata.get("gl_connectors_token") 93| user_id = metadata.get("user_id") 94| tenant_id = metadata.get("tenant_id") 95| conversation_id = metadata.get("conversation_id") 96| message_id = metadata.get("message_id") 97| email = metadata.get("email") 98| organization_id = metadata.get("organization_id") 99| chatbot_id = metadata.get("chatbot_id") 100| agent_metadata = metadata.get("agent") 101|
102|
103|Do not store these values in tool configuration or prompts. Treat them as runtime context for the current Digital Employee run.
104|
105|### User-Scoped Memory Example
106|
107|Enable memory in agent_config, then pass memory_user_id on each run() or arun() call.
108|
109|{% code lineNumbers="true" %}
110|
111|python 112|from digital_employee_core import DigitalEmployee, DigitalEmployeeIdentity, DigitalEmployeeJob 113|from digital_employee_core.configuration.agent_configuration import AgentConfigKeys, MemoryProvider 114| 115|job = DigitalEmployeeJob( 116| title="Memory-Enabled Assistant", 117| description="A digital employee that can remember user-specific facts across calls", 118| instruction="When the user tells you a personal preference or fact, remember it for future conversation.", 119|) 120| 121|identity = DigitalEmployeeIdentity(name="memory_assistant", email="memory.assistant@example.com", job=job) 122| 123|digital_employee = DigitalEmployee( 124| identity=identity, 125| agent_config={AgentConfigKeys.MEMORY: MemoryProvider.MEM0}, 126|) 127| 128|digital_employee.deploy() 129| 130|memory_user_id = "user-123" 131| 132|digital_employee.run( 133| message="My preferred report format is a short bullet summary. Please remember this.", 134| memory_user_id=memory_user_id, 135|) 136| 137|digital_employee.run( 138| message="How should you format my reports?", 139| memory_user_id=memory_user_id, 140|) 141|
142|
143|{% endcode %}
144|
145|> Note: If memory is enabled, memory_user_id is required. Digital Employee Core raises an error when run() or arun() is called without it.
146|
147|### User Information in Custom Tools
148|
149|Custom tools should treat delegated user tokens as runtime credentials. Do not store them in static config and do not include them in prompts.
150|
151|Digital Employee Core custom tools follow the LangChain BaseTool pattern:
152|
153|* Define an input schema with Pydantic.
154|* Define a tool config schema with Pydantic.
155|* Set tool_config_schema on the tool.
156|* Read static config with self.get_tool_config(config).
157|* Read delegated user tokens from RunnableConfig.metadata, such as config.get("metadata").get("gl_connectors_token").
158|
159|#### 1) Create a custom tool
160|
161|This example creates a custom user_profile_tool that calls GL Connectors to fetch the current user's profile. The API key is service-level authentication. The gl_connectors_token is delegated user authentication for the current run.
162|
163|Before using this pattern, ensure the target connector integration is configured in GL Connectors. See Integration Setup.
164|
165|{% code lineNumbers="true" %}
166|
167|python 168|import json 169|import requests 170|from typing import Any 171| 172|from gl_connectors_sdk import GLConnectors 173|from langchain_core.runnables import RunnableConfig 174|from langchain_core.tools import BaseTool 175|from pydantic import BaseModel, Field 176| 177|REQUEST_TIMEOUT_SECONDS = 30 178| 179| 180|class UserProfileToolInput(BaseModel): 181| """Input schema for user profile tool.""" 182| 183| include_contact: bool = Field(default=False, description="Whether to include contact fields in the response.") 184| 185| 186|class UserProfileToolConfig(BaseModel): 187| """Configuration schema for user profile tool.""" 188| 189| user_api_base_url: str = Field(description="The base URL for the downstream user profile API.") 190| gl_connectors_api_base_url: str = Field(description="The base URL for the GL Connectors API.") 191| gl_connectors_api_key: str = Field(description="The API key for authenticating with the GL Connectors API.") 192| user_authentication: bool = Field(description="Whether this tool requires delegated user authentication.", default=True) 193| 194| 195|class UserProfileTool(BaseTool): 196| """Tool for reading the current user's profile through GL Connectors.""" 197| 198| name: str = "user_profile_tool" 199| description: str = "Read the current user's profile information." 200| args_schema: type[BaseModel] = UserProfileToolInput 201| tool_config_schema: type[BaseModel] = UserProfileToolConfig 202| 203| def _run(self, include_contact: bool = False, config: RunnableConfig = None, **_kwargs: Any) -> str: 204| """Read the current user's profile.""" 205| tool_config = self.get_tool_config(config) 206| gl_connectors_token = ((config or {}).get("metadata") or {}).get("gl_connectors_token") 207| 208| if not gl_connectors_token: 209| return "Error: gl_connectors_token is required for this user-authenticated tool." 210| 211| try: 212| access_token = self._get_access_token_from_gl_connectors(tool_config, gl_connectors_token) 213| headers = {"Authorization": f"Bearer {access_token}"} 214| params = {"include_contact": include_contact} 215| 216| base_url = tool_config.user_api_base_url.rstrip("/") 217| url = f"{base_url}/user/profile" 218| response = requests.get(url, headers=headers, params=params, timeout=REQUEST_TIMEOUT_SECONDS) 219| response.raise_for_status() 220| return response.text 221| except requests.HTTPError as e: 222| return f"Failed to read user profile. Status code: {e.response.status_code}, Response: {e.response.text}" 223| except Exception as e: 224| return f"Error reading user profile: {str(e)}" 225| 226| def _get_access_token_from_gl_connectors( 227| self, 228| tool_config: UserProfileToolConfig, 229| gl_connectors_token: str, 230| ) -> str: 231| """Exchange the delegated GL Connectors token for downstream auth info.""" 232| connector = GLConnectors( 233| api_base_url=tool_config.gl_connectors_api_base_url, 234| api_key=tool_config.gl_connectors_api_key, 235| ) 236| 237| # Check which integration belongs to this delegated user. 238| user_info = connector.get_user_info(gl_connectors_token) 239| user_identifier = next( 240| integration.user_identifier 241| for integration in user_info.integrations 242| if integration.connector == "user-profile" 243| ) 244| 245| # GL Connectors returns integration-specific auth information as auth_string. 246| integration_info = connector.get_integration("user-profile", gl_connectors_token, user_identifier) 247| auth_string = integration_info.get("auth_string") 248| if not auth_string: 249| raise ValueError("auth_string is missing or empty") 250| 251| return json.loads(auth_string)["access_token"] 252|
253|
254|{% endcode %}
255|
256|#### 2) Add user_authentication in tool config
257|
258|Set user_authentication: true in the tool config template. This tells GL AIP that the tool needs delegated user authentication.
259|
260|config_templates/tool_configs.yaml:
261|
262|yaml 263|user_profile_tool: 264| user_api_base_url: "${USER_API_BASE_URL}" 265| gl_connectors_api_base_url: "${GL_CONNECTORS_API_BASE_URL}" 266| gl_connectors_api_key: "${GL_CONNECTORS_API_KEY}" 267| user_authentication: true 268|
269|
270|The config key must match the tool name:
271|
272|python 273|class UserProfileTool(BaseTool): 274| name: str = "user_profile_tool" 275|
276|
277|#### 3) Handle delegated tokens in tool logic
278|
279|The delegated token is passed to digital_employee.run() and then forwarded to the tool call through RunnableConfig.metadata. In the custom tool, read it from config.get("metadata").get("gl_connectors_token").
280|
281|python 282|def _run(self, include_contact: bool = False, config: RunnableConfig = None, **_kwargs: Any) -> str: 283| tool_config = self.get_tool_config(config) 284| gl_connectors_token = ((config or {}).get("metadata") or {}).get("gl_connectors_token") 285| 286| if not gl_connectors_token: 287| return "Error: gl_connectors_token is required for this user-authenticated tool." 288| 289| access_token = self._get_access_token_from_gl_connectors(tool_config, gl_connectors_token) 290| 291| headers = { 292| "Authorization": f"Bearer {access_token}", 293| } 294|
295|
296|Use the GL Connectors token to retrieve the integration auth_string, then parse the auth string for the downstream API credential. The connector and its integration must already be set up in GL Connectors. See Integration Setup.
297|
298|python 299|def _get_access_token_from_gl_connectors( 300| self, 301| tool_config: UserProfileToolConfig, 302| gl_connectors_token: str, 303|) -> str: 304| connector = GLConnectors( 305| api_base_url=tool_config.gl_connectors_api_base_url, 306| api_key=tool_config.gl_connectors_api_key, 307| ) 308| 309| user_info = connector.get_user_info(gl_connectors_token) 310| user_identifier = next( 311| integration.user_identifier 312| for integration in user_info.integrations 313| if integration.connector == "user-profile" 314| ) 315| 316| integration_info = connector.get_integration("user-profile", gl_connectors_token, user_identifier) 317| auth_string = integration_info.get("auth_string") 318| if not auth_string: 319| raise ValueError("auth_string is missing or empty") 320| 321| return json.loads(auth_string)["access_token"] 322|
323|
324|In this pattern:
325|
326|* gl_connectors_api_key authenticates the tool to GL Connectors.
327|* gl_connectors_token identifies the current delegated user in GL Connectors.
328|* auth_string contains the downstream integration credential for that delegated user.
329|* user_authentication: true signals that GL AIP should delegate the appropriate token to the tool.
330|
331|For GL Connectors setup details, see GL Connectors Integration Setup.
332|
333|### Configuration Propagation to Sub-Agents
334|
335|Digital Employee Core propagates tool configs from the parent Digital Employee to sub-agents by dependency name.
336|
337|This means a parent can define user_authentication: true once for a shared tool, and a sub-agent that uses the same dependency can receive the config automatically.
338|
339|{% code lineNumbers="true" %}
340|
341|python 342|digital_employee = DigitalEmployee( 343| identity=identity, 344| tools=[Tool.from_langchain(UserProfileTool)], 345| sub_agents=[profile_helper_agent], 346| configurations=configurations, 347|) 348| 349|digital_employee.deploy() 350|
351|
352|{% endcode %}
353|
354|If the sub-agent defines its own config for the same tool, the sub-agent config takes precedence.
355|
356|See also: Sub-Agents Configuration.
357|
358|### Integrate the Custom Tool in Digital Employee
359|
360|Wrap the custom tool with Tool.from_langchain() and add a config loader for the tool config template.
361|
362|For deployed GL AIP runs, AIP validates the delegation token and attaches integration-specific tokens to runtime metadata. For local or manual runs, pass the delegated token explicitly in run().
363|
364|{% code lineNumbers="true" %}
365|
366|python 367|import os 368|from pathlib import Path 369| 370|from dotenv import load_dotenv 371|from glaip_sdk import MCP, Agent, Tool 372| 373|from digital_employee_core import ( 374| DEFAULT_MODEL_NAME, 375| ConfigTemplateLoader, 376| DigitalEmployee, 377| DigitalEmployeeConfiguration, 378| DigitalEmployeeIdentity, 379| DigitalEmployeeJob, 380|) 381| 382|from my_project.tools.user_profile_tool import UserProfileTool 383| 384|load_dotenv() 385| 386| 387|class MyDigitalEmployee(DigitalEmployee): 388| """Digital Employee with custom tool config templates.""" 389| 390| def __init__( 391| self, 392| identity: DigitalEmployeeIdentity, 393| tools: list[Tool] | None = None, 394| sub_agents: list[Agent] | None = None, 395| mcps: list[MCP] | None = None, 396| configurations: list[DigitalEmployeeConfiguration] | None = None, 397| model: str | None = DEFAULT_MODEL_NAME, 398| ): 399| super().__init__(identity, tools, sub_agents, mcps, configurations, model) 400| 401| config_dir = Path(__file__).parent / "config_templates" 402| self.add_config_loader(ConfigTemplateLoader(template_dir=config_dir)) 403| 404| 405|identity = DigitalEmployeeIdentity( 406| name="profile_assistant", 407| email="profile.assistant@example.com", 408| job=DigitalEmployeeJob( 409| title="Profile Assistant", 410| description="Helps users retrieve their own profile information", 411| instruction="Use the user profile tool when the user asks about their own profile.", 412| ), 413|) 414| 415|configurations = [ 416| DigitalEmployeeConfiguration(key="USER_API_BASE_URL", value=os.getenv("USER_API_BASE_URL", "")), 417| DigitalEmployeeConfiguration(key="GL_CONNECTORS_API_BASE_URL", value=os.getenv("GL_CONNECTORS_API_BASE_URL", "")), 418| DigitalEmployeeConfiguration(key="GL_CONNECTORS_API_KEY", value=os.getenv("GL_CONNECTORS_API_KEY", "")), 419|] 420| 421|# GL_CONNECTORS_TOKEN is a delegated user token for the current run. 422|# See the GL AIP Delegate to Agent guide for how to obtain it. 423|gl_connectors_token = os.getenv("GL_CONNECTORS_TOKEN") 424| 425|digital_employee = MyDigitalEmployee( 426| identity=identity, 427| tools=[Tool.from_langchain(UserProfileTool)], 428| configurations=configurations, 429|) 430| 431|digital_employee.deploy() 432| 433|result = digital_employee.run( 434| message="Show my profile information", 435| gl_connectors_token=gl_connectors_token, 436|) 437|
438|
439|{% endcode %}
440|
441|### Notes / Best Practices
442|
443|* Do not put tokens in prompts. Pass delegated tokens as run() / arun() keyword arguments only for local/manual runs; in deployed GL AIP runs, read the resolved token from RunnableConfig.metadata.
444|* Do not parse GL IAM delegation tokens in custom tools. Use GL AIP for delegation-token validation and only consume the integration-specific token exposed to the tool.
445|* Use memory_user_id only for memory scoping. Do not use it as an authorization token.
446|* Enable user_authentication only when needed. Tools that only use service credentials do not need delegated user tokens.
447|* Keep service credentials in configuration. Use DigitalEmployeeConfiguration, config templates or environment variables.
448|* Propagate configs intentionally. Shared parent configs are convenient for sub-agents, but sub-agent-specific configs should be explicit when permissions differ.
449|* Validate original delegation tokens at service boundaries. If a receiving service handles the original GL IAM delegation token directly, validate it according to Validate Delegation Token.
450|
451|### Troubleshooting
452|
453|#### Memory is enabled but the run fails
454|
455|Check that every run() or arun() call includes a non-empty memory_user_id.
456|
457|python 458|digital_employee.run( 459| message="What did I tell you earlier?", 460| memory_user_id="user-123", 461|) 462|
463|
464|#### Tool does not receive the user token
465|
466|Check that:
467|
468|1. The tool config includes user_authentication: true.
469|2. The run call passes the integration token, for example gl_connectors_token.
470|3. The token is available in the environment when running locally.
471|
472|bash 473|export GL_CONNECTORS_TOKEN="<delegated_token>" 474|
475|
476|
477|---
478|
479|# Agent Instructions
480|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
481|
482|## Querying This Documentation
483|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
484|
485|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
486|
487| 488|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/advanced-examples/user-information-in-digital-employee-runs.md?ask=<question>&goal=<endgoal> 489|
490|
491|ask is the immediate question: it should be specific, self-contained, and written in natural language.
492|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
493|
494|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
495|
496|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
497|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Digital Employee Architecture
4|
5|## Overview
6|
7|
date_range_tool: Utility for handling date ranges.
85|* cv_extractor_tool: Utility for extracting information from Curricula Vitae.
86|* time_tool: Utility for time management.
87|
88|### User-Defined Tools
89|
90|Custom tools created to handle specific tasks not covered by generic tools. Implemented as single-file Python code. These are some of the user-defined tools:
91|
92|* get_employee_info_tool: Retrieves specific employee information.
93|* update_candidate_phase_tool: Updates the recruitment phase of a candidate.
94|* detect_sister_company_tool: Logic to detect associated sister companies.
95|
96|## Key Benefits
97|
98|{% stepper %}
99|{% step %}
100|Automation
101|
102|Significantly reduces manual intervention by enabling scheduled operations.
103|{% endstep %}
104|
105|{% step %}
106|Integration
107|
108|Offers seamless connectivity with Google Workspace and other enterprise systems.
109|{% endstep %}
110|
111|{% step %}
112|Flexibility
113|
114|Features an extensible architecture that supports both standard (Built-in) and custom (User-Defined) tools.
115|{% endstep %}
116|
117|{% step %}
118|Scalability
119|
120|The MCP-based architecture allows for the easy addition of new services and capabilities as needs evolve.
121|{% endstep %}
122|{% endstepper %}
123|
124|
125|---
126|
127|# Agent Instructions
128|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
129|
130|## Querying This Documentation
131|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
132|
133|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
134|
135| 136|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/digital-employee-architecture.md?ask=<question>&goal=<endgoal> 137|
138|
139|ask is the immediate question: it should be specific, self-contained, and written in natural language.
140|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
141|
142|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
143|
144|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
145|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Digital Employee Detailed Block Diagram
4|
5|
ask query parameter, and the optional goal query parameter:
43|
44| 45|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/digital-employee-architecture/digital-employee-detailed-block-diagram.md?ask=<question>&goal=<endgoal> 46|
47|
48|ask is the immediate question: it should be specific, self-contained, and written in natural language.
49|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
50|
51|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
52|
53|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
54|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Tech Stack Overview
4|
5|
ask query parameter, and the optional goal query parameter:
19|
20| 21|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/digital-employee-architecture/tech-stack-overview.md?ask=<question>&goal=<endgoal> 22|
23|
24|ask is the immediate question: it should be specific, self-contained, and written in natural language.
25|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
26|
27|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
28|
29|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
30|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Getting Started Example
4|
5|This quickstart guide walks you through a simple setup to create a digital employee in just a few minutes.
6|
7|## Prerequisites
8|
9|To follow this example, you will need to:
10|
11|* Install the digital-employee-core package
12|* If you are planning to deploy and/or the digital employee in a remote AIP server, set the AIP_API_URL and AIP_API_KEY environment variables in your terminal or in a .env file
13|
14|{% hint style="info" %}
15|You should provide AIP_API_URL and AIP_API_KEY, because the digital employee depends on these two environment variables when you are using a remote AIP server.
16|
17|To get the values for these environment variables, please ask your team which AIP server has been deployed, and then obtain the AIP key. For example, in CATAPA, we use AIP_API_URL=https://aip-dev.catapa.com/ as our AIP development environment.
18|
19|You can set the environment variables in your terminal by using:
20|
21|
export AIP_API_URL=<AIP_API_URL>
22|export AIP_API_KEY=<AIP_API_KEY>
23|
24|
25|{% endhint %}
26|
27|* If you are planning to run the digital employee locally, set the OPENAI_API_KEY environment variables in your terminal or in a .env file
28|
29|{% hint style="info" %}
30|Similar to AIP_API_URL and AIP_API_KEY, you can set OPENAI_API_KEY environment variable by using:
31|
32|console 33|export OPENAI_API_KEY=<OPENAI_API_KEY> 34|
35|
36|{% endhint %}
37|
38|## Build Digital Employee - A Basic Example
39|
40|Start by creating a simple digital employee that can interact with Gmail. This agent will use OpenAI GPT-5.1 as its language model, an MCP setup, and a simple prompt to guide its behavior.
41|
42|### Import the Package
43|
44|{% code lineNumbers="true" %}
45|
46|python 47|from digital_employee_core import ( 48| DigitalEmployee, 49| DigitalEmployeeConfiguration, 50| DigitalEmployeeIdentity, 51| DigitalEmployeeJob, 52|) 53|from digital_employee_core.connectors.mcps import google_mail_mcp 54|
55|
56|{% endcode %}
57|
58|### Initialize the Digital Employee
59|
60|{% code lineNumbers="true" %}
61|
62|python 63|# Identity and configuration 64|job = DigitalEmployeeJob( 65| title="Digital Assistant", 66| description="A helpful digital employee assistant", 67| instruction="You are a helpful digital employee assistant. When asked to send a welcome email, craft a warm, professional message that introduces the team and includes helpful resources. Do not ask for clarification, just proceed with sending the email.", 68|) 69|identity = DigitalEmployeeIdentity(name="Claudia", email="claudia@example.com", job=job) 70|configurations = [ 71| DigitalEmployeeConfiguration(key="GOOGLE_MAIL_MCP_URL", value="https://api.bosa.id/google_mail/mcp"), 72| DigitalEmployeeConfiguration(key="GOOGLE_MCP_X_API_KEY", value="[gl-connectors-x-api-key]"), 73|] 74| 75|# Initialize digital employee 76|digital_employee = DigitalEmployee(identity=identity, configurations=configurations, mcps=[google_mail_mcp]) 77|
78|
79|{% endcode %}
80|
81|### Run the Digital Employee Locally
82|
83|{% code lineNumbers="true" %}
84|
85|python 86|# Run the digital employee locally using a prompt 87|result = digital_employee.run( 88| message="Send a welcome email to [your-email] welcoming them to the team. Include a warm greeting, mention that you're excited to have them on board, and let them know you're here to help with any questions.", 89| local=True 90|) 91|print(result) 92|
93|
94|{% endcode %}
95|
96|### Run the Digital Employee in Remote AIP Server
97|
98|{% code lineNumbers="true" %}
99|
100|python 101|# Deploy the digital employee to AIP server 102|digital_employee.deploy() 103| 104|# Run the deployed digital employee using a prompt 105|result = digital_employee.run(message="Send a welcome email to [your-email] welcoming them to the team. Include a warm greeting, mention that you're excited to have them on board, and let them know you're here to help with any questions.") 106|print(result) 107|
108|
109|{% endcode %}
110|
111|{% hint style="info" %}
112|Before running the sample code, replace the following placeholders:
113|
114|1. Replace [your-email] with your email address.
115|2. Replace [gl-connectors-x-api-key] with x-api-key from GL Connectors. See below for one way to do it.
116|3. (Optional) Replace GOOGLE_MAIL_MCP_URL if you are using a different GL Connectors server instance.
117|
118|🔑 Get GL Connectors x-api-key
121| 122|1. Open https://api.bosa.id/console, then sign in. 123|2. In the Credentials section, expand the x-api-key panel and click Copy combined value button. Paste this value to replace[gl-connectors-x-api-key] .
124|
125|ask query parameter, and the optional goal query parameter:
153|
154| 155|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/getting-started-example.md?ask=<question>&goal=<endgoal> 156|
157|
158|ask is the immediate question: it should be specific, self-contained, and written in natural language.
159|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
160|
161|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
162|
163|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
164|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Install and Configure
4|
5|To install the digital employee core package:
6|
7|{% tabs %}
8|{% tab title="pip" %}
9|
10|console 11|pip install digital-employee-core 12|
13|
14|{% endtab %}
15|
16|{% tab title="poetry" %}
17|
18|console 19|poetry add digital-employee-core 20|
21|
22|{% endtab %}
23|
24|{% tab title="uv" %}
25|
26|console 27|uv add digital-employee-core 28|
29|
30|{% endtab %}
31|{% endtabs %}
32|
33|Please ensure you have installed pip, poetry, or uv before installing the digital employee core package.
34|
35|{% hint style="info" %}
36|You can check the PyPI resource: Digital Employee Core PyPI.
37|{% endhint %}
38|
39|
40|---
41|
42|# Agent Instructions
43|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
44|
45|## Querying This Documentation
46|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
47|
48|Perform an HTTP GET request on the current page URL with the ask query parameter, and the optional goal query parameter:
49|
50| 51|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/install-and-configure.md?ask=<question>&goal=<endgoal> 52|
53|
54|ask is the immediate question: it should be specific, self-contained, and written in natural language.
55|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
56|
57|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
58|
59|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
60|
1|> For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to page URLs; this page is available as Markdown.
2|
3|# Multi-tenant
4|
5|To support multi-tenancy, we created one digital employee per each tenant and can customize the prompt configuration, tool configuration, and MCP configuration at creation or runtime from each tenant. This approach allows us to make our digital employee generic.
6|
7|### Prompt Configuration
8|
9|Prompt configuration means we can configure the placeholders in the agent prompt (instruction), because sometimes we need to add placeholders to our prompt.
10|
11|Here is an example of a prompt (instruction) with placeholders:
12|
13| 14|**A3.2.6 Find matched experience** 15|- Check if detected_sister_companies list is not empty. 16|- If there is at least one match in detected_sister_companies: 17| - **A3.2.6.1** Send confirmation email (HTML) via MCP Gmail: 18| - Use `google_mail_send_email` tool 19| - To: candidate email; CC: {sister_company_email_cc}// Some code 20|
21|
22|{sister_company_email_cc} is the placeholder that needs to be replaced at runtime.
23|
24|### Tool Configuration
25|
26|Tool configuration is the configuration for the tool that will be consumed by the agent. Agent tools need an input to run the tool. For example, the audio_transcription tool needs a Prosa API key. In this case, we can configure which API key we will use for a specific tenant.
27|
28|### MCP Configuration
29|
30|MCP configuration is similar to tool configuration, but this works for MCP. For example, MCP Google Mail needs a BOSA API key to be able to send emails to specific recipients. We can configure that key for a specific tenant.
31|
32|
33|---
34|
35|# Agent Instructions
36|This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.
37|
38|## Querying This Documentation
39|If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.
40|
41|Perform an HTTP GET request on the current page URL with the
ask query parameter, and the optional goal query parameter:
42|
43| 44|GET https://gdplabs.gitbook.io/catapa/developer-documentation/digital-employee/multi-tenant.md?ask=<question>&goal=<endgoal> 45|
46|
47|ask is the immediate question: it should be specific, self-contained, and written in natural language.
48|goal is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.
49|
50|The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.
51|
52|Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
53|