Files
microsoft--promptflow/.github/skills/promptflow-to-maf/references/gotchas.md
T
wehub-resource-sync e768098d0e
Flake8 Lint / flake8 (push) Waiting to run
Publish Promptflow Doc / Build (push) Waiting to run
Publish Promptflow Doc / Deploy (push) Blocked by required conditions
Spell check CI / Spell_Check (push) Waiting to run
tools_continuous_delivery / Private PyPI non-main branch release (push) Has been skipped
tools_continuous_delivery / Private PyPI main branch release (push) Failing after 2m42s
chore: import upstream snapshot with attribution
2026-07-13 13:39:52 +08:00

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 after url:

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.