Python Client & Build Integration
The Python client (castor-client) implements a PEP 517/518 build backend wrapper (castor_client.build_meta) alongside high-performance JIT Dynamic Pre-Call Retrieval for Google ADK agents.
By declaring castor_client.build_meta in your project’s pyproject.toml, native Python package managers (uv build, pip install ., build) execute pre-build hooks that download, audit, and stage skill dependencies into the package tree prior to generating wheels (.whl) or source distributions (.tar.gz).
Configure castor_client.build_meta as your project’s build backend in pyproject.toml:
[build-system]
requires = ["castor-client>=1.0.0", "setuptools>=68.0.0"]
build-backend = "castor_client.build_meta"
[project]
name = "my-enterprise-agent"
version = "1.0.0"
description = "Enterprise Agent Application with Embedded Skills"
readme = "README.md"
requires-python = ">=3.11"
dependencies = [
"google-adk>=0.1.0",
"castor-client>=1.0.0",
"pydantic>=2.7.0",
]
[tool.castor-client]
# Target staging directory inside python package tree
dest = "src/my_enterprise_agent/.skills"
# Skill root URIs to resolve and package during build
dependencies = [
"castor://skills/example.com/testing/test-skill/1.0.0",
"github://google/skills@main/tree/main/skills/cloud/gemini-api",
"file://./local_skills/customer-search"
]
- PEP 517 Hook Interception: When
uv buildorpip installexecutes, thecastor_client.build_metabuild backend intercepts the build process (build_wheel,build_sdist,prepare_metadata_for_build_wheel). - Dependency Resolution: Reads
[tool.castor-client.dependencies]and downloads/resolves all specified skills from Castor Registry servers, GitHub, or local paths. - SDLC Audit Gate: Performs 5-point SDLC checks. If validation fails, the build terminates with an error.
- Staging & Packaging: Stages resolved skills into
dest(src/my_enterprise_agent/.skills/), ensuring they are packaged directly into the final.whlartifact.
In autonomous ADK agent workflows, loading all tools statically can cause context window bloat and tool hallucinations. SkillRegistry.suggest_skills() performs JIT semantic retrieval to rank and bound relevant skills to at most k ≤ 3:
from castor_client import SkillRegistry
# Initialize registry from workspace or embedded package tree
registry = SkillRegistry()
# Dynamically retrieve top 3 skills ranked by vector relevance
suggested = registry.suggest_skills(
prompt="Generate BigQuery SQL analytics statement for customer churn",
max_skills=3,
server_url="http://localhost:8000"
)
for skill in suggested:
print(f"Loaded Skill: {skill.name} ({skill.description})")
- Remote Vector Search: Dispatches
GET /api/v1/skills?s={query}&page_size=3to the centralCastor Registry. - Local Vector Search: If offline or unreachable, executes local TF-IDF cosine similarity search via
DiscoveryEngine. - Keyword & Substring Fallback: Falls back to tokenized keyword matching and domain heuristics.
import asyncio
from google.adk.agent import Agent
from loader import SkillRegistry
class EnterpriseCodingAgent:
def __init__(self, registry: SkillRegistry) -> None:
self.registry = registry
async def handle_prompt(self, user_prompt: str) -> str:
# Pre-call optimization: retrieve top 3 most relevant skills
relevant_skills = self.registry.suggest_skills(user_prompt, max_skills=3)
# Synthesize system instructions and tools
system_instructions = "You are an enterprise software engineering agent.\n"
tools = []
for skill in relevant_skills:
system_instructions += f"\n### Skill: {skill.name}\n{skill.instructions}\n"
tools.extend(skill.to_adk_tools())
agent = Agent(
name="enterprise-programmer",
model="gemini-2.0-flash",
instructions=system_instructions,
tools=tools,
)
return await agent.run_async(user_prompt)
In BUILD.bazel:
py_library(
name = "agent_lib",
srcs = glob(["src/my_enterprise_agent/*.py"]),
deps = [
"//clients/python:loader_lib",
],
)
py_binary(
name = "agent_binary",
srcs = ["src/my_enterprise_agent/main.py"],
deps = [":agent_lib"],
)
The Python client parses and executes scenarios declared in a skill’s scenarios/ directory:
from castor_client import load_skill_from_dir, run_skill_scenarios, evaluate_scenario
# Load skill with automatic scenario discovery
skill = load_skill_from_dir("./skills/my-skill")
# Inspect parsed scenarios
for name, scenario in skill.scenarios.items():
print(f"Scenario: {name}")
print(f" Prompt: {scenario.prompt}")
print(f" Expected Tools: {scenario.expected_skills}")
print(f" Threshold: {scenario.threshold}")
# Execute all scenarios against an agent runner callback
def agent_runner(prompt: str) -> tuple[str, list[str]]:
# Run agent prompt and capture response text + tools executed
agent_output = "Target built successfully."
tools_used = ["bazel"]
return agent_output, tools_used
results = run_skill_scenarios(skill, agent_runner)
for res in results:
status = "PASS" if res.passed else "FAIL"
print(f"[{status}] {res.scenario_name} (Similarity: {res.similarity_score:.2f})")
- Virtual Environment Isolation: Always manage dependencies using
uvand run scripts viauv run python main.py. Never run globalpip install. - Type Safety: Apply strict type hints across custom tools (
def my_tool(param: str) -> dict[str, Any]:). NoAnyunless unavoidable. - Async Parameter Handling: Await asynchronous parameter resolution in Next.js / FastAPI API route handlers.