pyagentic-core 2.14.1.dev1__tar.gz → 2.15.1.dev1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pyagentic_core-2.14.1.dev1/pyagentic_core.egg-info → pyagentic_core-2.15.1.dev1}/PKG-INFO +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/agent-linking.md +16 -16
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/api/creating-an-app.md +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/api/index.md +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/execution-modes.md +2 -2
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/getting-started.md +6 -4
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/inheritance.md +7 -7
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/observability.md +2 -2
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/phases.md +6 -6
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/policies/built-in.md +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/policies/custom.md +3 -3
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/policies/index.md +3 -3
- pyagentic_core-2.15.1.dev1/docs/prompts.md +140 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/reference/modules.md +18 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/responses.md +15 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/states.md +10 -10
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/structured-output.md +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/tools.md +9 -9
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/__init__.py +7 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_agent/_agent.py +11 -5
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_agent/_agent_linking.py +2 -2
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_agent/_agent_state.py +35 -3
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_depends.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_exceptions.py +22 -3
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_mcp.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_metaclasses.py +23 -9
- pyagentic_core-2.15.1.dev1/pyagentic/_base/_prompts.py +243 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_ref.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_spec.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_state.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_tool.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/models/response.py +9 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/policies/_policy.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/policies/messages.py +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1/pyagentic_core.egg-info}/PKG-INFO +1 -1
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic_core.egg-info/SOURCES.txt +3 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic_core.egg-info/scm_file_list.json +3 -0
- pyagentic_core-2.15.1.dev1/pyagentic_core.egg-info/scm_version.json +8 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent.py +5 -5
- pyagentic_core-2.15.1.dev1/tests/_base/test_prompts.py +289 -0
- pyagentic_core-2.14.1.dev1/pyagentic_core.egg-info/scm_version.json +0 -8
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.github/workflows/docs.yml +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.github/workflows/release.yml +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.github/workflows/testing.yml +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/.gitignore +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/CHANGELOG.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/LICENSE +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/README.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/api/deploying.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/api/jobs.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/api/running.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/declaration.svg +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/instantiation.svg +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/runtime.svg +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/source/README.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/source/declaration.d2 +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/source/instantiation.d2 +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/diagrams/source/runtime.d2 +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/images/langfuse.png +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/index.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/docs/reference/architecture.md +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/mkdocs.yml +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_agent/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_info.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_base/_validation.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_utils/_typing.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_utils/_warnings.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/_version_scheme.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_app.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_build.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_config.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_docker.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_mcp_server.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_models.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/_sessions.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/_models.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/_orchestrator.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/_routes.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/backends/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/backends/_base.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/backends/_in_process.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/store/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/store/_base.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/api/jobs/store/_sqlite.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/_anthropic.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/_gemini.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/_mock.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/_openai.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/llm/_provider.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/logging.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/models/llm.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/models/tracing.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/policies/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/policies/_events.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/policies/_list.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/tracing/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/tracing/_basic.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/tracing/_langfuse.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/tracing/_tracer.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic/updates.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic_core.egg-info/dependency_links.txt +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic_core.egg-info/requires.txt +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyagentic_core.egg-info/top_level.txt +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/pyproject.toml +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/setup.cfg +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/setup.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent_forking.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent_inheritance.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent_linking.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent_message_policies.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_agent_provider.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_params.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_phases.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_policy_list.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_state.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_tool.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/_base/test_validator.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/jobs/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/jobs/test_orchestrator.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/jobs/test_routes.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/jobs/test_store_sqlite.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_app.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_build_agent.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_config.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_construct_model.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_dependencies_integration.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_docker.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_mcp_server.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_metaclass_models.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_router.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/api/test_sessions.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/conftest.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/llm/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/llm/test_message_conversion.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/models/test_messages.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/models/test_responses.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/policies/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/policies/test_message_policies.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/tracing/__init__.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/tracing/test_basic_tracer.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/tracing/test_models.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/tests/tracing/test_tracer.py +0 -0
- {pyagentic_core-2.14.1.dev1 → pyagentic_core-2.15.1.dev1}/uv.lock +0 -0
|
@@ -16,14 +16,14 @@ The simplest way to link agents is by declaring them as typed attributes in your
|
|
|
16
16
|
from pyagentic import BaseAgent, Link, tool
|
|
17
17
|
|
|
18
18
|
class DatabaseAgent(BaseAgent):
|
|
19
|
-
|
|
19
|
+
__instructions__ = "I query databases"
|
|
20
20
|
__description__ = "Retrieves and analyzes data from databases"
|
|
21
21
|
|
|
22
22
|
@tool("Execute SQL query")
|
|
23
23
|
def query(self, sql: str) -> str: ...
|
|
24
24
|
|
|
25
25
|
class ReportAgent(BaseAgent):
|
|
26
|
-
|
|
26
|
+
__instructions__ = "I generate business reports"
|
|
27
27
|
|
|
28
28
|
# Using Link[T] (recommended for advanced features)
|
|
29
29
|
database: Link[DatabaseAgent]
|
|
@@ -45,15 +45,15 @@ Real-world applications often require coordination between multiple specialized
|
|
|
45
45
|
|
|
46
46
|
```python
|
|
47
47
|
class EmailAgent(BaseAgent):
|
|
48
|
-
|
|
48
|
+
__instructions__ = "I send emails"
|
|
49
49
|
__description__ = "Sends and manages email communications"
|
|
50
50
|
|
|
51
51
|
class CalendarAgent(BaseAgent):
|
|
52
|
-
|
|
52
|
+
__instructions__ = "I manage calendars"
|
|
53
53
|
__description__ = "Schedules meetings and manages calendar events"
|
|
54
54
|
|
|
55
55
|
class AssistantAgent(BaseAgent):
|
|
56
|
-
|
|
56
|
+
__instructions__ = "I help with daily tasks"
|
|
57
57
|
|
|
58
58
|
email: EmailAgent
|
|
59
59
|
calendar: CalendarAgent
|
|
@@ -75,14 +75,14 @@ Provide a default agent instance that will be used if none is provided during in
|
|
|
75
75
|
from pyagentic import BaseAgent, Link, spec
|
|
76
76
|
|
|
77
77
|
class AnalysisAgent(BaseAgent):
|
|
78
|
-
|
|
78
|
+
__instructions__ = "I analyze data"
|
|
79
79
|
__description__ = "Performs data analysis"
|
|
80
80
|
|
|
81
81
|
# Create a pre-configured analyzer
|
|
82
82
|
default_analyzer = AnalysisAgent(model="gpt-4", api_key="sk-...")
|
|
83
83
|
|
|
84
84
|
class ReportAgent(BaseAgent):
|
|
85
|
-
|
|
85
|
+
__instructions__ = "I generate reports"
|
|
86
86
|
|
|
87
87
|
# Will use default_analyzer if no analyzer is provided
|
|
88
88
|
analyzer: Link[AnalysisAgent] = spec.AgentLink(default=default_analyzer)
|
|
@@ -104,7 +104,7 @@ Use `default_factory` to create agent instances on-demand, similar to how `spec.
|
|
|
104
104
|
|
|
105
105
|
```python
|
|
106
106
|
class SearchAgent(BaseAgent):
|
|
107
|
-
|
|
107
|
+
__instructions__ = "I search databases"
|
|
108
108
|
__description__ = "Database search specialist"
|
|
109
109
|
|
|
110
110
|
def create_searcher():
|
|
@@ -116,7 +116,7 @@ def create_searcher():
|
|
|
116
116
|
)
|
|
117
117
|
|
|
118
118
|
class DataAgent(BaseAgent):
|
|
119
|
-
|
|
119
|
+
__instructions__ = "I manage data operations"
|
|
120
120
|
|
|
121
121
|
# Automatically creates a searcher if not provided
|
|
122
122
|
searcher: Link[SearchAgent] = spec.AgentLink(default_factory=create_searcher)
|
|
@@ -139,11 +139,11 @@ Link agents conditionally based on runtime state, enabling dynamic agent composi
|
|
|
139
139
|
from pyagentic import State
|
|
140
140
|
|
|
141
141
|
class ExpertAgent(BaseAgent):
|
|
142
|
-
|
|
142
|
+
__instructions__ = "I provide expert analysis"
|
|
143
143
|
__description__ = "Expert consultant for complex problems"
|
|
144
144
|
|
|
145
145
|
class SmartAgent(BaseAgent):
|
|
146
|
-
|
|
146
|
+
__instructions__ = "I handle tasks with optional expert help"
|
|
147
147
|
|
|
148
148
|
# State field to control expert availability
|
|
149
149
|
needs_expert: State[bool] = spec.State(default=False)
|
|
@@ -203,7 +203,7 @@ calls — for example so the parent can read the linked agent's state through
|
|
|
203
203
|
from pyagentic import BaseAgent, Link, spec
|
|
204
204
|
|
|
205
205
|
class Orchestrator(BaseAgent):
|
|
206
|
-
|
|
206
|
+
__instructions__ = "I coordinate a stateful researcher."
|
|
207
207
|
|
|
208
208
|
# One persistent researcher; its state survives across calls and is
|
|
209
209
|
# readable via ref.researcher.<field>.
|
|
@@ -226,13 +226,13 @@ Overriding the `__call__` method gives you complete control over how your linked
|
|
|
226
226
|
|
|
227
227
|
```python
|
|
228
228
|
class AnalysisAgent(BaseAgent):
|
|
229
|
-
|
|
229
|
+
__instructions__ = "I analyze data"
|
|
230
230
|
__description__ = "Performs statistical analysis on datasets"
|
|
231
231
|
|
|
232
232
|
async def __call__(self, data: str, analysis_type: str = "basic") -> str: ...
|
|
233
233
|
|
|
234
234
|
class ReportAgent(BaseAgent):
|
|
235
|
-
|
|
235
|
+
__instructions__ = "I generate reports"
|
|
236
236
|
analyzer: AnalysisAgent
|
|
237
237
|
|
|
238
238
|
# The LLM can now call the analyzer with specific parameters
|
|
@@ -254,7 +254,7 @@ class SearchParams(BaseModel):
|
|
|
254
254
|
include_metadata: bool = Field(default=True, description="Whether to include metadata in results")
|
|
255
255
|
|
|
256
256
|
class SearchAgent(BaseAgent):
|
|
257
|
-
|
|
257
|
+
__instructions__ = "I search databases"
|
|
258
258
|
__description__ = "Searches databases with advanced filtering"
|
|
259
259
|
|
|
260
260
|
async def __call__(self, params: SearchParams) -> str:
|
|
@@ -263,7 +263,7 @@ class SearchAgent(BaseAgent):
|
|
|
263
263
|
return f"Found {len(results)} results for '{params.query}'"
|
|
264
264
|
|
|
265
265
|
class DataAgent(BaseAgent):
|
|
266
|
-
|
|
266
|
+
__instructions__ = "I manage data operations"
|
|
267
267
|
searcher: SearchAgent
|
|
268
268
|
|
|
269
269
|
# The LLM can now call the searcher with structured parameters
|
|
@@ -69,7 +69,7 @@ agent class, exactly where you'd otherwise put `State[...]`:
|
|
|
69
69
|
from pyagentic import BaseAgent, State, Depends
|
|
70
70
|
|
|
71
71
|
class ResearchAgent(BaseAgent):
|
|
72
|
-
|
|
72
|
+
__instructions__ = "You research topics."
|
|
73
73
|
|
|
74
74
|
topic: State[Topic] # client-provided per session
|
|
75
75
|
db: Depends[Database] # injected server-side, never sent by clients
|
|
@@ -35,7 +35,7 @@ The real power of `__call__` is that you can override it to accept typed paramet
|
|
|
35
35
|
from typing import Optional
|
|
36
36
|
|
|
37
37
|
class CoursePlannerAgent(BaseAgent):
|
|
38
|
-
|
|
38
|
+
__instructions__ = "You design course curricula"
|
|
39
39
|
__description__ = "Creates structured course plans based on learning goals"
|
|
40
40
|
__response_format__ = CoursePlan
|
|
41
41
|
|
|
@@ -79,7 +79,7 @@ When you link an agent to another agent, PyAgentic extracts the parameters from
|
|
|
79
79
|
|
|
80
80
|
```python
|
|
81
81
|
class AssistantAgent(BaseAgent):
|
|
82
|
-
|
|
82
|
+
__instructions__ = "You help students with learning plans"
|
|
83
83
|
|
|
84
84
|
# Link the course planner
|
|
85
85
|
planner: CoursePlannerAgent
|
|
@@ -91,13 +91,15 @@ from pyagentic import BaseAgent
|
|
|
91
91
|
class ResearchAgent(BaseAgent):
|
|
92
92
|
"""An AI assistant for managing and analyzing research papers."""
|
|
93
93
|
|
|
94
|
-
|
|
94
|
+
__instructions__ = """
|
|
95
95
|
You are a research assistant that helps organize and analyze academic papers.
|
|
96
96
|
You maintain a collection of research papers and can answer questions about them.
|
|
97
97
|
You are knowledgeable, precise, and helpful in academic contexts.
|
|
98
98
|
"""
|
|
99
99
|
```
|
|
100
100
|
|
|
101
|
+
The `__instructions__` attribute defines the agent's system prompt — it can be a plain string like this, a Jinja template rendered with the agent's state, or a reference to a prompt managed outside your code (see [Prompt Management](prompts.md)).
|
|
102
|
+
|
|
101
103
|
This creates a basic conversational agent, but it can't do much beyond chat. Let's add some memory and capabilities.
|
|
102
104
|
|
|
103
105
|
## Step 2: Adding State with State Fields
|
|
@@ -114,7 +116,7 @@ from arxiv import Result as Paper
|
|
|
114
116
|
class ResearchAgent(BaseAgent):
|
|
115
117
|
"""An AI assistant for managing and analyzing research papers."""
|
|
116
118
|
|
|
117
|
-
|
|
119
|
+
__instructions__ = """
|
|
118
120
|
You are a research assistant that helps organize and analyze academic papers.
|
|
119
121
|
You have full access to the Arxiv
|
|
120
122
|
|
|
@@ -200,7 +202,7 @@ class ResearchState(BaseModel):
|
|
|
200
202
|
return [paper.get_short_id() for paper in self.papers.get(self.current_topic, [])]
|
|
201
203
|
|
|
202
204
|
class ResearchAgent(BaseAgent):
|
|
203
|
-
# ...
|
|
205
|
+
# ... instructions ...
|
|
204
206
|
|
|
205
207
|
state: State[ResearchState] = spec.State(default_factory=ResearchState)
|
|
206
208
|
|
|
@@ -266,7 +268,7 @@ class ResearchState(BaseModel):
|
|
|
266
268
|
class ResearchAgent(BaseAgent):
|
|
267
269
|
"""An AI assistant for managing and analyzing research papers."""
|
|
268
270
|
|
|
269
|
-
|
|
271
|
+
__instructions__ = """
|
|
270
272
|
You are a research assistant that helps organize and analyze academic papers.
|
|
271
273
|
You have full access to the Arxiv
|
|
272
274
|
|
|
@@ -4,7 +4,7 @@ PyAgentic supports standard Python inheritance, allowing you to build agent hier
|
|
|
4
4
|
|
|
5
5
|
## How Inheritance Works
|
|
6
6
|
|
|
7
|
-
Agent inheritance follows Python's standard rules with some PyAgentic-specific behaviors. When you inherit from an agent, you get all its tools, context items, and linked agents. However, each agent must define its own
|
|
7
|
+
Agent inheritance follows Python's standard rules with some PyAgentic-specific behaviors. When you inherit from an agent, you get all its tools, context items, and linked agents. However, each agent must define its own instructions to maintain clear identity and purpose.
|
|
8
8
|
|
|
9
9
|
PyAgentic builds the complete agent schema at class definition time, combining inherited elements with new ones to create a fully-typed, predictable agent interface.
|
|
10
10
|
|
|
@@ -21,7 +21,7 @@ PyAgentic follows specific rules about what gets inherited and what must be rede
|
|
|
21
21
|
|
|
22
22
|
#### What Must Be Redefined ❌
|
|
23
23
|
|
|
24
|
-
- **
|
|
24
|
+
- **Instructions** - Each agent must define its own `__instructions__` to maintain clear identity
|
|
25
25
|
- **Input Templates** - `__input_template__` is not inherited, allowing agent-specific formatting
|
|
26
26
|
|
|
27
27
|
This design ensures that while agents can share functionality, each maintains its own distinct purpose and behavior.
|
|
@@ -32,7 +32,7 @@ Extend agents using normal Python inheritance to build specialized capabilities
|
|
|
32
32
|
|
|
33
33
|
```python
|
|
34
34
|
class BaseAssistantAgent(BaseAgent):
|
|
35
|
-
|
|
35
|
+
__instructions__ = "I am a helpful AI assistant"
|
|
36
36
|
|
|
37
37
|
user_name: State[str] = spec.State(default="User")
|
|
38
38
|
session_id: State[str] = spec.State(default="")
|
|
@@ -48,7 +48,7 @@ class BaseAssistantAgent(BaseAgent):
|
|
|
48
48
|
def update_context(self, new_context: str) -> str: ...
|
|
49
49
|
|
|
50
50
|
class CodeAssistantAgent(BaseAssistantAgent):
|
|
51
|
-
|
|
51
|
+
__instructions__ = "I help with programming tasks and code review"
|
|
52
52
|
|
|
53
53
|
# Inherit user_name, session_id, conversation_context, and basic tools
|
|
54
54
|
# Add coding-specific state
|
|
@@ -69,7 +69,7 @@ Inheritance also works with tool overriding to enhance parent functionality:
|
|
|
69
69
|
|
|
70
70
|
```python
|
|
71
71
|
class AdvancedCodeAssistantAgent(CodeAssistantAgent):
|
|
72
|
-
|
|
72
|
+
__instructions__ = "I provide advanced programming assistance with security analysis"
|
|
73
73
|
|
|
74
74
|
# Override parent tool with enhanced functionality
|
|
75
75
|
@tool("Format and validate code snippet")
|
|
@@ -110,7 +110,7 @@ Simply include extensions in your agent's inheritance list:
|
|
|
110
110
|
|
|
111
111
|
```python
|
|
112
112
|
class ResearchAgent(BaseAgent, FileOperationsExtension, WebSearchExtension):
|
|
113
|
-
|
|
113
|
+
__instructions__ = "I help with research by searching the web and managing files"
|
|
114
114
|
|
|
115
115
|
research_topic: State[str] = spec.State(default="")
|
|
116
116
|
|
|
@@ -134,7 +134,7 @@ class ConversationExtension(AgentExtension):
|
|
|
134
134
|
def remember(self, info: str) -> str: ...
|
|
135
135
|
|
|
136
136
|
class ChatbotAgent(BaseAgent, MemoryExtension, ConversationExtension):
|
|
137
|
-
|
|
137
|
+
__instructions__ = "I am a conversational AI"
|
|
138
138
|
|
|
139
139
|
# MRO: ChatbotAgent -> MemoryExtension -> ConversationExtension -> BaseAgent
|
|
140
140
|
# The MemoryExtension.remember() method will be used
|
|
@@ -36,7 +36,7 @@ The BasicTracer is the simplest way to get started with tracing. It stores all t
|
|
|
36
36
|
from pyagentic import BaseAgent
|
|
37
37
|
|
|
38
38
|
class YourAgent(BaseAgent):
|
|
39
|
-
|
|
39
|
+
__instructions__ = "Your agent description"
|
|
40
40
|
# ... your agent implementation ...
|
|
41
41
|
|
|
42
42
|
# Create an agent - The BasicTracer is the default tracer of any declared agent
|
|
@@ -93,7 +93,7 @@ from pyagentic import BaseAgent
|
|
|
93
93
|
from pyagentic.tracing import LangfuseTracer
|
|
94
94
|
|
|
95
95
|
class YourAgent(BaseAgent):
|
|
96
|
-
|
|
96
|
+
__instructions__ = "Your agent description"
|
|
97
97
|
# ... your agent implementation ...
|
|
98
98
|
|
|
99
99
|
# Create an agent with the tracer
|
|
@@ -20,7 +20,7 @@ Define phases on your agent class using the `phases` class variable:
|
|
|
20
20
|
from pyagentic import BaseAgent, tool, spec
|
|
21
21
|
|
|
22
22
|
class ResearchAgent(BaseAgent):
|
|
23
|
-
|
|
23
|
+
__instructions__ = """
|
|
24
24
|
You are a research agent.
|
|
25
25
|
Current phase: {{ phase }}
|
|
26
26
|
{% if phase == "planning" %}
|
|
@@ -120,19 +120,19 @@ Linked agents can be restricted to specific phases using the `phases` parameter
|
|
|
120
120
|
from pyagentic import BaseAgent, tool, spec, Link
|
|
121
121
|
|
|
122
122
|
class PlannerAgent(BaseAgent):
|
|
123
|
-
|
|
123
|
+
__instructions__ = "I create detailed plans"
|
|
124
124
|
__description__ = "Planning specialist for strategy development"
|
|
125
125
|
|
|
126
126
|
class ExecutorAgent(BaseAgent):
|
|
127
|
-
|
|
127
|
+
__instructions__ = "I execute plans"
|
|
128
128
|
__description__ = "Execution specialist for implementing plans"
|
|
129
129
|
|
|
130
130
|
class ReviewerAgent(BaseAgent):
|
|
131
|
-
|
|
131
|
+
__instructions__ = "I review completed work"
|
|
132
132
|
__description__ = "Quality assurance and review specialist"
|
|
133
133
|
|
|
134
134
|
class ProjectAgent(BaseAgent):
|
|
135
|
-
|
|
135
|
+
__instructions__ = """
|
|
136
136
|
You manage projects through phases: {{ phase }}
|
|
137
137
|
"""
|
|
138
138
|
|
|
@@ -178,7 +178,7 @@ You can access the current phase in several ways:
|
|
|
178
178
|
Use the `{{ phase }}` variable in your system message template:
|
|
179
179
|
|
|
180
180
|
```python
|
|
181
|
-
|
|
181
|
+
__instructions__ = """
|
|
182
182
|
You are in the {{ phase }} phase.
|
|
183
183
|
{% if phase == "planning" %}
|
|
184
184
|
Create a detailed plan.
|
|
@@ -19,7 +19,7 @@ Attach them to an agent with the `__message_policies__` class attribute:
|
|
|
19
19
|
|
|
20
20
|
```python
|
|
21
21
|
class ResearchAgent(BaseAgent):
|
|
22
|
-
|
|
22
|
+
__instructions__ = "You research topics using tools."
|
|
23
23
|
__message_policies__ = [
|
|
24
24
|
ToolOutputClipPolicy(max_chars=8000),
|
|
25
25
|
ToolEvictionPolicy(keep_last_n=5),
|
|
@@ -55,7 +55,7 @@ class RangePolicy(Policy[int]):
|
|
|
55
55
|
|
|
56
56
|
|
|
57
57
|
class GameAgent(BaseAgent):
|
|
58
|
-
|
|
58
|
+
__instructions__ = "You run a text adventure."
|
|
59
59
|
|
|
60
60
|
score: State[int] = spec.State(
|
|
61
61
|
default=0,
|
|
@@ -141,7 +141,7 @@ class NoEmptyStrings(Policy):
|
|
|
141
141
|
|
|
142
142
|
|
|
143
143
|
class NotesAgent(BaseAgent):
|
|
144
|
-
|
|
144
|
+
__instructions__ = "You keep notes."
|
|
145
145
|
|
|
146
146
|
notes: State[list] = spec.State(default_factory=list, policies=[NoEmptyStrings()])
|
|
147
147
|
|
|
@@ -333,7 +333,7 @@ LLM saw) versus `agent.state._messages` (what actually happened):
|
|
|
333
333
|
@pytest.mark.asyncio
|
|
334
334
|
async def test_budget_policy_in_agent():
|
|
335
335
|
class _Agent(BaseAgent):
|
|
336
|
-
|
|
336
|
+
__instructions__ = "test"
|
|
337
337
|
__message_policies__ = [AgentResultBudgetPolicy(max_chars=100)]
|
|
338
338
|
|
|
339
339
|
agent = _Agent(model="_mock::test-model", api_key="k")
|
|
@@ -18,7 +18,7 @@ from pyagentic import BaseAgent, State, spec
|
|
|
18
18
|
from pyagentic.policies import ToolEvictionPolicy, CompactionPolicy
|
|
19
19
|
|
|
20
20
|
class SupportAgent(BaseAgent):
|
|
21
|
-
|
|
21
|
+
__instructions__ = "You help customers with {{ product }}."
|
|
22
22
|
|
|
23
23
|
# Message policies: keep the LLM context bounded
|
|
24
24
|
__message_policies__ = [
|
|
@@ -85,7 +85,7 @@ whether by your code, a tool body, or the autogenerated `get_*`/`set_*` tools.
|
|
|
85
85
|
|
|
86
86
|
```python
|
|
87
87
|
class GameAgent(BaseAgent):
|
|
88
|
-
|
|
88
|
+
__instructions__ = "You run a text adventure."
|
|
89
89
|
|
|
90
90
|
score: State[int] = spec.State(default=0, policies=[RangePolicy(0, 100)])
|
|
91
91
|
```
|
|
@@ -109,7 +109,7 @@ and results) and once more over the whole list right before each LLM call.
|
|
|
109
109
|
|
|
110
110
|
```python
|
|
111
111
|
class ResearchAgent(BaseAgent):
|
|
112
|
-
|
|
112
|
+
__instructions__ = "You research topics using tools."
|
|
113
113
|
__message_policies__ = [
|
|
114
114
|
ToolOutputClipPolicy(max_chars=8000),
|
|
115
115
|
ToolEvictionPolicy(keep_last_n=5),
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
# Prompt Management
|
|
2
|
+
|
|
3
|
+
Every agent declares its instructions — the system prompt the LLM receives — with the `__instructions__` class attribute. Instructions can be a plain string, but as agents mature, prompts tend to outgrow the code: they get edited more often than the logic around them, they need versioning, and non-developers may need to change them. PyAgentic's prompt engines let agents pull their instructions from a managed source instead of a hardcoded string.
|
|
4
|
+
|
|
5
|
+
## Naming
|
|
6
|
+
|
|
7
|
+
Two terms, one for each side of the rendering step:
|
|
8
|
+
|
|
9
|
+
- **Instructions** — the raw template you declare: a string or a prompt-engine reference. Available at runtime as `agent.state.instructions`.
|
|
10
|
+
- **System message** — the rendered result actually sent to the LLM, with state interpolated into the template. Available as `agent.state.system_message`.
|
|
11
|
+
|
|
12
|
+
```python
|
|
13
|
+
class ResearchAgent(BaseAgent):
|
|
14
|
+
__instructions__ = "You research {{ topic }}"
|
|
15
|
+
|
|
16
|
+
topic: State[str] = spec.State(default="transformers")
|
|
17
|
+
|
|
18
|
+
agent.state.instructions # "You research {{ topic }}"
|
|
19
|
+
agent.state.system_message # "You research transformers"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
!!! note "Deprecated: `__system_message__`"
|
|
23
|
+
Older versions used `__system_message__` for what is now `__instructions__`. The old name still works but emits a `DeprecationWarning` — new code should declare `__instructions__`.
|
|
24
|
+
|
|
25
|
+
## Prompt Engines
|
|
26
|
+
|
|
27
|
+
A `PromptEngine` is a source of managed prompts. Engines load prompt text by key and return a `PromptSource` carrying the text plus metadata (where it came from, which version, when it was loaded). Instead of assigning a string, assign a reference:
|
|
28
|
+
|
|
29
|
+
```python
|
|
30
|
+
from pyagentic import BaseAgent, LocalPromptEngine
|
|
31
|
+
|
|
32
|
+
prompts = LocalPromptEngine(".prompts")
|
|
33
|
+
|
|
34
|
+
class ResearchAgent(BaseAgent):
|
|
35
|
+
__instructions__ = prompts.ref("researcher")
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
`ref(key)` returns a `PromptRef` — a deferred pointer that is resolved **when the agent is instantiated**, not when the class is defined:
|
|
39
|
+
|
|
40
|
+
- Every `ResearchAgent(...)` re-reads the prompt, so edits are picked up by new instances without restarting anything.
|
|
41
|
+
- The prompt is stable for the lifetime of an instance — it never changes mid-conversation.
|
|
42
|
+
- `agent.fork()` re-resolves too, so forked linked-agent calls also get the latest version.
|
|
43
|
+
|
|
44
|
+
To pin a specific version instead of tracking the latest:
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
__instructions__ = prompts.ref("researcher", version="v2")
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## LocalPromptEngine
|
|
51
|
+
|
|
52
|
+
`LocalPromptEngine` serves prompts from files on the local file system. A **pattern** defines how your prompt storage is laid out under the root directory (default: `.prompts/` in the working directory). The pattern is a template with a required `{key}` placeholder and an optional `{version}` placeholder.
|
|
53
|
+
|
|
54
|
+
### Flat layout (default)
|
|
55
|
+
|
|
56
|
+
The default pattern is `"{key}.md"` — one file per prompt, versioned by content hash:
|
|
57
|
+
|
|
58
|
+
```
|
|
59
|
+
.prompts/
|
|
60
|
+
├── researcher.md
|
|
61
|
+
└── summarizer.md
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
prompts = LocalPromptEngine(".prompts")
|
|
66
|
+
prompts.load("researcher") # reads .prompts/researcher.md
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
Any flat layout works by changing the pattern, e.g. `pattern="{key}.txt"` or `pattern="agents/{key}/prompt.md"`.
|
|
70
|
+
|
|
71
|
+
### Versioned layout
|
|
72
|
+
|
|
73
|
+
When the pattern contains `{version}`, the file structure holds the versions and the engine derives versioning from it:
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
.prompts/
|
|
77
|
+
└── researcher/
|
|
78
|
+
├── v1.md
|
|
79
|
+
├── v2.md
|
|
80
|
+
└── v10.md
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
prompts = LocalPromptEngine(".prompts", pattern="{key}/{version}.md")
|
|
85
|
+
|
|
86
|
+
prompts.load("researcher") # latest → v10
|
|
87
|
+
prompts.load("researcher", version="v1") # pinned
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
An unpinned load picks the latest version using natural sorting, so `v10` correctly orders after `v2`. Version names are up to you — `v1`, `2026-07-08`, `1.0.3` all work. Flat versioned layouts like `pattern="{key}_{version}.md"` work the same way.
|
|
91
|
+
|
|
92
|
+
A missing key (or pinned version) raises `PromptNotFound`. Requesting a `version` on a pattern without `{version}` raises `ValueError`.
|
|
93
|
+
|
|
94
|
+
## Prompt provenance on responses
|
|
95
|
+
|
|
96
|
+
Every agent response records which prompt produced it in the `prompt` field, as a `PromptSource`:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
response = await agent.run("hello")
|
|
100
|
+
|
|
101
|
+
response.prompt.text # the raw template that was loaded
|
|
102
|
+
response.prompt.source # ".prompts/researcher/v2.md"
|
|
103
|
+
response.prompt.source_type # "local"
|
|
104
|
+
response.prompt.version # "v2"
|
|
105
|
+
response.prompt.loaded_at # when it was resolved
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
This works for plain-string instructions too: they produce an `inline` source named after the agent, with a content-hash version:
|
|
109
|
+
|
|
110
|
+
```python
|
|
111
|
+
class PlainAgent(BaseAgent):
|
|
112
|
+
__instructions__ = "You are plain"
|
|
113
|
+
|
|
114
|
+
response.prompt.source_type # "inline"
|
|
115
|
+
response.prompt.source # "PlainAgent"
|
|
116
|
+
response.prompt.version # "3f2a9c1b04de" (content hash)
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Because the version is always populated — from the file structure or a content hash — you can group or diff runs by prompt version regardless of where the prompt lives. The same metadata is available on the agent as `agent.state.prompt_source`.
|
|
120
|
+
|
|
121
|
+
## Writing a custom engine
|
|
122
|
+
|
|
123
|
+
Engines for other backends (a database, object storage, a prompt-management service) subclass `PromptEngine` and implement one method:
|
|
124
|
+
|
|
125
|
+
```python
|
|
126
|
+
from pyagentic import PromptEngine, PromptSource
|
|
127
|
+
|
|
128
|
+
class MyServiceEngine(PromptEngine):
|
|
129
|
+
def load(self, key: str, version: str | None = None) -> PromptSource:
|
|
130
|
+
record = my_service.get_prompt(key, version=version or "latest")
|
|
131
|
+
return PromptSource(
|
|
132
|
+
text=record.text,
|
|
133
|
+
source=f"my-service/{key}",
|
|
134
|
+
source_type="my-service",
|
|
135
|
+
version=record.version,
|
|
136
|
+
loaded_at=record.fetched_at,
|
|
137
|
+
)
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
`ref()`, instantiation-time resolution, and response provenance all come from the base class — a custom engine only decides how to fetch text and what to call a version.
|
|
@@ -48,3 +48,21 @@ Complete API documentation for PyAgentic's public interfaces.
|
|
|
48
48
|
### ref
|
|
49
49
|
|
|
50
50
|
::: pyagentic.ref
|
|
51
|
+
|
|
52
|
+
## Prompt Management
|
|
53
|
+
|
|
54
|
+
### PromptEngine
|
|
55
|
+
|
|
56
|
+
::: pyagentic.PromptEngine
|
|
57
|
+
|
|
58
|
+
### LocalPromptEngine
|
|
59
|
+
|
|
60
|
+
::: pyagentic.LocalPromptEngine
|
|
61
|
+
|
|
62
|
+
### PromptRef
|
|
63
|
+
|
|
64
|
+
::: pyagentic.PromptRef
|
|
65
|
+
|
|
66
|
+
### PromptSource
|
|
67
|
+
|
|
68
|
+
::: pyagentic.PromptSource
|
|
@@ -9,6 +9,7 @@ Every agent response is a Pydantic model with a predictable structure:
|
|
|
9
9
|
- `final_output: str` - The natural language response from the LLM
|
|
10
10
|
- `tool_responses: List[ToolResponse]` - Details of any tools called (if agent has tools)
|
|
11
11
|
- `agent_responses: List[AgentResponse]` - Responses from linked agents (if agent has linked agents)
|
|
12
|
+
- `prompt: PromptSource` - The prompt behind the agent's instructions, with source and version metadata
|
|
12
13
|
|
|
13
14
|
The response model is predetermined when the agent class is defined. Each agent automatically gets its own response class that knows exactly what tools and linked agents it can use. This means you get full type safety and IDE autocompletion before you ever run the agent.
|
|
14
15
|
|
|
@@ -88,3 +89,17 @@ response.agent_responses[0].final_output # Database agent response
|
|
|
88
89
|
```
|
|
89
90
|
|
|
90
91
|
This nested structure lets you trace exactly what each agent did and access the results of any sub-agent calls. The parent agent's response includes both its own tool calls and the complete responses from any linked agents it used.
|
|
92
|
+
|
|
93
|
+
## Prompt Provenance
|
|
94
|
+
|
|
95
|
+
Every response records which prompt produced it in the `prompt` field. For instructions loaded from a [prompt engine](prompts.md), the version comes from the engine; plain-string instructions get an `inline` source named after the agent, versioned by content hash:
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
response = await agent.run("hello")
|
|
99
|
+
|
|
100
|
+
response.prompt.source # ".prompts/researcher/v2.md" or "ResearchAgent"
|
|
101
|
+
response.prompt.source_type # "local" or "inline"
|
|
102
|
+
response.prompt.version # "v2" or a content hash
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Since the version is always populated, you can group or diff runs by prompt version — useful when evaluating prompt changes across a set of conversations.
|