|
15 | 15 | """Agent factory for creating Agent Builder Assistant with embedded schema.""" |
16 | 16 |
|
17 | 17 | from pathlib import Path |
| 18 | +import textwrap |
| 19 | +from typing import Any |
18 | 20 | from typing import Callable |
19 | 21 | from typing import Optional |
20 | 22 | from typing import Union |
@@ -128,25 +130,175 @@ def _load_schema() -> str: |
128 | 130 | # CENTRALIZED ADK AGENTCONFIG SCHEMA LOADING: Use common utility function |
129 | 131 | # This avoids duplication across multiple files and provides consistent |
130 | 132 | # ADK AgentConfig schema loading with caching and error handling. |
131 | | - schema_content = load_agent_config_schema( |
132 | | - raw_format=True, # Get as JSON string |
| 133 | + schema_dict = load_agent_config_schema() |
| 134 | + return AgentBuilderAssistant._build_schema_reference(schema_dict) |
| 135 | + |
| 136 | + @staticmethod |
| 137 | + def _build_schema_reference(schema: dict[str, Any]) -> str: |
| 138 | + """Create compact AgentConfig reference text for prompt embedding.""" |
| 139 | + |
| 140 | + defs: dict[str, Any] = schema.get("$defs", {}) |
| 141 | + top_level_fields: dict[str, Any] = schema.get("properties", {}) |
| 142 | + wrapper = textwrap.TextWrapper(width=78) |
| 143 | + lines: list[str] = [] |
| 144 | + |
| 145 | + def add(text: str = "", indent: int = 0) -> None: |
| 146 | + """Append wrapped text with indentation.""" |
| 147 | + if not text: |
| 148 | + lines.append("") |
| 149 | + return |
| 150 | + indent_str = " " * indent |
| 151 | + wrapper.initial_indent = indent_str |
| 152 | + wrapper.subsequent_indent = indent_str |
| 153 | + lines.extend(wrapper.fill(text).split("\n")) |
| 154 | + |
| 155 | + add("ADK AgentConfig quick reference") |
| 156 | + add("--------------------------------") |
| 157 | + |
| 158 | + add() |
| 159 | + add("LlmAgent (agent_class: LlmAgent)") |
| 160 | + add( |
| 161 | + "Required fields: name, instruction. ADK best practice is to always set" |
| 162 | + " model explicitly.", |
| 163 | + indent=2, |
| 164 | + ) |
| 165 | + add("Optional fields:", indent=2) |
| 166 | + add("agent_class: defaults to LlmAgent; keep for clarity.", indent=4) |
| 167 | + add("description: short summary string.", indent=4) |
| 168 | + add("sub_agents: list of AgentRef entries (see below).", indent=4) |
| 169 | + add( |
| 170 | + "before_agent_callbacks / after_agent_callbacks: list of CodeConfig " |
| 171 | + "entries that run before or after the agent loop.", |
| 172 | + indent=4, |
| 173 | + ) |
| 174 | + add("model: string model id (required in practice).", indent=4) |
| 175 | + add( |
| 176 | + "disallow_transfer_to_parent / disallow_transfer_to_peers: booleans to " |
| 177 | + "restrict automatic transfer.", |
| 178 | + indent=4, |
| 179 | + ) |
| 180 | + add( |
| 181 | + "input_schema / output_schema: JSON schema objects to validate inputs " |
| 182 | + "and outputs.", |
| 183 | + indent=4, |
| 184 | + ) |
| 185 | + add("output_key: name to store agent output in session context.", indent=4) |
| 186 | + add( |
| 187 | + "include_contents: bool; include tool/LLM contents in response.", |
| 188 | + indent=4, |
| 189 | + ) |
| 190 | + add("tools: list of ToolConfig entries (see below).", indent=4) |
| 191 | + add( |
| 192 | + "before_model_callbacks / after_model_callbacks: list of CodeConfig " |
| 193 | + "entries around LLM calls.", |
| 194 | + indent=4, |
| 195 | + ) |
| 196 | + add( |
| 197 | + "before_tool_callbacks / after_tool_callbacks: list of CodeConfig " |
| 198 | + "entries around tool calls.", |
| 199 | + indent=4, |
| 200 | + ) |
| 201 | + add( |
| 202 | + "generate_content_config: passes directly to google.genai " |
| 203 | + "GenerateContentConfig (supporting temperature, topP, topK, " |
| 204 | + "maxOutputTokens, safetySettings, responseSchema, routingConfig," |
| 205 | + " etc.).", |
| 206 | + indent=4, |
133 | 207 | ) |
134 | 208 |
|
135 | | - # Format as indented code block for instruction embedding |
136 | | - # |
137 | | - # Why indentation is needed: |
138 | | - # - The ADK AgentConfig schema gets embedded into instruction templates using .format() |
139 | | - # - Proper indentation maintains readability in the final instruction |
140 | | - # - Code block markers (```) help LLMs recognize this as structured data |
141 | | - # |
142 | | - # Example final instruction format: |
143 | | - # "Here is the ADK AgentConfig schema: |
144 | | - # ```json |
145 | | - # {"type": "object", "properties": {...}} |
146 | | - # ```" |
147 | | - lines = schema_content.split("\n") |
148 | | - indented_lines = [" " + line for line in lines] # 2-space indent |
149 | | - return "```json\n" + "\n".join(indented_lines) + "\n ```" |
| 209 | + add() |
| 210 | + add("Workflow agents (LoopAgent, ParallelAgent, SequentialAgent)") |
| 211 | + add( |
| 212 | + "Share BaseAgent fields: agent_class, name, description, sub_agents, " |
| 213 | + "before/after_agent_callbacks. Never declare model, instruction, or " |
| 214 | + "tools on workflow orchestrators.", |
| 215 | + indent=2, |
| 216 | + ) |
| 217 | + add( |
| 218 | + "LoopAgent adds max_iterations (int) controlling iteration cap.", |
| 219 | + indent=2, |
| 220 | + ) |
| 221 | + |
| 222 | + add() |
| 223 | + add("AgentRef") |
| 224 | + add( |
| 225 | + "Used inside sub_agents lists. Provide either config_path (string path " |
| 226 | + "to another YAML file) or code (dotted Python reference) to locate the " |
| 227 | + "sub-agent definition.", |
| 228 | + indent=2, |
| 229 | + ) |
| 230 | + |
| 231 | + add() |
| 232 | + add("ToolConfig") |
| 233 | + add( |
| 234 | + "Items inside tools arrays. Required field name (string). For built-in " |
| 235 | + "tools use the exported short name, for custom tools use the dotted " |
| 236 | + "module path.", |
| 237 | + indent=2, |
| 238 | + ) |
| 239 | + add( |
| 240 | + "args: optional object of additional keyword arguments. Use simple " |
| 241 | + "key-value pairs (ToolArgsConfig) or structured ArgumentConfig entries " |
| 242 | + "when a list is required by callbacks.", |
| 243 | + indent=2, |
| 244 | + ) |
| 245 | + |
| 246 | + add() |
| 247 | + add("ArgumentConfig") |
| 248 | + add( |
| 249 | + "Represents a single argument. value is required and may be any JSON " |
| 250 | + "type. name is optional (null allowed). Often used in callback args.", |
| 251 | + indent=2, |
| 252 | + ) |
| 253 | + |
| 254 | + add() |
| 255 | + add("CodeConfig") |
| 256 | + add( |
| 257 | + "References Python code for callbacks or dynamic tool creation." |
| 258 | + " Requires name (dotted path). args is an optional list of" |
| 259 | + " ArgumentConfig items executed when invoking the function.", |
| 260 | + indent=2, |
| 261 | + ) |
| 262 | + |
| 263 | + add() |
| 264 | + add("GenerateContentConfig highlights") |
| 265 | + add( |
| 266 | + "Controls LLM generation behavior. Common fields: maxOutputTokens, " |
| 267 | + "temperature, topP, topK, candidateCount, responseMimeType, " |
| 268 | + "responseSchema/responseJsonSchema, automaticFunctionCalling, " |
| 269 | + "safetySettings, routingConfig; see Vertex AI GenAI docs for full " |
| 270 | + "semantics.", |
| 271 | + indent=2, |
| 272 | + ) |
| 273 | + |
| 274 | + add() |
| 275 | + add( |
| 276 | + "All other schema definitions in AgentConfig.json remain available but " |
| 277 | + "are rarely needed for typical agent setups. Refer to the source file " |
| 278 | + "for exhaustive field descriptions when implementing advanced configs.", |
| 279 | + ) |
| 280 | + |
| 281 | + if top_level_fields: |
| 282 | + add() |
| 283 | + add("Top-level AgentConfig fields (from schema)") |
| 284 | + for field_name in sorted(top_level_fields): |
| 285 | + description = top_level_fields[field_name].get("description", "") |
| 286 | + if description: |
| 287 | + add(f"{field_name}: {description}", indent=2) |
| 288 | + else: |
| 289 | + add(field_name, indent=2) |
| 290 | + |
| 291 | + if defs: |
| 292 | + add() |
| 293 | + add("Additional schema definitions") |
| 294 | + for def_name in sorted(defs): |
| 295 | + description = defs[def_name].get("description", "") |
| 296 | + if description: |
| 297 | + add(f"{def_name}: {description}", indent=2) |
| 298 | + else: |
| 299 | + add(def_name, indent=2) |
| 300 | + |
| 301 | + return "```text\n" + "\n".join(lines) + "\n```" |
150 | 302 |
|
151 | 303 | @staticmethod |
152 | 304 | def _load_instruction_with_schema( |
|
0 commit comments