← 3.2 Agents + AI


Very basic demos that show how deterministic agents use TF/UFA semantic capabilities (these are marked with “@” and are explained on page 2.5 Agentic LLM (TF/UFA semantic) functionality).

  • 1b Basic tool (with AI)
  • 3 Filesystem
  • 5 RAG
  • 6c MCP (LLM tool choice)

For details see the lab notes docx #606_ai_ides.docx on the Gdrive.


1b Basic tool (with AI) demo

This is actually the first demo where TF/UFA semantic capabilities directly enable external deterministic execution.

drones

1b.1 py script

# ai_demo_01b_tool_openai.py
# Basic OpenAI tool-calling style demo
import json
import os
from dotenv import load_dotenv
from openai import OpenAI

# -----------------------------------
# Load API key
# -----------------------------------

load_dotenv()
client = OpenAI(
    api_key=os.getenv("OPENAI_API_KEY")
)

# -----------------------------------
# Tool implementation
# -----------------------------------

def calculator(operation, a, b):
    if operation == "add":
        return a + b
    if operation == "subtract":
        return a - b
    if operation == "multiply":
        return a * b
    if operation == "divide":
        return a / b
    raise ValueError(f"Unknown operation: {operation}")

# -----------------------------------
# Prompt
# -----------------------------------

user_prompt = "What is 11 multiplied by 6?"

system_prompt = """
You are an AI agent.
Return ONLY JSON.
Valid format:
{
  "tool": "calculator",
  "operation": "multiply",
  "a": 12,
  "b": 7
}
"""

# -----------------------------------
# Call OpenAI (REAL LLM OUTPUT)
# -----------------------------------

response = client.chat.completions.create(
    model="gpt-4.1-mini",
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt}
    ],
    temperature=0
)
llm_output = response.choices[0].message.content
print("\nLLM OUTPUT:")
print(llm_output)

# -----------------------------------
# Parse JSON
# -----------------------------------

action = json.loads(llm_output)

# -----------------------------------
# External agent executes tool 
# -----------------------------------

if action["tool"] == "calculator":

    result = calculator(
        action["operation"],
        action["a"],
        action["b"]
    )

    final_result = {
        "tool": action["tool"],
        "operation": action["operation"],
        "result": result
    }

    print("\nCODE EXECUTED:")
    print(json.dumps(final_result, indent=2))

1b.2 Test

system_prompt = """
You are an AI agent.
Return ONLY JSON.
Valid format:
{
  "tool": "calculator",
  "operation": "multiply",
  "a": 12,
  "b": 7
}
"""

user_prompt = “What is 11 multiplied by 6?”

python ai_demo_01b_tool_openai.py 
LLM OUTPUT:
{
  "tool": "calculator",
  "operation": "multiply",
  "a": 11,
  "b": 6
}
CODE EXECUTED:
{
  "tool": "calculator",
  "operation": "multiply",
  "result": 66
}

user_prompt = “What is 5 plus 3?”

python ai_demo_01b_tool_openai.py
LLM OUTPUT:
{
  "tool": "calculator",
  "operation": "add",
  "a": 5,
  "b": 3
}
CODE EXECUTED:
{
  "tool": "calculator",
  "operation": "add",
  "result": 8
}

1b.3 MOST important TF/UFA capabilities are:

@8 Semantic interpretation / inference

The TF must understand: “What is 12 multiplied by 7?” That is semantic interpretation.

But it also understood “What is 5 plus 3?” with the same system prompt.

@7 Structured constrained output generation

The TF must generate: { “tool”: “calculator”, “operation”: “multiply”, “a”: 12, “b”: 7 } in valid machine-readable structure. That is constrained structured output generation.

1b.4 Weak/minor involvement

@1 Generalization

Minor involvement. The TF can handle: “What is 12 times 7?” “What is 12 multiplied by 7?” “Compute 12 x 7” without exact training matches.

@4 Human-language robustness

Minor involvement. Could tolerate: “wat is 12 tiemz 7”

1b.5 NOT really involved

These are mostly NOT important in Step 1:

  • @2 contextual tracking
  • @3 feature abstraction
  • @5 ontology alignment
  • @6 explanation synthesis
  • @9 planning/workflow synthesis

because the demo is intentionally simple.


3 Filesystem tool

drones

This demo is important because it begins demonstrating:

  • TF/UFA semantic capabilities
  • controlling access to external context. That is conceptually VERY close to:
  • RAG
  • MCP
  • agentic retrieval systems. You are now very near the critical conceptual bridge.

Core idea: User asks for file

  • → LLM proposes read_file JSON
  • → Python validates filename
  • → Python reads local file
  • → result returned

3.1 py script

# ai_demo_03_filesystem.py
# LLM = propose
# Code = executes filesystem tool

import json
import os
from pathlib import Path

from dotenv import load_dotenv
from openai import OpenAI

# -----------------------------------
# Load API key
# -----------------------------------

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# -----------------------------------
# Demo file setup
# -----------------------------------

DATA_DIR = Path("demo_files")
DATA_DIR.mkdir(exist_ok=True)

(DATA_DIR / "taipei_shipments.txt").write_text(
    "Truck 12 delayed in Taipei due to flooding.\n"
    "Truck 18 on schedule in Taipei.\n",
    encoding="utf-8"
)

(DATA_DIR / "supplier_notes.txt").write_text(
    "Supplier A reported outage affecting brake components.\n",
    encoding="utf-8"
)

# -----------------------------------
# Filesystem tool
# -----------------------------------

def read_file(filename):
    safe_path = DATA_DIR / filename

    if not safe_path.exists():
        raise FileNotFoundError(f"File not found: {filename}")

    return safe_path.read_text(encoding="utf-8")

# -----------------------------------
# Prompt
# -----------------------------------

user_prompt = "Read the Taipei shipment file."

system_prompt = """
You are an AI agent.
Return ONLY JSON.
You may use this tool:
{
  "tool": "read_file",
  "filename": "taipei_shipments.txt"
}
Allowed filenames:
- taipei_shipments.txt
- supplier_notes.txt
"""

# -----------------------------------
# LLM proposes tool call
# -----------------------------------

response = client.chat.completions.create(
    model="gpt-4.1-mini",
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt}
    ],
    temperature=0
)

llm_output = response.choices[0].message.content

print("\nLLM OUTPUT:")
print(llm_output)

# -----------------------------------
# Code executes tool
# -----------------------------------

action = json.loads(llm_output)

if action["tool"] == "read_file":
    file_content = read_file(action["filename"])

    result = {
        "tool": "read_file",
        "filename": action["filename"],
        "content": file_content
    }

    print("\nCODE EXECUTED:")
    print(json.dumps(result, indent=2))

3.2 test

user_prompt = “Read the Taipei shipment file.” (OK)

$ python ai_demo_03_filesystem.py

LLM OUTPUT:
{"tool": "read_file", "filename": "taipei_shipments.txt"}

CODE EXECUTED:
{
  "tool": "read_file",
  "filename": "taipei_shipments.txt",
  "content": "Truck 12 delayed in Taipei due to flooding.\nTruck 18 on schedule in Taipei.\n"
}

user_prompt = “I need info about suppliers.” (FAIL)

$ python ai_demo_03_filesystem.py

LLM OUTPUT:
{
  "request": "Please specify the type of information you need about suppliers. For example, details about supplier names, contact information, shipment records, or any other specific data."
}
Traceback (most recent call last):
  File "C:\Users\terry\Downloads\d1_agent\ai_demo_03_filesystem.py", line 98, in <module>
    if action["tool"] == "read_file":
       ~~~~~~^^^^^^^^
KeyError: 'tool'

user_prompt = “read info about suppliers.” (OK)

$ python ai_demo_03_filesystem.py

LLM OUTPUT:
{"tool": "read_file", "filename": "supplier_notes.txt"}

CODE EXECUTED:
{
  "tool": "read_file",
  "filename": "supplier_notes.txt",
  "content": "Supplier A reported outage affecting brake components.\n"
}

user_prompt = “how many suppliers had problems (read info).” (same answer)

$ python ai_demo_03_filesystem.py

LLM OUTPUT:
{"tool": "read_file", "filename": "supplier_notes.txt"}

CODE EXECUTED:
{
  "tool": "read_file",
  "filename": "supplier_notes.txt",
  "content": "Supplier A reported outage affecting brake components.\n"
}

3.3 TF/UFA capabilities

MORE TF/UFA capabilities are now involved. This is important because: the TF is now interacting with external semantic context.

3.3.1 Strongly involved

@7 Structured constrained output generation

Very important. TF generated: {“tool”: “read_file”, “filename”: “taipei_shipments.txt”} Machine-readable structure.

@8 Semantic interpretation / inference

Very important. TF understood: “Read the Taipei shipment file” means: filename = taipei_shipments.txt This is semantic interpretation.

@1 Latent pattern generalization / approximation

Moderately important. TF can likely also handle: “open the Taiwan shipment document” “show the Taipei truck file” “display shipment notes” without exact matching. That is generalization.

@2 Contextual token dependency tracking

Now somewhat involved. Because the TF must:

  • maintain awareness of: o available filenames o tool schema o user request
  • connect them properly. This is small-scale contextual dependency tracking.

3.3.2 Weak/minor involvement

@4 Human-language robustness

Minor. Would help with typos: “reed taipei shipmnt file”

@6 Explanation/summarization synthesis

Very weak here. Would become important later if: LLM summarizes file contents.

3.3.3 NOT really involved

@3 Semantic feature abstraction

Underlying mechanism, but not directly visible.

@5 Ontology alignment

Not really. No synonym normalization/ontology mapping yet.

@9 Hierarchical planning/workflow synthesis

Not really. Single-step action only.



5 RAG retrieval tool (BINGO)

drones

VERY important insight

You are now seeing why modern RAG depends heavily on TF/UFA capabilities. Because the TF must:

  • semantically interpret retrieved text
  • integrate it with user question
  • reason over combined context
  • synthesize final answer

Without TF/UFA semantics: RAG is mostly just search. The TF is what makes the retrieved text meaningfully usable.

VERY important conceptual insight

Your RAG demo now demonstrates:

  • external retrieval
  • +
  • TF/UFA semantic interpretation

And THAT is the key reason modern RAG became practical. The retrieval itself is old. The revolutionary part is: TF semantic understanding of retrieved context.

Expected idea: user question

  • → Python retrieves relevant local docs
  • → docs injected into prompt
  • → LLM answers from retrieved context
  • This is the core RAG pattern.

5.1 py script

# ai_demo_05_rag.py
# Basic RAG demo:
# retrieve relevant text
# inject retrieved text into LLM prompt
# LLM answers from retrieved context

import os
from pathlib import Path

from dotenv import load_dotenv
from openai import OpenAI

# -----------------------------------
# Load API key
# -----------------------------------

load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

# -----------------------------------
# Demo documents
# -----------------------------------

DOCS_DIR = Path("rag_docs")
DOCS_DIR.mkdir(exist_ok=True)

(DOCS_DIR / "taipei_shipments.txt").write_text(
    "Truck 12 delayed in Taipei due to flooding. "
    "Truck 18 is on schedule in Taipei.",
    encoding="utf-8"
)

(DOCS_DIR / "supplier_notes.txt").write_text(
    "Supplier A reported an outage affecting brake components.",
    encoding="utf-8"
)

(DOCS_DIR / "weather_notes.txt").write_text(
    "Heavy rain caused flooding near Taipei logistics routes.",
    encoding="utf-8"
)

# -----------------------------------
# Simple retrieval tool
# -----------------------------------

def retrieve_docs(query, top_k=2):
    query_words = set(query.lower().split())
    scored_docs = []

    for path in DOCS_DIR.glob("*.txt"):
        text = path.read_text(encoding="utf-8")
        text_words = set(text.lower().split())

        score = len(query_words.intersection(text_words))

        scored_docs.append({
            "filename": path.name,
            "score": score,
            "text": text
        })

    scored_docs.sort(key=lambda x: x["score"], reverse=True)
    return scored_docs[:top_k]

# -----------------------------------
# User question
# -----------------------------------

user_question = "Why is Truck 12 delayed?"

# -----------------------------------
# RAG step 1: retrieve docs
# -----------------------------------

retrieved = retrieve_docs(user_question)

context = "\n\n".join(
    f"[{doc['filename']}]\n{doc['text']}"
    for doc in retrieved
)

print("\nRETRIEVED CONTEXT:")
print(context)

# -----------------------------------
# RAG step 2: inject context into prompt
# -----------------------------------

system_prompt = """
You are an AI assistant.

Answer the user question using ONLY the retrieved context.
If the answer is not in the context, say: "I do not know from the provided context."
"""

user_prompt = f"""
Retrieved context:

{context}

User question:
{user_question}
"""

response = client.chat.completions.create(
    model="gpt-4.1-mini",
    messages=[
        {"role": "system", "content": system_prompt},
        {"role": "user", "content": user_prompt}
    ],
    temperature=0
)

answer = response.choices[0].message.content

print("\nLLM ANSWER:")
print(answer)

5.2 Test

python ai_demo_05_rag.py

RETRIEVED CONTEXT:
[taipei_shipments.txt]
Truck 12 delayed in Taipei due to flooding. Truck 18 is on schedule in Taipei.

[supplier_notes.txt]
Supplier A reported an outage affecting brake components.

LLM ANSWER:
Truck 12 is delayed in Taipei due to flooding.

Content of 3 rag_docs

  • Supplier A reported an outage affecting brake components.
  • Truck 12 delayed in Taipei due to flooding. Truck 18 is on schedule in Taipei.
  • Heavy rain caused flooding near Taipei logistics routes.

VERY important distinction

Your demo used:

  • non-semantic retrieval
  • +
  • semantic TF interpretation

Modern advanced RAG often additionally uses:

  • semantic embedding retrieval Meaning: documents are converted into:
  • embedding vectors

query is converted into:

  • embedding vector

Then:

  • vector similarity search retrieves semantically related docs.

THAT is: vector RAG

But EVEN THEN: after retrieval, the retrieved text STILL usually gets: inserted into the prompt/context window for the TF to process semantically.

5.3 TF/UFA capabilites

Strongly involved

@1 Latent pattern generalization / approximation

VERY important. The TF can connect:

  • “Why is Truck 12 delayed?” with retrieved text:
  • “Truck 12 delayed in Taipei due to flooding” without rigid hardcoded rules.

@2 Contextual token dependency tracking

VERY important. The TF must maintain:

  • user question
  • retrieved documents
  • instructions
  • entity references inside one evolving context window. This is central to RAG.

@8 Semantic interpretation / inference

VERY important. The TF must:

  • understand question meaning
  • understand retrieved docs
  • connect them semantically
  • infer the answer. This is core RAG behavior.

@5 Semantic normalization / ontology alignment

Moderate involvement. The TF may connect: “delay” “late” “behind schedule” semantically. Very important in advanced RAG.

Moderately involved

@7 Structured constrained output generation

Moderately involved. The TF follows: “Answer ONLY using retrieved context.” This is a constrained semantic behavior. Would become even stronger if JSON outputs used.

@9 Hierarchical planning / workflow synthesis

Moderately involved. The overall RAG pipeline is: retrieve → inject context → answer Simple workflow orchestration. In advanced RAG this becomes much larger. Moderately involved

@6 Explanation/summarization synthesis

Moderately involved. The TF synthesizes:

  • retrieved info
  • semantic relationships into:
  • final human-readable answer.

@4 Human-language robustness

Moderate involvement. The TF could still likely answer: “y truck 12 late?” despite noisy input. Weak/directly invisible

@3 Semantic feature abstraction

This underlies EVERYTHING internally. But:

  • not directly visible in demo behavior
  • more mechanistic/internal TF operation.


6c MCP-connected tool (with LLM choosing tool) (BINGO)

drones

Perfect. That demo now proves the real MCP + LLM pattern:

  • MCP server exposes tools
  • → extAgent gets dynamic tool list
  • → extAgent injects tool metadata into prompt
  • → LLM chooses correct tool + args
  • → extAgent executes MCP tool

Key lesson:

  • MCP = dynamic tool infrastructure
  • LLM = semantic tool selection
  • extAgent = validation/execution

This is much stronger than 6b.

Main insight:

  • MCP alone = tool infrastructure
  • MCP + LLM = semantic tool selection
  • MCP + extAgent = controlled execution

6c.1a server py script

# ai_demo_06c_mcp_llm_server.py
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("DemoTools")

@mcp.tool()
def read_shipment_status(truck_id: str) -> str:
    """Use this tool to get shipment status. Valid truck_id values: truck_12, truck_18."""

    if truck_id == "truck_12":
        return "Truck 12 is delayed in Taipei due to flooding."
    if truck_id == "truck_18":
        return "Truck 18 is on schedule in Taipei."
    return f"No shipment status found for {truck_id}."

@mcp.tool()
def read_supplier_status(supplier_id: str) -> str:
    """Use this tool to get supplier status. Valid supplier_id values: supplier_a, supplier_b."""

    if supplier_id == "supplier_a":
        return "Supplier A has an outage affecting brake components."
    if supplier_id == "supplier_b":
        return "Supplier B is operating normally."
    return f"No supplier status found for {supplier_id}."

if __name__ == "__main__":
    mcp.run()

6c.1b client py script

# ai_demo_06c_mcp_llm_client.py
# MCP exposes tools dynamically.
# LLM chooses the correct tool.
# Code executes.
import asyncio
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
load_dotenv()
client = OpenAI(api_key=os.getenv("OPENAI_API_KEY"))

def build_tools_text(tools):
    lines = []
    for tool in tools.tools:
        lines.append(f"- {tool.name}: {tool.description}")
    return "\n".join(lines)

async def main():
    server_params = StdioServerParameters(
        command="python",
        args=["ai_demo_06c_mcp_llm_server.py"]
    )
    async with stdio_client(server_params) as streams:
        async with ClientSession(streams[0], streams[1]) as session:
            await session.initialize()
            tools = await session.list_tools()
            tools_text = build_tools_text(tools)
            print("\nDYNAMIC MCP TOOL LIST:") #>2
            print(tools_text)
            user_prompt = "What is the status of supplier A?"
            system_prompt = f"""
You are an AI agent.
Available MCP tools:
{tools_text}
Return ONLY JSON in this format:

tool
}}
"""
            response = client.chat.completions.create(
                model="gpt-4.1-mini",
                messages=[
                    {"role": "system", "content": system_prompt},
                    {"role": "user", "content": user_prompt}
                ],
                temperature=0
            )
            llm_output = response.choices[0].message.content

            print("\nLLM OUTPUT:") #>4
            print(llm_output)

            action = json.loads(llm_output)
            result = await session.call_tool(
                action["tool"],
                action["arguments"]
            )

            print("\nMCP TOOL RESULT:") #>6
            print(result)

asyncio.run(main())

6c.2 Test

python ai_demo_06c_mcp_llm_client.py
#1,2 DYNAMIC MCP TOOL LIST:
- read_shipment_status: Use this tool to get shipment status. Valid truck_id values: truck_12, truck_18.
- read_supplier_status: Use this tool to get supplier status. Valid supplier_id values: supplier_a, supplier_b.

#3,4 LLM OUTPUT:
{
  "tool": "read_supplier_status",
  "arguments": {
    "supplier_id": "supplier_a"
  }
}

#5,6 MCP TOOL RESULT:
meta=None content=[TextContent(type='text', text='Supplier A has an outage affecting brake components.', annotations=None, meta=None)] structuredContent={'result': 'Supplier A has an outage affecting brake components.'} isError=False

For Step 6c — MCP + LLM tool choice, the key @1–@9 capabilities used are:

Strongly used

@7 Structured constrained output generation

LLM generated valid tool-call JSON:

{
  "tool": "read_supplier_status",
  "arguments": {
    "supplier_id": "supplier_a"
  }
}

@8 Semantic interpretation / inference LLM understood:

  • “What is the status of supplier A?” means use:
  • read_supplier_status not:
  • read_shipment_status

@2 Contextual token dependency tracking LLM used the dynamically injected MCP tool list:

  • read_shipment_status
  • read_supplier_status
  • valid ids…
  • and connected it to the user request.

Also used

@1 Latent pattern generalization / approximation. It mapped “supplier A” to supplier_a after the valid IDs were provided.

**@9 Hierarchical planning / workflow synthesis. Lightly used. The workflow was simple:

  • choose tool → fill args → return JSON
  • Not central here

@3 Semantic feature abstraction. Underlying TF mechanism, not directly visible.

@4 Human-language robustness. Would matter if input had typos.

@5 Semantic normalization / ontology alignment. Lightly present in mapping “supplier A” → supplier_a.

@6 Explanation/summarization synthesis. Not used yet because the demo stops at tool result.


26.0521