MoreRSS

site iconThe Practical DeveloperModify

A constructive and inclusive social network for software developers.
Please copy the RSS to your reader, or quickly subscribe to:

Inoreader Feedly Follow Feedbin Local Reader

Rss preview of Blog of The Practical Developer

[POG-Task-02] From Governance to Execution: POG Task Design and MVP

2026-02-09 23:31:47

From Governance to Execution: POG Task Design and MVP

If governance cannot reach execution, it is merely theory.

Why POG Task Must Be in an "Executable Format"

In the previous article, we established a premise: Prompt Orchestration Governance (POG) cannot hold if it cannot constrain actual execution.

But governance cannot stop at principles or abstract models. It must be able to answer practical questions:

  • Which task is the AI currently executing?
  • Can the state and context of this task be understood by humans?

POG Task v1.1.0 exists precisely to answer these questions.

POG Task v1.1.0 is not a complete task system. It deliberately only satisfies three conditions:

  1. AI can directly read and write it
  2. Humans can directly review it
  3. State can be presented by tools but is not controlled by them

This leads to the entire design logic of v1.1.0.

Physical Structure: File as Truth

Before discussing tools, we must first look at where the data lives. POG Task v0 implemented a strict file structure to ensure portability and reviewability.

Directory Structure

The system exists entirely within your codebase, typically under the pog-task root directory:

pog-task/
├─ README.md                                # Root document
├─ task.schema.json                         # YAML format validation definition
├─ pog-task-agent-instructions.md           # AI Agent "Protocol"
├─ pog-task-design.md                       # System Design
└─ list/                                    # Task list (layered by project/module)
    └── {project}/
        └── {module}/
            ├── {task-title}.yaml           # Structured State Stream (State)
            └── record/{uuid}/record.md     # Execution & Reasoning Log (History)
  • list/{project}/{module}/*.yaml: This is the structured intent of "what needs to be done."
  • record/{uuid}/record.md: This is the reasoning and execution log of "what happened."

Why POG Task Uses YAML as the Task Carrier

In v1.1.0, we chose YAML for its readability and precision.

It satisfies the following:

  • Structured Intent: Supports nested structures and complex checklists.
  • Human and AI Friendly: YAML is extremely easy to read and edit, reducing parsing hallucinations.
  • Strict Validation: Ensures every task complies with specifications through JSON Schema.
  • Git Native: Fits perfectly with Git, diff, and review workflows.

Minimal Task Structure

A POG Task only cares about "governable facts," for example:

type: task
id: "h7a8b9c0-d1e2..."
title: "Improve task governance clarity"
status: "in_progress"
created_at: "2026-02-01T10:32:00Z"
checklist: 
  - task: "Update documentation"
    done: false

This is not a simple workflow, but Structured Intent.

Why Task and Record Must Be Separated

POG Task clearly distinguishes between two types of data:

  • Task List (YAML): What intents currently exist?
  • Record (Markdown): How was this task "executed"?

Therefore, every Task corresponds to an independent execution record file (e.g., record/{uuid}/record.md).

  • Execution steps
  • Rationale for decisions
  • Mid-course corrections
  • Completion condition judgment

This ensures that governance no longer relies on "chat memory" but on re-readable factual records.

POG Task v1.1.0 does not have a dedicated Web UI. The first human interface chosen was VS Code for very pragmatic reasons:

  1. Developers and Agents collaborate here frequently.
  2. Native support for file system and directory semantics.
  3. Plugins can "observe and assist" without "taking over the logic."

Plugin Overview

POG Task Manager Screenshot

The POG Task Manager extension is designed as a passive observer that visualizes your task state without owning the data.

Get POG Task Manager Plugin

1. Explorer View

The plugin doesn't force you to read raw YAML; instead, it provides a structured Tree View in the sidebar:

  • Grouping: Tasks are automatically grouped by project and module.
  • Status Indicators: Icons clearly show if a task is todo, in_progress, or done.
  • Context: The intent is displayed as the primary label.

2. Task ↔ Record Navigation

This is the core "governance action."

  • Clicking a task: Immediately splits the editor.
    • Left side: YAML file.
    • Right side: Corresponding record.md file.
  • If the record doesn't exist, the plugin prompts to create it from a standard template, ensuring every execution has a complete reasoning context.

3. Execution Alignment

  • Copy Prompt: A one-click action to generate a "Handoff Prompt" for your AI agent. This prompt includes the task context and instructions to update the record, bridging the gap between the task definition and the AI's context window.

What the Plugin "Does Not" Do

To maintain a "governance-first" philosophy, the plugin has strict boundaries:

  • No Drag-and-Drop Status Changes: You cannot drag a task to "done." You must update the underlying file or record.
  • No Hidden Database: All data is just text files. If you remove the plugin, your tasks remain 100% readable.

The Role of LLM Agents in POG

In the current architecture, the LLM Agent is responsible for:

  1. Reading task intent
  2. Claiming and executing specified tasks
  3. Writing reasoning processes and artifacts into the record
  4. Updating task status and checklists

The Agent does not have the final say on the task state. It is only responsible for leaving enough clues for humans to understand.

POG Agent Interaction

Agent Protocol: pog-task-agent-instructions.md

To ensure reliable Agent behavior, POG has established a set of strict "Protocols" documented in pog-task-agent-instructions.md. This is not just a document, but an Operation Manual that every Agent must read before acting.

Key highlights of the protocol include:

  1. Layered Path Rules:
    • pog-task/list/{project}/{module}/{task-title}.yaml
  2. Status Transition Protocol:
    • Claiming: Agents must update the status to in_progress and fill in claimed_by.
    • History: Every key action must be appended to the history.
  3. "Intent-First" and "Record Perpetuity":
    • Immediately initialize record.md after creating a task.
    • Original Intent: The user's original request must be recorded in record.md to prevent execution drift.

This protocol transforms the AI execution "black box" into a predictable, observable process.

Conclusion: v1.1.0 as a Governance Defensive Line

POG Task v1.1.0 does not try to make AI faster. It exists to ensure:

Before AI starts "acting autonomously," we still have a full line of sight for governance.

YAML and the VS Code Plugin are not aesthetic choices; they are the embodiment of a governance stance.

10. Roadmap and Future Work

Phase Features
v1.1 Core YAML structure, record.md, Agent flow, VS Code Plugin optimization
v1.2 Nested tasks, automatic Checklist analysis, enhanced history tracking
v2 Web UI + Dashboard, Jira/Git integration, Multi-agent orchestration
v3 Automated evaluation & reporting, KPI metrics, AI governance rules

Complete content at: https://enjtorian.github.io/pog-task/

Crypto Needs Clarity, Not Another Hype Cycle

2026-02-09 23:21:16

The crypto industry is often dominated by hype, buzzwords, and pitch decks. What it actually needs is clarity and informed discussion. Crypto Minds, Unfiltered provides exactly that—real insights from professionals who manage significant capital.

The first guest, Vincent Liu, CIO at Kronos Research, offers an unvarnished perspective. Key takeaways from his discussion include:

  • BTC price misconceptions: Liu dismantles common myths that mislead retail and institutional participants.
  • Institutional errors: He identifies recurring mistakes made by “wannabe institutions” and how these missteps impact risk management.
  • Meaningful metrics: Liu highlights which metrics genuinely matter when large-scale capital is at stake.
  • Emerging infrastructure: From AI-driven trading to stablecoins, he emphasizes the importance of understanding crypto infrastructure beyond narratives.

This conversation is a reality check for anyone navigating crypto’s next phase. Unlike conference soundbites, it focuses on practical knowledge and risk-aware decision-making.

For those looking to understand how capital is moved safely in crypto and the metrics that truly matter, the full discussion is highly recommended:

Read the full article

How to Turn Messy Data, DAX Headaches, and Ugly Dashboards into Decisions Using Power BI

2026-02-09 23:20:17

Let’s be honest, no dataset has ever arrived on an analyst's desk completely clean. Not once. In the real world, data usually shows up in a chaotic state. Dates saved as text, revenue strings mixed with currency symbols, twelve different spellings for the same county, blanks that should be zeros, and zeros that are actually missing values.

Inevitably, this is followed by a stakeholder asking, "Can you build a dashboard by Friday?" The answer is usually "sure," but the gap between receiving that raw data and delivering a "wow" dashboard is where the actual work happens. It is a journey that moves from invisible data engineering to strategic business storytelling.

The Invisible Foundation: Power Query

The first reality of analytics is that building the dashboard is often the easiest part; dragging charts onto a canvas takes minutes. The real labor lies in ensuring those charts tell the truth, and that work begins in Power Query. This is the "cleaning room" where we fix data types (because revenue should never be text), standardize categories, remove duplicates, and handle null values properly. It is also where we create derived fields, such as "Age Group" or "Price Band," to make analysis easier later on.

If you skip this step, you will inevitably try to fix data quality issues using DAX measures. This is a mistake. DAX is a calculation engine, not a cleanup tool, and it will punish you with slow performance and overly complex formulas if your data isn't prepared correctly.

The Backbone: Data Modeling

Most beginners make the mistake of dumping all their data into one massive, flat table. While this might work for simple spreadsheets, Power BI’s engine is optimized for a Star Schema. This means separating your data into a Fact table (containing transactions, visits, or sales numbers) and Dimension tables (containing descriptive context like dates, counties, products, or departments).

When your relationships are modeled correctly in a star schema, filters flow logically, totals don't double-count, and performance improves significantly. A bad model forces you to write complex DAX to work around the structure; a good model allows for elegant, simple DAX.

The Logic: Context Over Formulas

Once the model is solid, we move to DAX. On the surface, it looks simple—a formula like Total Revenue = SUM(Sales[Revenue]) seems straightforward. However, the real power of DAX is context. That single measure will return different results based on slicers, filters, relationships, and the visual it is placed in.

For example, a measure like Revenue per Visit = DIVIDE([Total Revenue], [Total Visits]) does more than just report a number; it measures performance. Understanding "filter context" how the user's interaction with the report changes the calculation on the fly—is the moment DAX stops being frustrating and starts making sense.

The Art of Visualization: How to Choose the Right Chart

The visual layer is where your data meets the user’s eye. The wrong visual can obscure the truth, while the right one illuminates it. To choose the best visual, you must first identify the question you are trying to answer.

Here is a framework for selecting the right visual.

1. Comparison
If you want to compare values across categories (e.g., Sales by Department), use a Bar Chart. If you are comparing values over time (e.g., Sales by Month), use a Line Chart or Area Chart to show the trend

2. Correlation
If you need to see if two variables are related for instance, "Does higher patient visits always mean higher revenue?" you should use a Scatter Plot. With "Total Visits" on the X-axis and "Total Revenue" on the Y-axis, you can instantly see positive correlations, outliers, or underperforming counties.

3. Composition
If you need to show how parts make up a whole (e.g., Market Share), use a Donut Chart or Treemap. Use these sparingly; too many slices make them unreadable.

4. KPIs
If you just need to show a single, critical number (e.g., Total Year-to-Date Revenue), use a Card or a KPI Visual that shows the number alongside a trend indicator.

Dashboards are storytelling tools. Each visual should answer exactly one question. If a chart requires a paragraph of explanation, it is likely the wrong chart.

From Insight to Action

The ultimate goal of this entire process is not to build a dashboard that looks "clean," but to drive action. We transform messy data into business strategy by highlighting anomalies and trends that require intervention.

For example, seeing high visits but low revenue might indicate a pricing issue. Noticing high medication usage in a specific age group drives inventory planning. A declining trend over time signals operational risk. A good analyst reports the numbers; a great analyst explains what they mean and what to do next.

Final Thoughts

Power BI is not just a dashboard tool; it is a thinking tool. The workflow—moving from Messy Data to Clean Transformations, building a Strong Model, writing Smart DAX, designing Clear Visuals, and finally driving Business Action—is the true craft of analytics.

It isn’t about knowing every DAX function by heart. It’s about knowing when to clean, how to simplify logic, and how to explain insights to stakeholders. That is what makes a good analyst, and honestly, that is what makes analytics fun.

Special Allowance and the 50% Wage Rule in Code on Wages 2019 | Payroll, PF Contribution & Wages Impact

2026-02-09 23:19:27

Is Special Allowance Part of the 50% Wage Rule? A Complete Guide
In the world of Indian labour laws, understanding the intricacies of compensation components is crucial for both employers and employees. One question that often arises is whether special allowance is included under the 50% basic wages rule as defined by the Code on Wages 2019. This topic has significant implications for payroll management, PF (Provident Fund) contributions, and statutory compliance.
In this article, we explain the concept in a clear, practical, and human-friendly way, helping HR professionals, business owners, accountants, and employees grasp how special allowance fits into the larger picture of wage calculation and statutory compliance.

What Is the 50% Basic Wages Rule?
Before diving into whether special allowance is included, it’s important to understand what the 50% basic wages rule actually means.
Under the Code on Wages 2019, employee remuneration is divided into several components, including basic pay, dearness allowance, and a range of other allowances. The purpose of the 50% rule is to ensure that basic components of pay continue to form a meaningful part of an employee’s wages structure.
In simple terms:
• Basic pay + dearness allowance should be at least 50% of the total remuneration.
• When this threshold is not met, certain allowances may be reclassified as part of wages for statutory calculations such as PF, gratuity, bonus, and other compliance purposes.
This rule was brought into sharper focus with the unification of multiple piece-meal wage regulations into the Code on Wages 2019, making it essential for payroll teams to revisit salary structures.

What Is Special Allowance?
Special allowance is a flexible pay component that employers often use to adjust salaries for various reasons — market conditions, performance, job location adjustments, incentives, or retention purposes.
Unlike fixed allowances like House Rent Allowance (HRA) or conveyance allowance, special allowance is not tied to specific statutory definitions. This flexibility makes it a frequent subject of scrutiny when employers and payroll professionals seek to structure compensation effectively while remaining compliant.

Is Special Allowance Part of the 50% Wage Rule?
This is the core question — and the answer depends on how the salary package is structured.
When Special Allowance Is Not Included in Wages
If the basic pay + dearness allowance make up 50% or more of the total remuneration, then special allowance may remain excluded from wages for statutory purposes. In such cases, the special allowance component is treated purely as an allowance and not counted as part of wages when calculating statutory dues.
For example:
• Basic Pay: ₹15,000
• Dearness Allowance: ₹5,000
• Total Salary: ₹40,000
• Basic + DA = ₹20,000 (50%)
Here, since the basic pay plus DA equal 50% of total salary, the special allowance remains a non-wage component and does not impact payroll calculations for statutory dues like PF and gratuity.
When Special Allowance Becomes Part of Wages
However, if the combined basic pay + dearness allowance is less than 50% of total remuneration, any excess amount — including special allowance — must be included as part of wages. This reclassification ensures that statutory benefits calculated based on wages remain fair and in line with labour laws.
Suppose:
• Basic Pay: ₹15,000
• Dearness Allowance: ₹4,000
• Total Salary: ₹40,000
• Basic + DA = ₹19,000 (47.5%)
In this case, the shortfall to reach 50% (₹1,000) must be made up from allowances, including special allowance. Therefore, ₹1,000 of special allowance gets treated as part of wages for PF and other statutory purposes. The remaining portion of special allowance may remain excluded.

Impact on Payroll Administration
Understanding whether special allowance is treated as part of wages has a direct impact on payroll handling.
Payroll Structure and Accuracy
Payroll teams must structure salary components carefully to ensure:
• Correct calculation of statutory dues
• Consistency in salary reporting
• Legal compliance with the Code on Wages 2019
Using payroll software that can dynamically evaluate salary components against the 50% basic wages rule is now a necessity, especially for organisations with diverse pay structures.
PF Contribution Adjustments
Provident Fund (PF) contributions are calculated on wages. If an allowance is reclassified as part of wages under the 50% rule, both employer and employee PF liabilities increase.
For example:
• If special allowance is included as a wage component due to the 50% rule, PF contributions on that amount will be higher.
• This increases the statutory cost for employers and also affects the net take-home for employees.

Other Statutory Implications
The implications of reclassifying special allowance as wages extend beyond payroll and PF.
Bonus Calculations
Under existing laws, bonus calculations are often tied to the definition of wages. A higher wage base due to inclusion of special allowance could affect bonus entitlements, potentially increasing bonus payouts.
Gratuity
Gratuity is typically calculated on the last drawn wages. If special allowance forms part of wages due to the 50% rule, this could increase the gratuity payout at the time of an employee’s exit.
Compliance Reporting
Failure to correctly classify wages can attract notices and penalties from labour authorities — making it essential for companies to stay compliant, document their wage structures clearly, and align them with statutory requirements.

Best Practices for Employers
To effectively navigate this rule and mitigate compliance risks, employers should adopt several best practices:
Regular Salary Structure Audits
Conduct periodic reviews of salary components to ensure that basic pay and DA are structured in a way that minimises unintended wage inclusion.
Use of Smart Payroll Systems
Modern payroll software must be configured to automatically assess salary heads against statutory limits and rules like the 50% basic wages rule.
Clear Communication with Employees
Employees should be informed about how their salary structure affects statutory deductions, PF contributions, and take-home pay. Transparency fosters trust and reduces confusion during payroll cycles.
Legal Compliance Checks
Engage legal or compliance experts to periodically audit wage structures and payroll practices, especially when changes are made to compensation policies.

• Basic: ₹16,000
• DA: ₹4,000
• Special Allowance: ₹20,000
• Total: ₹40,000
In this scenario, basic + DA = ₹20,000 (50%). While this appears okay, additional performance allowances could push the profile above statutory thresholds, requiring payroll teams to reassess inclusion.
These examples illustrate the practical nuances of applying the 50% rule and help payroll professionals make informed decisions.

FAQs About Special Allowance and the 50% Rule
Q1. Is special allowance always excluded from wages?
No. It is excluded only if the combined basic pay and dearness allowance make up at least 50% of total remuneration. If they don’t, a portion of special allowance becomes part of wages for statutory calculations.

Q2. How does this affect PF contributions?
If special allowance is included as part of wages under the 50% rule, PF contributions for both employer and employee increase, as they are calculated on the revised wage base.

Q3. Does this rule affect bonus and gratuity?
Yes. A larger wage base increases bonus payouts and gratuity calculations, as they are tied to the definition of wages under the law.

Q4. Who should be responsible for checking compliance?
HR, payroll teams, and legal compliance officers should work together to ensure that wage structures align with the Code on Wages 2019.

Q5. Should employers audit salary components regularly?
Absolutely. Regular audits help maintain compliance and ensure that payroll calculations remain accurate and lawful.

Conclusion
Understanding whether special allowance is part of the 50% wage rule under the Code on Wages 2019 is essential for payroll accuracy, statutory compliance, and financial planning. The rule affects not only how wages are computed but also influences PF contributions, bonus eligibility, gratuity pay-outs, and overall salary design.
Whether you are an HR leader, payroll executive, or business owner, integrating this understanding into your payroll systems and compliance checks will keep your organisation legally sound and financially efficient.
If you need help reviewing your salary structures or ensuring compliance with the Code on Wages 2019, consulting with experienced payroll and labour law specialists is always a prudent step.

Hello again, here's a LangChain Ollama helper sheet :)

2026-02-09 23:19:25

LangChain + Ollama: A Practical Guide to Building AI Agents with Python

This guide teaches you how to build real, working AI agents using Ollama and LangChain.

What You'll Learn

In this guide, you'll discover:

  • ✅ How to set up Ollama + LangChain (10 minutes)
  • ✅ When to use ollama.chat() vs ChatOllama() (quick decision tree)
  • ✅ How to build agents that remember things (persistent storage)
  • ✅ Real, working examples (copy & paste ready)
  • ✅ Performance tuning for your machine
  • ✅ How to deploy to production

Quick Decision: Which Tool to Use?

┌─────────────────────────────────────────┐
│  Want to use AI in your Python code?    │
└────────────┬────────────────────────────┘
             │
             ▼
┌──────────────────────────────────────────┐
│  Building a multi-step AI agent that     │
│  makes decisions and uses tools?         │
└────────────┬─────────────────────────────┘
             │
        YES  │  NO
             │   └──────────────────────┐
             │                          │
             ▼                          ▼
      Use ChatOllama()          Use ollama.chat()
      ✅ For agents             ✅ For simple queries
      ✅ For tools              ✅ For streaming
      ✅ For state mgmt         ✅ For speed
      ✅ For production         ✅ For prototyping

Performance at a Glance

Operation Time Notes
ollama.chat() response 15-25ms Fastest
ChatOllama() response 35-55ms More features
Streaming first token 5-20ms Real-time feedback
Tool execution 2-12ms Overhead varies

Real-world: On a laptop with 8GB RAM, you'll get responses in under 100ms most of the time.

For local AI, this is blazingly fast. (Cloud APIs add 500ms+ of network latency)

Part 1: Simple Queries with ollama.chat()

When to use: You just need to ask the AI something and get an answer.

Setup (2 minutes)

First, make sure Ollama is running:

# Terminal 1: Start Ollama
ollama serve

Now you're ready to code.

Your First Query

import ollama

response = ollama.chat(
    model="qwen2.5-coder:latest",
    messages=[
        {"role": "user", "content": "What is 2 + 2?"}
    ]
)

print(response['message']['content'])
# Output: "2 + 2 equals 4"

Streaming (See Responses as They Generate)

Want to see the AI think in real-time?

import ollama

print("AI: ", end="", flush=True)

for chunk in ollama.chat(
    model="qwen2.5-coder:latest",
    messages=[
        {"role": "user", "content": "Write a haiku about code"}
    ],
    stream=True
):
    print(chunk['message']['content'], end="", flush=True)

print()  # Newline at end

Output:

AI: Lines of logic dance,
Bugs and fixes both take turns—
Code shapes the future.

Multi-Turn Conversation (Remember Context)

Ask follow-up questions:

import ollama

messages = []

while True:
    user_input = input("You: ")

    # Add your message
    messages.append({"role": "user", "content": user_input})

    # Get response
    response = ollama.chat(
        model="qwen2.5-coder:latest",
        messages=messages
    )

    ai_response = response['message']['content']
    print(f"\nAI: {ai_response}\n")

    # Add AI's response so it remembers context
    messages.append({"role": "assistant", "content": ai_response})

Try this conversation:

You: What is a lambda function in Python?
AI: A lambda function is a small anonymous function...

You: How is it different from a regular function?
AI: Great question! The key differences are...

Notice how the AI knows you're talking about Python, because it remembers the context.
Once context limit is reached, expect errors to appear.

Part 2: Building AI Agents with ChatOllama()

When to use: You're building something more sophisticated—agents that make decisions, use tools, and manage state.

Setup (5 minutes)

pip install langchain-ollama langchain langgraph

Your First Agent

An agent is an AI that can:

  1. ✅ Make decisions
  2. ✅ Use tools to accomplish tasks
  3. ✅ Keep track of conversation state
  4. ✅ Handle multiple steps

Let's build one that can tell time:

from langchain_ollama import ChatOllama
from langchain.tools import tool
from langchain.agents import create_agent

# Step 1: Create a tool
@tool
def get_current_time() -> str:
    """Get the current time."""
    from datetime import datetime
    return datetime.now().strftime("%H:%M:%S")

# Step 2: Create the AI
llm = ChatOllama(
    model="qwen2.5-coder:latest",
    temperature=0.0  # Be deterministic
)

# Step 3: Create the agent
agent = create_agent(
    llm,
    tools=[get_current_time],
    system_prompt="You are a helpful time assistant."
)

# Step 4: Use it
result = agent.invoke({
    "messages": [{"role": "user", "content": "What time is it right now?"}]
})

print(result['output'])
# Output: "It is currently 14:23:45"

What just happened?

  1. You asked the agent what time it is
  2. The agent decided it needed to use the get_current_time tool
  3. It called the tool and got the time
  4. It gave you a friendly response

The agent made the decision. You just provided the tools.

Adding Multiple Tools

Tools let your agent accomplish real things:

from langchain.tools import tool

@tool
def add_numbers(a: int, b: int) -> int:
    """Add two numbers together."""
    return a + b

@tool
def multiply_numbers(a: int, b: int) -> int:
    """Multiply two numbers together."""
    return a * b

# Create agent with multiple tools
agent = create_agent(
    llm,
    tools=[add_numbers, multiply_numbers, get_current_time],
    system_prompt="You are a helpful math assistant."
)

# The agent will decide which tool to use
result = agent.invoke({
    "messages": [{"role": "user", "content": "What's 25 * 4?"}]
})

print(result['output'])
# Output: "25 * 4 equals 100"

The agent automatically chose the multiply_numbers tool!
If you need, you can add verbose logging in each of the functions to keep track of which tools were used by the agent. This is also how you protect these tools by creating an input request to confirm the usage of the tool to avoid the agent doing the wrong actions.

Agents that Remember Things

What if you want the agent to remember user preferences or conversation history?

from agent_workspace.hybrid_store import HybridStore

# Create persistent storage
store = HybridStore(
    storage_dir="agent_workspace/storage"
)

# Tool that saves preferences
@tool
def save_preference(key: str, value: str, runtime) -> str:
    """Save a user preference that persists."""
    store = runtime.store
    store.put(("preferences",), key, {"value": value})
    return f"Saved: {key} = {value}"

@tool
def get_preference(key: str, runtime) -> str:
    """Retrieve a saved preference."""
    store = runtime.store
    pref = store.get(("preferences",), key)
    if pref:
        return f"Your {key} is: {pref.value['value']}"
    return "No preference found"

# Create agent WITH persistent storage
agent = create_agent(
    llm,
    tools=[save_preference, get_preference],
    store=store,  # Connect the storage
    system_prompt="You help manage user preferences."
)

# Session 1: Save preference
print("=== Session 1 ===")
result1 = agent.invoke({
    "messages": [{"role": "user", "content": "Remember that my favorite color is blue"}]
})
print(result1['output'])

# Session 2: Retrieve preference (even after restart!)
print("\n=== Session 2 (After Restart) ===")
result2 = agent.invoke({
    "messages": [{"role": "user", "content": "What's my favorite color?"}]
})
print(result2['output'])
# Output: "Your favorite color is: blue"

The magic: Data saved in Session 1 is still there in Session 2, even if you restart your computer! The HybridStore will be available in MagicPythong Library, it is a custom made class to save/restore the runtime store from LangChain to file.

Part 3: Real-World Examples

Example 1: A Personal Code Assistant

from langchain.tools import tool
from langchain.agents import create_agent
from langchain_ollama import ChatOllama

@tool
def check_python_syntax(code: str) -> str:
    """Check if Python code is valid."""
    try:
        compile(code, '<string>', 'exec')
        return "✅ Syntax is valid!"
    except SyntaxError as e:
        return f"❌ Syntax error: {e}"

@tool
def explain_code(code: str) -> str:
    """Provide a simple explanation of what code does."""
    # In a real app, you'd call the LLM here
    return "This code does X, Y, and Z"

llm = ChatOllama(model="qwen2.5-coder:latest", temperature=0.0)

agent = create_agent(
    llm,
    tools=[check_python_syntax, explain_code],
    system_prompt="You are a Python code assistant. Help the user write and understand code."
)

# Usage
code = """
def greet(name):
    print(f"Hello, {name}!")
"""

result = agent.invoke({
    "messages": [{"role": "user", "content": f"Is this Python code valid?\n\n{code}"}]
})

print(result['output'])
# Output: "Yes, this Python code is valid..."

Example 2: A Data Analysis Agent

import json
from langchain.tools import tool
from langchain.agents import create_agent
from langchain_ollama import ChatOllama
from agent_workspace.hybrid_store import HybridStore

# Sample data
SALES_DATA = [
    {"product": "Laptop", "sales": 15},
    {"product": "Phone", "sales": 42},
    {"product": "Tablet", "sales": 28},
    {"product": "Headphones", "sales": 35}
]

@tool
def get_sales_data() -> str:
    """Get the latest sales data."""
    return json.dumps(SALES_DATA)

@tool
def save_report(summary: str, runtime) -> str:
    """Save analysis report."""
    store = runtime.store
    store.put(("reports",), "latest", {"summary": summary})
    return "Report saved!"

@tool
def get_saved_report(runtime) -> str:
    """Retrieve the latest saved report."""
    store = runtime.store
    report = store.get(("reports",), "latest")
    if report:
        return f"Latest report: {report.value['summary']}"
    return "No report found"

llm = ChatOllama(model="qwen2.5-coder:latest", temperature=0.0)
store = HybridStore()

agent = create_agent(
    llm,
    tools=[get_sales_data, save_report, get_saved_report],
    store=store,
    system_prompt="You are a data analyst. Help users understand their sales data."
)

# Usage
result = agent.invoke({
    "messages": [{"role": "user", "content": "Analyze our sales data and give me a summary"}]
})

print(result['output'])

Part 4: Choosing the Right Model

Ollama has different size models. Pick based on your computer:

If you have 4GB or less RAM

Use Qwen2.5-Coder 1.5B

llm = ChatOllama(model="qwen2.5-coder:1.5b")

✅ Fast

⚠️ Less capable

If you have 8GB RAM

Use Qwen2.5-Coder 7B

llm = ChatOllama(model="qwen2.5-coder:7b")

✅ Good balance

✅ Handles most tasks

If you have 16GB+ RAM

Use Qwen3-Coder 30B

llm = ChatOllama(model="qwen3-coder:30b")

✅ Most capable

⚠️ Slower

Pull a model:

ollama pull qwen2.5-coder:7b

Part 5: Tuning Performance

Make responses faster

llm = ChatOllama(
    model="qwen2.5-coder:7b",
    temperature=0.0,      # ← Deterministic (faster)
    num_predict=128,      # ← Shorter responses
)

Make responses more creative

llm = ChatOllama(
    model="qwen2.5-coder:7b",
    temperature=0.7,      # ← More creative
    num_predict=512,      # ← Longer responses
)

Use GPU (if you have NVIDIA)

llm = ChatOllama(
    model="qwen2.5-coder:7b",
    num_gpu=35,           # ← Use GPU layers
)

Part 6: Common Issues & Fixes

Issue 1: "Connection refused"

Problem: Getting an error when trying to use the AI

Fix:

# Terminal 1: Start Ollama
ollama serve

Then run your Python code in a different terminal.

Issue 2: "Model not found"

Problem: Error says the model doesn't exist

Fix:

# Download the model
ollama pull qwen2.5-coder:latest

Issue 3: "Out of memory"

Problem: "CUDA out of memory" or system slows down

Fix: Use a smaller model

# Instead of 32B
llm = ChatOllama(model="qwen2.5-coder:7b")

Issue 4: Slow responses

Problem: Takes too long to get a response

Fix:

llm = ChatOllama(
    model="qwen2.5-coder:1.5b",  # Smaller model
    temperature=0.0,              # Deterministic
    num_predict=128,              # Shorter output
)

Part 7: Next Steps

You now have enough to build:

  • ✅ Chat bots
  • ✅ Code assistants
  • ✅ Data analysis agents
  • ✅ Personal AI assistants

Resources

Happy coding! 🚀

Fast Multi-Platform Builds on GitHub

2026-02-09 23:15:00

If you want to build multi-architecture Docker containers in GitHub Actions, the standard recommendation you'll find online is to install BuildX and QEMU. The downside of this approach is that QEMU emulation is about 10x slower than native hardware. Building my simple Hello World project went from 30 seconds to 3 minutes.

In this post, I will show you several ways to speed up your builds. The options you have are:

  • Switching to runners that have cross-platform remote builds pre-configured
  • Building single-architecture images in a matrix and merging them manually
  • Using GitHub Actions instances as remote builders with Tailscale
  • Setting up your own machines for remote builds
  • Using a Kubernetes cluster for remote builds

Switching to a different Runner Provider

This is the simplest solution, but it requires signing up for a new service and will cost some money. There are alternative runner providers that have their runners preconfigured with BuildX and remote builders with native hardware.

One such provider that I tried is Namespace.so. Signing up was fast and easy, and switching to them in my workflows only required changing the runs-on field in my YAML.

Building Single Images and Merging

This option is probably the simplest way to get cross-platform builds without leaving GitHub. You build an individual image for each of the architectures that you want, then merge them with a buildx command like:

docker buildx imagetools create -t nabsul/myproject:v1.0.0 nabsul/myproject:v1.0.0-amd64 nabsul/myproject:v1.0.0-arm64

In this example, I use a matrix of jobs to reduce duplicate YAML, and then a merge job to create the final image.

GitHub Remote Builders with Tailscale

Honestly, this is cool in a nerdy way, but I wouldn't recommend it for production. For each hardware architecture, I spin up a job that starts a buildkitd server. I then join my tailnet with a pre-determined hostname that allows the builder to find the machine. The final step in the job is printf "HTTP/1.1 200 OK\r\nContent-Length: 16\r\n\r\nShutting down..." | nc -l -p 8080 which just waits until someone hits port 8080 and then shuts down.

The main build step configures itself with the remote builders from the previous step. It then joins the tailnet and uses those remote instances to do a cross-platform build. After the build is done, I use a curl command to cause the other jobs to end.

Like I said, this is a pretty cool setup, but there's just so much that can go wrong. If the curl fails, you'll get jobs that hang for a long time, and you'll have to worry about tailnet configurations and security.

Kubernetes Remote Builders

If you happen to have a Kubernetes cluster that has both Intel and ARM nodes in it, you can use them as remote builders. In this example, I create a temporary namespace for each build, run the builds there, and then clean up afterwards.

Overall this is not a bad option if you already have a Kubernetes cluster being used for other purposes. But you probably don't want to be creating one just for the purpose of your builds.

TCP Remote Builders

You can also just run individual VMs of your different hardware types and use them for remote builds. In this example, I created one ARM and one Intel VM and secured them with TLS certs. I then configured BuildX to use those remote runners for the build. You could also leverage Tailscale for this and avoid the need for TLS certificates.

Conclusion

So there you have it, several options to get faster multi-architecture builds on GitHub Actions. Personally, I currently lean towards Namespace.so simply because it only costs me about $2 a month and I'm lazy. If Namespace started to get expensive, I would probably go with the separate builds and merge pattern. And if my builds were starting to get expensive on GitHub, I might look into setting up builders at home and doing remote builds over Tailscale.