

















Most AI assistants fail when they move beyond simple questions. The moment real data, decisions, and actions are involved, control becomes critical.
Teams need more than just a model. They need systems that can filter sensitive input, route requests correctly, involve humans when needed, and decide when to retrieve data or take action.
This guide shows how to build a fully governed AI assistant using AISquared’s UNIFI platform. The workflow combines multiple layers: input guardrails, query normalization, conditional routing, human approval, knowledge base retrieval, and agent-driven tool usage.
The example focuses on drug-related queries, but the pattern applies to any domain where accuracy, safety, and control matter: healthcare, finance, legal, or operations.
This workflow builds a fully governed drug inquiry assistant. User input is screened for PII by the first Guardrails node, then lowercased and normalized by the Python Executor. A Conditional node checks whether the query is a comparison — if yes, it routes to Human-in-the-Loop for reviewer authorization; if no, it routes directly to the Knowledge Base for semantic retrieval. Both branches converge at the Prompt node, which aggregates the user’s question with either the HITL-approved comparison query or the Knowledge Base search results. The Agent receives this prompt alongside the Drug Graph MCP Server tools — it determines whether to call an MCP endpoint (primarily for comparisons) or synthesize an answer from the Knowledge Base context. The Agent’s response is screened by a final Guardrails node for content compliance before reaching the Chat Output.
Use case: A drug information assistant where all general queries (drug info, lists, similar drugs, illness lookups) are answered via Knowledge Base retrieval, while drug comparisons require human reviewer sign-off before the Agent calls the compare MCP endpoint. The Agent intelligently selects whether to use MCP tools or the retrieved KB context depending on what the question requires.
The workflow has two branches, determined by a single Conditional check. Both branches converge at the Prompt node before the Agent.
| Condition | Path | Retrieval Source |
|---|---|---|
| Query contains ‘compare’ | HITL → Prompt → Agent | Agent uses MCP tool (compare_two_drugs_mcp) after human authorization |
| All other drug queries | Knowledge Base → Prompt → Agent | Agent uses Knowledge Base semantic search results; may also call MCP tools (drug info, list, similar) as needed |
Tip: The Agent has access to both the Knowledge Base context (via the Prompt’s Search Results input) and the MCP tools at all times. For general queries, it primarily uses the KB results; for comparisons (post-HITL), it calls compare_two_drugs_mcp directly. The Agent determines the best approach autonomously based on the question and available context.
The diagram below shows the full node graph as configured in the UNIFI AI Workflows canvas.

This workflow uses ten nodes across four categories. The Conditional node has two outputs only — compare queries go to HITL, everything else goes to the Knowledge Base. Both paths merge at the Prompt node before the Agent.
| Category | Node Name | Role |
|---|---|---|
| Input / Output | Chat Input | Entry point — receives the user’s drug question |
| Execution | Guardrails (Input) | PII compliance screen — blocks queries containing personal data before any processing |
| Execution | Python Executor | Lowercases input and normalizes drug name substrings (e.g. adds default dosage to bare ibuprofen mentions) |
| Execution | Conditional | Single check: ‘compare’ in query → HITL; all other drug queries → Knowledge Base |
| Execution | Human in the Loop | Authorization gate for comparison queries only — Approve proceeds to Prompt; Reject returns a denial message |
| Data | Knowledge Base | Semantic search on the Default (non-compare) path — Search Results feed into the Prompt node for all general drug queries |
| Model | Prompt | Aggregates user question with either HITL-authorized compare query or Knowledge Base search results; passes rendered prompt to Agent |
| Core | Drug Graph MCP Server | MCP Tool node — exposes selected Drug Graph API endpoints to the Agent; Agent calls these autonomously based on the question |
| Core | Agent | Autonomous LLM — receives rendered prompt + MCP tools; uses KB context for general queries, MCP tools for comparisons and structured lookups |
| Execution | Guardrails (Output) | Content compliance screen — blocks Agent responses containing hate speech, harassment, or policy violations before delivery |
| Input / Output | Chat Output | Returns the approved Agent response to the user |
Before building the canvas, register the AI/ML source and the MCP server Tool in UNIFI.
Sources → AI/ML Sources → Add AI/ML Source
Register the LLM that will power the Agent node. The example below uses Anthropic Claude, but any supported provider can be used.
| Field | Value / Notes |
|---|---|
| Provider | Anthropic (or OpenAI, etc.) |
| API Key | Obtain from console.anthropic.com → API Keys |
| HTTP Timeout | 30 seconds (default) |
| Request Format | {“model”:”claude-haiku-4-5-20251001″,”max_tokens”:256,”messages”:[{“role”:”user”,”content”:”Hi.”}],”stream”:false} |
| Response Format | {“id”:”msg_0123ABC”,”type”:”message”,”role”:”assistant”,”content”:[{“type”:”text”,”text”:”Hello!”}]} |
| Model Inputs | Map the messages content field as a dynamic input |
| Model Outputs | Map content[0].text as the response text output |
Tools → Add Tool → Custom MCP
MCP servers are registered under Tools, not Sources. This Tool is selected when adding the Tool node to the canvas.
| Field | Value / Example | Notes |
|---|---|---|
| Server URL | https://kaiser-drug-graph-rag-backend.onrender.com/mcp | Full URL of the MCP server endpoint |
| Transport Protocol | streamable_http | Use streamable_http for HTTP-based servers; other option: sse |
| Authentication Type | none (or bearer / basic / api_key) | Set to none if the server is open; otherwise select the auth type that matches the server |
| Secret / Token | (API key or bearer token if required) | API key, bearer token, or password depending on auth_type |
| Header Name | (if API key header auth) | Custom header name, e.g. X-API-Key |
| Custom Headers | (optional) | Additional HTTP headers — add via + Add Item |
| Timeout (seconds) | 30 | Request timeout before the MCP call is cancelled |
Tip: After saving the Tool, UNIFI introspects the MCP server and discovers its available API endpoints. You will select which specific endpoints to expose to the Agent when configuring the Tool node on the canvas in Step 2.7.
The Knowledge Base used by the canvas workflow must be created and populated before building the canvas. It requires three sub-steps: creating the vector store table, syncing data into it, and creating the Knowledge Base configuration that wraps it.
Sources → Add Data Source → AISquared Vector Store → Create Table
Navigate to Sources, add a new AISquared Vector Store data source, then click Create Table. Define the schema for your drug vector table — at minimum it must include an embedding (vector) column and a text or metadata column. The example below matches the standard document_vector_embeddings schema:
| Column Name | Data Type | Notes |
|---|---|---|
| id | int8 | Primary Key — auto-incrementing row identifier |
| text | text | The raw text content that was embedded — used by the Knowledge Base for retrieval context |
| embedding | vector | The embedding vector — must be present for semantic similarity search to work |
| created_at | timestamp | Record creation timestamp — useful for incremental sync tracking |
Additional metadata columns (e.g. drug_name, category, source) can be added using the + Add column button. Include any fields you want to filter or display alongside the retrieved text.
Tip: You can also click Define SQL Schema at the bottom of the Create Table dialog to paste a raw CREATE TABLE statement instead of filling in fields manually — useful when replicating an existing schema.
Once the table is created, populate it by running a sync from your drug data source. Refer to the AISquared Vector Store Sync workflow documentation for full setup instructions — configure the sync to map your source fields to the table columns defined above, with the embedding column receiving the generated vector output.
Important: The embedding model used when syncing data into the vector store must match the embedding model configured in the Knowledge Base (Step C below). A mismatch will produce meaningless similarity scores and irrelevant retrieval results.
Knowledge Bases → New Knowledge Base
With the vector store table populated, create the Knowledge Base configuration that the canvas workflow will use for semantic retrieval.
| Field | Value / Example | Notes |
|---|---|---|
| Embedding Provider | open_ai (or matching provider) | Must match the provider used when data was synced into the vector store |
| Embedding Model | text-embedding-3-small | Must match the exact model used at sync/ingest time |
| API Key | (provider API key) | Used to embed search queries at runtime |
| Chunk Size | 1000 (tokens or characters) | Controls how source text is split before embedding — match the value used during the original sync |
| Chunk Overlap | 250 | Overlap between consecutive chunks — preserves context across chunk boundaries |
| Vector Storage | Select the AIS Vector Store table created in Step A | This connects the Knowledge Base to the populated drug embedding table |
Tip: The Knowledge Base name you assign here is what you will select in the Knowledge Base node configuration on the AI Workflow canvas. Give it a clear, descriptive name (e.g. ‘Drug Knowledge Base’) so it is easy to identify when building the workflow.
In UNIFI: AI Workflows → New Workflow → open the canvas editor
Important: Always wire the Guardrails Fail path. An unconnected Fail output results in a silent failure — the user receives no response and no indication that their query was blocked.
Category: Execution — preprocesses the query before routing
The Python Executor lowercases the entire input and normalizes bare ibuprofen mentions to include a default dosage. This ensures consistent casing for the Conditional node’s string matching and adds clinical context for the Agent.
def run(input_msg): # import re # import json # import requests input_msg = input_msg.lower() if “ibuprofen” in input_msg and “200mg” not in input_msg: return input_msg.replace(“ibuprofen”, “ibuprofen 200mg”) return input_msg
| Sample Input | Expected Output |
|---|---|
| “Tell me about Ibuprofen” | “tell me about ibuprofen 200mg” (lowercased + dosage added) |
| “compare ibuprofen and acetaminophen” | “compare ibuprofen 200mg and acetaminophen” (lowercased + dosage added) |
| “what is aspirin?” | “what is aspirin?” (lowercased, no other change) |
| “ibuprofen 200mg dosage” | “ibuprofen 200mg dosage” (already has 200mg — no duplicate) |
Tip: Because the input is lowercased before the Conditional evaluates it, the Conditional’s string matching conditions should also be written in lowercase (e.g. ‘compare’ not ‘Compare’). The lowercase transform makes routing more reliable by eliminating case-sensitivity issues.
The Conditional node performs a single check: does the query contain the word ‘compare’? If yes, the query requires human authorization before any drug data is retrieved. If no, the query proceeds directly to the Knowledge Base for semantic retrieval.
| Output | Condition | Destination |
|---|---|---|
| Condition 1 | Input contains ‘compare’ | Human in the Loop → Input Text |
| Default | All other drug queries | Knowledge Base → Search Query |
Tip: The Conditional node is lowercased input so ‘compare’, ‘Compare’, and ‘COMPARE’ are all caught by a single contains condition. Consider also adding a condition for ‘comparison’, ‘versus’, or ‘vs’ using additional Condition outputs if you want to catch those phrasings too.
Category: Execution — pauses the workflow for a reviewer to approve drug comparison queries
Drug comparison queries require explicit human authorization before the Agent is allowed to retrieve and surface comparative drug data. The reviewer sees the user’s preprocessed question and approves or rejects it. Approved queries skip the Knowledge Base entirely and go directly to the Prompt node — the Agent will use the compare MCP tool to fulfill the request.
Important: Always wire both Approve and Reject. The Human in the Loop node pauses asynchronously — the user receives no response until a reviewer acts. Set clear expectations in the welcome message that comparison requests require review.
Category: Data (Knowledge Base) + Model (Prompt) — Knowledge Base handles all non-compare queries; Prompt aggregates results from either branch
The Knowledge Base is on the Default path — all queries that are not comparisons are routed here for semantic retrieval against the drug vector store. This covers general drug inquiries, illness lookups, drug detail queries, similar drug requests, and anything else that doesn’t require a comparison. The Prompt node then aggregates whichever input it receives — either KB search results (from the Default path) or the HITL-authorized comparison question (from the Approve path) — and renders a single prompt for the Agent.
You are a drug information assistant with access to a Drug Graph database.Use the retrieved context below and available MCP tools to answer the user’s question.
– For general drug queries: use the retrieved context from the knowledge base.- For drug comparisons (pre-authorized): use the compare_two_drugs_mcp tool.- For structured lookups: use get_drug_info_mcp, get_drug_list_mcp, or get_similar_drug_list_mcp as appropriate.
Be concise and factual. Do not speculate beyond retrieved data or tool results.
User question: {{Input Message}}
Retrieved context from knowledge base (empty for comparison queries):{{Search Results}}
Tip: The Agent has both KB context and MCP tools available on every call. For general queries arriving via the Default path, the KB search results will be populated and the Agent will primarily use them. For comparison queries arriving via HITL Approve, Search Results will be empty and the Agent will call compare_two_drugs_mcp. The prompt template guides this decision explicitly.
Category: Core — exposes MCP endpoints as tools for the Agent
The Tool node connects the Drug Graph MCP Server to the Agent. You configure which specific API endpoints the Agent has access to — restrict this to only the endpoints needed for the use case.
| Endpoint | Enable? | When Agent Uses It |
|---|---|---|
| get_drug_list_mcp | Yes | User asks for a full list of drugs — Agent calls this regardless of KB results |
| get_similar_drug_list_mcp | Yes | User asks for drugs similar to a specific one |
| get_drug_info_mcp | Yes | User asks for detailed info on a named drug — Agent may supplement or replace KB results |
| compare_two_drugs_mcp | Yes | Drug comparison — only reached after HITL authorization; KB Search Results will be empty on this path |
Tip: Even though compare_two_drugs_mcp is enabled in the Tool node, the workflow’s HITL gate ensures the Agent can only reach the Prompt and Tool nodes after a comparison query has been human-authorized. The tool selection restriction and the HITL gate work together as defense-in-depth.
Category: Core — autonomous LLM that uses the MCP tools to answer the question
Category: Execution — screens the Agent’s response for hate speech, harassment, and policy violations
The output Guardrails node applies content moderation rules to the Agent’s response before it reaches the user. This is distinct from the input Guardrails node, which screens PII — this node focuses on the quality and safety of the generated output.
| Guardrails Node | What It Screens |
|---|---|
| Guardrails (Input) | PII in user queries — names, emails, phone numbers, financial identifiers. Blocks before any processing. |
| Guardrails (Output) | Policy violations in Agent responses — hate speech, harassment, threatening content. Blocks before delivery to user. |
The table below maps every connection. The Conditional has two outputs — compare queries go to HITL, all others go to Knowledge Base. Both converge at the Prompt node. Chat Input → Message is also wired directly to Prompt → Input Message so the original question is available for the final LLM synthesis.
| From | → | To | Data / Notes |
|---|---|---|---|
| Chat Input → Message | → | Guardrails (Input) → input | Raw user question |
| Chat Input → Message | → | Prompt → Input Message | Original question also carried forward directly for LLM context |
| Guardrails (Input) → Pass | → | Python Executor → input_msg | PII-clean query |
| Guardrails (Input) → Fail | → | Chat Output (PII rejection) | PII detected — no further processing |
| Python Executor → Output Data | → | Conditional → input | Lowercased + normalized query |
| Conditional → Condition 1 | → | Human in the Loop → Input Text | Contains ‘compare’ — requires authorization |
| Conditional → Default | → | Knowledge Base → Search Query | All other drug queries — semantic retrieval |
| Human in the Loop → Approve | → | Prompt → Input Message | Authorized comparison query — bypasses KB, goes straight to Prompt |
| Human in the Loop → Reject | → | Chat Output (HITL rejection) | Reviewer denied — no KB search, no Agent call |
| Knowledge Base → Search Results | → | Prompt → Search Results | Retrieved drug context — populated for all non-compare queries; empty for compare path |
| Prompt → Rendered Prompt Output | → | Agent → Input Message | Aggregated prompt: question + KB context (or empty for compare) |
| Tool node → outputs | → | Agent → Tools | MCP endpoints available; Agent decides whether to call based on question type |
| Agent → Response | → | Guardrails (Output) → input | Agent’s generated answer — screened before delivery |
| Guardrails (Output) → Pass | → | Chat Output → Text | Approved response delivered to user |
| Guardrails (Output) → Fail | → | Chat Output (content violation) | Policy violation in output — blocked |
| Agent → Execution Trace | → | Secondary Chat Output (dev only) | Tool call log — remove in production |
| Test Case | Sample Input | Expected Behavior |
|---|---|---|
| General drug query | ‘What is aspirin?’ | Guardrails pass → Python lowercases → Conditional Default → KB retrieves drug context → Prompt → Agent uses KB results → output Guardrails → Chat Output |
| Illness lookup | ‘What drug treats hypertension?’ | Conditional Default → KB semantic search returns drug-condition matches → Agent synthesizes answer from KB context |
| Drug list | ‘List all available drugs’ | Conditional Default → KB → Prompt → Agent calls get_drug_list_mcp for structured list response |
| Similar drugs | ‘What drugs are similar to ibuprofen?’ | Python normalizes to ‘ibuprofen 200mg’ → Conditional Default → KB → Agent calls get_similar_drug_list_mcp |
| Ibuprofen normalization | ‘Tell me about Ibuprofen’ | Python Executor outputs ‘tell me about ibuprofen 200mg’ — verify via Execution Trace that normalized string reaches Agent |
| Comparison (approved) | ‘Compare ibuprofen and acetaminophen’ | Conditional Condition 1 → HITL → Reviewer approves → Prompt (Search Results empty) → Agent calls compare_two_drugs_mcp → output Guardrails → Chat Output |
| Comparison (rejected) | ‘Compare ibuprofen and acetaminophen’ | Conditional Condition 1 → HITL → Reviewer rejects → denial Chat Output; KB and Agent never reached |
| PII input | Query with a real name or email | Guardrails (Input) Fail → rejection; all downstream nodes never reached |
| Output policy violation | Query designed to elicit harmful content | Agent response → Guardrails (Output) Fail → content violation output; response withheld |
Tip: For comparison queries, verify via the Agent Execution Trace that Search Results is empty (KB was bypassed) and that compare_two_drugs_mcp was called. For all other queries, verify Search Results is populated and the Agent used that context. If the Agent ignores KB results and calls MCP tools unnecessarily, tighten the prompt template instructions.
| Setting | Recommended Value |
|---|---|
| Assistant Name | ‘Drug Inquiry Assistant’ or your preferred display name |
| Feedback Options | Enable thumbs up/down |
| Welcome Message | e.g. ‘Ask me about any prescription drug. Drug comparison requests require reviewer authorization and may take a few minutes.’ |
| Placeholder Text | e.g. ‘Ask about a drug, dosage, side effects, or similar drugs…’ |
Click Save, then Export or Embed to generate the embeddable assistant snippet. Copy the dataAppId and dataAppUseCaseId from the export panel and insert them into the HTML template below:
| Field | Description |
|---|---|
| dataAppId | Numeric Data App ID from the UNIFI export panel |
| dataAppUseCaseId | Unique use case ID string from the UNIFI export panel — replace ‘YOUR_USE_CASE_ID_HERE’ |
| data_apps_runner.js | Self-contained UNIFI script — includes rendering, data fetching, and authentication. No additional dependencies needed. |
| <div id=”…”> | Mount point for the chat widget — place anywhere in the page body |
Tip: The embed snippet connects to the latest published version of the workflow via the dataAppUseCaseId. Updating the workflow in UNIFI (adding guardrail rules, changing the prompt, etc.) does not require changing the HTML embed code.
| Step | Action | Key Detail |
|---|---|---|
| 1a | Add AI/ML source | Sources → AI/ML Sources; provider, API key, request/response format, model inputs/outputs |
| 1b | Register MCP Tool | Tools → Add Tool → Custom MCP; Server URL, Transport Protocol (streamable_http), Auth Type, Timeout |
| 2a | Chat Input + Guardrails (In) | PII screen; Pass → Python Executor; Fail → rejection output — always wire both |
| 2b | Python Executor | run(input_msg): lowercase entire input; add ‘200mg’ to bare ibuprofen; always return input_msg as fallback; imports inside run() |
| 2c | Conditional (2 outputs) | Condition 1 (contains ‘compare’) → HITL; Default (everything else) → Knowledge Base |
| 2d | Human in the Loop | Compare gate; Approve → Prompt (bypasses KB); Reject → denial output; wire both paths + timeout |
| 2e | Knowledge Base + Prompt | KB on Default path — all non-compare queries; KB Search Results + Input Message → Prompt; HITL Approve also wires to Prompt Input Message; Rendered Prompt → Agent |
| 2f | Tool node (MCP) | All 4 endpoints enabled; Agent uses KB context for general queries, calls compare_two_drugs_mcp for authorized comparisons |
| 2g | Agent | AI/ML source; Input Message from Prompt; Tools from Tool node; uses KB or MCP based on question; Response → Guardrails (Output) |
| 2h | Guardrails (Output) | Content compliance (hate speech, harassment); Pass → Chat Output; Fail → violation output |
| 3 | Test 9 cases | General, illness, list, similar, normalization, comparison approved/rejected, PII, output violation |
| 4 | Configure + Export + Embed | Interface Settings → Save → Export → copy IDs → HTML embed template → deploy |
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。