agentme 0.22.1 → 0.24.0
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.
- package/.xdrs/agentme/edrs/application/019-ai-agents-development-standards.md +16 -0
- package/.xdrs/agentme/edrs/application/021-ai-workflow-development-standards.md +10 -127
- package/.xdrs/agentme/edrs/application/029-ai-workflow-naming-conventions.md +281 -0
- package/.xdrs/agentme/edrs/index.md +1 -0
- package/package.json +1 -1
|
@@ -197,6 +197,22 @@ The current OS is: [operating system name].
|
|
|
197
197
|
| `<OUTPUT_FORMAT>` | Required | MUST include a concrete schema or templated example; do not leave it vague. When multiple output formats are possible, MUST use mandatory language to specify exactly which one to use and explicitly exclude the others. |
|
|
198
198
|
| `<WORKFLOW_CONTEXT>` | Conditional | MUST be omitted for standalone agents. MUST be present when the agent runs as a node inside a LangGraph workflow. |
|
|
199
199
|
|
|
200
|
+
**Formatting rules:**
|
|
201
|
+
|
|
202
|
+
- MUST use XML tags to delimit every section.
|
|
203
|
+
- The content of each section MUST start on the line immediately after the opening tag — never inline with it.
|
|
204
|
+
- Each closing tag MUST be followed by a blank line before the next opening tag, so sections are visually separated.
|
|
205
|
+
|
|
206
|
+
```xml
|
|
207
|
+
<OBJECTIVE>
|
|
208
|
+
Produce a plan for the current batch of files.
|
|
209
|
+
</OBJECTIVE>
|
|
210
|
+
|
|
211
|
+
<ROLE>
|
|
212
|
+
You are the batch_plan_agent.
|
|
213
|
+
</ROLE>
|
|
214
|
+
```
|
|
215
|
+
|
|
200
216
|
#### 07-agent-output-format
|
|
201
217
|
|
|
202
218
|
The format of an agent's final output MUST be chosen based on who or what consumes it:
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: agentme-edr-policy-021-ai-workflow-development-standards
|
|
3
|
-
description: Defines the standard toolchain, framework, observability, and workflow patterns for building LangGraph workflows in Python. Use when scaffolding, reviewing, or extending AI workflow projects that orchestrate LLM calls, agents, and algorithmic nodes. For simple LLM calls see agentme-edr-018, for agentic patterns see agentme-edr-019.
|
|
3
|
+
description: Defines the standard toolchain, framework, observability, and workflow patterns for building LangGraph workflows in Python. Use when scaffolding, reviewing, or extending AI workflow projects that orchestrate LLM calls, agents, and algorithmic nodes. For simple LLM calls see agentme-edr-018, for agentic patterns see agentme-edr-019. For naming conventions (nodes, states, routes, judge output schema) see agentme-edr-029.
|
|
4
4
|
apply-to: AI workflow projects using LangGraph StateGraph built with Python
|
|
5
5
|
valid-from: 2026-06-05
|
|
6
6
|
---
|
|
@@ -105,34 +105,7 @@ Eval folder structure and script requirements are defined in [agentme-edr-028](0
|
|
|
105
105
|
|
|
106
106
|
#### 09-node-naming-conventions
|
|
107
107
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
| Convention | Node type | When to use |
|
|
111
|
-
|---|---|---|
|
|
112
|
-
| suffix `_llm` | LLM call | Any node whose primary action is a direct LLM inference call (see [agentme-edr-018](018-ai-llm-development-standards.md)) |
|
|
113
|
-
| suffix `_step` | Algorithmic step | Deterministic logic with no LLM involvement (transformation, validation, routing) |
|
|
114
|
-
| suffix `_tool` | Tool/API call | A node that wraps a single external tool or API (e.g. a REST endpoint, DB query) |
|
|
115
|
-
| suffix `_agent` | Subgraph agent | A node that invokes a nested subgraph containing its own tool-invocation cycle and LLM calls; use the **deepagents** library for these nodes (see [agentme-edr-019](019-ai-agents-development-standards.md)) |
|
|
116
|
-
| prefix `evaluate_` | Judge node | A node that evaluates the quality, correctness, completeness, or progress of prior outputs and returns a structured verdict; MUST follow rule `13-judge-node-output-format` |
|
|
117
|
-
|
|
118
|
-
The Python function implementing the node SHOULD share the same name as the node alias passed to `add_node`, so that graph definitions and stack traces remain unambiguous:
|
|
119
|
-
|
|
120
|
-
```python
|
|
121
|
-
def draft_doc_llm(state): ...
|
|
122
|
-
graph.add_node("draft_doc_llm", draft_doc_llm)
|
|
123
|
-
|
|
124
|
-
# Tool node — calls the Stripe API
|
|
125
|
-
def stripe_api_tool(state): ...
|
|
126
|
-
graph.add_node("stripe_api_tool", stripe_api_tool)
|
|
127
|
-
|
|
128
|
-
# Agent node — uses deepagents for tool-invocation loop
|
|
129
|
-
def code_reviewer_agent(state): ...
|
|
130
|
-
graph.add_node("code_reviewer_agent", code_reviewer_agent)
|
|
131
|
-
```
|
|
132
|
-
|
|
133
|
-
Names MUST NOT use generic labels such as `node1`, `process`, or `run`. Each name must clearly express what action the node performs.
|
|
134
|
-
|
|
135
|
-
Judge nodes use a **prefix** convention instead of a suffix: the name MUST start with `evaluate_` followed by the subject being judged (e.g. `evaluate_progress`, `evaluate_quality`, `evaluate_completeness`, `evaluate_relevance`). This makes judge nodes immediately distinguishable from all other node types at a glance.
|
|
108
|
+
See [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `01-node-naming-conventions`.
|
|
136
109
|
|
|
137
110
|
#### 10-workflow-unit-testing
|
|
138
111
|
|
|
@@ -186,110 +159,15 @@ Workflows MUST accept the LLM instance as a constructor parameter so that unit t
|
|
|
186
159
|
|
|
187
160
|
#### 11-state-type-conventions
|
|
188
161
|
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
**Naming reference:**
|
|
192
|
-
|
|
193
|
-
| Owner | Naming pattern | Example |
|
|
194
|
-
|---|---|---|
|
|
195
|
-
| Single agent / agent subgraph | `<agent_name>_agent_state` | `reviewer_agent_state` |
|
|
196
|
-
| Full workflow (`StateGraph`) | `<workflow_name>_workflow_state` | `document_workflow_state` |
|
|
197
|
-
| Named group of nodes sharing state | `<group_responsibility>_state` | `retrieval_pipeline_state` |
|
|
198
|
-
|
|
199
|
-
**Boundary rules:**
|
|
200
|
-
|
|
201
|
-
- Each agent or agent subgraph MUST define its own dedicated state type. Do not reuse or extend a generic state across unrelated agents.
|
|
202
|
-
- Each workflow (`StateGraph`) MUST define its own top-level state type. The workflow state is the authoritative boundary for that graph's inputs and outputs.
|
|
203
|
-
- When a group of nodes (not a full workflow and not a single agent) shares a state type, the type name MUST clearly reflect the shared responsibility. Generic names such as `shared_state`, `common_state`, or `global_state` are FORBIDDEN.
|
|
204
|
-
- Large workflows MUST NOT use a single monolithic state that all nodes read and write. Split the state into per-phase or per-agent state types scoped to the subgraph or set of nodes that produce or consume each field.
|
|
205
|
-
|
|
206
|
-
State type names SHOULD align with the agent or node names defined in rule `09-node-naming-conventions` (e.g., an agent node named `draft_doc_agent` has a state type named `draft_doc_agent_state`).
|
|
162
|
+
See [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `02-state-type-conventions`.
|
|
207
163
|
|
|
208
164
|
#### 12-workflow-naming-conventions
|
|
209
165
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
Choose a name that summarises what the workflow consumes, processes, and produces — avoid generic labels such as `Pipeline`, `Flow`, `Graph`, or `Process`.
|
|
213
|
-
|
|
214
|
-
| Context | Pattern | Example |
|
|
215
|
-
|---|---|---|
|
|
216
|
-
| Python class | `<DescriptiveName>Workflow` | `FileMapJudgeReduceWorkflow` |
|
|
217
|
-
| Python variable / instance | `<descriptive_name>_workflow` | `file_map_judge_reduce_workflow` |
|
|
218
|
-
| Directory under `app/workflows/` | `<descriptive_name>_workflow` | `financial_report_analysis_workflow/` |
|
|
219
|
-
|
|
220
|
-
**Good names** communicate purpose at a glance:
|
|
221
|
-
|
|
222
|
-
- `FileMapJudgeReduceWorkflow` — maps files, judges each, then reduces results
|
|
223
|
-
- `FinancialReportAnalysisWorkflow` — analyses financial report inputs
|
|
224
|
-
- `MarketingCampaignExecutorWorkflow` — executes a marketing campaign end-to-end
|
|
225
|
-
|
|
226
|
-
**Bad names** (FORBIDDEN): `MainWorkflow`, `AgentGraph`, `ProcessFlow`, `Workflow1`, `RunGraph`.
|
|
166
|
+
See [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `04-workflow-naming-conventions`.
|
|
227
167
|
|
|
228
168
|
#### 13-judge-node-output-format
|
|
229
169
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
**Required output schema:**
|
|
233
|
-
|
|
234
|
-
```python
|
|
235
|
-
from typing import Literal, Optional
|
|
236
|
-
from dataclasses import dataclass, field
|
|
237
|
-
|
|
238
|
-
FindingLevel = Literal["OK", "INFO", "WARNING", "ERROR"]
|
|
239
|
-
|
|
240
|
-
@dataclass
|
|
241
|
-
class JudgeFinding:
|
|
242
|
-
level: FindingLevel
|
|
243
|
-
# MUST: short action-oriented label; < 10 words
|
|
244
|
-
title: str
|
|
245
|
-
# MUST when level != "OK": why this is an issue; < 30 words
|
|
246
|
-
reason: Optional[str] = None
|
|
247
|
-
# MUST when level != "OK": notes/findings using mandatory (MUST) or advisory (SHOULD) language; < 400 words
|
|
248
|
-
details: Optional[str] = None
|
|
249
|
-
# OPTIONAL: possible fixes, only when directly inferrable from the finding without further analysis; < 200 words
|
|
250
|
-
fix: Optional[str] = None
|
|
251
|
-
|
|
252
|
-
@dataclass
|
|
253
|
-
class JudgeVerdict:
|
|
254
|
-
# MUST: highest severity level across all findings; "OK" only when every finding is "OK"
|
|
255
|
-
verdict: FindingLevel
|
|
256
|
-
# MUST: at least one finding present
|
|
257
|
-
findings: list[JudgeFinding] = field(default_factory=list)
|
|
258
|
-
```
|
|
259
|
-
|
|
260
|
-
Example (for logging, state storage, and inter-node communication):
|
|
261
|
-
|
|
262
|
-
```json
|
|
263
|
-
{
|
|
264
|
-
"verdict": "WARNING",
|
|
265
|
-
"findings": [
|
|
266
|
-
{
|
|
267
|
-
"level": "OK",
|
|
268
|
-
"title": "All required sections present"
|
|
269
|
-
},
|
|
270
|
-
{
|
|
271
|
-
"level": "WARNING",
|
|
272
|
-
"title": "Code coverage below threshold",
|
|
273
|
-
"reason": "Current coverage is 62%, minimum required is 80%.",
|
|
274
|
-
"details": "The following modules have no test coverage: auth.py, payments.py. SHOULD add unit tests for all public methods in these modules.",
|
|
275
|
-
"fix": "Add unit tests for auth.py and payments.py. Run `make test-coverage` to verify the threshold is met."
|
|
276
|
-
}
|
|
277
|
-
]
|
|
278
|
-
}
|
|
279
|
-
```
|
|
280
|
-
|
|
281
|
-
**Routing from judge nodes:**
|
|
282
|
-
|
|
283
|
-
Downstream conditional edges MUST route on `verdict` only:
|
|
284
|
-
|
|
285
|
-
```python
|
|
286
|
-
def route_after_evaluate_quality(state) -> str:
|
|
287
|
-
if state["evaluate_quality_result"].verdict in ("ERROR", "WARNING"):
|
|
288
|
-
return "revise_draft_llm"
|
|
289
|
-
return "publish_step"
|
|
290
|
-
```
|
|
291
|
-
|
|
292
|
-
**Logging:** Log `verdict` and the count of each level as MLflow metrics on the current run per rule `03-observability-and-experiment-tracking`.
|
|
170
|
+
See [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `03-judge-node-output-format`.
|
|
293
171
|
|
|
294
172
|
#### 15-workflow-state-persistence
|
|
295
173
|
|
|
@@ -320,8 +198,13 @@ result = graph.invoke(input_state, config={"thread_id": "session-123"})
|
|
|
320
198
|
- Workflows that may fail mid-execution and need to be retried from the last successful node
|
|
321
199
|
- Multi-session workflows where state persists across user interactions
|
|
322
200
|
|
|
201
|
+
#### 16-cross-element-naming-coherence
|
|
202
|
+
|
|
203
|
+
See [agentme-edr-029](029-ai-workflow-naming-conventions.md) rule `05-cross-element-naming-coherence`.
|
|
204
|
+
|
|
323
205
|
## References
|
|
324
206
|
|
|
207
|
+
- [agentme-edr-029](029-ai-workflow-naming-conventions.md) — AI workflow naming conventions: node suffixes/prefixes, state types, judge output schema, workflow names, and cross-element coherence
|
|
325
208
|
- [agentme-edr-018](018-ai-llm-development-standards.md) — LLM development standards: LangChain framework, provider configuration, LLM observability, and unit test mocking
|
|
326
209
|
- [agentme-edr-019](019-ai-agents-development-standards.md) — Agent development standards: deepagents framework, tool-invocation loops, and agent patterns
|
|
327
210
|
- [agentme-edr-026](026-pragmatic-hexagonal-architecture.md) — Adapter/application layer separation that defines the project layout
|
|
@@ -0,0 +1,281 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: agentme-edr-policy-029-ai-workflow-naming-conventions
|
|
3
|
+
description: Defines the naming vocabulary for LangGraph workflow elements: node suffixes/prefixes, state type names, state attribute grouping, workflow class/variable names, judge node output schema, route function names, and cross-element coherence rules. Use when naming any part of a LangGraph workflow — nodes, states, routes, or the workflow itself. For workflow structure and toolchain see agentme-edr-021.
|
|
4
|
+
apply-to: AI workflow projects using LangGraph StateGraph built with Python
|
|
5
|
+
valid-from: 2026-06-21
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# agentme-edr-policy-029: AI workflow naming conventions
|
|
9
|
+
|
|
10
|
+
## Context and Problem Statement
|
|
11
|
+
|
|
12
|
+
LangGraph workflows grow complex quickly. Without a shared naming vocabulary, node functions, state fields, route functions, and workflow classes diverge in style, making graphs hard to read, trace, and review. A reader should be able to follow the full lifecycle of a concept through a graph using names alone — without reading implementation code.
|
|
13
|
+
|
|
14
|
+
Which naming conventions should LangGraph workflow elements follow to make graphs self-documenting and unambiguous?
|
|
15
|
+
|
|
16
|
+
## Decision Outcome
|
|
17
|
+
|
|
18
|
+
**Adopt a suffix/prefix role convention for nodes, a `_state` suffix for state types, a grouping-prefix discipline for state attributes, and a coherence rule that ties all names to a single vocabulary word per concept.**
|
|
19
|
+
|
|
20
|
+
### Details
|
|
21
|
+
|
|
22
|
+
#### 01-node-naming-conventions
|
|
23
|
+
|
|
24
|
+
LangGraph node names MUST follow a suffix convention that communicates the node's role at a glance. Names MUST be action-oriented and descriptive.
|
|
25
|
+
|
|
26
|
+
| Convention | Node type | When to use |
|
|
27
|
+
|---|---|---|
|
|
28
|
+
| suffix `_llm` | LLM call | Any node whose primary action is a direct LLM inference call (see [agentme-edr-018](018-ai-llm-development-standards.md)) |
|
|
29
|
+
| suffix `_step` | Algorithmic step | Deterministic logic with no LLM involvement (transformation, validation, routing) |
|
|
30
|
+
| suffix `_tool` | Tool/API call | A node that wraps a single external tool or API (e.g. a REST endpoint, DB query) |
|
|
31
|
+
| suffix `_agent` | Subgraph agent | A node that invokes a nested subgraph containing its own tool-invocation cycle and LLM calls; use the **deepagents** library for these nodes (see [agentme-edr-019](019-ai-agents-development-standards.md)) |
|
|
32
|
+
| prefix `evaluate_` | Judge node | A node that evaluates the quality, correctness, completeness, or progress of prior outputs and returns a structured verdict; MUST follow rule `03-judge-node-output-format` |
|
|
33
|
+
|
|
34
|
+
The Python function implementing the node SHOULD share the same name as the node alias passed to `add_node`, so that graph definitions and stack traces remain unambiguous:
|
|
35
|
+
|
|
36
|
+
```python
|
|
37
|
+
def draft_doc_llm(state): ...
|
|
38
|
+
graph.add_node("draft_doc_llm", draft_doc_llm)
|
|
39
|
+
|
|
40
|
+
# Tool node — calls the Stripe API
|
|
41
|
+
def stripe_api_tool(state): ...
|
|
42
|
+
graph.add_node("stripe_api_tool", stripe_api_tool)
|
|
43
|
+
|
|
44
|
+
# Agent node — uses deepagents for tool-invocation loop
|
|
45
|
+
def code_reviewer_agent(state): ...
|
|
46
|
+
graph.add_node("code_reviewer_agent", code_reviewer_agent)
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Names MUST NOT use generic labels such as `node1`, `process`, or `run`. Each name must clearly express what action the node performs.
|
|
50
|
+
|
|
51
|
+
Judge nodes use a **prefix** convention instead of a suffix: the name MUST start with `evaluate_` followed by the subject being judged (e.g. `evaluate_progress`, `evaluate_quality`, `evaluate_completeness`, `evaluate_relevance`). This makes judge nodes immediately distinguishable from all other node types at a glance.
|
|
52
|
+
|
|
53
|
+
**Grouping prefix for related nodes:** When multiple nodes deal with the same subject, entity, or workflow region, SHOULD use a shared grouping word as a prefix followed by a verb and the role suffix. The pattern is `<group>_<verb>_<role_suffix>`. This makes the graph topology scannable and clusters related nodes together alphabetically in logs, traces, and code.
|
|
54
|
+
|
|
55
|
+
```python
|
|
56
|
+
# Nodes grouped under the "invoice" subject
|
|
57
|
+
def invoice_fetch_tool(state): ... # fetches invoice data from an API
|
|
58
|
+
def invoice_validate_step(state): ... # validates invoice fields deterministically
|
|
59
|
+
def invoice_summarize_llm(state): ... # summarizes invoice content with an LLM
|
|
60
|
+
def invoice_review_agent(state): ... # runs an agent loop to review the invoice
|
|
61
|
+
|
|
62
|
+
graph.add_node("invoice_fetch_tool", invoice_fetch_tool)
|
|
63
|
+
graph.add_node("invoice_validate_step", invoice_validate_step)
|
|
64
|
+
graph.add_node("invoice_summarize_llm", invoice_summarize_llm)
|
|
65
|
+
graph.add_node("invoice_review_agent", invoice_review_agent)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
The grouping prefix is optional for workflows where all nodes clearly belong to a single domain. It MUST be used when a workflow spans multiple subjects or regions (e.g. `invoice_*`, `payment_*`, `notification_*`) to prevent name collisions and to make the graph structure self-documenting.
|
|
69
|
+
|
|
70
|
+
Grouping names MUST be consistent across the entire workflow. Do not use synonyms or near-synonyms for the same concept (e.g. do not mix `invoice_*` and `bill_*`, or `user_*` and `account_*`, when they refer to the same entity). Pick one word per concept and apply it everywhere.
|
|
71
|
+
|
|
72
|
+
**Semantic word order in compound names:** When a name combines multiple semantic parts (group prefix, action, subject, role suffix), adjacent words MUST group semantically related concepts together. Do not insert an unrelated word between two words that belong together. More explicit names are preferred over ambiguous orderings, provided the name is not already verbose.
|
|
73
|
+
|
|
74
|
+
The test: read each pair of adjacent words — they should feel like a natural phrase. If an unrelated word splits two related words apart, reorder.
|
|
75
|
+
|
|
76
|
+
```python
|
|
77
|
+
# Preferred: "evaluate_skip" groups the evaluation and the action being evaluated
|
|
78
|
+
def map_evaluate_skip_step(state): ... # map phase → evaluates whether to skip → deterministic
|
|
79
|
+
|
|
80
|
+
# Avoid: "skip" splits the "map" group from the "evaluate" role
|
|
81
|
+
def map_skip_evaluate_step(state): ... # ambiguous: does it skip, or does it evaluate a skip?
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
This applies equally to state attributes and route function names.
|
|
85
|
+
|
|
86
|
+
#### 02-state-type-conventions
|
|
87
|
+
|
|
88
|
+
All TypedDict and dataclass types that represent LangGraph node or workflow state MUST end with `_state` in their name. This suffix signals at a glance that the type is a state boundary, not a plain data model.
|
|
89
|
+
|
|
90
|
+
**Naming reference:**
|
|
91
|
+
|
|
92
|
+
| Owner | Naming pattern | Example |
|
|
93
|
+
|---|---|---|
|
|
94
|
+
| Single agent / agent subgraph | `<agent_name>_agent_state` | `reviewer_agent_state` |
|
|
95
|
+
| Full workflow (`StateGraph`) | `<workflow_name>_workflow_state` | `document_workflow_state` |
|
|
96
|
+
| Named group of nodes sharing state | `<group_responsibility>_state` | `retrieval_pipeline_state` |
|
|
97
|
+
|
|
98
|
+
**Boundary rules:**
|
|
99
|
+
|
|
100
|
+
- Each agent or agent subgraph MUST define its own dedicated state type. Do not reuse or extend a generic state across unrelated agents.
|
|
101
|
+
- Each workflow (`StateGraph`) MUST define its own top-level state type. The workflow state is the authoritative boundary for that graph's inputs and outputs.
|
|
102
|
+
- When a group of nodes (not a full workflow and not a single agent) shares a state type, the type name MUST clearly reflect the shared responsibility. Generic names such as `shared_state`, `common_state`, or `global_state` are FORBIDDEN.
|
|
103
|
+
- Large workflows MUST NOT use a single monolithic state that all nodes read and write. Split the state into per-phase or per-agent state types scoped to the subgraph or set of nodes that produce or consume each field.
|
|
104
|
+
|
|
105
|
+
State type names SHOULD align with the agent or node names defined in rule `01-node-naming-conventions` (e.g., an agent node named `draft_doc_agent` has a state type named `draft_doc_agent_state`).
|
|
106
|
+
|
|
107
|
+
**State attribute naming — grouping and consistency:**
|
|
108
|
+
|
|
109
|
+
State attributes MUST follow the same grouping-prefix discipline as node names. When multiple attributes belong to the same subject, entity, or workflow phase, they MUST share a common prefix so that related fields cluster together and the state definition is self-documenting.
|
|
110
|
+
|
|
111
|
+
- Use `<group>_<attribute>` for fields that belong to a specific subject or phase (e.g. `invoice_raw`, `invoice_validated`, `invoice_summary`).
|
|
112
|
+
- The grouping prefix MUST be the same word used in the corresponding node names for that subject (e.g. nodes named `invoice_fetch_tool`, `invoice_validate_step` → state fields named `invoice_raw`, `invoice_validated`).
|
|
113
|
+
- Do not use synonyms or near-synonyms for the same concept across attributes or across nodes and attributes (e.g. do not mix `invoice_*` fields with `bill_*` fields, or `user_*` fields with `account_*` fields when they refer to the same entity). Pick one word per concept and apply it everywhere.
|
|
114
|
+
|
|
115
|
+
```python
|
|
116
|
+
class document_workflow_state(TypedDict):
|
|
117
|
+
# "invoice" group — all fields related to the invoice entity
|
|
118
|
+
invoice_raw: str
|
|
119
|
+
invoice_validated: bool
|
|
120
|
+
invoice_summary: str
|
|
121
|
+
|
|
122
|
+
# "payment" group — all fields related to the payment entity
|
|
123
|
+
payment_status: str
|
|
124
|
+
payment_amount: float
|
|
125
|
+
|
|
126
|
+
# "evaluate" group — judge verdicts
|
|
127
|
+
evaluate_invoice_verdict: JudgeVerdict
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
Generic attribute names such as `data`, `result`, `output`, `info`, or `item` are FORBIDDEN unless they are top-level workflow inputs/outputs with no meaningful domain label.
|
|
131
|
+
|
|
132
|
+
#### 03-judge-node-output-format
|
|
133
|
+
|
|
134
|
+
Every node whose name starts with `evaluate_` (a judge node) MUST return a structured verdict object as its output. This ensures all judge nodes are interchangeable and their results can be uniformly consumed by downstream routing logic, logged, and compared across runs.
|
|
135
|
+
|
|
136
|
+
**Required output schema:**
|
|
137
|
+
|
|
138
|
+
```python
|
|
139
|
+
from typing import Literal, Optional
|
|
140
|
+
from dataclasses import dataclass, field
|
|
141
|
+
|
|
142
|
+
FindingLevel = Literal["OK", "INFO", "WARNING", "ERROR"]
|
|
143
|
+
|
|
144
|
+
@dataclass
|
|
145
|
+
class JudgeFinding:
|
|
146
|
+
level: FindingLevel
|
|
147
|
+
# MUST: short action-oriented label; < 10 words
|
|
148
|
+
title: str
|
|
149
|
+
# MUST when level != "OK": why this is an issue; < 30 words
|
|
150
|
+
reason: Optional[str] = None
|
|
151
|
+
# MUST when level != "OK": notes/findings using mandatory (MUST) or advisory (SHOULD) language; < 400 words
|
|
152
|
+
details: Optional[str] = None
|
|
153
|
+
# OPTIONAL: possible fixes, only when directly inferrable from the finding without further analysis; < 200 words
|
|
154
|
+
fix: Optional[str] = None
|
|
155
|
+
|
|
156
|
+
@dataclass
|
|
157
|
+
class JudgeVerdict:
|
|
158
|
+
# MUST: highest severity level across all findings; "OK" only when every finding is "OK"
|
|
159
|
+
verdict: FindingLevel
|
|
160
|
+
# MUST: at least one finding present
|
|
161
|
+
findings: list[JudgeFinding] = field(default_factory=list)
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
Example (for logging, state storage, and inter-node communication):
|
|
165
|
+
|
|
166
|
+
```json
|
|
167
|
+
{
|
|
168
|
+
"verdict": "WARNING",
|
|
169
|
+
"findings": [
|
|
170
|
+
{
|
|
171
|
+
"level": "OK",
|
|
172
|
+
"title": "All required sections present"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"level": "WARNING",
|
|
176
|
+
"title": "Code coverage below threshold",
|
|
177
|
+
"reason": "Current coverage is 62%, minimum required is 80%.",
|
|
178
|
+
"details": "The following modules have no test coverage: auth.py, payments.py. SHOULD add unit tests for all public methods in these modules.",
|
|
179
|
+
"fix": "Add unit tests for auth.py and payments.py. Run `make test-coverage` to verify the threshold is met."
|
|
180
|
+
}
|
|
181
|
+
]
|
|
182
|
+
}
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Routing from judge nodes:**
|
|
186
|
+
|
|
187
|
+
Downstream conditional edges MUST route on `verdict` only:
|
|
188
|
+
|
|
189
|
+
```python
|
|
190
|
+
def route_after_evaluate_quality(state) -> str:
|
|
191
|
+
if state["evaluate_quality_result"].verdict in ("ERROR", "WARNING"):
|
|
192
|
+
return "revise_draft_llm"
|
|
193
|
+
return "publish_step"
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
**Logging:** Log `verdict` and the count of each level as MLflow metrics on the current run per [agentme-edr-021](021-ai-workflow-development-standards.md) rule `03-observability-and-experiment-tracking`.
|
|
197
|
+
|
|
198
|
+
#### 04-workflow-naming-conventions
|
|
199
|
+
|
|
200
|
+
LangGraph `StateGraph` instances and their enclosing classes MUST be given a meaningful name that conveys the workflow's input, output, and/or behavior. The name MUST end with `Workflow` (PascalCase class) or `_workflow` (snake_case variable or directory).
|
|
201
|
+
|
|
202
|
+
Choose a name that summarises what the workflow consumes, processes, and produces — avoid generic labels such as `Pipeline`, `Flow`, `Graph`, or `Process`.
|
|
203
|
+
|
|
204
|
+
| Context | Pattern | Example |
|
|
205
|
+
|---|---|---|
|
|
206
|
+
| Python class | `<DescriptiveName>Workflow` | `FileMapJudgeReduceWorkflow` |
|
|
207
|
+
| Python variable / instance | `<descriptive_name>_workflow` | `file_map_judge_reduce_workflow` |
|
|
208
|
+
| Directory under `app/workflows/` | `<descriptive_name>_workflow` | `financial_report_analysis_workflow/` |
|
|
209
|
+
|
|
210
|
+
**Good names** communicate purpose at a glance:
|
|
211
|
+
|
|
212
|
+
- `FileMapJudgeReduceWorkflow` — maps files, judges each, then reduces results
|
|
213
|
+
- `FinancialReportAnalysisWorkflow` — analyses financial report inputs
|
|
214
|
+
- `MarketingCampaignExecutorWorkflow` — executes a marketing campaign end-to-end
|
|
215
|
+
|
|
216
|
+
**Bad names** (FORBIDDEN): `MainWorkflow`, `AgentGraph`, `ProcessFlow`, `Workflow1`, `RunGraph`.
|
|
217
|
+
|
|
218
|
+
#### 05-cross-element-naming-coherence
|
|
219
|
+
|
|
220
|
+
All naming across a workflow MUST form a coherent, self-documenting vocabulary. By reading any single name — a workflow class, a node function, a state type, a state attribute, or a route function — it MUST be immediately clear what entity or phase it belongs to, what role it plays, and where its output lives in the state. A reader MUST be able to trace the full lifecycle of a concept through the graph using names alone, without reading implementation code.
|
|
221
|
+
|
|
222
|
+
**Coherence rules:**
|
|
223
|
+
|
|
224
|
+
1. **Workflow → state → directory:** A workflow class named `InvoiceAnalysisWorkflow` MUST own a top-level state named `invoice_analysis_workflow_state` and live in directory `invoice_analysis_workflow/`. The same root phrase (`invoice_analysis_workflow`) MUST appear in all three.
|
|
225
|
+
|
|
226
|
+
2. **Node → state field:** When a node writes a result to state, the state field MUST share the node's grouping prefix and communicate what was produced. The node `invoice_summarize_llm` writes to `invoice_summary`, not to `summary`, `llm_output`, or `result`. The group prefix (`invoice_`) MUST match exactly.
|
|
227
|
+
|
|
228
|
+
3. **Route → node:** Route functions MUST be named `route_after_<node_name>` using the exact name of the node they follow (e.g. `route_after_evaluate_invoice`). Do not name routes after what they decide — name them after their position in the graph.
|
|
229
|
+
|
|
230
|
+
4. **Agent node → agent state:** An agent node named `code_review_agent` MUST have a corresponding state type named `code_review_agent_state`. Do not reuse the parent workflow state or a generic state for the agent's internal fields.
|
|
231
|
+
|
|
232
|
+
5. **One word per concept, everywhere:** A concept introduced as `invoice` in a node name MUST remain `invoice` in every state attribute, route function, and subgraph that refers to it. Synonyms, abbreviations, and near-synonyms (e.g. mixing `invoice_*` with `bill_*`, or `user_*` with `account_*` for the same entity) are FORBIDDEN.
|
|
233
|
+
|
|
234
|
+
**Coherent example:**
|
|
235
|
+
|
|
236
|
+
```python
|
|
237
|
+
# Workflow and state share the same root phrase
|
|
238
|
+
class InvoiceProcessingWorkflow: ...
|
|
239
|
+
|
|
240
|
+
class invoice_processing_workflow_state(TypedDict):
|
|
241
|
+
invoice_raw: str # written by invoice_fetch_tool
|
|
242
|
+
invoice_validated: bool # written by invoice_validate_step
|
|
243
|
+
invoice_summary: str # written by invoice_summarize_llm
|
|
244
|
+
evaluate_invoice_verdict: JudgeVerdict # written by evaluate_invoice
|
|
245
|
+
|
|
246
|
+
# Node names align with the state fields they produce
|
|
247
|
+
def invoice_fetch_tool(state): ... # → invoice_raw
|
|
248
|
+
def invoice_validate_step(state): ... # → invoice_validated
|
|
249
|
+
def invoice_summarize_llm(state): ... # → invoice_summary
|
|
250
|
+
def evaluate_invoice(state): ... # → evaluate_invoice_verdict
|
|
251
|
+
|
|
252
|
+
# Route named after the exact node it follows
|
|
253
|
+
def route_after_evaluate_invoice(state) -> str:
|
|
254
|
+
if state["evaluate_invoice_verdict"].verdict == "ERROR":
|
|
255
|
+
return "invoice_summarize_llm"
|
|
256
|
+
return END
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
**Incoherent counter-example (FORBIDDEN):**
|
|
260
|
+
|
|
261
|
+
```python
|
|
262
|
+
class InvoiceProcessingWorkflow: ...
|
|
263
|
+
|
|
264
|
+
class billing_pipeline_state(TypedDict): # FORBIDDEN: "billing" ≠ "invoice"
|
|
265
|
+
raw_data: str # FORBIDDEN: no group prefix
|
|
266
|
+
is_valid: bool # FORBIDDEN: no group prefix
|
|
267
|
+
summary: str # FORBIDDEN: which node produced this?
|
|
268
|
+
llm_result: JudgeVerdict # FORBIDDEN: which judge, which subject?
|
|
269
|
+
|
|
270
|
+
def fetch_invoice(state): ... # FORBIDDEN: no role suffix
|
|
271
|
+
def validate(state): ... # FORBIDDEN: no group, no suffix
|
|
272
|
+
def summarize_invoice(state): ... # FORBIDDEN: verb before group (wrong order)
|
|
273
|
+
def check_quality(state): ... # FORBIDDEN: "check" ≠ "evaluate" prefix
|
|
274
|
+
def after_quality_check(state): ... # FORBIDDEN: not named "route_after_<node>"
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## References
|
|
278
|
+
|
|
279
|
+
- [agentme-edr-021](021-ai-workflow-development-standards.md) — Workflow structure, LangGraph toolchain, observability, and testing patterns
|
|
280
|
+
- [agentme-edr-018](018-ai-llm-development-standards.md) — LLM development standards (drives `_llm` node suffix and mocking patterns)
|
|
281
|
+
- [agentme-edr-019](019-ai-agents-development-standards.md) — Agent development standards (drives `_agent` node suffix and state conventions)
|
|
@@ -35,6 +35,7 @@ Language and framework-specific tooling and project structure.
|
|
|
35
35
|
- [agentme-edr-019](application/019-ai-agents-development-standards.md) - **AI agents development standards** - Structural patterns for agents: framework selection, sandbox setup, naming conventions, composition, and system prompt structure
|
|
36
36
|
- [agentme-edr-020](application/020-ai-agents-quality-standards.md) - **AI agents implementation quality standards** - Tool definition patterns, error handling, observability, and unit testing for agents
|
|
37
37
|
- [agentme-edr-021](application/021-ai-workflow-development-standards.md) - **AI workflow development standards** - Standard toolchain (LangGraph), evaluation, and testing patterns for workflow projects
|
|
38
|
+
- [agentme-edr-029](application/029-ai-workflow-naming-conventions.md) - **AI workflow naming conventions** - Node suffix/prefix roles, state type and attribute naming, judge output schema, workflow class names, and cross-element coherence rules
|
|
38
39
|
- [agentme-edr-028](application/028-ai-eval-standards.md) - **AI eval standards** - Folder structure, script requirements, and MLflow tracking for eval tests across LLM, Agent, and Workflow tiers
|
|
39
40
|
- [agentme-edr-024](application/024-ml-dataset-structure.md) - **ML dataset structure** - Standard folder layout and file conventions for ML datasets
|
|
40
41
|
- [agentme-edr-025](application/025-ai-agent-xdrs-knowledge-layer.md) - **AI agent XDRS knowledge layer** - How to integrate XDRS as the runtime source of truth for policies and skills in AI agents (apply only when the project explicitly uses XDRS)
|