Skip to content

Latest commit

 

History

3 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

aip-lang

Exploring uncertainty-aware programming for AI agents: standardizing what an agent does once it has a confidence number — escalate, verify, ask a human — as explicit, enforced control flow (Prediction[T], on_confidence) instead of ad hoc if statements scattered through the codebase.

What this project is not: a way to make confidence numbers themselves trustworthy. Whether confidence=0.6 means anything is a statistics/ML problem — it's exactly as hard in plain Python as it is here, and no library removes it. aip only standardizes what happens after you've decided to trust a number; getting that number right is still on you, same as it would be without this package.

from aip import Agent, Evidence, Policy, Prediction, confidence, on_confidence

class CustomerSupport(Agent):
    model = "gpt"
    policy = Policy(max_steps=5, require_evidence=True)

    def goal(self, question: str) -> Prediction[str]:
        samples = sample_model(question, n=5)  # your provider call, x5
        return Prediction(
            value=majority_vote(samples),
            confidence=confidence.from_self_consistency(samples),
            evidence=[Evidence(source="policy.md", excerpt="30-day return window")],
        )

    @on_confidence(below=0.70)
    def verify(self, prediction: Prediction[str]) -> Prediction[str]:
        ...  # re-check and return an updated, higher-confidence Prediction

    @on_confidence(below=0.40)
    def ask_human(self, prediction: Prediction[str]) -> None:
        ...  # terminal escalation — no re-check, run() stops here

Status

Early prototype: an embedded Python DSL, not a new language/syntax. aip/ is a small, working package — Agent, Prediction, Policy, on_confidence, tool, confidence — no parser, no compiler, just plain Python classes and decorators, so it gets IDE/type-checker support for free and costs nothing to iterate on.

Where this landed, in one paragraph: the control-flow layer (Agent/Policy/on_confidence/resolve()) is validated — it caught a real rule-ordering bug class in a head-to-head comparison against hand-written Python, and survived a structural stress test (a multi-step agent) that forced a real refactor, not a cosmetic one. Grounded, evidence-based confidence checking (RAG + from_entailment) is the one confidence-computation approach with a positive result, validated twice — once with mocked data, once fully live (real retrieval, real generation, real LLM-as-judge — examples/rag_live.py). Self-contained confidence estimation with no evidence to check against — self-consistency in three forms, self-report in two — was tested against a real model across four questions designed to need different confidence levels, and all five failed to discriminate reliably; see docs/confidence-findings.md for why each one fails and what hasn't been tried yet. That's the project's actual, evidence-backed scope right now: a validated control-flow layer, one validated way to compute trustworthy confidence (when you have something to check against), and a documented open problem for when you don't.

Run it:

pip install -e ".[dev]"
python examples/customer_support.py
pytest

What's validated so far: the control-flow mechanics — when multiple on_confidence rules match, the lowest threshold (most severe escalation) wins; handlers that return a new Prediction get re-evaluated against the rules; policy.require_evidence / policy.max_steps are enforced as exceptions (EvidenceRequired, StepLimitExceeded).

aip/confidence.py exists to make that "on you" part less error-prone to implement, not to solve it — it's a set of tested, reusable implementations of known techniques, so you're not re-deriving self-consistency voting or entailment scoring from scratch in every project. Self-reported "how confident are you" scores from an LLM are known to be poorly calibrated, so that's included only as one explicit, visible option among several interchangeable strategies — pick one per Prediction, or combine them:

Strategy Signal Needs
from_self_consistency agreement rate across N sampled outputs any provider, N calls
from_logprobs geometric mean of token log-probabilities provider exposes logprobs
from_entailment a verifier/NLI model's P(evidence entails value) a second, separate model
from_self_report the model's own stated confidence nothing extra — and the least trustworthy
combine(*scores, weights=...) weighted mean of any of the above
naive_lexical_overlap / best_matching_evidence word-overlap stand-in for from_entailment's score, until you wire up a real verifier

examples/customer_support.py wires up from_self_consistency end to end (samples are still mocked — no provider is called yet — but the confidence number is genuinely computed from sample agreement, not hand-picked).

Two RAG examples exercise from_entailment end to end — retrieve → generate → check the answer against retrieved evidence:

  • examples/rag_support.py checks one whole answer against its evidence, and catches a hallucination: a mocked answer fabricates a claim the retrieved doc never made, and low entailment routes it to ask_human() instead of returning it silently.
  • examples/rag_policy_summary.py follows a more staged, industry-typical pipeline (retrieve → rerank → generate → decompose into claims → per-claim grounding check → aggregate) and catches something the first example's whole-answer check would have missed: one fabricated claim buried in an otherwise well-grounded, multi-sentence answer, invisible in the aggregate confidence (0.74 — looks fine) but caught the moment each claim is checked individually.

naive_lexical_overlap/best_matching_evidence started as near-identical code duplicated across both RAG examples; once a second example showed the same function was genuinely needed twice, it moved into aip/confidence.py — the orchestration around it (single-answer vs. per-claim, "regenerate" vs. "widen the search pool") differs enough between the two examples that a shared RAGAgent-style base class isn't justified yet.

examples/researcher.py stress-tests the design against a multi-step agent (see Agent.resolve() in aip/agent.py).

None of these strategies are calibrated out of the box — a 0.7 here is a relative signal, not "70% empirically correct," until you check it against labeled examples (e.g. temperature/Platt scaling). That's true whether you call confidence.from_self_consistency or hand-roll the same voting logic yourself; the function just saves you from re-writing and re-debugging it.

That calibration check has been run, against a real provider, and the full results — including a positive one — are in docs/confidence-findings.md. examples/customer_support_live.py and examples/confidence_comparison.py wire from_self_consistency (plus embedding-based clustering and self-report) up to real OpenAI calls across four test questions chosen to need different confidence levels — and none of those five methods reliably separates "the model knows this" from "the model is guessing or appropriately declining." examples/rag_live.py is the positive follow-up: real embedding-based retrieval, real generation, and a real LLM-as-judge entailment check (no mocked data, no lexical-overlap proxy) on top of the one path that did work — grounded/evidence-based checking, the same mechanism as the mocked RAG examples below, now run against a live model end to end.

Layout

aip/           the package: Agent, Prediction, Policy, on_confidence, tool, confidence
examples/      runnable example agents
tests/         pytest suite (also doubles as executable spec for the rules)
docs/          confidence-findings.md — the real-API investigation results

About

No description, website, or topics provided.

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages