Find every AI agent in your Python code: OpenAI Agents SDK, LangGraph, CrewAI and Google ADK An open-source tool called Marshal can inventory AI agents written in Python by parsing source files with Python's built-in ast module, without importing or executing any code, according to the tool's author. Marshal recognizes agent constructor calls across the OpenAI Agents SDK, LangGraph, CrewAI and Google ADK, plus direct calls to the OpenAI, Anthropic, Google GenAI, LiteLLM and Bedrock Runtime APIs, which it records as inferred agents. Run against four public example repositories — OpenAI's Agents SDK examples, Google's ADK samples, CrewAI's project template and Anthropic's quickstarts — Marshal found 22 agents, with per-agent completeness scores ranging from 10% to 37% because model names and other values read from environment variables are recorded as unknown. Agents written in code don’t show up in any admin console. Static analysis can list them, with their models and tools, without running a line of the code. An agent built in Copilot Studio at least appears in Copilot Studio’s admin pages. An agent written in Python appears nowhere. It’s a constructor call in some module, with its model name in a config file, its prompt in a text file and its tools in three other modules. It changes with every commit, and it may be one of a dozen across your repositories. If someone asks which of your services run agents, which models they call and which functions they can invoke, the answer is often a grep and a guess. I wrote a reader for Marshal, an open-source AI agent inventory, that answers it from the code instead. In an earlier article I covered the inventory for agents built on platforms. This one is about agents in code. Marshal parses Python files with Python’s own ast module, the way a linter does. Nothing is imported or executed, so it's safe to point at any repository, and it needs no API keys. Each call to an agent constructor is one agent: Code that calls a model API directly and passes it tools is an agent loop too, even without a framework. Marshal records those calls to the OpenAI, Anthropic, Google GenAI, LiteLLM and Bedrock Runtime APIs as agents, marked as inferred. A small evaluator resolves what can be known without running anything: string literals, f-strings, names in the same module, imports between the project’s own modules, and prompt files read from disk. Anything else, such as a model name read from an environment variable, is recorded as unknown with the reason. Marshal doesn’t fill in a value it can’t see in the code. I ran it on four public example repositories: OpenAI’s Agents SDK examples, Google’s ADK samples, CrewAI’s project template and Anthropic’s quickstarts. bash $ for repo in /; do marshal collect "$repo" --event shipped --environment prod; done$ marshal lsAGENT PLATFORM LATEST SHIPPED FRESHNESS COMPLETEanthropic-quickstarts/sampling-loop anthropic-api v1 prod v1 prod: manual 0d 25%crewai-template/reporting-analyst crewai v1 prod v1 prod: manual 0d 30%crewai-template/researcher crewai v1 prod v1 prod: manual 0d 30%google-adk/critic-agent google-adk v1 prod v1 prod: manual 0d 37%google-adk/reviser-agent google-adk v1 prod v1 prod: manual 0d 25%openai-agents/faq-agent openai-agents v1 prod v1 prod: manual 0d 20%openai-agents/seat-booking-agent openai-agents v1 prod v1 prod: manual 0d 20%openai-agents/triage-agent openai-agents v1 prod v1 prod: manual 0d 10% 14 more openai-agents rows That’s 22 agents across four frameworks. The Anthropic one has no framework at all: it’s the computer-use demo’s sampling loop, found because it calls the Messages API with tools. The COMPLETE column says how much of each agent the code states. A low number is information too. It tells you which agents depend on values that only exist at run time. Here is part of the record for the FAQ agent in OpenAI’s customer service example: agent: customer service/faq-agentplatform: openai-agentssource: format: python artifact: main.py locator: agents.Agent at line 94spec: display name: value: FAQ Agent provenance: declared model: requested: provenance: unknown basis: the SDK's default model is used provider: value: openai provenance: derived basis: the OpenAI Agents SDK's default client instructions: provenance: derived basis: 'main.py:94; unresolved parts in braces: RECOMMENDED PROMPT PREFIX' tools: - ref: tool/python.customer service/main:faq lookup tool name: faq lookup tool description: Lookup frequently asked questions. Every value says where it came from. The name is declared in the code. The provider is derived from a documented rule, because the SDK uses OpenAI’s client unless you pass another. The model is unknown, because the agent relies on the SDK’s default, which a new SDK release can change. The instructions are recorded with the one part that couldn’t be resolved, a prefix imported from the SDK package, left in braces. For an audit, that unknown model is a finding. It means the agent’s model changes when someone upgrades a dependency, without any change to the agent’s own code. The inventory gets more useful once it runs on every pull request. Here is a one-line change to the same example that gives the FAQ agent the seat-booking agent’s tool: - tools= faq lookup tool ,+ tools= faq lookup tool, update seat , Marshal compares the proposed agents with what’s shipped and ranks the change: customer service/faq-agent openai-agents prod: v1 → v2 HIGH + tool tool/python.customer service/main:update seat scope unknown, reversible unknown Completeness 15% It’s HIGH because the code doesn’t say whether update seat only reads or also writes, and Marshal doesn't guess from a function's name. A new tool whose effect is unknown gets the same rank as a new write tool. In CI, the GitHub Action posts this as a pull request comment, and the GitLab CI template does the same on a merge request. The reverse question matters before you change a function that agents call: bash $ marshal deps "tool/python. :update seat"AGENT VERSION WHERE RELATION REFcustomer service/faq-agent v2 latest tool tool/python.customer service/main:update seatcustomer service/seat-booking-agent v1 prod latest tool tool/python.customer service/main:update seat Tool references include the project, so two copies of the same template in different repositories stay two different tools. Static analysis has limits, and the records say where they are. A model name from an environment variable or a function parameter is unknown. Tools assembled at run time are reported with a warning; the Anthropic sampling loop builds its tool list at run time, so its record says so. Custom LangGraph StateGraph workflows aren't read yet, only the prebuilt agent constructors, and neither are model calls whose arguments arrive through kwargs. When a framework changes its API, a weekly job in Marshal’s repository runs the tests against each framework’s latest public examples and opens an issue when they fail. Find every AI agent in your Python code: OpenAI Agents SDK, LangGraph, CrewAI and Google ADK https://pub.towardsai.net/find-every-ai-agent-in-your-python-code-openai-agents-sdk-langgraph-crewai-and-google-adk-af49a4d4f2a9 was originally published in Towards AI https://pub.towardsai.net on Medium, where people are continuing the conversation by highlighting and responding to this story.