Files
NotesAgentic/backend/scripts/phase2-integration.py
T

57 lines
4.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""显式隔离演示:检索、读取、创建三个任务,再调用真实只读 MCP。
只批准本次运行产生的 tasks.write 权限票据,需要质量验收用 Vault。
脚本仅调用公共 API 契约,不把伪造的完成状态写入 SQLite。
"""
import argparse, json, time
from pathlib import Path
from urllib.request import Request,urlopen
def main(args):
def api(path,body=None):
req=Request(args.base_url+'/api'+path,data=json.dumps(body).encode() if body is not None else None,
headers={'Content-Type':'application/json'})
with urlopen(req,timeout=60) as response:return json.load(response)
directory=args.data_dir.resolve()
if not (directory/'vault/.phase2-fixture').exists():raise SystemExit('Isolated fixture Vault required')
core=json.loads((directory/'benchmarks/rag-phase2-v1.json').read_text(encoding='utf-8'))
note=next(c for c in core['cases'] if c['case_id']=='deadlock-0')['expected_note_ids'][0]
cases=[{'case_id':'retrieval-tasks','prompt':f'按顺序执行:1. 用 rag.search 搜索“死锁”,明确使用 mode=fts2. 用 notes.read 读取笔记 {note}3. 根据内容用 tasks.create 分别创建且仅创建三个任务,标题严格为“验收-互斥条件”、“验收-循环等待”、“验收-锁顺序”;4. 总结死锁条件并引用搜索来源。请不要调用其他工具,不重复创建。',
'allowed_tools':['rag.search','notes.read','tasks.create'],
'expected_tools':[{'name':'rag.search','arguments':{'query':'死锁','mode':'fts'}},{'name':'notes.read','arguments':{'note_id':note}}]+
[{'name':'tasks.create','arguments':{'title':title}} for title in ['验收-互斥条件','验收-循环等待','验收-锁顺序']],
'citation_required':True,'tasks_created':3,'output_contains':['死锁'],'tags':['rag','notes','tasks','permissions']}]
tools=api('/tools')['items']; mcp=next((t for t in tools if t['name'].endswith('.web_search') and t['name'].startswith('mcp.')),None)
if mcp:
cases.append({'case_id':'mcp-search','prompt':f'调用一次 {mcp["name"]}query 严格使用 Python official documentation tutorial。根据工具真实返回给出一句总结。',
'allowed_tools':[mcp['name']], 'expected_tools':[{'name':mcp['name'],'arguments':{'query':'Python official documentation tutorial'}}], 'tags':['mcp','real-network']})
dataset={'dataset_id':'agent-integration-v1','kind':'agent','version':'1.0.0','description':'Isolated phase2 cross-module Demo; existing MCP binding captured','cases':cases}
(directory/'benchmarks/agent-integration-v1.json').write_text(json.dumps(dataset,ensure_ascii=False,indent=2),encoding='utf-8')
providers=api('/providers')['items']; provider=next(p for p in providers if p['enabled'] and p['provider_type']!='mock')
run=api('/benchmarks/agent/runs',{'dataset_id':dataset['dataset_id'],'provider_id':provider['provider_id'],'model':provider['default_model'],
'max_steps':10,'timeout_seconds':150,'token_budget':10000,'allow_network':True})
approved=set();deadline=time.monotonic()+360
while run['status'] in ['queued','running']:
if time.monotonic()>deadline:
api('/benchmarks/runs/'+run['run_id']+'/cancel',{});raise RuntimeError('Demo deadline')
active=run['config_snapshot'].get('active_agent_run_id')
if active:
trace=api('/agent/runs/'+active+'/trace')
for event in trace['items']:
data=event['data'];ticket=data.get('request_id')
if event['event']=='PermissionRequired' and data.get('permission')=='tasks.write' and ticket not in approved:
api('/agent/runs/'+active+'/permissions/'+ticket,{'decision':'allow_once'});approved.add(ticket)
time.sleep(.3);run=api('/benchmarks/runs/'+run['run_id'])
report=api('/benchmarks/runs/'+run['run_id']+'/report')
report['permission_approvals']=len(approved)
report['mcp_present']=bool(mcp)
report['tasks']= [{'task_id':t['task_id'],'title':t['title']} for t in api('/tasks')['items'] if t['title'].startswith('验收-')]
args.output.write_text(json.dumps(report,ensure_ascii=False,indent=2),encoding='utf-8')
print(json.dumps({'metrics':report['metrics'],'cases':report['cases'],'permission_approvals':len(approved)},ensure_ascii=False))
if __name__=='__main__':
p=argparse.ArgumentParser();p.add_argument('--base-url',default='http://127.0.0.1:8017');p.add_argument('--data-dir',type=Path,required=True);p.add_argument('--output',type=Path,required=True);p.add_argument('--execute',action='store_true');args=p.parse_args()
if not args.execute:p.error('--execute required; creates three tasks only in the isolated fixture application')
if args.base_url not in {'http://127.0.0.1:8017','http://localhost:8017'}:p.error('Use the isolated local acceptance server on port 8017')
main(args)