92 lines
4.6 KiB
Plaintext
92 lines
4.6 KiB
Plaintext
# agent_chat_generic.dspy - 纯通用会话(不挂任何产线插件)
|
||
# 用于对照测试:确定通用 agent 本身的能力,隔离产线工具/技能/角色/记忆的干扰
|
||
# generic=True 时 gateway 跳过 resolve_project,不 load_agent_config 产线能力,只用 GENERAL_TOOLS + 通用心智
|
||
|
||
import json
|
||
|
||
uid = await get_user()
|
||
if not uid:
|
||
uid = 'user-01'
|
||
|
||
action = (params_kw or {}).get('action', 'send_message')
|
||
|
||
if action == 'send_message':
|
||
prompt = (params_kw or {}).get('prompt', '') or ''
|
||
prompt = prompt.strip()
|
||
if not prompt:
|
||
return json.dumps({"error": "prompt 必填"}, ensure_ascii=False)
|
||
|
||
# 停止类指令
|
||
stop_kw = ['停止', '取消', '停下', '终止', '停', 'stop', 'cancel', 'abort']
|
||
if prompt.strip().lower() in stop_kw:
|
||
async def _stop():
|
||
yield json.dumps({"content": "已停止。"}, ensure_ascii=False) + '\n'
|
||
return await stream_response(request, _stop, 'text/plain; charset=utf-8')
|
||
|
||
from pipeline_service.gateway import get_gateway
|
||
gateway = get_gateway()
|
||
|
||
# 前端模型下拉选中的模型必须透传——之前忽略导致用户选了模型仍走默认值,
|
||
# 默认模型名与 llm 表不匹配时直接"无可用模型"卡死。
|
||
model_id = (params_kw or {}).get('model_id', '') or ''
|
||
|
||
# 上传文件:之前完全忽略 file 字段(上传静默丢弃)。与 agent_chat 走同一套处理:
|
||
# 落盘到 workspace 会话目录(generic 无项目,read_file 根=WORKSPACE_BASE,相对路径可读)。
|
||
import os
|
||
from ahserver.filestorage import FileStorage
|
||
from sqlor.dbpools import DBPools
|
||
from pipeline_core.upload_tools import extract_text, resolve_upload_dir, save_uploads, build_file_context
|
||
_uploads = []
|
||
_fval = (params_kw or {}).get('file')
|
||
_fpaths = _fval if isinstance(_fval, list) else ([_fval] if _fval else [])
|
||
for _fp in _fpaths:
|
||
try:
|
||
_abs = FileStorage().realPath(_fp)
|
||
_uploads.append((_abs, os.path.basename(_abs)))
|
||
except Exception:
|
||
pass
|
||
if _uploads:
|
||
try:
|
||
_dbname = get_module_dbname('pipeline_core')
|
||
async with DBPools().sqlorContext(_dbname) as sor:
|
||
_udir, _rel = await resolve_upload_dir(sor, uid, '')
|
||
_saved = save_uploads(_udir, _uploads)
|
||
_byname = {os.path.basename(src): n for (src, _n), (n, _p) in zip(_uploads, _saved)}
|
||
_items = []
|
||
for _src, _name in _uploads:
|
||
_preview, _total, _trunc = extract_text(_src, _name)
|
||
_isbin = (not _preview and _total == 0)
|
||
_relp = (_rel + _byname.get(_name, _name)) if _name in _byname else ''
|
||
_items.append((_name, _relp, _preview, _total, _trunc, _isbin))
|
||
_fctx = build_file_context(_items)
|
||
if _fctx:
|
||
prompt = _fctx + "\n用户指令:" + prompt
|
||
except Exception:
|
||
pass
|
||
|
||
async def agent_stream():
|
||
async for chunk in gateway.run_message("web", uid, prompt, generic=True, model_id=model_id):
|
||
data = json.loads(chunk)
|
||
t = data.get('type', '')
|
||
if t == 'tool_call':
|
||
yield json.dumps({"content": "**🔧 调用: " + data.get('tool', '') + "**\n```\n" + json.dumps(data.get('params', {}), ensure_ascii=False) + "\n```\n\n"}, ensure_ascii=False) + '\n'
|
||
elif t == 'tool_result':
|
||
yield json.dumps({"content": data.get('result', '') + "\n\n"}, ensure_ascii=False) + '\n'
|
||
elif t == 'reply':
|
||
yield json.dumps({"content": data.get('message', '')}, ensure_ascii=False) + '\n'
|
||
elif t == 'ask_user':
|
||
yield json.dumps({"content": "❓ " + data.get('message', '')}, ensure_ascii=False) + '\n'
|
||
elif t == 'confirm':
|
||
_tool = data.get('tool', '')
|
||
_params = data.get('params', {}) or {}
|
||
_cmd = _params.get('command', '') if isinstance(_params, dict) else ''
|
||
yield json.dumps({"content": "⚠️ 需要你确认执行命令:`" + str(_cmd)[:120] + "`\n请回复「确认」执行、「全部确认」本会话不再询问,或「取消」放弃。"}, ensure_ascii=False) + '\n'
|
||
elif t == 'error':
|
||
yield json.dumps({"error": data.get('message', '')}, ensure_ascii=False) + '\n'
|
||
else:
|
||
yield json.dumps({"content": chunk}, ensure_ascii=False) + '\n'
|
||
|
||
return await stream_response(request, agent_stream, 'text/plain; charset=utf-8')
|
||
|
||
return json.dumps({"error": "Unknown action: " + str(action)}, ensure_ascii=False)
|