{"id":43772500,"url":"https://github.com/bitsky-tech/bridgic","last_synced_at":"2026-03-17T09:10:27.665Z","repository":{"id":320127582,"uuid":"1055449071","full_name":"bitsky-tech/bridgic","owner":"bitsky-tech","description":"Bridgic is the next-generation agent development framework for building intelligent systems.","archived":false,"fork":false,"pushed_at":"2026-03-10T08:47:04.000Z","size":40210,"stargazers_count":117,"open_issues_count":5,"forks_count":10,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-03-10T16:04:17.154Z","etag":null,"topics":["agent","ai-agent","anthropic","deepseek","framework","gemini","grok","kimi","llm","mistral","multi-agent-system","openai","python","qwen","sdk","workfow","zhipu"],"latest_commit_sha":null,"homepage":"https://docs.bridgic.ai/latest/","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/bitsky-tech.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2025-09-12T09:29:11.000Z","updated_at":"2026-02-26T06:09:11.000Z","dependencies_parsed_at":"2025-10-22T05:34:25.550Z","dependency_job_id":"64d01af4-43e7-4320-923b-a2b4b28a062c","html_url":"https://github.com/bitsky-tech/bridgic","commit_stats":null,"previous_names":["bitsky-tech/bridgic"],"tags_count":14,"template":false,"template_full_name":null,"purl":"pkg:github/bitsky-tech/bridgic","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsky-tech%2Fbridgic","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsky-tech%2Fbridgic/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsky-tech%2Fbridgic/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsky-tech%2Fbridgic/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/bitsky-tech","download_url":"https://codeload.github.com/bitsky-tech/bridgic/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/bitsky-tech%2Fbridgic/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30619845,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T08:10:05.930Z","status":"ssl_error","status_checked_at":"2026-03-17T08:10:04.972Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["agent","ai-agent","anthropic","deepseek","framework","gemini","grok","kimi","llm","mistral","multi-agent-system","openai","python","qwen","sdk","workfow","zhipu"],"created_at":"2026-02-05T17:00:22.618Z","updated_at":"2026-03-17T09:10:27.659Z","avatar_url":"https://github.com/bitsky-tech.png","language":"Python","funding_links":[],"categories":["Python"],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n    \u003cpicture\u003e\n        \u003cimg src=\"https://raw.githubusercontent.com/bitsky-tech/bridgic/refs/heads/main/docs/docs/assets/logo_white_bg.svg\" alt=\"Bridgic\" width=\"500\"\u003e\n    \u003c/picture\u003e\n\u003c/p\u003e\n\n---\n\n[![License MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT)\n[![Python Version](https://img.shields.io/badge/python-3.9%2B-blue.svg)](https://www.python.org/)\n[![PyPI Version](https://img.shields.io/pypi/v/bridgic.svg)](https://pypi.org/search/?q=bridgic)\n[![PyPI Downloads](https://img.shields.io/pypi/dm/bridgic.svg)](https://pypi.org/search/?q=bridgic)\n\n\n**Bridgic** is the next-generation agent development framework for building intelligent systems.\n\nBy redefining the boundary between workflows and agents, Bridgic introduces a unified orchestration and runtime model which enables developers to seamlessly transition between predictable workflows and autonomous, creative agents within one system.\n\n\u003e ✨ The name \"**Bridgic**\" embodies our core philosophy — **\"Bridging Logic and Magic\"**, where:\n\u003e   - *Logic* represents deterministic and predictable execution flows, forming the foundation of reliable systems.\n\u003e   - *Magic* refers to the autonomous parts that can make dynamic decisions and solve problems creatively.\n\n\u003cdiv align=\"center\"\u003e\n\n```mermaid\ngraph\n    subgraph \" \"\n        A[\"Deterministic Workflows\u003cbr/\u003e(Logic)\"]\n        B[\"Autonomous Agents\u003cbr/\u003e(Magic)\"]\n    end\n    \n    A ---\u003e B\n    B ---\u003e A\n    \n    style A fill:#f9f9f9,stroke:#333,stroke-width:2px\n    style B fill:#f9f9f9,stroke:#333,stroke-width:2px\n```\n\n\u003c/div\u003e\n\n\n## 📦 Installation\n\nBridgic requires Python 3.9 or newer.\n\n### Using pip\n\n```bash\npip install bridgic\npython -c \"from bridgic.core import __version__; print(f'Bridgic version: {__version__}')\"\n```\n\n### Using uv\n\n```bash\nuv add bridgic\nuv run python -c \"from bridgic.core import __version__; print(f'Bridgic version: {__version__}')\"\n```\n\n\n## 🔗 Key Features\n\n### 🌀 Core Runtime Engine\n\n* **Unified DDG Foundation**: Both deterministic workflows and autonomous agents are orchestrated through the same [Dynamic Directed Graph](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/dynamic_topology/) runtime model, providing a unified foundation for intelligent systems.\n* **Dynamic Topology \u0026 Routing**: The mechanism of [dynamic topology](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/dynamic_topology/) allows graph structure to be modified at runtime, while [dynamic routing](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/dynamic_routing/) enables conditional branching through an intuitive [`ferry_to()`](https://docs.bridgic.ai/latest/reference/bridgic-core/bridgic/core/automa/#bridgic.core.automa.GraphAutoma.ferry_to) API.\n* **Multi-Layered Orchestration APIs**: Unified under DDG, Bridgic provides a **Declarative API** and **[ASL (Agent Structure Language)](https://docs.bridgic.ai/latest/tutorials/items/asl/quick_start/)**, which gives developers flexibility in structuring their code.\n\n### 🚀 Consistent Development Experience for Workflows and Agents\n\n* **Program-defined Execution Mode**: Build deterministic workflows with explicit control flow, where execution paths are defined by your code structure.\n* **Model-driven Autonomous Execution Mode**: Leverage [ReCentAutoma](https://docs.bridgic.ai/latest/tutorials/items/agentic_module/about_recent_automa.ipynb) and other autonomous agent modules where LLMs make dynamic decisions about tool selection and execution paths.\n\n### 🧩 Modular Development Support\n\n* **Modular Application Building**: Complex intelligent systems can be composed through [modularity](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/modularity/), enabling component reusing and hierarchical nesting.\n* **Parameter Resolving \u0026 Binding**: The mechanism of [parameter resolving](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/parameter_resolving/) enables passing data among workers/automas, eliminating the complexity of global state management.\n* **Agent Structure Language**: [ASL](https://docs.bridgic.ai/latest/tutorials/items/asl/quick_start/) is a Python-native DSL that enables developers to express sophisticated agentic structures within a limited amount of code, optimized for AI-assisted development.\n\n### 👥 Powerful Human-in-the-Loop Support\n\n* **Human Interaction Based on Asynchronous Awaiting**: Systems can pause execution and [await human feedback](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/human_in_the_loop/) asynchronously, enabling seamless integration of human judgment into automated workflows / agents.\n* **Human Interaction Based on Interruption \u0026 Resuming**: Long-running systems can be [interrupted at any point](https://docs.bridgic.ai/latest/tutorials/items/core_mechanism/human_in_the_loop/#reimbursement-workflow) , request external feedbacks, and seamlessly resume execution with state persistence and recovery.\n\n### 🔌 Seamless Third-Party Integration\n\n* **Technology-neutral Model Integration**: [Model integration](https://docs.bridgic.ai/latest/tutorials/items/model_integration/) allows seamlessly integration with any LLM provider through a unified abstraction.\n* **Systematic MCP Integration**: [MCP integration](https://docs.bridgic.ai/latest/tutorials/items/protocol_integration/mcp_quick_start/) allows your application to connect to MCP servers and to use their tools as first-class workers, in a systematic way in Bridgic.\n* **Seamless Enterprise Observability Integration**: Support for integrating the leading observability platforms (like [Opik](https://docs.bridgic.ai/latest/tutorials/items/observability/opik_integration/), [LangWatch](https://docs.bridgic.ai/latest/tutorials/items/observability/lang_watch_integration/)) ensures your agentic systems are transparent, debuggable, and optimizable.\n\n## 🚀 Get Started\n\nThis section demonstrates Bridgic's core capabilities through practical examples.\n\nYou'll learn how to build an intelligent system with Bridgic, from a simple chatbot to autonomous agentic system. In these cases, you will see features such as worker orchestration, dynamic routing, dynamic topology changing, and parameter resolving.\n\nPart-I examples includes both implementations in normal APIs and ASL, showing how ASL simplifies workflow definition with declarative syntax.\n\n### LLM Setup\n\nBefore diving into the examples, set up your LLM instance.\n\n```python\nimport os\nfrom bridgic.llms.openai import OpenAILlm, OpenAIConfiguration\n\n_api_key = os.environ.get(\"OPENAI_API_KEY\")\n_api_base = os.environ.get(\"OPENAI_API_BASE\")\n_model_name = os.environ.get(\"OPENAI_MODEL_NAME\")\n\nllm = OpenAILlm(\n    api_key=_api_key,\n    api_base=_api_base,\n    configuration=OpenAIConfiguration(model=_model_name),\n    timeout=120,\n)\n```\n\n---\n\n## Part I: Workflow Orchestration\n\nEach example in this part provides two implementations:\n\n- the declarative API approach helps you understand how Bridgic works under the hood\n- the ASL approach shows how it simplifies workflow definition with declarative syntax.\n\n\u003cimg src=\"./docs/images/bridgic_api_hierarchy.png\" alt=\"Bridgic API Hierarchy\" width=\"620\"/\u003e\n\n### Example 1: Build Your First Chatbot Using Bridgic\n\n**Core Features:**\n- Declare static dependencies between workflow steps\n- Mark start and output workers within the workflow\n- Reuse already implemented Automa components\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild with Normal API\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/question_solver_bot.py)\n\n```python\nfrom typing import List, Dict, Optional\n\nfrom bridgic.core.model.types import Message\nfrom bridgic.core.automa import GraphAutoma, worker, RunningOptions\n\nclass DivideConquerWorkflow(GraphAutoma):\n    \"\"\"Break down a query into sub-queries and answer each one.\"\"\"\n    \n    @worker(is_start=True)\n    async def break_down_query(self, user_input: str) -\u003e List[str]:\n        \"\"\"Break down the query into a list of sub-queries.\"\"\"\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(\n                    text=\"Break down the query into multiple sub-queries and only return the sub-queries\",\n                    role=\"system\"\n                ),\n                Message.from_text(text=user_input, role=\"user\"),\n            ]\n        )\n        return [item.strip() for item in llm_response.message.content.split(\"\\n\") if item.strip()]\n\n    @worker(dependencies=[\"break_down_query\"], is_output=True)\n    async def query_answer(self, queries: List[str]) -\u003e Dict[str, str]:\n        \"\"\"Generate answers for each sub-query.\"\"\"\n        answers = []\n        for query in queries:\n            response = await llm.achat(\n                messages=[\n                    Message.from_text(text=\"Answer the given query briefly\", role=\"system\"),\n                    Message.from_text(text=query, role=\"user\"),\n                ]\n            )\n            answers.append(response.message.content)\n        \n        return {\n            query: answer\n            for query, answer in zip(queries, answers)\n        }\n\nclass QuestionSolverBot(GraphAutoma):\n    \"\"\"A bot that solves questions by breaking them down and merging answers.\"\"\"\n    \n    def __init__(self, name: Optional[str] = None, running_options: Optional[RunningOptions] = None):\n        super().__init__(name=name, running_options=running_options)\n        # Add DivideConquerWorkflow as a sub-automa\n        divide_conquer = DivideConquerWorkflow()\n        self.add_worker(\n            key=\"divide_conquer_workflow\",\n            worker=divide_conquer,\n            is_start=True\n        )\n        # Set dependency: merge_answers depends on divide_conquer_workflow\n        self.add_dependency(\"merge_answers\", \"divide_conquer_workflow\")\n    \n    @worker(is_output=True)\n    async def merge_answers(self, qa_pairs: Dict[str, str], user_input: str) -\u003e str:\n        \"\"\"Merge individual answers into a unified response.\"\"\"\n        answers = \"\\n\".join([v for v in qa_pairs.values()])\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(text=\"Answer the question in bullet points.\", role=\"system\"),\n                Message.from_text(text=f\"Question: {user_input}\\nAnswers: {answers}\", role=\"user\"),\n            ]\n        )\n        return llm_response.message.content\n\n# Run the chatbot\nasync def main():\n    chatbot = QuestionSolverBot(running_options=RunningOptions(debug=False))\n    answer = await chatbot.arun(user_input=\"When and where was Einstein born?\")\n    print(answer)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe execution result will be like:\n\n```text\n- Albert Einstein was born on March 14, 1879.\n- He was born in Ulm.\n- Ulm is located in the Kingdom of Württemberg.\n- At the time of his birth, it was part of the German Empire.\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild in ASL\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/question_solver_bot_asl.py)\n\n```python\nfrom typing import List, Dict\n\nfrom bridgic.core.automa import RunningOptions\nfrom bridgic.core.model.types import Message\nfrom bridgic.asl import ASLAutoma, graph\n\n# Break down the query into a list of sub-queries.\nasync def break_down_query(user_input: str) -\u003e List[str]:\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(\n                text=\"Break down the query into multiple sub-queries and only return the sub-queries\",\n                role=\"system\"\n            ),\n            Message.from_text(text=user_input, role=\"user\"),\n        ]\n    )\n    return [item.strip() for item in llm_response.message.content.split(\"\\n\") if item.strip()]\n\n# Generate answers for each sub-query.\nasync def query_answer(queries: List[str]) -\u003e Dict[str, str]:\n    answers = []\n    for query in queries:\n        response = await llm.achat(\n            messages=[\n                Message.from_text(text=\"Answer the given query briefly\", role=\"system\"),\n                Message.from_text(text=query, role=\"user\"),\n            ]\n        )\n        answers.append(response.message.content)\n    \n    return {\n        query: answer\n        for query, answer in zip(queries, answers)\n    }\n\nclass DivideConquerWorkflow(ASLAutoma):\n    with graph as g:\n        a = break_down_query\n        b = query_answer\n        +a \u003e\u003e ~b\n\nasync def merge_answers(qa_pairs: Dict[str, str], user_input: str) -\u003e str:\n    \"\"\"Merge individual answers into a unified response.\"\"\"\n    answers = \"\\n\".join([v for v in qa_pairs.values()])\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(text=\"Answer the question in bullet points.\", role=\"system\"),\n            Message.from_text(text=f\"Question: {user_input}\\nAnswers: {answers}\", role=\"user\"),\n        ]\n    )\n    return llm_response.message.content\n\n# Define the QuestionSolverBot agent, reuse DivideConquerWorkflow in a component-oriented fashion.\nclass QuestionSolverBot(ASLAutoma):\n    with graph as g:\n        a = DivideConquerWorkflow()  # Component reuse\n        b = merge_answers\n        +a \u003e\u003e ~b\n\n# Run the chatbot\nasync def main():\n    chatbot = QuestionSolverBot(running_options=RunningOptions(debug=False))\n    answer = await chatbot.arun(user_input=\"When and where was Einstein born?\")\n    print(answer)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe execution result will be like:\n\n```text\n- Albert Einstein was born on March 14, 1879.\n- He was born in Ulm.\n- Ulm is located in the Kingdom of Württemberg.\n- At the time of his birth, it was part of the German Empire.\n```\n\n\u003c/details\u003e\n\n---\n\n### Example 2: Go Beyond Chatbots by Dynamic Routing\n\n**Core Features:**\n- Runtime conditional branching via `ferry_to` API\n- Intelligent decision making by LLM\n- Multiple output workers (but only one will be)\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild with Normal API\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/dynamic_routing.py)\n\n```python\nfrom pydantic import BaseModel\nfrom bridgic.core.model.types import Message\nfrom bridgic.core.model.protocols import PydanticModel\nfrom bridgic.core.automa import GraphAutoma, worker, RunningOptions\n\nclass QueryCategory(BaseModel):\n    category: str\n\nclass SimpleAssistant(GraphAutoma):\n    @worker(is_start=True)\n    async def router(self, request: str) -\u003e str:\n        \"\"\"Classify the request and route to the corresponding handler.\"\"\"\n        print(f\"Routing request: {request}\")\n        \n        classification: QueryCategory = await llm.astructured_output(\n            constraint=PydanticModel(model=QueryCategory),\n            messages=[\n                Message.from_text(\n                    text=(\n                        \"You are a classifier. Given a single user request, decide whether it is:\\n\"\n                        \"- 'questionn_answer': a factual or explanatory question;\\n\"\n                        \"- 'createive_writing': a creative writing instruction;\\n\"\n                        \"- 'code_writing': a request to write or modify code;\\n\"\n                        \"- 'unknown': anything else.\\n\"\n                    ),\n                    role=\"system\",\n                ),\n                Message.from_text(text=request, role=\"user\"),\n            ],\n        )\n        \n        category = classification.category\n        if category == \"questionn_answer\":\n            self.ferry_to(\"hq\", question=request)\n        elif category == \"createive_writing\":\n            self.ferry_to(\"creative\", instruction=request)\n        elif category == \"code_writing\":\n            self.ferry_to(\"code\", instruction=request)\n        else:\n            self.ferry_to(\"unknown\", original=request)\n    \n    @worker(key=\"hq\", is_output=True)\n    async def handle_question(self, question: str) -\u003e str:\n        print(\"❓ QUESTION\")\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(\n                    text=\"You answer factual or explanatory questions. Give a concise, accurate answer.\",\n                    role=\"system\",\n                ),\n                Message.from_text(text=question, role=\"user\"),\n            ]\n        )\n        return llm_response.message.content\n    \n    @worker(key=\"creative\", is_output=True)\n    async def handle_creative(self, instruction: str) -\u003e str:\n        print(\"🎨 CREATIVE\")\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(\n                    text=\"You are a creative writer. Follow the user's instruction and create vivid, engaging content.\",\n                    role=\"system\",\n                ),\n                Message.from_text(text=instruction, role=\"user\"),\n            ]\n        )\n        return llm_response.message.content\n    \n    @worker(key=\"code\", is_output=True)\n    async def handle_code(self, instruction: str) -\u003e str:\n        print(\"💻 CODE\")\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(\n                    text=\"You are a code assistant. Write correct, minimal code that satisfies the user's request.\",\n                    role=\"system\",\n                ),\n                Message.from_text(text=instruction, role=\"user\"),\n            ]\n        )\n        return llm_response.message.content\n    \n    @worker(key=\"unknown\", is_output=True)\n    async def handle_unknown(self, original: str) -\u003e str:\n        print(\"⚪ UNKNOWN\")\n        return original\n\n# Run the router\nasync def main():\n    router = SimpleAssistant(running_options=RunningOptions(debug=False))\n    response = await router.arun(request=\"When and where was Einstein born?\")\n    print(response)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe execution and result will be like:\n\n```text\n================================================================================\nRouting request: When and where was Einstein born?\n❓ QUESTION\nAlbert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, German Empire.\n\n================================================================================\nRouting request: Create a one-sentence poem about the spring season.\n🎨 CREATIVE\nIn a gentle whisper of blooms and dew, spring unfurls like laughter across the waking earth.\n\n================================================================================\nRouting request: Write a shell command to list all files in /bin directory.\n💻 CODE\n```sh\nls /bin\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild in ASL\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/dynamic_routing_asl.py)\n\n```python\nfrom pydantic import BaseModel\nfrom bridgic.core.model.types import Message\nfrom bridgic.core.model.protocols import PydanticModel\nfrom bridgic.core.automa import GraphAutoma\nfrom bridgic.core.automa.args import System\nfrom bridgic.asl import ASLAutoma, graph\n\nclass QueryCategory(BaseModel):\n    category: str\n\nasync def handle_question(question: str) -\u003e str:\n    print(\"❓ QUESTION\")\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(\n                text=\"You answer factual or explanatory questions. Give a concise, accurate answer.\",\n                role=\"system\",\n            ),\n            Message.from_text(text=question, role=\"user\"),\n        ]\n    )\n    return llm_response.message.content\n\nasync def handle_creative(instruction: str) -\u003e str:\n    print(\"🎨 CREATIVE\")\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(\n                text=\"You are a creative writer. Follow the user's instruction and create vivid, engaging content.\",\n                role=\"system\",\n            ),\n            Message.from_text(text=instruction, role=\"user\"),\n        ]\n    )\n    return llm_response.message.content\n\nasync def handle_code(instruction: str) -\u003e str:\n    print(\"💻 CODE\")\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(\n                text=\"You are a code assistant. Write correct, minimal code that satisfies the user's request.\",\n                role=\"system\",\n            ),\n            Message.from_text(text=instruction, role=\"user\"),\n        ]\n    )\n    return llm_response.message.content\n\nasync def handle_unknown(original: str) -\u003e str:\n    print(\"⚪ UNKNOWN\")\n    return original\n\nasync def router(\n    request: str,\n    automa: GraphAutoma = System(\"automa\")\n) -\u003e str:\n    \"\"\"Classify the request and route to the corresponding handler.\"\"\"\n    print(f\"Routing request: {request}\")\n    \n    classification: QueryCategory = await llm.astructured_output(\n        constraint=PydanticModel(model=QueryCategory),\n        messages=[\n            Message.from_text(\n                text=(\n                    \"You are a classifier. Given a single user request, decide whether it is:\\n\"\n                    \"- 'questionn_answer': a factual or explanatory question;\\n\"\n                    \"- 'createive_writing': a creative writing instruction;\\n\"\n                    \"- 'code_writing': a request to write or modify code;\\n\"\n                    \"- 'unknown': anything else.\\n\"\n                ),\n                role=\"system\",\n            ),\n            Message.from_text(text=request, role=\"user\"),\n        ],\n    )\n    \n    category = classification.category\n    if category == \"questionn_answer\":\n        automa.ferry_to(\"hq\", question=request)\n    elif category == \"createive_writing\":\n        automa.ferry_to(\"creative\", instruction=request)\n    elif category == \"code_writing\":\n        automa.ferry_to(\"code\", instruction=request)\n    else:\n        automa.ferry_to(\"unknown\", original=request)\n\nclass SimpleAssistant(ASLAutoma):\n    with graph as g:\n        start = router\n        hq = handle_question\n        creative = handle_creative\n        code = handle_code\n        unknown = handle_unknown\n        \n        +start, ~hq, ~creative, ~code, ~unknown\n\n# Run the router\nasync def main():\n    router = SimpleAssistant()\n    response = await router.arun(request=\"When and where was Einstein born?\")\n    print(response)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\n```text\n================================================================================\nRouting request: When and where was Einstein born?\n❓ QUESTION\nAlbert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, German Empire.\n\n================================================================================\nRouting request: Create a one-sentence poem about the spring season.\n🎨 CREATIVE\nIn a gentle whisper of blooms and dew, spring unfurls like laughter across the waking earth.\n\n================================================================================\nRouting request: Write a shell command to list all files in /bin directory.\n💻 CODE\n```sh\nls /bin\n```\n\n\u003c/details\u003e\n\n---\n\n### Example 3: Let Runtime Decision Making Affect the Topology\n\n**Core Features:**\n- Runtime graph topology changing\n- Dynamic worker instantiation\n- Result aggregation from dynamic nodes by `ArgsMappingRule`\n\n\u003cimg src=\"./docs/docs/tutorials/imgs/dynamic_topo.png\" alt=\"Dynamic Topology Demo\" width=\"400\"/\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild with Normal API\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/dynamic_topology.py)\n\n```python\nfrom typing import List\n\nfrom bridgic.core.automa import GraphAutoma, worker, RunningOptions\nfrom bridgic.core.automa.args import ArgsMappingRule\n\nclass DynamicGraph(GraphAutoma):\n    \"\"\"A dynamic graph that creates handlers based on the number of tasks.\"\"\"\n    \n    @worker(is_start=True)\n    async def produce_task(self, user_input: int) -\u003e List[int]:\n        \"\"\"Produce a list of tasks and dynamically create handlers for each task.\"\"\"\n        tasks = [i for i in range(user_input)]\n        handler_keys = []\n        \n        # Dynamically create handlers for each task\n        for task in tasks:\n            handler_key = f\"handler_{task}\"\n            self.add_func_as_worker(\n                key=handler_key,\n                func=self.task_handler\n            )\n            # Use ferry_to to trigger each handler with its corresponding task\n            self.ferry_to(handler_key, sub_task=task)\n            handler_keys.append(handler_key)\n        \n        # Create collector that depends on all dynamic handlers\n        self.add_func_as_worker(\n            key=\"collect\",\n            func=self.collect,\n            dependencies=handler_keys,\n            args_mapping_rule=ArgsMappingRule.MERGE,\n            is_output=True\n        )\n        return tasks\n    \n    async def task_handler(self, sub_task: int) -\u003e int:\n        \"\"\"Handle a single sub-task.\"\"\"\n        res = sub_task + 1\n        return res\n    \n    async def collect(self, res_list: List[int]) -\u003e List[int]:\n        \"\"\"Collect results from all task handlers.\"\"\"\n        return res_list\n\n# Run the dynamic graph\nasync def main():\n    dynamic_graph = DynamicGraph(running_options=RunningOptions(debug=False))\n    result = await dynamic_graph.arun(user_input=3)\n    print(f\"Result: {result}\")\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\n```text\nResult: [1, 2, 3]\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild in ASL\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/dynamic_topology_asl.py)\n\n```python\nfrom typing import List\n\nfrom bridgic.core.automa.args import ResultDispatchingRule\nfrom bridgic.asl import ASLAutoma, graph, concurrent, Settings, ASLField\n\nasync def produce_task(user_input: int) -\u003e List[int]:\n    \"\"\"Produce a list of tasks based on user input.\"\"\"\n    tasks = [i for i in range(user_input)]\n    return tasks\n\nasync def task_handler(sub_task: int) -\u003e int:\n    \"\"\"Handle a single sub-task.\"\"\"\n    res = sub_task + 1\n    return res\n\nclass DynamicGraph(ASLAutoma):\n    with graph(user_input=ASLField(type=int)) as g:\n        producer = produce_task\n        \n        with concurrent(\n            tasks=ASLField(\n                type=list,\n                dispatching_rule=ResultDispatchingRule.IN_ORDER\n            )\n        ) as c:\n            # Lambda expression dynamically creates handlers for each task\n            dynamic_handler = lambda tasks: (\n                task_handler *Settings(key=f\"handler_{task}\")\n                for task in tasks\n            )\n        \n        +producer \u003e\u003e ~c\n\n# Run the dynamic graph\nasync def main():\n    dynamic_graph = DynamicGraph()\n    result = await dynamic_graph.arun(user_input=3)\n    print(f\"Result: {result}\")\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\n```text\nResult: [1, 2, 3]\n```\n\n\u003c/details\u003e\n\n---\n\n### Example 4: Simplify Input Acquisition by Parameter Resolving Mechanism\n\n**Core Features:**\n- Arguments mapping and merging\n- Parameter injection with `From()`\n- Concurrent execution result aggregation\n\n\u003cimg src=\"./docs/images/parameter_resolving_demo.png\" alt=\"Parameter Resolving Demo\" width=\"400\"/\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild with Normal API\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/parameter_resolving.py)\n\n```python\nfrom typing import List, Tuple\n\nfrom bridgic.core.model.types import Message\nfrom bridgic.core.automa import GraphAutoma, worker, RunningOptions\nfrom bridgic.core.automa.args import ArgsMappingRule, From\n\nclass RAGProcessor(GraphAutoma):\n    \"\"\"A RAG processor that uses keyword and semantic search, then synthesizes the results.\"\"\"\n    \n    @worker(is_start=True)\n    async def pre_process(self, user_input: str) -\u003e str:\n        \"\"\"Pre-process the user input.\"\"\"\n        return user_input.strip()\n    \n    @worker(dependencies=[\"pre_process\"])\n    async def keyword_search(self, query: str) -\u003e List[str]:\n        \"\"\"Simulate keyword search by returning a fixed list of chunks.\"\"\"\n        chunks = [\n            \"Albert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, Germany.\",\n            \"Einstein was born into a secular Jewish family.\",\n            \"Einstein had one sister, Maja, who was born two years after him.\",\n        ]\n        return chunks\n    \n    @worker(dependencies=[\"pre_process\"])\n    async def semantic_search(self, query: str) -\u003e List[str]:\n        \"\"\"Simulate semantic search by returning a fixed list of chunks.\"\"\"\n        chunks = [\n            \"Albert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg in the German Empire.\",\n            \"Shortly after his birth, his family moved to Munich, where he spent most of his childhood.\",\n            \"Einstein excelled at physics and mathematics from an early age.\",\n        ]\n        return chunks\n    \n    @worker(\n        dependencies=[\"keyword_search\", \"semantic_search\"],\n        args_mapping_rule=ArgsMappingRule.MERGE,\n        is_output=True\n    )\n    async def synthesize_response(\n        self,\n        search_results: Tuple[List[str], List[str]],\n        query: str = From(\"pre_process\")  # Inject from pre_process\n    ) -\u003e str:\n        \"\"\"Synthesize a response from the search results.\"\"\"\n        chunks_by_keyword, chunks_by_semantic = search_results\n        all_chunks = chunks_by_keyword + chunks_by_semantic\n        prompt = f\"{query}\\n---\\nAnswer the above question based on the following references.\\n{all_chunks}\"\n        llm_response = await llm.achat(\n            messages=[\n                Message.from_text(text=\"You are a helpful assistant\", role=\"system\"),\n                Message.from_text(text=prompt, role=\"user\"),\n            ]\n        )\n        return llm_response.message.content\n\n# Run the RAG processor\nasync def main():\n    rag = RAGProcessor(running_options=RunningOptions(debug=False))\n    result = await rag.arun(user_input=\"When and where was Einstein born?\")\n    print(result)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe answer will be like:\n\n```text\nAlbert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, Germany (now part of modern Germany).\n```\n\n\u003c/details\u003e\n\n\u003cdetails\u003e\n\u003csummary\u003eBuild in ASL\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/orchestration/parameter_resolving_asl.py)\n\n```python\nfrom typing import List, Tuple\n\nfrom bridgic.core.model.types import Message\nfrom bridgic.asl import ASLAutoma, graph, Settings\nfrom bridgic.core.automa import RunningOptions\nfrom bridgic.core.automa.args import ArgsMappingRule, From\n\nasync def pre_process(user_input: str) -\u003e str:\n    \"\"\"Pre-process the user input.\"\"\"\n    return user_input.strip()\n\nasync def keyword_search(query: str) -\u003e List[str]:\n    \"\"\"Simulate keyword search by returning a fixed list of chunks.\"\"\"\n    chunks = [\n        \"Albert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, Germany.\",\n        \"Einstein was born into a secular Jewish family.\",\n        \"Einstein had one sister, Maja, who was born two years after him.\",\n    ]\n    return chunks\n\nasync def semantic_search(query: str) -\u003e List[str]:\n    \"\"\"Simulate semantic search by returning a fixed list of chunks.\"\"\"\n    chunks = [\n        \"Albert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg in the German Empire.\",\n        \"Shortly after his birth, his family moved to Munich, where he spent most of his childhood.\",\n        \"Einstein excelled at physics and mathematics from an early age.\",\n    ]\n    return chunks\n\nasync def synthesize_response(\n    search_results: Tuple[List[str], List[str]], \n    query: str = From(\"pre_process\")  # Inject from pre_process\n) -\u003e str:\n    \"\"\"Synthesize a response from the search results.\"\"\"\n    chunks_by_keyword, chunks_by_semantic = search_results\n    all_chunks = chunks_by_keyword + chunks_by_semantic\n    prompt = f\"{query}\\n---\\nAnswer the above question based on the following references.\\n{all_chunks}\"\n    llm_response = await llm.achat(\n        messages=[\n            Message.from_text(text=\"You are a helpful assistant\", role=\"system\"),\n            Message.from_text(text=prompt, role=\"user\"),\n        ]\n    )\n    return llm_response.message.content\n\nclass RAGProcessor(ASLAutoma):\n    with graph as g:\n        pre_process = pre_process\n        k = keyword_search\n        s = semantic_search\n        output = synthesize_response *Settings(\n            args_mapping_rule=ArgsMappingRule.MERGE\n        )\n        \n        +pre_process \u003e\u003e (k \u0026 s) \u003e\u003e ~output\n\n# Run the RAG processor\nasync def main():\n    rag = RAGProcessor(running_options=RunningOptions(debug=False))\n    result = await rag.arun(user_input=\"When and where was Einstein born?\")\n    print(result)\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe answer will be like:\n\n```text\nAlbert Einstein was born on March 14, 1879, in Ulm, in the Kingdom of Württemberg, Germany (now part of modern Germany).\n```\n\n\u003c/details\u003e\n\n---\n\n## Part II: Building Complex Agentic Systems\n\nThe examples in this part demonstrate how to develop more complex intelligent systems in a relaxed and natural way, using Bridgic's powerful mechanisms and components. With the underlying unified DDG runtime model, powerful third-party integrations, and appropriate programming techniques, you can easily build more complex workflows and agents.\n\n### Example 5: Interactive CLI with MCP Integration\n\n**Core Features:**\n- Human-in-the-loop interactions with interrupt-resume mechanism\n- MCP integration for CLI tool access\n- Dynamic worker creation for multi-turn interactions\n- Connection reuse across execution cycles\n\n\u003cdetails\u003e\n\u003csummary\u003eView Code\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/agentic/cli_automa_mcp.py)\n\n```python\nimport os\nimport uuid\nimport tempfile\n\nimport mcp\n\nfrom bridgic.core.automa import GraphAutoma, worker, RunningOptions\nfrom bridgic.core.automa.interaction import Event, InteractionFeedback, InteractionException\nfrom bridgic.core.utils._console import printer\nfrom bridgic.protocols.mcp import (\n    McpServerConnectionStdio,\n    McpServerConnectionManager,\n)\n\n# Create a temporary directory for the CLI MCP server\ntemp_dir = os.path.realpath(tempfile.mkdtemp())\nprint(f\"Using temporary directory: {temp_dir}\")\n\n# Create a file with written content for demonstration\nwith open(os.path.join(temp_dir, \"dream.txt\"), \"w\", encoding=\"utf-8\") as f:\n    f.write(\"Bridging Logic and Magic\")\n\n# Connect to CLI MCP server\n# Note: This requires uvx to be installed (or use npx with @modelcontextprotocol/server-cli)\ncli_connection = McpServerConnectionStdio(\n    name=\"connection-cli-stdio\",\n    command=\"uvx\",\n    args=[\"cli-mcp-server\"],\n    env={\n        \"ALLOWED_DIR\": temp_dir,\n        \"ALLOWED_COMMANDS\": \"ls,cat,wc,pwd,echo,touch\",\n        \"ALLOWED_FLAGS\": \"all\",\n        \"ALLOW_SHELL_OPERATORS\": \"true\",\n    },\n)\n\n# Register the connection with a dedicated manager\nMcpServerConnectionManager.get_instance(\"terminal-use\").register_connection(cli_connection)\ncli_connection.connect()\n\nprint(f\"✓ Connected to CLI MCP server: {cli_connection.name}\\n\")\n\n\nclass CliAutoma(GraphAutoma):\n    \"\"\"\n    An interactive CLI automa that supports human-in-the-loop interactions.\n    \n    The automa:\n    - Welcomes the user\n    - Requests commands via interact_with_human()\n    - Executes commands using MCP CLI tools\n    - Supports interrupt-resume cycles for multi-turn interactions\n    - Reuses the same MCP connection across all cycles\n    \"\"\"\n    \n    @worker(is_start=True)\n    def start(self):\n        \"\"\"Start the CLI automa and request the first command.\"\"\"\n        printer.print(f\"Welcome to the example CLI Automa.\", color=\"gray\")\n        self.ferry_to(\"human_input\")\n\n    @worker()\n    def human_input(self):\n        \"\"\"\n        Handle human input through interrupt-resume mechanism.\n        \n        - On first run: pauses (raising InteractionException)\n        - On resume: receives feedback (the human command) and continues\n        \"\"\"\n        event = Event(event_type=\"get_human_command\")\n        feedback: InteractionFeedback = self.interact_with_human(event)\n        human_command = feedback.data\n\n        printer.print(f\"\u003e {human_command}\")\n\n        if human_command in [\"quit\", \"exit\"]:\n            self.ferry_to(\"end\")\n        else:\n            # Generate unique keys for dynamic workers\n            tool_key = f\"tool-\u003c{uuid.uuid4().hex[:8]}\u003e\"\n            collect_key = f\"collect-\u003c{uuid.uuid4().hex[:8]}\u003e\"\n\n            async def _collect_command_result(command_result: mcp.types.CallToolResult):\n                \"\"\"Collect and display the command result, then request next command.\"\"\"\n                printer.print(f\"{command_result.content[0].text.strip()}\\n\", color=\"gray\")\n                self.ferry_to(\"human_input\")\n\n            # Reuse the same connection across all interrupt-resume cycles\n            real_connection = McpServerConnectionManager.get_connection(\"connection-cli-stdio\")\n\n            # Filter the \"run_command\" tool spec from cli-mcp-server\n            command_tool = next(t for t in real_connection.list_tools() if t.tool_name == \"run_command\")\n\n            # Use the tool specification to create worker instance and then add it dynamically\n            self.add_worker(tool_key, command_tool.create_worker())\n            self.add_func_as_worker(collect_key, _collect_command_result, dependencies=[tool_key])\n            self.ferry_to(tool_key, command=human_command)\n\n    @worker(is_output=True)\n    def end(self):\n        \"\"\"End the CLI automa session.\"\"\"\n        printer.print(f\"See you again.\\n\", color=\"gray\")\n\n\nasync def main():\n    \"\"\"\n    Main function demonstrating the CliAutoma usage.\n    \n    The lifecycle of an MCP server connection is independent from the execution\n    of an automa. Once a connection is established and managed by a connection\n    manager, it remains open until you close it.\n    \"\"\"\n    hi_automa = CliAutoma(\n        name=\"human-interaction-automa\",\n        running_options=RunningOptions(debug=False)\n    )\n\n    interaction_id = None\n\n    async def continue_automa(feedback_data=None) -\u003e str:\n        \"\"\"Helper function to run or resume the automa.\"\"\"\n        try:\n            await hi_automa.arun(feedback_data=feedback_data)\n        except InteractionException as e:\n            interaction_id = e.interactions[0].interaction_id\n            return interaction_id\n\n    # First run: automa reaches human_input, calls interact_with_human, pauses\n    interaction_id = await continue_automa()\n\n    # Each iteration we send the human command as feedback to resume the execution\n    commands = [\n        \"pwd\",\n        \"ls -l\",\n        \"wc -l ./dream.txt\",\n        \"cat ./dream.txt\",\n        \"exit\",\n    ]\n    \n    for command in commands:\n        interaction_feedback = InteractionFeedback(\n            interaction_id=interaction_id,\n            data=command\n        )\n        interaction_id = await continue_automa(interaction_feedback)\n\n    # Close the connection when done\n    cli_connection.close()\n    print(f\"✓ Connection closed: {not cli_connection.is_connected}\")\n\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe execution result will be like:\n\n```text\nUsing temporary directory: /private/var/folders/9t/5r9fms9s5q33p6xty_0_k1mw0000gn/T/tmptuak4y7n\n✓ Connected to CLI MCP server: connection-cli-stdio\n  Connection status: True\n\nWelcome to the example CLI Automa.\n\u003e pwd\n/private/var/folders/9t/5r9fms9s5q33p6xty_0_k1mw0000gn/T/tmptuak4y7n\n\n\u003e ls -l\ntotal 8\n-rw-r--r--  1 xushili  staff  24 Jan 28 02:32 dream.txt\n\n\u003e wc -l ./dream.txt\n0 /private/var/folders/9t/5r9fms9s5q33p6xty_0_k1mw0000gn/T/tmptuak4y7n/dream.txt\n\n\u003e cat ./dream.txt\nBridging Logic and Magic\n\n\u003e exit\nSee you again.\n\n✓ Connection closed: True\n```\n\n\u003c/details\u003e\n\n---\n\n### Example 6: BMI Weight Management Assistant\n\n**Core Features:**\n- ReCentAutoma for goal-oriented autonomous execution\n- Custom tool integration\n- Built-in memory management and goal tracking\n- Multi-step task planning and execution\n\n\u003cdetails\u003e\n\u003csummary\u003eView Code\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/agentic/bmi_weight_assistant.py)\n\n```python\nfrom bridgic.core.automa import RunningOptions\nfrom bridgic.core.agentic.recent import ReCentAutoma, StopCondition\n\n\nasync def calculate_bmi(weight_kg: float, height_m: float) -\u003e str:\n    \"\"\"\n    Calculate Body Mass Index (BMI) from weight and height.\n\n    Parameters\n    ----------\n    weight_kg : float\n        Weight in kilograms. Must be a positive number.\n    height_m : float\n        Height in meters. Must be a positive number.\n\n    Returns\n    -------\n    str\n        A formatted string containing the BMI value and its interpretation. BMI categories:\n        - Underweight: \u003c 18.5\n        - Normal: 18.5 - 24.9\n        - Overweight: 25 - 29.9\n        - Obese: \u003e= 30\n    \"\"\"\n    if weight_kg \u003c= 0 or height_m \u003c= 0:\n        return \"Error: Weight and height must be positive numbers.\"\n\n    bmi = weight_kg / (height_m ** 2)\n\n    if bmi \u003c 18.5:\n        category = \"Underweight\"\n    elif bmi \u003c 25:\n        category = \"Normal\"\n    elif bmi \u003c 30:\n        category = \"Overweight\"\n    else:\n        category = \"Obese\"\n\n    return f\"BMI: {bmi:.2f} ({category}). Weight: {weight_kg} kg, Height: {height_m} m.\"\n\n\nasync def main():\n    # Create an agent with the custom tools\n    weight_assistant = ReCentAutoma(\n        llm=llm,\n        tools=[calculate_bmi],\n        stop_condition=StopCondition(max_iteration=5, max_consecutive_no_tool_selected=1),\n        running_options=RunningOptions(debug=False),\n    )\n\n    # Example person data\n    person_weight = 82\n    person_height = 1.70\n    person_name = \"John Smith\"\n    person_gender = \"male\"\n\n    result = await weight_assistant.arun(\n        goal=(\n            f\"Calculate a person's BMI and provide personalized suggestions for effective weight management.\"\n            f\"\\n- Name: {person_name}\"\n            f\"\\n- Gender: {person_gender}\"\n            f\"\\n- Weight: {person_weight} kg\"\n            f\"\\n- Height: {person_height} m\"\n        ),\n        guidance=(\n            \"First calculate the BMI of the person and then give out a suggestion about the weight management.\"\n        ),\n    )\n    print(result)\n\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe execution result will be like:\n\n```text\n### Comprehensive BMI Calculation and Weight Management Suggestions for John Smith\n\n**Personal Information:**\n- **Name:** John Smith\n- **Gender:** Male\n- **Weight:** 82 kg\n- **Height:** 1.7 m\n\n**BMI Calculation:**\n- **BMI Result:** 28.37\n- **BMI Category:** Overweight\n\n**Weight Management Suggestions:**\nTo effectively manage weight and improve overall health, here are some personalized suggestions for John Smith:\n\n1. **Dietary Adjustments:**\n   - **Balanced Diet:** Focus on a diet rich in whole foods, including fruits, vegetables, lean proteins, and whole grains. \n   - **Caloric Deficit:** Aim to consume fewer calories than the body expends to promote weight loss. A modest deficit of 500 calories per day can result in approximately 0.5 kg weight loss per week.\n   - **Hydration:** Increase water intake while reducing sugary drinks and alcohol.\n\n2. **Physical Activity:**\n   - **Regular Exercise:** Aim for at least 150 minutes of moderate-intensity aerobic activity per week, such as brisk walking, cycling, or swimming.\n   - **Strength Training:** Incorporate strength training exercises at least twice a week to build muscle and boost metabolism.\n\n3. **Healthy Lifestyle Choices:**\n   - **Sleep:** Ensure 7-9 hours of quality sleep each night, as inadequate sleep can affect weight management and overall health.\n   - **Stress Management:** Practice stress-reducing techniques such as yoga, meditation, or mindfulness, as stress can lead to unhealthy eating habits.\n\n4. **Regular Monitoring:**\n   - **Track Progress:** Keep a journal of food intake and physical activity to monitor progress and stay accountable.\n   - **Consult a Professional:** Consider working with a registered dietitian or nutritionist for personalized dietary advice and support.\n\nBy implementing these suggestions, John Smith can work towards achieving a healthier weight, improving his overall health and well-being.\n```\n\n\u003c/details\u003e\n\n---\n\n### Example 7: Autonomous Browser Agent for Gold Price Query\n\n**Core Features:**\n- `ReCentAutoma` with Playwright MCP integration for browser automation\n- Goal-oriented task execution with memory management\n- Guidance-enabled control on the behavior of agents\n\n\u003cdetails\u003e\n\u003csummary\u003eView Code\u003c/summary\u003e\n\n[View full code](https://github.com/bitsky-tech/bridgic-examples/blob/main/agentic/browser_gold_price_agent.py)\n\n```python\nimport os\nimport tempfile\n\nfrom bridgic.core.automa import RunningOptions\nfrom bridgic.core.agentic.recent import ReCentAutoma, ReCentMemoryConfig, StopCondition\nfrom bridgic.protocols.mcp import (\n    McpServerConnectionStdio,\n    McpServerConnectionManager,\n)\n\n\nasync def main():\n    \"\"\"\n    Main function demonstrating browser automation with ReCentAutoma and Playwright MCP.\n    \n    In this example, the agent autonomously uses a browser to navigate to the\n    Hong Kong Gold Exchange website and search for the gold price. The entire\n    execution process is fully recorded during the execution of ReCentAutoma.\n    \"\"\"\n    # Create a temporary directory for Playwright output (videos, screenshots, etc.)\n    temp_dir = os.path.realpath(tempfile.mkdtemp())\n    print(f\"✓ Using temporary directory: {temp_dir}\")\n\n    # Connect to Playwright MCP server\n    # Note: This requires Node.js and npx to be installed\n    playwright_connection = McpServerConnectionStdio(\n        name=\"connection-playwright-stdio\",\n        command=\"npx\",\n        args=[\n            \"@playwright/mcp@latest\",\n            f\"--output-dir={temp_dir}\",\n            \"--viewport-size=1920x1080\",\n            \"--save-video=1920x1080\",\n        ],\n        request_timeout=60,\n    )\n\n    # Register the connection with a dedicated manager\n    # This allows isolation from other MCP connections (e.g., CLI, filesystem)\n    McpServerConnectionManager.get_instance(\"browser-use\").register_connection(playwright_connection)\n\n    # Establish the connection and verify connection and list available tools\n    # Note: registration must be done before calling connect()\n    playwright_connection.connect()\n    print(f\"✓ Connected to Playwright MCP server: {playwright_connection.name}\")\n    print(f\"  Connection status: {playwright_connection.is_connected}\")\n\n    # List tools\n    tools = playwright_connection.list_tools()\n    print(f\"✓ Found {len(tools)} available browser tools\\n\")\n\n    # Create a browser automation agent with Playwright MCP tools\n    browser_agent = ReCentAutoma(\n        llm=llm,\n        tools=tools,\n        memory_config=ReCentMemoryConfig(\n            llm=llm,\n            max_node_size=8,\n            max_token_size=1024 * 32,\n        ),\n        stop_condition=StopCondition(max_iteration=20, max_consecutive_no_tool_selected=1),\n        running_options=RunningOptions(debug=False),\n    )\n\n    # Use the agent to find recent gold prices on Hong Kong Gold Exchange website\n    result = await browser_agent.arun(\n        goal=(\n            \"Find the recent gold prices on Hong Kong Gold Exchange website.\"\n        ),\n        guidance=(\n            \"Do the following steps one by one:\\n\"\n            \"1. Navigate to https://hkgx.com.hk/en\\n\"\n            \"2. Hover on the 'Market \u0026 Data' button to show more button options\\n\"\n            \"3. Click the 'History Price' button to access the historical price page\\n\"\n            \"4. Since the current date was selected, only need to select the option of RMB-denominated kilo gold\\n\"\n            \"5. Click the search button and have a look at the recent gold price trends\\n\"\n            \"6. Close the browser and give out a summary of recent gold price trends\\n\"\n        ),\n    )\n\n    print(\"Final Result:\\n\\n\")\n    print(result)\n\n    # Close the connection when done\n    playwright_connection.close()\n    print(f\"\\n✓ Connection closed: {not playwright_connection.is_connected}\")\n\n\nif __name__ == \"__main__\":\n    import asyncio\n    asyncio.run(main())\n```\n\n**Execution Result:**\n\nThe agent autonomously interact with the browser, navigates the Hong Kong Gold Exchange website and search for gold price information. The entire browser automation process is demonstrated as below.\n\n\u003c/details\u003e\n\n![Browser Automation Demo](./docs/images/HKGX-browser-screen.gif)\n\n---\n\n## 📚 Documents\n\nFor more about development skills of Bridgic, see:\n\n- [Tutorials](https://docs.bridgic.ai/latest/tutorials/)\n- [Understanding](https://docs.bridgic.ai/latest/understanding/introduction/)\n- [Learning ASL](https://docs.bridgic.ai/latest/tutorials/items/asl/quick_start/)\n- [Model Integration](https://docs.bridgic.ai/latest/tutorials/items/model_integration/)\n- [Protocol Integration](https://docs.bridgic.ai/latest/tutorials/items/protocol_integration/)\n- [Observability Integration](https://docs.bridgic.ai/latest/tutorials/items/observability/)\n\nWant to explore more examples? Check out the [bridgic-examples](https://github.com/bitsky-tech/bridgic-examples) repository.\n\n\n## 👥 Community\n\nJoin our WeChat group to connect with other developers and get support:\n\n\u003cimg src=\"./docs/images/WeChat-Group-QR.png\" alt=\"WeChat Group QR Code\" width=\"180\"/\u003e\n\n\n## 📄 License\n\nThis repository is licensed under the [MIT License](/LICENSE).\n\n\n## 🤝 Contributing\n\nFeel free to report bugs or submit feature requests on the [Issues Page](https://github.com/bitsky-tech/bridgic/issues). If you are interested in contributing, check out [CONTRIBUTING](/CONTRIBUTING.md) for information and guidelines.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsky-tech%2Fbridgic","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbitsky-tech%2Fbridgic","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbitsky-tech%2Fbridgic/lists"}