.NET: Python: Add AGENTS.md files and update coding standards (#3644)

* Add AGENTS.md files and update coding standards for Python

- Add root python/AGENTS.md with project structure and package links
- Add AGENTS.md for each package describing purpose and main classes
- Update .github/copilot-instructions.md with improved structure
- Update python/CODING_STANDARD.md with API review guidance:
  - Future annotations convention (#3578)
  - TypeVar naming convention (#3594)
  - Mapping vs MutableMapping (#3577)
  - Avoid shadowing built-ins (#3583)
  - Explicit exports (#3605)
  - Exception documentation guidelines (#3410)
- Simplify python/.github/instructions/python.instructions.md to reference AGENTS.md
- Remove AGENTS.md from .gitignore

* Fix purview import path in AGENTS.md

* Address PR review comments and restructure instructions

- Slim down .github/copilot-instructions.md to reference language-specific docs
- Add ADR section explaining templates and purpose
- Create dotnet/AGENTS.md with .NET-specific build commands, conventions, and sample guidance
- Update Python build/test instructions for core vs isolated changes
- Fix Microsoft.Extensions.AI package references
- Update kwargs guidance per issue #3642
- Fix Python sample helper placement (top, not bottom)
- Document new 'typing' poe task in DEV_SETUP.md

* Add 'typing' poe task to run both pyright and mypy

* Add kwargs guidelines from issue #3642 to CODING_STANDARD.md

* Clarify that connector packages pull in core as dependency
This commit is contained in:
Eduard van Valkenburg
2026-02-05 11:27:46 +01:00
committed by GitHub
Unverified
parent de80543302
commit eaad042241
30 changed files with 1026 additions and 201 deletions
+93 -9
View File
@@ -10,12 +10,66 @@ We use [ruff](https://github.com/astral-sh/ruff) for both linting and formatting
- **Target Python version**: 3.10+
- **Google-style docstrings**: All public functions, classes, and modules should have docstrings following Google conventions
## Type Annotations
### Future Annotations
> **Note:** This convention is being adopted. See [#3578](https://github.com/microsoft/agent-framework/issues/3578) for progress.
Use `from __future__ import annotations` at the top of files to enable postponed evaluation of annotations. This prevents the need for string-based type hints for forward references:
```python
# ✅ Preferred - use future annotations
from __future__ import annotations
class Agent:
def create_child(self) -> Agent: # No quotes needed
...
# ❌ Avoid - string-based type hints
class Agent:
def create_child(self) -> "Agent": # Requires quotes without future annotations
...
```
### TypeVar Naming Convention
> **Note:** This convention is being adopted. See [#3594](https://github.com/microsoft/agent-framework/issues/3594) for progress.
Use the suffix `T` for TypeVar names instead of a prefix:
```python
# ✅ Preferred - suffix T
ChatResponseT = TypeVar("ChatResponseT", bound=ChatResponse)
AgentT = TypeVar("AgentT", bound=Agent)
# ❌ Avoid - prefix T
TChatResponse = TypeVar("TChatResponse", bound=ChatResponse)
TAgent = TypeVar("TAgent", bound=Agent)
```
### Mapping Types
> **Note:** This convention is being adopted. See [#3577](https://github.com/microsoft/agent-framework/issues/3577) for progress.
Use `Mapping` instead of `MutableMapping` for input parameters when mutation is not required:
```python
# ✅ Preferred - Mapping for read-only access
def process_config(config: Mapping[str, Any]) -> None:
...
# ❌ Avoid - MutableMapping when mutation isn't needed
def process_config(config: MutableMapping[str, Any]) -> None:
...
```
## Function Parameter Guidelines
To make the code easier to use and maintain:
- **Positional parameters**: Only use for up to 3 fully expected parameters
- **Keyword parameters**: Use for all other parameters, especially when there are multiple required parameters without obvious ordering
- **Positional parameters**: Only use for up to 3 fully expected parameters (this is not a hard rule, but a guideline there are instances where this does make sense to exceed)
- **Keyword-only parameters**: Arguments after `*` in function signatures are keyword-only; prefer these for optional parameters
- **Avoid additional imports**: Do not require the user to import additional modules to use the function, so provide string based overrides when applicable, for instance:
```python
def create_agent(name: str, tool_mode: ChatToolMode) -> Agent:
@@ -28,8 +82,19 @@ def create_agent(name: str, tool_mode: Literal['auto', 'required', 'none'] | Cha
if isinstance(tool_mode, str):
tool_mode = ChatToolMode(tool_mode)
```
- **Document kwargs**: Always document how `kwargs` are used, either by referencing external documentation or explaining their purpose
- **Separate kwargs**: When combining kwargs for multiple purposes, use specific parameters like `client_kwargs: dict[str, Any]` instead of mixing everything in `**kwargs`
- **Avoid shadowing built-ins**: Do not use parameter names that shadow Python built-ins (e.g., use `next_handler` instead of `next`). See [#3583](https://github.com/microsoft/agent-framework/issues/3583) for progress.
### Using `**kwargs`
> **Note:** This convention is being adopted. See [#3642](https://github.com/microsoft/agent-framework/issues/3642) for progress.
Avoid `**kwargs` unless absolutely necessary. It should only be used as an escape route, not for well-known flows of data:
- **Prefer named parameters**: If there are known extra arguments being passed, use explicit named parameters instead of kwargs
- **Subclassing support**: kwargs is acceptable in methods that are part of classes designed for subclassing, allowing subclass-defined kwargs to pass through without issues. In this case, clearly document that kwargs exists for subclass extensibility and not for passing arbitrary data
- **Remove when possible**: In other cases, removing kwargs is likely better than keeping it
- **Separate kwargs by purpose**: When combining kwargs for multiple purposes, use specific parameters like `client_kwargs: dict[str, Any]` instead of mixing everything in `**kwargs`
- **Always document**: If kwargs must be used, always document how it's used, either by referencing external documentation or explaining its purpose
## Method Naming Inside Connectors
@@ -212,7 +277,7 @@ pip install agent-framework-core[all]
# or (equivalently):
pip install agent-framework
# Install specific connector
# Install specific connector (pulls in core as dependency)
pip install agent-framework-azure-ai
```
@@ -234,10 +299,9 @@ They should contain:
- Type and default values do not have to be specified, they will be pulled from the definition.
- Returns are specified after a header called `Returns:` or `Yields:`, with the return type and explanation of the return value.
- Keyword arguments are specified after a header called `Keyword Args:`, with each argument being specified in the same format as `Args:`.
- A header for exceptions can be added, called `Raises:`, but should only be used for:
- Agent Framework specific exceptions (e.g., `ServiceInitializationError`)
- Base exceptions that might be unexpected in the context
- Obvious exceptions like `ValueError` or `TypeError` do not need to be documented
- A header for exceptions can be added, called `Raises:`, following these guidelines:
- **Always document** Agent Framework specific exceptions (e.g., `AgentInitializationError`, `AgentExecutionException`)
- **Only document** standard Python exceptions (TypeError, ValueError, KeyError, etc.) when the condition is non-obvious or provides value to API users
- Format: `ExceptionType`: Explanation of the exception.
- If a longer explanation is needed, it should be placed on the next line, indented by 4 spaces.
- Code examples can be added using the `Examples:` header followed by `.. code-block:: python` directive.
@@ -328,6 +392,26 @@ def create_agent(name: str, chat_client: ChatClientProtocol) -> Agent:
If in doubt, use the link above to read much more considerations of what to do and when, or use common sense.
## Public API and Exports
### Explicit Exports
> **Note:** This convention is being adopted. See [#3605](https://github.com/microsoft/agent-framework/issues/3605) for progress.
Define `__all__` in each module to explicitly declare the public API. Avoid using `from module import *` in `__init__.py` files as it can impact performance and makes the public API unclear:
```python
# ✅ Preferred - explicit __all__ and imports
__all__ = ["ChatAgent", "ChatMessage", "ChatResponse"]
from ._agents import ChatAgent
from ._types import ChatMessage, ChatResponse
# ❌ Avoid - star imports
from ._agents import *
from ._types import *
```
## Performance considerations
### Cache Expensive Computations