') + ')', 'gi'); if (regex.test(text)) { found = true; var frag = document.createDocumentFragment(); var parts = text.split(regex); parts.forEach(function(part, i) { if (i % 2 === 0) { frag.appendChild(document.createTextNode(part)); } else { var span = document.createElement('span'); span.className = 'userscript-highlight'; span.textContent = part; frag.appendChild(span); } }); node.parentNode.replaceChild(frag, node); } }); } else if (node.nodeType === 1 && node.childNodes) { // element var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT']; if (!skipTags.includes(node.tagName)) { Array.from(node.childNodes).forEach(highlight); } } } highlight(document.body); // Re-highlight on dynamic content var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1 || node.nodeType === 3) highlight(node); }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Strip utm_, fbclid, gclid, etc. from all links on page (function() { var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content', 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid', 'ref', 'ref_src', 'source', 'medium', 'campaign']; function cleanUrl(url) { try { var u = new URL(url, window.location.origin); var changed = false; trackingParams.forEach(function(p) { if (u.searchParams.has(p)) { u.searchParams.delete(p); changed = true; } }); return changed ? u.toString() : url; } catch (e) { return url; } } function cleanLinks() { document.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } cleanLinks(); var observer = new MutationObserver(function(mutations) { mutations.forEach(function(m) { m.addedNodes.forEach(function(node) { if (node.nodeType === 1) { if (node.tagName === 'A') cleanLinks(); node.querySelectorAll('a[href]').forEach(function(a) { var clean = cleanUrl(a.href); if (clean !== a.href) a.href = clean; }); } }); }); }); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Auto-enable theater mode on YouTube (function() { function tryTheater() { var btn = document.querySelector('button[aria-label="Theater mode"], ytd-player #player button[title="Theater mode"]'); if (btn && !btn.classList.contains('activated')) { btn.click(); } } // Try immediately tryTheater(); // Try after navigation (SPA) var lastUrl = location.href; setInterval(function() { if (location.href !== lastUrl) { lastUrl = location.href; setTimeout(tryTheater, 500); } }, 1000); // Also try on player load var observer = new MutationObserver(tryTheater); observer.observe(document.body, { childList: true, subtree: true }); })(); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + ', 'i'); if (__m === '*' || __re.test(location.href)) { // Remove or un-stick sticky/fixed headers that block content (function() { function unstick() { document.querySelectorAll('header, nav, [role="banner"], .header, .navbar, .sticky, .fixed-top, [style*="position: fixed"], [style*="position:sticky"]').forEach(function(el) { if (el.style.position === 'fixed' || el.style.position === 'sticky' || getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') { el.style.position = 'static'; el.style.top = 'auto'; el.style.zIndex = 'auto'; } }); } unstick(); var observer = new MutationObserver(unstick); observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] }); })(); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); })(); GitHub - sust4in/agentproof · GitHub
Skip to content

Repository files navigation

agentproof

PyPI versionPython versionsLicense: MIT

Composable reward signals from agent trajectories using programmatic verification. agentproof runs real tools against agent outputs and produces deterministic scores for RL training (GRPO, DPO, SFT filtering) or agent quality analysis.

Quick start

pip install agentproof

Compose multiple verifiers into a single reward

fromagentproofimportRewardComposer, verifierfromagentproof.verifiersimportCodeExecution, FormatCheck, StepEfficiency# Define a custom verifier -- just decorate a function@verifier(deterministic=True)defvuln_eliminated(target) ->float:
"""Check if SAST findings decreased after the agent's patch."""before=run_scanner(target.context["original_code"])
after=run_scanner(target.outcome["patched_code"])
returnmax(0, len(before.findings) -len(after.findings)) /max(len(before.findings), 1)
# Compose verifiers with weights and gatingcomposer=RewardComposer([
vuln_eliminated.with_weight(0.5, required=True), # required: total=0 if this failsCodeExecution(cmd="pytest").with_weight(0.3, required=True),
FormatCheck(schema="output_schema.json").with_weight(0.1),
StepEfficiency(max_steps=15).with_weight(0.1),
])
# Score a trajectoryresult=my_agent.run(task)
scored=composer.score(trajectory=result.trajectory, context={"original_code": src})
print(scored.reward) # 0.0 if any required verifier failed, weighted sum otherwiseprint(scored.breakdown) # per-verifier scores and evidence

required=True is the anti-reward-hacking mechanism. If a required verifier returns 0, the entire composed reward is 0 -- no matter how well other verifiers score. An agent cannot game easy signals while failing on the ones that matter.

CLI Usage

# Generate a config file
agentproof init
# Score trajectories from a JSONL file
agentproof check traces.jsonl
# JSON output for scripts
agentproof check --format json | jq .summary
# JUnit XML for CI dashboards (GitHub Actions, Jenkins)
agentproof check --format junit > results.xml
# Override threshold from CLI
agentproof check -t 0.8 traces.jsonl

Export scored data for training

fromagentproofimportto_sft, to_dpofromagentproof.sourcesimportJSONLSource# Load historical trajectories and score themtrajectories=JSONLSource("./agent_runs.jsonl").fetch()
scored=composer.score_batch(trajectories)
# Export for different training methodsto_sft(scored, "./data", min_reward=0.7) # filter to good examplesto_dpo(scored, "./data") # preference pairs

Installation

pip install agentproof # core + built-in verifiers
pip install agentproof[jsonschema] # with JSON schema validation support
pip install agentproof[langsmith] # with LangSmith source adapter

Built-in verifiers

VerifierWhat it does
CodeExecutionRuns a shell command against trajectory output. Score 1.0 for exit code 0.
FormatCheckValidates trajectory outcome against a JSON schema.
RegexMatchChecks if trajectory outcome matches a regex pattern.
StepEfficiencyPenalizes trajectories with too many steps.

Custom verifiers

The @verifier decorator is the primary extension point:

fromagentproofimportverifier@verifier(deterministic=True)deftests_pass(target) ->float:
"""Run pytest and return 1.0 if all tests pass."""result=subprocess.run(["pytest", target.context["test_path"]], capture_output=True)
return1.0ifresult.returncode==0else0.0

For verifiers needing configuration or state, use the class form:

fromagentproofimportVerifier, VerifyResultclassSASTDiffVerifier(Verifier):
name="sast_diff"deterministic=Truedef__init__(self, scanner_cmd: str):
self.scanner_cmd=scanner_cmddefverify(self, target) ->VerifyResult:
# Run scanner before/after comparison
...

Third-party verifier packs can register via entry_points:

[project.entry-points."agentproof.verifiers"]
my_verifier = "my_package:MyVerifier"

Export to training frameworks

Export adapters produce JSONL files that TRL, veRL, and OpenRLHF can consume. agentproof never imports training libraries.

ExportFormatUse case
to_sftinstruction/response JSONLFilter high-scoring trajectories for supervised fine-tuning
to_dpoprompt/chosen/rejected JSONLCreate preference pairs for DPO training
to_grpogrouped completions JSONLBatch GRPO training with group-level reward normalization

v1.3: Training Loop Validation

v1.3 added closed-loop validation between agentproof scoring and real training frameworks.

Feedback Ingestion from LangSmith

Fetch human and automated feedback from LangSmith and use it as a reward signal:

fromagentproofimportLangSmithSource, get_feedback, verifiersource=LangSmithSource(project_name="my-agent")
trajectories=source.fetch(include_feedback=True)
@verifier(deterministic=True)defhuman_score(target) ->float:
"""Use human correctness feedback as the reward signal."""fb=get_feedback(target.context, "correctness")
returnfloat(fb.get("score", 0.0)) iffbisnotNoneelse0.5

Ground Truth Matching

Match trajectories against a LangSmith dataset and verify against expected outputs:

trajectories=source.fetch(dataset_name="my-labeled-dataset")
fromagentproofimportget_ground_truth@verifier(deterministic=True)defexact_match(target) ->float:
gt=get_ground_truth(target.context)
ifgtisNone:
return0.0return1.0ifstr(target.trajectory.outcome) ==str(gt.get("answer", "")) else0.0

TRL Integration

Wrap a composer as a live TRL reward function for online GRPO training:

fromagentproof.export.grpoimportas_trl_reward_funcreward_fn=as_trl_reward_func(composer)
# Pass to TRL: GRPOTrainer(reward_funcs=[reward_fn])

Documentation

Full documentation: https://ogulcanarbc.github.io/agentproof/

Development

See CONTRIBUTING.md for setup instructions and development workflow.

git clone https://github.com/ogulcanarbc/agentproof.git
cd agentproof
make install # installs dev deps via uv
make all # runs lint + format-check + typecheck + test

License

MIT

About

No description, website, or topics provided.

Resources

Contributing

Security policy

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages