4.5 KiB
Gotchas — Common Pitfalls When Converting Prompt Flow to MAF
Read this every time you generate or debug MAF code. These are mistakes the agent makes repeatedly.
High-frequency (almost always relevant)
1. Agent.run() returns AgentResponse, not str
Always use response.text to get the string output, then pass that to ctx.yield_output().
2. No AzureOpenAIChatClient class
Use OpenAIChatClient with azure_endpoint=... for Azure routing.
3. @handler message type must match upstream
If the upstream executor sends a str via ctx.send_message(str), the downstream @handler parameter must be typed as str.
4. Never name a @handler method execute
The base Executor class has an execute() method that the workflow engine calls with internal arguments (message, source_executor_ids, state, runner_context, trace_contexts, source_span_ids). If a subclass defines a @handler method also named execute, it shadows the base method, causing TypeError: got an unexpected keyword argument 'trace_contexts' at runtime. Use any other name (e.g., run_code, process, handle, invoke).
5. MAF workflows do not support concurrent run() calls
Calling workflow.run() on an instance that is already running throws RuntimeError: Workflow is already running. Concurrent executions are not allowed. Always export a create_workflow() factory function and create a fresh instance per invocation. This applies to all workflows — not just evaluation flows — so callers can safely parallelize.
6. Preserve LLM parameters from the original flow
If the Prompt Flow YAML sets temperature, max_tokens, etc. on an LLM node, these MUST be carried over to the MAF Agent.run() call via OpenAIChatOptions. Omitting them changes model behavior (e.g., higher temperature = less deterministic outputs, missing max_tokens = truncated/verbose responses).
7. LLM responses wrapped in markdown fences
Modern LLMs often wrap JSON output in ```json ... ``` code fences even when not asked to. When parsing JSON from Agent.run() responses, always strip markdown fences before calling json.loads():
text = response.text.strip()
if text.startswith("```"):
text = text.split("\n", 1)[1].rsplit("```", 1)[0].strip()
result = json.loads(text)
Without this, json.loads() raises JSONDecodeError and the fallback silently returns wrong results.
Mid-frequency (situation-specific)
8. Chat history cannot be passed as list[dict] to Agent.run()
Format it into a single prompt string instead.
9. Fan-in delivers list[T]
The aggregator's @handler receives a list of all upstream messages.
10. Condition functions receive the message
condition=fn where fn(message) -> bool.
11. Environment variables auto-read
OpenAIChatClient can auto-read AZURE_OPENAI_ENDPOINT, AZURE_OPENAI_API_KEY, AZURE_OPENAI_CHAT_MODEL from env, but explicit constructor args are clearer.
12. Internal package imports
When a flow imports from sibling Python packages (e.g., from my_utils.helpers import build_index), copy the entire package directory into the MAF output folder. Do not rewrite utility code. The Executor files import directly from the local copy since they live in the same directory. Do not use sys.path hacks.
Topic-specific (only when applicable)
13. Multimodal inputs require Message, not str
When a flow has image inputs (e.g., GPT-4V), you must build a Message("user", [Content.from_uri(...), "text"]) and pass it to Agent.run(). Joining image URLs into a plain string will NOT send the image to the model. See topics/multimodal.md.
14. Prompt Flow image format — handle both forms
Prompt Flow image inputs come in two formats:
- Dict format (from CLI):
{"data:image/png;url": "https://example.com/img.png"}— extract the URL from the dict value - String format (from YAML defaults):
"data:image/png;url: https://example.com/img.png"— parse the URL afterurl:
Both must be converted to Content.from_uri(url, media_type="image/png").
15. Evaluation aggregation functions must return a dict
The original PromptFlow aggregation nodes call log_metric(key, value) to report metrics. In MAF, replace these with a returned dict mapping metric names to values. Remove all log_metric imports and calls. See topics/evaluation-flows.md.