{
  "source_file": "0930",
  "source_platform": "Medium",
  "author": "Lak Lakshmanan",
  "title_listed": "Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK",
  "keywords": "Agent框架,PydanticAI,LangChain",
  "domain": "大模型算法",
  "content_type": "深度技术解析",
  "is_paid_flag": "部分免费/会员墙",
  "url": "https://levelup.gitconnected.com/comparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0",
  "publish_date": "2025-10",
  "corpus_stage": "预训练/SFT",
  "notes": "",
  "id": "9b51deccb15d",
  "collected_at": "2026-10-02T00:36:54",
  "title": "Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK | by Lak Lakshmanan | Level Up Coding",
  "body_markdown": "[Sitemap](https://levelup.gitconnected.com/sitemap/sitemap.xml)\n[Open in app](https://play.google.com/store/apps/details?id=com.medium.reader&referrer=utm_source%3DmobileNavBar&source=---top_nav_layout_nav-------------------------------------------)\nSign up\n[Sign in](https://medium.com/m/signin?operation=login&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=post_page---top_nav_layout_nav-----------------------global_nav--------------------)\n[Medium Logo](https://medium.com/?source=---top_nav_layout_nav-------------------------------------------)\nGet app\n[Write](https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2Fnew-story&source=---top_nav_layout_nav-----------------------new_post_topnav--------------------)\n[Search](https://medium.com/search?source=---top_nav_layout_nav-------------------------------------------)\nSign up\n[Sign in](https://medium.com/m/signin?operation=login&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=post_page---top_nav_layout_nav-----------------------global_nav--------------------)\n![Unknown user](https://miro.medium.com/v2/resize:fill:64:64/1*dmbNkD5D-u45r44go_cf0g.png)\n## [Level Up Coding](https://levelup.gitconnected.com/?source=post_page---publication_nav-5517fd7b58a6-4d2d46d927f0-----------------------------------------)\n·\nFollow publication\n  1. [Non-agentic code](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#828e \"Non-agentic code\")\n  2. [Composable approach with Pydantic AI](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#9579 \"Composable approach with Pydantic AI\")\n  3. [Pydantic AI with Subagents](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#ea3a \"Pydantic AI with Subagents\")\n  4. [Tool Calling and Structured Outputs](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#91f2 \"Tool Calling and Structured Outputs\")\n  5. [Scraping a URL to get real-time data + workaround Gemini limitation](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#fe30 \"Scraping a URL to get real-time data + workaround Gemini limitation\")\n  6. [Reflection](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#19d8 \"Reflection\")\n  7. [Limitations of low-level LLM abstraction](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#6716 \"Limitations of low-level LLM abstraction\")\n  8. [LangChain 1.0](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#e579 \"LangChain 1.0\")\n  9. [No async tools](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#354f \"No async tools\")\n  10. [Native structured outputs not supported](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#bf3c \"Native structured outputs not supported\")\n  11. [UrlContext is not supported](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#8703 \"UrlContext is not supported\")\n  12. [Conflicting capabilities between model and agent](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#c42c \"Conflicting capabilities between model and agent\")\n  13. [Advantages of LangChain: Observability, Deep Agents](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#1a78 \"Advantages of LangChain: Observability, Deep Agents\")\n  14. [Google ADK](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#3841 \"Google ADK\")\n  15. [It’s a chatbot and it has an event loop](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#a887 \"It’s a chatbot and it has an event loop\")\n  16. [User input into a state variable](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#b1fb \"User input into a state variable\")\n  17. [Structured Outputs go away](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#07d4 \"Structured Outputs go away\")\n  18. [Reflection](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#5ace \"Reflection\")\n  19. [Summary Thoughts](https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------#4a00 \"Summary Thoughts\")\n\n\n[![Level Up Coding](https://miro.medium.com/v2/resize:fill:76:76/1*5D9oYBd58pyjMkV_5-zXXQ.jpeg)](https://levelup.gitconnected.com/?source=post_page---post_publication_sidebar-5517fd7b58a6-4d2d46d927f0-----------------------------------------)\nCoding tutorials and news. The developer homepage [gitconnected.com](http://gitconnected.com) && [skilled.dev](http://skilled.dev) && [levelup.dev](http://levelup.dev)\nFollow publication\n[Generative Ai Tools](https://medium.com/tag/generative-ai-tools?source=post_page---header_tags--4d2d46d927f0-----------------------------------------)\n[AI Agent](https://medium.com/tag/ai-agent?source=post_page---header_tags--4d2d46d927f0-----------------------------------------)\n[Langchain](https://medium.com/tag/langchain?source=post_page---header_tags--4d2d46d927f0-----------------------------------------)\n[Pydantic Ai](https://medium.com/tag/pydantic-ai?source=post_page---header_tags--4d2d46d927f0-----------------------------------------)\n[Google Adk](https://medium.com/tag/google-adk?source=post_page---header_tags--4d2d46d927f0-----------------------------------------)\n# Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK\n## Is LangChain 1.0 ready for prime time? Is Google ADK worth betting on? Should we stick with Pydantic AI?\n[![Lak Lakshmanan](https://miro.medium.com/v2/resize:fill:64:64/2*TveVoapl-TEk-jBTrbis8w.jpeg)](https://lakshmanok.medium.com/?source=post_page---byline--4d2d46d927f0-----------------------------------------)\n[Lak Lakshmanan](https://lakshmanok.medium.com/?source=post_page---byline--4d2d46d927f0-----------------------------------------)\nFollow\n18 min read\n·\nOct 27, 2025\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgitconnected%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&user=Lak+Lakshmanan&userId=247b0630b5d6&source=---header_actions--4d2d46d927f0---------------------clap_footer--------------------)\n3\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Frepost%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&user=Lak+Lakshmanan&userId=247b0630b5d6&source=---header_actions--4d2d46d927f0---------------------repost_header--------------------)\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fbookmark%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------bookmark_footer--------------------)\n[Listen](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2Fplans%3Fdimension%3Dpost_audio_button%26postId%3D4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------post_audio_button--------------------)\nShare\nSix months ago, I was in favor of building using a low-level LLM abstraction library (Pydantic AI). I explicitly decomposed workflows into a sequence of LLM-calls one at a time, enabled the “agents” with task-specific tools and populated each one’s context appropriately. Then, I incorporated logging, guardrails, etc. into the LLM calls explicitly. That approach works, of course, and you can build complex LLM-based applications with it. The reason I chose that approach then was that agent frameworks were more trouble than they were worth. But this space moves fast, and it might be time to revisit some of that advice.\nPress enter or click to view image in full size\n![](https://miro.medium.com/v2/resize:fit:700/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png)\nThe release of LangChain 1.0 prompted us to take a second look at these frameworks. Is it a better choice? What do I want in an agent framework?\n  * It should simplify the building of production applications. (as opposed to toy demos)\n  * We should be able to easily collect datasets that we can use for evaluation and continuous training. This is a key value-proposition of Obin (my company! We are hiring. See open positions here: <https://www.obin.ai/careers/>).\n  * We should be able to run and test agents in isolation.\n  * We should be able to easily view traces, following a single user request or data preparation job end-to-end.\n  * We should be able to drop down to the model provider’s unique capabilities (Google URL context grounding, Claude skills, etc.) at any time\n  * We should be able to take control of the agentic loop for consistent workflows.\n\n\nFew low-code and no-code frameworks can provide us these capabilites. So, that leaves code-first frameworks like LangChain 1.0, Google ADK, Microsoft’s AG2, and Crew AI.\nLangChain just did a massive ($125M) fund raising round for their Series B, and so should be around for a while. That said pre-1.0 LangChain was a mess in terms of conflicting documentation, and layers of complexity that kept building over time. LangChain 1.0 is crisp and clean now, but it’s still the same team. Will they get it right this time, or will the wide-ranging needs of the community make them lose their way again?\nI’ve been impressed by the velocity with which Google ADK is shipping. Obin is on Google Cloud, and we use Gemini quite extensively. So, Google ADK might be a natural choice. So, it’s worth looking at.\nMaybe I’ll look at AG2 and CrewAI next weekend.\nI’m take a simple, but realistic, use case and build it with the various options so that we can put these systems through their paces, and see how well these options meet our criteria.\n## Non-agentic code\nAs a point of comparison and so that you understand what’s happening in the framework examples, let’s implement the use case with a non-agentic approach. Essentially, I’ll populate the context with the necessary data and make an LLM call.\nGiven a stock symbol, we want to create a highly structured report with specific sections. Let’s create a data model representing what we want (full code is at <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/data_model.py>)\n\n```\nfrom typing import List  \nfrom pydantic import BaseModel, Field  \nclass Company(BaseModel):  \n  name: str  \n  ticker: str  \n  latest_price: float = Field(description=\"The latest price of the stock.\")  \n…  \nclass StockReport(BaseModel):  \n  company: Company  \n  company_overview: CompanyOverview  \n  key_financial_data: KeyFinancialData  \n  buy_case: List[BulletPoint]  \n  sell_case: List[BulletPoint]\n```\n\nIn a non-agentic application, we simply prompt the LLM — there are no tools, self-checks, or sub-agents. Using the Gemini client API, this is what that looks like: (full code is in <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/1_non_agentic.py>):\n\n```\nprice = await tools.get_stock_price(ticker)  \nprompt = f\"Create a stock analysis report for {ticker}. The current price is ${price:.2f}.\"  \nclient = genai.Client()  \nresponse = client.models.generate_content(  \nmodel=\"gemini-2.5-flash\",  \ncontents=prompt,  \nconfig={  \n\"response_mime_type\" : \"application/json\",  \n\"response_schema\" : StockReport,  \n}  \n)  \nobj: StockReport = response.parsed\n```\n\nNote a few things:\n  * Because the stock price is a real-time value, we call a function to get the current value and add it to the context. The point of a tool is that the LLM decides when to call it, but if you know it’s always needed, why bother with all that? You can just do stuff.\n  * We are using Gemini’s Structured Outputs functionality to get back a precise Pydantic object. This is an essential part of building robust production applications because downstream classes and functions can rely on the data structure. Dicts, JSON, etc don’t give you this — data classes help you write code that is not littered with error checks.\n\n\nThe code is pretty straightforward, and simple, but there are issues with the report as generated:\n  * The price is correct because we passed it in, but the key financial data might be old or hallucinated.\n  * The data structure says that we want a list of bullet points. In the tearsheets commonly used in finance, there is often a fixed space allowed and so we need more precise control on the bullet points in the buy-side and sell-side cases. We can not easily do that by defining just an output schema.\n\n\nLet’s fix both of these with an agentic approach. We’ll start with the composable agents approach that we currently follow at Obin, with Pydantic AI as our LLM framework.\n## Composable approach with Pydantic AI\nIt’s worth discussing about what a composable approach to this problem looks like. You’d assign one agent each to every section of the report. The Company agent would be armed with a tool call to get price. The Key Financials agent might be given a specific URL to scrape the data from. The buy-side and sell-side agents might be armed with a Google Search tool to pull down the latest analyst reports and summarize them. Every prompt will get created by a templatized Prompt Service that uses Jinja2. You would log the prompts in this service, and make sure to log all AI generated responses for evaluation. Then, you’d use the logging configuration to ensure that prompts go to a different log file than evals.\nYou can see an example of this approach in the [GitHub repository](https://github.com/lakshmanok/generative-ai-design-patterns/blob/main/composable_app/agents/generic_writer_agent.py#L74) of our book Generative AI Design Patterns (O’Reilly, 2025):\n\n```\nasync def revise_article(self, topic: str, initial_draft: Article, panel_review: str) -> Article:  \n# the prompt is the same for all writers  \nprompt_vars = {  \n\"prompt_name\": \"AbstractWriter_revise_article\",  \n\"topic\": topic,  \n\"content_type\": self.get_content_type(),  \n\"additional_instructions\": ltm.search_relevant_memories(f\"{self.writer.name}, revise {topic}\"),  \n\"initial_draft\": initial_draft.to_markdown(),  \n\"panel_review\": panel_review  \n}  \nprompt = PromptService.render_prompt(**prompt_vars) # this logs prompt  \nresult: Article = await self.revise_response(prompt)  \nawait evals.record_ai_response(\"revised_draft\", # you'd use this to filter  \nai_input=prompt_vars, # useful for simulation  \nai_response=result) # this logs response  \nreturn result\n```\n\nYou can see the explicit use of long-term memory (ltm), Jinja templating of prompts (PromptService), and logging for evals. Please do refer to that repository if you want to see what these layers look like. I’ll skip them here in the interest of keeping the code simple and easy to understand.\n## Pydantic AI with Subagents\nThe full code implementing the stock report workflow with Pydantic AI is at <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/2b_pydanticai_subagents.py>. To keep the example concise, I’ve not added the PromptService, llm wrapper, evals, etc. that we’d want to use in production.\nLet’s walk backwards. To generate the final report, we’ll basically execute a number of independent LLM calls (subagents) in parallel (possible because they are independent):\n\n```\nasync def generate_analysis(ticker) -> data_model.StockReport:  \n\"\"\"Generates a stock analysis using the Gemini API.\"\"\"  \ndata = await asyncio.gather(  \ngenerate_company(ticker),  \ngenerate_company_overview(ticker),  \ngenerate_key_financials(ticker),  \ngenerate_case(True, ticker, 3),  \ngenerate_case(False, ticker, 3)  \n)  \ndata_as_dict = {  \n\"company\": data[0],  \n\"company_overview\": data[1],  \n\"key_financial_data\": data[2],  \n\"buy_case\": data[3],  \n\"sell_case\": data[4]  \n}  \nreturn data_model.StockReport(**data_as_dict)\n```\n\nLet’s look at each of these subagents because they illustrate different aspects of a realistic agent workflow and help us answer the questions that I started this article with.\n## Tool Calling and Structured Outputs\nThe company information includes the current price of the stock. The agent can be armed with a tool call to call the function that gets the price:\n\n```\ndef get_model_settings(temperature: float = 0.25) -> GeminiModelSettings:  \nreturn GeminiModelSettings(  \ntemperature=0.25,  \ngemini_safety_settings=[  \n{  \n'category': 'HARM_CATEGORY_DANGEROUS_CONTENT',  \n'threshold': 'BLOCK_ONLY_HIGH',  \n}  \n]  \n)  \ndef create_agent() -> Agent:  \nreturn Agent(  \n\"gemini-2.5-flash\",  \nmodel_settings=get_model_settings(),  \nretries=2,  \nsystem_prompt=f\"\"\"  \nYou are a research analyst. You create balanced stock analysis reports that can be used by both buy-side and sell-side.  \n\"\"\",  \n)  \nasync def generate_company(ticker) -> data_model.Company:  \nagent = create_agent()  \n@agent.tool_plain  \nasync def get_stock_price(ticker: str) -> float:  \n\"\"\"Get the latest closing stock price of a publicly traded stock.\"\"\"  \nreturn await tools.get_stock_price(ticker)  \nprint(\"Calling Gemini via Pydantic for Company Info and using Yahoo Finance for stock price\")  \nresult = await agent.run(  \nf\"\"\"  \nGet company information for {ticker}. If the company is commonly known by another name, provide that name also.  \nFor example, for WW, the name would be \"WW International Inc., formerly Weight Watchers International, Inc.\"  \n\"\"\",  \noutput_type=data_model.Company  \n)  \nreturn result.output\n```\n\nA few things to note:\n  * Pydantic allows us to break the LLM abstraction if needed and get to the original LLM. We are using it to turn on Gemini’s built-in guardrails at an appropriate level. This will save us latency over trying to do Guardrails via postprocessing (this is a common theme: latency is the bug-bear of production applications, and you want to do as much as possible server-side without an intervening network call)\n  * Along with the system prompt, we can specify retries when creating the agent. Pydantic takes care of this for us.\n  * The code illustrates specifying a function tool (get_stock_price) and an output schema (data_model.Company). This appears to be basic stuff, but you’ll see LangChain and ADK struggle to do this cleanly.\n\n\n## Scraping a URL to get real-time data + workaround Gemini limitation\nLet’s say that we want to get actual financial data from Yahoo Finance. We could download the website, and pass the text content to the model within the context. But this is surprisingly error-prone in production applications. Many websites block or throttle bots. Ads, popups, etc. also make it likely that you will not be able to properly parse the page.\nA good solution is to use Gemini’s UrlContext feature. Essentially, Google uses their own index of the website to populate the context for you. Not only is there a latency benefit, you don’t have to deal with things like ads and popups.\nNow, only Gemini does this. At this time, Claude/GPT-5 don’t. But Pydantic AI doesn’t try to make a generic LLM wrapper, so they do have a UrlContextTool which will tell Gemini to do its thing:\n\n```\nasync def generate_key_financials(ticker: str) -> data_model.KeyFinancialData:  \nprint(\"Using Gemini's UrlContext for Key Financial Data\")  \nagent_with_url = Agent(  \n\"gemini-2.5-flash\",  \nmodel_settings=get_model_settings(temperature=0.1),  \nretries=2,  \nbuiltin_tools=[UrlContextTool()]  \n)  \nresult = await agent_with_url.run(  \nf\"\"\"  \nGet key financial data on {ticker} from https://finance.yahoo.com/quote/{ticker}/financials/  \nDo not make up any numbers.  \n\"\"\",  \noutput_type=PromptedOutput(data_model.KeyFinancialData) # Gemini does not support output tools and built-in tools at the same time. Use `output_type=PromptedOutput(data_model.KeyFinancialData)  \n)  \nreturn result.output\n```\n\nThere is one point above that you have to be aware of. This is going to trip us.\nGoogle, probably because this was built by different teams that don’t talk to each other, makes you choose between Structured Outputs and Built-in Tools. So, if you use UrlContext, you don’t get to say that you want the financial data in a structured dataclass. Yikes!\nFortunately, Pydantic AI gives you a workaround — they will take the output and format it for you using the LLM in the structure you want. That’s what the PromptedOutput does.\n## Reflection\nThe buy-side and sell-side case generation shows us using Reflection, a key aspect of many agents.\nThe idea is that we use Google Search to find analyst reports and get a list of bullet points:\n\n```\nagent_with_search = Agent(  \n\"gemini-2.5-flash\",  \nmodel_settings=get_model_settings(temperature=0.1),  \nretries=2,  \nsystem_prompt=f\"\"\"  \nSearch the web and find relatively recent analyst reports and summarize  \nthe key points to make the {which_side} case for the given stock.  \n\"\"\",  \nbuiltin_tools=[WebSearchTool()]  \n)  \nresult = await agent_with_search.run(  \nf\"\"\"  \nMake the {which_side} case for {ticker}. Produce exactly {n_points} points.  \nEach title should be less than 10 words, and each point should be less than 200 words.  \n\"\"\",  \noutput_type=PromptedOutput(List[data_model.BulletPoint])  \n)\n```\n\nThen, we validate the result, checking that it does fit the specifications:\n\n```\ndef validate(points: List[data_model.BulletPoint]) -> List[str]:  \nerror_messages = []  \nif len(points) != n_points:  \nerror_messages.append(f\"You wrote {len(points)} points, not {n_points}\")  \nfor bulletno, bullet in enumerate(points):  \nnum_title_words = len(bullet.title.split())  \nif num_title_words > 10:  \nerror_messages.append(f\"The title of bullet no {bulletno+1} is too long\")  \nnum_bullet_words = len(bullet.explanation.split())  \nif num_bullet_words > 200:  \nerror_messages.append(f\"The explanation in bullet no {bulletno+1} is too long\")  \nreturn error_messages  \nerror_messages = validate(result.output)\n```\n\nBecause this is just Python, it’s as simple as calling the method on the returned value. Again, seemingly simple stuff, but you’ll see LangChain and ADK struggle with this.\nThen, we invoke the agents with additional instructions formed from the error messages and get them to correct their response.\n## Limitations of low-level LLM abstraction\nI like Pydantic AI because it gives me a low-level LLM abstraction without getting in the way too much. The limitations of this approach started to become evident as we started to build more and more at Obin. It is fine for small projects (and allows an individual developer to move fast), but breaks down for larger teams and as you start to use LLM-specific capabilities:\nWhat limitations?\n  * Abstractions are imperfect: The same feature (such as structured outputs) can be implemented very differently. Claude does post-formatting, so has twice the latency whereas Gemini zeroes out the logits and so does so with a single class. The tradeoff is the Claude has a lower refusal rate.\n  * Abstractions give you the lowest common denominator: The LLM providers’ API is often much richer than what the abstraction of the world expose. For example, the Claude SDK supports Excel handling, financial data through MCP, has prompt improvement capabilities, and now has skills. Gemini has very good media caching, Google Search-based grounding, the ability to load URLs into the context, a sandbox to run generated code, etc. You often lose these or lag significantly behind when you use an abstraction layer over the LLM.\n  * Composability complicates context curation. You don’t want to keep appending things to context; you often need to curate them, and this requires knowledge of what the downstream agents still need.\n  * Composability can complicate testing unless you take care to design every agent as being stateless. It’s hard to test agents individually when they depend on the context having been formed properly by upstream agents. On the other hand, stateless agents might waste tokens and network bandwidth if you don’t take the effort to curate context.\n\n\nUltimately, best practices are unenforceable as teams grow. For example, it is hard to enforce proper use of the PromptService, evals logging, etc. as team size increases. New developers don’t realize why these wrappers exist, directly invoke the underlying service, and then you start to have holes in your logs and guardrails.\n## Get Lak Lakshmanan’s stories in your inbox\nJoin Medium for free to get updates from this writer.\nSubscribe\nSubscribe\nRemember me for faster sign in\nNow, let’s see whether LangChain and ADK make this easier, and how.\n## LangChain 1.0\nLet’s build the same use case with LangChain. You can see the full code at <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/3_langchain.py>\nAs before, let’s work backwards. In LangChain, as in PydanticAI, you can compose the report by running a bunch of subagents in parallel:\n\n```\nasync def generate_analysis(ticker) -> data_model.StockReport:  \n\"\"\"Generates a stock analysis using the Gemini API.\"\"\"  \ndata = await asyncio.gather(  \ngenerate_company(ticker),  \ngenerate_company_overview(ticker),  \ngenerate_key_financials(ticker),  \ngenerate_case(True, ticker, 3),  \ngenerate_case(False, ticker, 3)  \n)  \ndata_as_dict = {  \n\"company\": data[0],  \n\"company_overview\": data[1],  \n\"key_financial_data\": data[2],  \n\"buy_case\": data[3],  \n\"sell_case\": data[4]  \n}  \nreturn data_model.StockReport(**data_as_dict)\n```\n\nThe immaturity of LangChain 1.0 starts to show up when you start to look at real-world needs.\n## No async tools\nTake the simplest thing. All tool calls have to be synchronous. I had to do:\n\n```\n@tool  \ndef get_stock_price(ticker: str) -> float:  \n\"\"\"Get the latest closing stock price of a publicly traded stock.\"\"\"  \nreturn asyncio.run(  \n# no way to call async tools!  \ntools.get_stock_price(ticker)  \n)\n```\n\nGiven that tool calls in most LLM applications involve network calls, this is a suprising miss.\n## Native structured outputs not supported\nEven in cases where Gemini supports Structured Outputs, LangChain fails. I found that I had to use ToolStrategy (which involves reformatting the output response) rather than ProviderStrategy all the time:\n\n```\nagent_without_tools = create_agent(  \nmodel,  \nsystem_prompt=f\"\"\"  \nYou are research analyst. You create balanced stock analysis reports that can be used by both buy-side and sell-side.  \nReturn structured JSON in the desired format.  \n\"\"\",  \n# Bug: with ProviderStrategy https://github.com/langchain-ai/langchainjs/issues/8585  \nresponse_format=ToolStrategy(data_model.CompanyOverview)  \n)\n```\n\n## UrlContext is not supported\nRemember the really cool built-in Gemini feature where they serve webpage content out of their index? Well, you can’t use it in LangChain. <https://docs.langchain.com/oss/python/integrations/providers/google> only lists API-based access to Google Search and no UrlContext.\nTrying to download webpage content runs into all the limitations I mentioned earlier.\n## Conflicting capabilities between model and agent\nIn the previous section, I said that the website that lists integrations with Google does not list the Google Search tool that’s built-in to Gemini. Instead, only an API-based Google Search is listed.\nWell it turns out that those are the integrations for agents. If you use the model, you can get the built-in tool.\n\n```\nresult = model.invoke(  \nprompt,  \ntools=[GenAITool(google_search={})]  \n)\n```\n\nThe documentation is confusing. The integration with agents is not the only way to access model capabilities, so there should be some hint that you can drop down to the model. I suspect that this distinction without a difference is going to be cause of many a few wasted hours.\nBut as, with Pydantic, the ugly Google error that built-in tools and Structured Outputs don’t mix rears its head. Where Pydantic gives you a helpful workaround, LangChain leaves you to figure out what’s happening (it’s not easy) and solve it yourself. I had to write a reformatter:\n\n```\nasync def reformat_into_points(model, content) -> List[data_model.BulletPoint]:  \nclass Case(BaseModel):  \npoints: List[data_model.BulletPoint]  \nformat_prompt = f\"\"\"  \nConvert the information below into the desired format.  \n{content}  \n\"\"\"  \nformatter_agent = create_agent(  \nmodel,  \nresponse_format=ToolStrategy(Case)  \n)  \nresult = await formatter_agent.ainvoke(  \n{  \n\"messages\": [  \n{\"role\": \"user\", \"content\": format_prompt}  \n]  \n}  \n)  \nreturn result[\"structured_response\"].points\n```\n\nLangChain pre-1.0 was notorious for confusing layers of abstraction and open issues that were hard to troubleshoot. So, this is not a good sign.\nOverall, it took me 176 lines to implement the subagent workflow in Pydantic AI, and 266 lines to do so in LangChain 1.0. So, they are comparable.\n## Advantages of LangChain: Observability, Deep Agents\nOne big advantage with building with LangChain is the ability to incorporate middleware and horizontal capabilities easily. See <https://docs.langchain.com/oss/python/langchain/middleware>. You can boostrap logging for evals onto agents through middleware, but it is still one agent at a time. Tracing can be accomplished through LangSmith and integration with Arize Phoenix is straightforward.\nOf course, LangChain is not _meant_ to be used when you want such precise control. Creating precisely formatted stock analysis reports with non-hallucinated data is not the use case that they are going after — they are going after use cases where you can give up quite a bit of the planning and execution responsibility to the LLM.\nWhat this means in practice is that, rather than orchestrating the report explicitly in code, you might use “Deep Agents”, telling it to use sub agents as needed:\n\n```\nasync def generate_analysis(ticker) -> data_model.StockReport:  \nagent = create_deep_agent(  \nmodel=create_model(),  \nsystem_prompt=f\"\"\"  \nYou are an unbiased research analyst. You create balanced stock analysis reports that can be used  \nby both buy-side and sell-side firms.  \nYou have access to a number of tools that can retrieve information on different topics from trusted topics.  \nUse these tools to retrieve any information that you require.  \nYou should weave the information together into a coherent report. This might involve having to call  \na tool again based on newly discovered information. For example, if the buy-side case says that a company is  \nexperiencing growth in a product line, you should make sure that this product is listed in the key products  \nand that the growth area is part of the company overview. Use the additional_instructions field in all the tools to  \ncall the tool again asking for such additional information.  \nFinally, render the report in Markdown.  \n\"\"\",  \ntools=[  \ngenerate_company,  \ngenerate_company_overview,  \ngenerate_key_financials,  \ngenerate_case,  \ntools.render_report  \n]  \n)  \nprompt = f\"Write a stock analysis report on {ticker}\"  \nresult = await agent.ainvoke(  \n{  \n\"messages\": [  \n{\"role\": \"user\", \"content\": prompt}  \n]  \n}  \n)  \nreturn result[\"messages\"][-1].content\n```\n\nThe key benefit here is how easy it was to ask the final agent to _weave_ together the information into a coherent report and go back and ask _any_ agent to redo its work given the work. If the definition of an agentic application is that you can call tools and subagents in a loop, this final change is what makes the application fully agentic.\nThe full code for a deep agent implementation is at <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/3c_langchain_deepagents.py>\n## Google ADK\nLet’s build the same use case with Google ADK. The full code for this section is at <https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/4_adk/agent.py>\n## It’s a chatbot and it has an event loop\nThe first thing to realize is that Google ADK is _very_ opionionated. By default, they expect you to build a conversational application. You can build a non-conversational application, but then you have to create a session, runner, etc. and populate it with nonsensical user-ids and session-ids (and remember what they are, because the internal state is tracked by user and session).\nLet’s not fight them. As before, let’s walk backwards. Here, we want a sequential pipeline of sub-agent calls:\n\n```\ndef create_pipeline_agent():  \nfinal_pipeline_agent = SequentialAgent(  \nname=\"pipeline_agent\",  \ndescription='Creates balanced stock analysis reports using a specific set of steps.',  \nsub_agents=[  \n# will run in this order  \nextract_ticker_agent(),  \ncreate_company_agent(),  \ncreate_company_overview_agent(),  \ncreate_key_financials_agent(),  \ncreate_case_creation_agent(True),  \ncreate_case_creation_agent(False),  \ncreate_final_report_agent()  \n]  \n)  \nreturn final_pipeline_agent  \n# To run this program: adk run 4_adk  \nroot_agent = create_pipeline_agent() # has to be called root_agent\n```\n\nNote that, unlike with Pydantic AI or LangChain, we are just constructing agents. We are _not_ invoking the agents. That’s because ADK runs everything in an event loop (like Streamlit or Spring) and it takes care of invoking the agents. This also means you can not just run the main(). Instead you launch the chat application as:\n\n```\nadk run <parentdir>\n```\n\nThere is also a web interface to the chat application which gives you a lot of bells and whistles. It’s really nice.\n## User input into a state variable\nHowever, because it’s a chat application, you see that I can not just pass the ticker into the application. No siree. You can figure out how to add something to the session state that is managed by the event loop (good luck), or take the simpler way out of taking the user question and extracting the ticker symbol from it:\n\n```\ndef extract_ticker_agent():  \noutput_key = \"ticker\"  \nprint(f\"Calling Gemini for {output_key}\")  \nreturn Agent(  \nmodel=WORKER_MODEL,  \nname=f\"{output_key}_agent\",  \ngenerate_content_config=create_model_settings(),  \ndescription=\"Finds ticker\",  \ninstruction=f\"\"\"  \nFrom the user query, extract the stock market symbol that the user wants a report on.  \n<example>  \nUser: Please write a report on Microsoft  \nAI: MSFT  \n</example>  \n<example>  \nUser: Analyze WW  \nAI: WW  \n</example>  \n<example>  \nUser: Nvidia  \nAI: NVDA  \n</example>  \n\"\"\",  \noutput_key=output_key, # result will be in state[output_key]  \n)\n```\n\nYou notice that I’ve made lemonade from lemons. If we are going to extract the ticker from the user input, we might as well make it more powerful and allow the user more leeway.\nADK is opionionated, but its opinions are usually good ones. It can, however, get frustrating to figure out what the intended path is when the obvious path of passing in a ticker to the first agent is closed off.\n## Structured Outputs go away\nSurprisingly, ADK has not figured out how to marry tool calling and structured outputs. You pretty much has to forget having clean contracts between steps of your workflow and hope that the LLM can reformat the outputs of one step into the inputs of the next step.\nWhat I found helpful was to create examples and use those examples as JSON to help guide the LLM on what the contract of the next step was:\n\n```\nexample = data_model.Company(name=\"WW International Inc., formerly Weight Watches International, Inc.\",  \nticker=\"WW\",  \nlatest_price=23.3)  \nreturn Agent(  \nmodel=WORKER_MODEL,  \nname=f\"{output_key}_agent\",  \ngenerate_content_config=create_model_settings(),  \ndescription=\"Finds company info\",  \ninstruction=\"\"\"  \nGet company information for {ticker}.  \nIf the company is commonly known by another name, provide that name also.  \n\"\"\" + f\"Example output: {example.model_dump_json()}\",  \noutput_key=output_key, # result will be in state[output_key]  \ntools=[  \nFunctionTool(tools.get_stock_price_mock),  \n],  \n)\n```\n\nBecause the output key is “ticker”, I can use “{ticker}” within any prompt and this variable will get injected into downstream agents.\nOf course, this also means that you can not use f-strings in Python for your prompts because that’s the same exact syntax that Python expects. Not sure what the ADK designers were thinking. This explains the weird mixture of non-f-string and f-string in the instruction parameter above. Good bye code readability and maintainability.\n## Reflection\nAs with LangChain, I had to explicitly format the output of the agent in order to validate it, but the presence of higher-level control structures like LoopAgent means that reflection can be done quite easily:\n\n```\ncase_creation_agent = LoopAgent(  \nname=f\"{output_key}_loop_agent\",  \nmax_iterations=2,  \nsub_agents=[  \ncase_creation_agent,  \nformatter_agent,  \nvalidation_agent,  \n]  \n)\n```\n\nThe Google ADK implementation runs to 268 lines (Pydantic was 176 and LangChain was 266). Surprisingly, the higher-level abstraction did not result in fewer lines of code. But that may be because I had to write a couple of extra agents and longer prompts.\nUltimately, Google ADK gives me TensorFlow 1.0 vibes. It’s written by engineers who are far better than me for people like them and who have their needs. The event loop paradigm reminds me of the struggles with TensorFlow’s data reading mechanism (feed_dict, placeholder, etc.) — it was very scalable but no one quite understood it and by the time Google listened to feedback and implemented tf.data, the world had moved on. I suspect that Google ADK will have the same fate.\nGoogle builds great tools — as you can see from this post, I really love Gemini’s capabilities, and its built-in tools like context caching, search tool, UrlContext, etc. are incredibly effective in production applications. But they seem to consistently drop the ball when it comes to developer frameworks. The tools (and LLM) are great because they are tested at Google scale. Unfortunately, it’s a double-edged sword. The developer framework is hard to understand and use because the needs of internal Google are very unlike the needs of everyday applications.\n## Summary Thoughts\nLangChain 1.0 is very promising:\n  * The middleware hooks will enable a large ecosystem of tools to hooking into LangChain. We already see this with observability (Arize), guardrails (Guardrails AI), etc.\n  * The community is building frameworks for deep agents, deep research, etc. Like HuggingChain for open-weights models, I can see LangChain becoming the defacto place that new OSS agentic capabilities are found.\n  * It’s comparable to Pydantic AI in its ability to support precise control of workflows.\n  * That precise control does not come at the expense of building more autonomous applications where the LLM can itself choose which subagent or tools to call.\n\n\nLangChain 1.0 is still immature:\n  * The LLM abstraction is spotty. You are meant to be able to use the high-level abstraction, but still be able to drop down to the model if necessary. But I found several key capabilities of the underlying LLM missing. I am using Gemini, but I suspect that anyone using Claude in anger with LangChain will find analagous limitations.\n  * Tool calls have to be synchronous, and so this will add latency to many applications that wrap remotely hosted APIs via tool calls.\n  * There is a lot of confusion between model and agent in the API. This is likely to get worse as more capabilities get added. Seriously, they should have copied or adopted Pydantic AI’s clean API surface instead of trying to invent their own.\n\n\nBetween LangChain 1.0 and Google ADK, I’d choose LangChain. Pydantic AI is clean and nice, but falls down for larger teams and projects. If you are not using an agent framework today, and want to build high-level agents, use LangChain 1.0. But prepare to be frustrated.\n_Lak is co-founder and CTO of Obin.ai which is building deep domain agents for finance. We are hiring. Please see open positions here:_[_https://www.obin.ai/careers/_](https://www.obin.ai/careers/)\n[Generative Ai Tools](https://medium.com/tag/generative-ai-tools?source=post_page---footer_tags--4d2d46d927f0-----------------------------------------)\n[AI Agent](https://medium.com/tag/ai-agent?source=post_page---footer_tags--4d2d46d927f0-----------------------------------------)\n[Langchain](https://medium.com/tag/langchain?source=post_page---footer_tags--4d2d46d927f0-----------------------------------------)\n[Pydantic Ai](https://medium.com/tag/pydantic-ai?source=post_page---footer_tags--4d2d46d927f0-----------------------------------------)\n[Google Adk](https://medium.com/tag/google-adk?source=post_page---footer_tags--4d2d46d927f0-----------------------------------------)\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgitconnected%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&user=Lak+Lakshmanan&userId=247b0630b5d6&source=---footer_actions--4d2d46d927f0---------------------clap_footer--------------------)\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgitconnected%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&user=Lak+Lakshmanan&userId=247b0630b5d6&source=---footer_actions--4d2d46d927f0---------------------clap_footer--------------------)\n3\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Frepost%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&user=Lak+Lakshmanan&userId=247b0630b5d6&source=---footer_actions--4d2d46d927f0---------------------repost_footer--------------------)\n[](https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fbookmark%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---footer_actions--4d2d46d927f0---------------------bookmark_footer--------------------)\n[![Lak Lakshmanan](https://miro.medium.com/v2/resize:fill:96:96/2*TveVoapl-TEk-jBTrbis8w.jpeg)](https://lakshmanok.medium.com/?source=post_page---post_author_info--4d2d46d927f0-----------------------------------------)\n[![Lak Lakshmanan](https://miro.medium.com/v2/resize:fill:128:128/2*TveVoapl-TEk-jBTrbis8w.jpeg)](https://lakshmanok.medium.com/?source=post_page---post_author_info--4d2d46d927f0-----------------------------------------)\nFollow\n## [Written by Lak Lakshmanan](https://lakshmanok.medium.com/?source=post_page---post_author_info--4d2d46d927f0-----------------------------------------)\n[11.3K followers](https://lakshmanok.medium.com/followers?source=post_page---post_author_info--4d2d46d927f0-----------------------------------------)\n·[89 following](https://medium.com/@lakshmanok/following?source=post_page---post_author_info--4d2d46d927f0-----------------------------------------)\nCo-founder and CTO of [Obin.ai](http://Obin.ai) -- we build deep domain agents for finance. We are hiring. Please see open positions here: <https://www.obin.ai/careers/>\nFollow\n[![Level Up Coding](https://miro.medium.com/v2/resize:fill:96:96/1*5D9oYBd58pyjMkV_5-zXXQ.jpeg)](https://levelup.gitconnected.com/?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------)\n[![Level Up Coding](https://miro.medium.com/v2/resize:fill:128:128/1*5D9oYBd58pyjMkV_5-zXXQ.jpeg)](https://levelup.gitconnected.com/?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------)\nFollow\n## [Published in Level Up Coding](https://levelup.gitconnected.com/?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------)\n[362K followers](https://levelup.gitconnected.com/followers?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------)\n·[Last published 1 day ago](https://levelup.gitconnected.com/sandbox-your-ai-agent-in-15-minutes-with-nvidias-openshell-9386e4fd9084?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------)\nCoding tutorials and news. The developer homepage [gitconnected.com](http://gitconnected.com) && [skilled.dev](http://skilled.dev) && [levelup.dev](http://levelup.dev)\nFollow\n[Help](https://help.medium.com/hc/en-us?source=post_page-----4d2d46d927f0-----------------------------------------)\n[Status](https://status.medium.com/?source=post_page-----4d2d46d927f0-----------------------------------------)\n[About](https://medium.com/about?autoplay=1&source=post_page-----4d2d46d927f0-----------------------------------------)\n[Careers](https://medium.com/jobs-at-medium/work-at-medium-959d1a85284e?source=post_page-----4d2d46d927f0-----------------------------------------)\nPress\n[Blog](https://blog.medium.com/?source=post_page-----4d2d46d927f0-----------------------------------------)\n[Store](https://medium.com/store)\n[Privacy](https://policy.medium.com/medium-privacy-policy-f03bf92035c9?source=post_page-----4d2d46d927f0-----------------------------------------)\n[Rules](https://policy.medium.com/medium-rules-30e5502c4eb4?source=post_page-----4d2d46d927f0-----------------------------------------)\n[Terms](https://policy.medium.com/medium-terms-of-service-9db0094a1e0f?source=post_page-----4d2d46d927f0-----------------------------------------)\n[Text to speech](https://speechify.com/medium?source=post_page-----4d2d46d927f0-----------------------------------------)\n",
  "metadata": {
    "title": "Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK | by Lak Lakshmanan | Level Up Coding",
    "description": "Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK Is LangChain 1.0 ready for prime time? Is Google ADK worth betting on? Should we stick with Pydantic AI? Six months ago, I was in …",
    "keywords": null,
    "author": "Lak Lakshmanan",
    "og:site_name": "Medium",
    "og:type": "article",
    "og:title": "Comparing Agent Frameworks: PydanticAI, LangChain 1.0 and Google ADK",
    "og:description": "Is LangChain 1.0 ready for prime time? Is Google ADK worth betting on? Should we stick with Pydantic AI?",
    "og:url": "https://levelup.gitconnected.com/comparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0",
    "og:image": "https://miro.medium.com/v2/resize:fit:1024/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
    "twitter:app:name:iphone": "Medium",
    "twitter:app:id:iphone": "828256236",
    "twitter:site": "@gitconnected",
    "twitter:app:url:iphone": "medium://p/4d2d46d927f0",
    "twitter:image:src": "https://miro.medium.com/v2/resize:fit:1024/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
    "twitter:card": "summary_large_image",
    "twitter:label1": "Reading time",
    "twitter:data1": "18 min read",
    "article:published_time": "2025-10-31T01:37:02.737Z",
    "article:author": "https://lakshmanok.medium.com"
  },
  "paywall": {
    "listed_paid": true,
    "detected": false,
    "marker": "",
    "public_only": true,
    "policy": "付费/会员内容只保留页面公开可见部分，不绕过付费墙"
  },
  "crawl": {
    "success": true,
    "status_code": 307,
    "error": ""
  },
  "code_blocks": [
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_000.txt",
      "chars": 401
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_001.txt",
      "chars": 392
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_002.txt",
      "chars": 763
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_003.txt",
      "chars": 526
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_004.txt",
      "chars": 1220
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_005.txt",
      "chars": 691
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_006.txt",
      "chars": 608
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_007.txt",
      "chars": 617
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_008.txt",
      "chars": 526
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_009.txt",
      "chars": 212
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_010.txt",
      "chars": 391
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_011.txt",
      "chars": 75
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_012.txt",
      "chars": 491
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_013.txt",
      "chars": 1334
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_014.txt",
      "chars": 595
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_015.txt",
      "chars": 20
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_016.txt",
      "chars": 609
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_017.txt",
      "chars": 611
    },
    {
      "language": "txt",
      "file": "attachments/9b51deccb15d/code/block_018.txt",
      "chars": 173
    }
  ],
  "images": [
    {
      "src": "https://miro.medium.com/v2/resize:fill:64:64/1*dmbNkD5D-u45r44go_cf0g.png",
      "alt": "Unknown user",
      "local_path": "attachments/9b51deccb15d/images/000.png",
      "bytes": 1474,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fill:76:76/1*5D9oYBd58pyjMkV_5-zXXQ.jpeg",
      "alt": "Level Up Coding",
      "local_path": "attachments/9b51deccb15d/images/001.jpeg",
      "bytes": 1669,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fill:64:64/2*TveVoapl-TEk-jBTrbis8w.jpeg",
      "alt": "Lak Lakshmanan",
      "local_path": "attachments/9b51deccb15d/images/002.jpeg",
      "bytes": 2153,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:700/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/003.png",
      "bytes": 753451,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:640/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/004.png",
      "bytes": 39456,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:720/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/005.png",
      "bytes": 45234,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:750/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/006.png",
      "bytes": 48020,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:786/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/007.png",
      "bytes": 50844,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:828/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/008.png",
      "bytes": 54032,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:1100/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/009.png",
      "bytes": 62168,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:1400/format:webp/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/010.png",
      "bytes": 62168,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:640/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/011.png",
      "bytes": 636727,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:720/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/012.png",
      "bytes": 794145,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:750/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/013.png",
      "bytes": 857125,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:786/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/014.png",
      "bytes": 935865,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:828/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/015.png",
      "bytes": 1030120,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:1100/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/016.png",
      "bytes": 1316675,
      "downloaded": true
    },
    {
      "src": "https://miro.medium.com/v2/resize:fit:1400/1*5Wj-2I5KAQkCI_Aj1aQh-Q.png",
      "alt": "",
      "local_path": "attachments/9b51deccb15d/images/017.png",
      "bytes": 1316675,
      "downloaded": true
    }
  ],
  "audio": [],
  "videos": [],
  "links": [
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/sitemap/sitemap.xml"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/?source=post_page---publication_nav-5517fd7b58a6-4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/?source=post_page-----4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/?source=post_page---post_publication_sidebar-5517fd7b58a6-4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "internal",
      "href": "http://gitconnected.com"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/followers?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "internal",
      "href": "https://levelup.gitconnected.com/sandbox-your-ai-agent-in-15-minutes-with-nvidias-openshell-9386e4fd9084?source=post_page---post_publication_info--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://play.google.com/store/apps/details?id=com.medium.reader&referrer=utm_source%3DmobileNavBar&source=---top_nav_layout_nav-------------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?operation=login&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=post_page---top_nav_layout_nav-----------------------global_nav--------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/?source=---top_nav_layout_nav-------------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?operation=register&redirect=https%3A%2F%2Fmedium.com%2Fnew-story&source=---top_nav_layout_nav-----------------------new_post_topnav--------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/search?source=---top_nav_layout_nav-------------------------------------------"
    },
    {
      "type": "external",
      "href": "http://skilled.dev"
    },
    {
      "type": "external",
      "href": "http://levelup.dev"
    },
    {
      "type": "external",
      "href": "https://medium.com/tag/generative-ai-tools?source=post_page---header_tags--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/tag/ai-agent?source=post_page---header_tags--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/tag/langchain?source=post_page---header_tags--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/tag/pydantic-ai?source=post_page---header_tags--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/tag/google-adk?source=post_page---header_tags--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://lakshmanok.medium.com/?source=post_page---byline--4d2d46d927f0-----------------------------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fvote%2Fgitconnected%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------clap_footer--------------------&user=Lak+Lakshmanan&userId=247b0630b5d6"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Frepost%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------repost_header--------------------&user=Lak+Lakshmanan&userId=247b0630b5d6"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2F_%2Fbookmark%2Fp%2F4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------bookmark_footer--------------------"
    },
    {
      "type": "external",
      "href": "https://medium.com/m/signin?actionUrl=https%3A%2F%2Fmedium.com%2Fplans%3Fdimension%3Dpost_audio_button%26postId%3D4d2d46d927f0&operation=register&redirect=https%3A%2F%2Flevelup.gitconnected.com%2Fcomparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0&source=---header_actions--4d2d46d927f0---------------------post_audio_button--------------------"
    },
    {
      "type": "external",
      "href": "https://www.obin.ai/careers/"
    },
    {
      "type": "external",
      "href": "https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/data_model.py"
    },
    {
      "type": "external",
      "href": "https://github.com/lakshmanok/lakblogs/blob/main/stockanalysis/1_non_agentic.py"
    }
  ],
  "repair_version": 2,
  "repaired_at": "2026-10-14T11:11:49Z",
  "source_url": "https://levelup.gitconnected.com/comparing-agent-frameworks-pydanticai-langchain-1-0-and-google-adk-4d2d46d927f0",
  "delivery_status": "excluded_paid",
  "discovery_status": "excluded_paid",
  "missing_reason": "按原约定排除付费/会员全文"
}