惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Engineering at Meta
Engineering at Meta
D
Docker
IT之家
IT之家
博客园_首页
罗磊的独立博客
V
V2EX
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
美团技术团队
Y
Y Combinator Blog
博客园 - 聂微东
量子位
阮一峰的网络日志
阮一峰的网络日志
GbyAI
GbyAI
Microsoft Security Blog
Microsoft Security Blog
博客园 - Franky
Martin Fowler
Martin Fowler
Jina AI
Jina AI
大猫的无限游戏
大猫的无限游戏
C
Check Point Blog
月光博客
月光博客
G
Google Developers Blog
B
Blog
T
The Blog of Author Tim Ferriss
爱范儿
爱范儿

Recent Gists from ZC502

暂无文章

langsmith_order_sensitive_mre.py
ZC502 · 2026-04-09 · via Recent Gists from ZC502
""" LangSmith trajectory evaluation MRE for order-sensitive tool workflows. What this demonstrates ---------------------- - exact-match trajectory scoring is too rigid - any-order scoring is too loose - a deterministic causal / precedence evaluator fills the gap Requirements ------------ pip install -U langsmith Environment ----------- export LANGSMITH_TRACING=true export LANGSMITH_API_KEY=... (optional) export LANGSMITH_WORKSPACE_ID=... """ from __future__ import annotations from collections import Counter from typing import Any from uuid import uuid4 from langsmith import Client, traceable from langsmith.schemas import Example, Run # ----------------------------- # 1) Create a small dataset # ----------------------------- client = Client() dataset_name = f"sara-order-sensitive-mre-{uuid4().hex[:8]}" dataset = client.create_dataset( dataset_name=dataset_name, description=( "Order-sensitive trajectory evaluation MRE. " "Shows the gap between exact-match, any-order, and causal scoring." ), ) # All examples share the same canonical policy: # - Required tools: set_private, read_data # - Precedence: set_private must happen before read_data # # But the candidate trajectories differ. examples = [ { "inputs": { "trajectory_name": "exact_safe", "tool_order": ["set_private", "read_data"], }, "outputs": { "expected_exact_order": ["set_private", "read_data"], "expected_unordered_tools": ["set_private", "read_data"], "required_tools": ["set_private", "read_data"], "precedence_rules": [["set_private", "read_data"]], "expected_causal_pass": True, }, }, { "inputs": { "trajectory_name": "alternative_safe", "tool_order": ["set_private", "audit_access", "read_data"], }, "outputs": { "expected_exact_order": ["set_private", "read_data"], "expected_unordered_tools": ["set_private", "read_data"], "required_tools": ["set_private", "read_data"], "precedence_rules": [["set_private", "read_data"]], "expected_causal_pass": True, }, }, { "inputs": { "trajectory_name": "unsafe_same_tools", "tool_order": ["read_data", "set_private"], }, "outputs": { "expected_exact_order": ["set_private", "read_data"], "expected_unordered_tools": ["set_private", "read_data"], "required_tools": ["set_private", "read_data"], "precedence_rules": [["set_private", "read_data"]], "expected_causal_pass": False, }, }, ] client.create_examples(dataset_id=dataset.id, examples=examples) # ----------------------------- # 2) A tiny traceable target # ----------------------------- @traceable(run_type="tool") def set_private(state: dict) -> dict: state = dict(state) state["visibility"] = "private" return state @traceable(run_type="tool") def audit_access(state: dict) -> dict: state = dict(state) state["audit_logged"] = True return state @traceable(run_type="tool") def read_data(state: dict) -> dict: state = dict(state) if state.get("visibility") != "private": state["leak"] = True state["data_read"] = True return state @traceable(name="policy_flow") def run_policy_flow(inputs: dict) -> dict: """ Simulates an agent/tool trajectory. Intentionally returns the SAME final answer text for every path so that the difference must be captured from the trajectory, not the answer alone. """ state = { "visibility": "public", "audit_logged": False, "data_read": False, "leak": False, } for step in inputs["tool_order"]: if step == "set_private": state = set_private(state) elif step == "audit_access": state = audit_access(state) elif step == "read_data": state = read_data(state) else: raise ValueError(f"Unknown step: {step}") return { "answer": "Access handled safely.", "trajectory_name": inputs["trajectory_name"], "leak": state["leak"], "final_state": state, } # ----------------------------- # 3) Helper: extract trajectory from Run # ----------------------------- def _extract_tool_order(run: Run) -> list[str]: """ Extract tool execution order from the trace. We use child runs because LangSmith docs explicitly support intermediate-step evaluation via the Run object. """ tool_runs = [ child for child in (run.child_runs or []) if getattr(child, "run_type", None) == "tool" ] def sort_key(r: Run) -> Any: return ( str(getattr(r, "start_time", "")), str(getattr(r, "dotted_order", "")), ) tool_runs = sorted(tool_runs, key=sort_key) return [r.name for r in tool_runs] # ----------------------------- # 4) Evaluators # ----------------------------- def exact_trajectory_match(run: Run, example: Example) -> dict: actual = _extract_tool_order(run) expected = list(example.outputs["expected_exact_order"]) passed = actual == expected return { "key": "trajectory_exact_match", "score": 1.0 if passed else 0.0, "comment": f"expected={expected}, actual={actual}", } def any_order_match(run: Run, example: Example) -> dict: actual = _extract_tool_order(run) expected_tools = list(example.outputs["expected_unordered_tools"]) passed = Counter(actual) == Counter(expected_tools) return { "key": "trajectory_any_order_match", "score": 1.0 if passed else 0.0, "comment": f"expected_tools={expected_tools}, actual={actual}", } def logical_causality_score(run: Run, example: Example) -> dict: """ Deterministic partial-order evaluator. This is the key MRE: - exact matching rejects valid alternative safe paths - any-order matching accepts logically unsafe reorderings - this evaluator checks only the causal constraints that matter """ actual = _extract_tool_order(run) required_tools = list(example.outputs["required_tools"]) precedence_rules = list(example.outputs["precedence_rules"]) violations: list[str] = [] missing = sorted(set(required_tools) - set(actual)) if missing: violations.append(f"missing required tools: {missing}") index = {tool: i for i, tool in enumerate(actual)} for before, after in precedence_rules: if before in index and after in index and index[before] > index[after]: violations.append( f"causality violation: '{before}' must precede '{after}'" ) passed = len(violations) == 0 return { "key": "trajectory_logical_causality", "score": 1.0 if passed else 0.0, "comment": "OK" if passed else "; ".join(violations), } def leaked_before_lock(run: Run, example: Example) -> dict: """ Optional extra evaluator: checks whether the simulated final state shows data leakage. """ leak = bool(run.outputs.get("leak", False)) return { "key": "state_leak_detected", "value": "leak" if leak else "safe", "comment": f"final_state={run.outputs.get('final_state')}", } # ----------------------------- # 5) Run evaluation # ----------------------------- results = client.evaluate( run_policy_flow, data=dataset_name, evaluators=[ exact_trajectory_match, any_order_match, logical_causality_score, leaked_before_lock, ], experiment_prefix="order-sensitive-trajectory-mre", metadata={ "category": "trajectory_evals", "topic": "order_sensitive_tool_workflows", }, ) print(f"Dataset: {dataset_name}") print(results)