Docs Frameworks

Haystack Agents

Status: verified. The adapter ships in kiff-guard and passes the conformance suite. The seam was verified against the real haystack-ai package.

Shape: vote (inverted-control). Haystack runs the tool itself; a ConfirmationStrategy only votes — return a decision with execute=True to allow, execute=False to block. So the guard uses observe() / decide_only(), not a run callback. See how the guard works.

Install

pip install "git+https://github.com/kiffhq/kiff-guard.git#subdirectory=packages/python/kiff-guard"
# plus Haystack, the framework you're guarding:
pip install haystack-ai

Observe — audit with zero config

from haystack.components.agents import Agent
from kiff_guard import Guard
from kiff_guard.adapters.haystack import kiff_confirmation_strategy

guard = Guard(mode="observe")     # no client, no tenant needed

agent = Agent(
    chat_generator=...,
    tools=[refund_order, send_email],
    confirmation_strategies={
        "refund_order": kiff_confirmation_strategy(guard),
        "send_email": kiff_confirmation_strategy(guard),
    },
)

Run the agent as usual, then read the trail:

for r in guard.receipts:
    print(r.state, r.tool, r.outcome)

Activate

Derive a starter domain from what you observed, review it, and activate it in the dashboard:

from kiff_guard import export_yaml
print(export_yaml("my-domain", guard.catalog))

Enforce — govern at runtime

Once you have a tenant and an active domain, point the guard at KIFF and switch to enforce. Only the guard changes:

from kiff_guard import Guard, HTTPClient, ToolMap
from haystack.components.agents import Agent
from kiff_guard.adapters.haystack import kiff_confirmation_strategy

client = HTTPClient(
    api_key="kiff_live_...",                       # mint in the dashboard
    tool_map=ToolMap().bind(
        "refund_order", action="REFUND_ORDER",
        entity_type="Order", entity_arg="order_id"),
)
guard = Guard(client=client, tenant="<tenant>", agent="support", mode="enforce")

agent = Agent(chat_generator=..., tools=[refund_order],
              confirmation_strategies={"refund_order": kiff_confirmation_strategy(guard)})

In enforce mode a withheld decision returns execute=False with the reason as feedback, so the tool never runs. An allowed decision returns execute=True and the tool runs. On a transport error the guard fails closed (blocks) by default.

The seam (verified)

Haystack calls a ConfirmationStrategy.run(*, tool_name, tool_description, tool_params, tool_call_id, ...) before executing a tool; it returns a ToolExecutionDecision(execute=bool, feedback=...). The guard’s strategy is passed via Agent(confirmation_strategies=...).