Files
WechatExplorer/src/main/query-agent-poc-cli.ts
T
Wxw-Gu ab3b3731b7 feat: 重构问问微信并完善知识库增量检索
统一问问微信与 Agent Hub 的查询链路
完善查询覆盖度、新鲜度和部分结果表达,避免索引滞后产生错误结论
基于会话实现真正的增量追新与历史补齐
支持后台同步、取消恢复、重启续传以及同步期间继续查询
优化知识库跨会话检索、同步状态、进度展示和侧栏布局
2026-09-11 17:44:48 +08:00

98 lines
3.7 KiB
TypeScript
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.
/**
* Query Agent CLI 的参数解析。
*
* 单独抽成纯函数便于单测,避免 entry 文件的副作用(app bootstrap / app.whenReady)影响测试。
*
* 两种模式:
* 1. 自然语言:`"问题" [--scope '{...}']` → 走完整 Runtime(LLM tool loop)
* 2. 裸工具诊断:`--tool <name> --args '{...json...}'` → 直接调 Local Query API,
* 用于在没有 GUI 的情况下审计 engine 侧数据(group 解析、覆盖度、Evidence 计数)
*/
import type { QueryCorpusScope } from '../shared/local-query-api'
export function parsePocQuestion(argv: readonly string[]): string {
const args = [...argv]
// npm / pnpm 在复合 script(`build && electron ...`)里会把 `--` 一并追加到命令末尾,
// 于是 separator 会落到 POC 的 argv 里,污染问题正文。
// 只移除开头的这一个独立 separator;问题正文中间的合法 `--` 必须保留。
if (args[0] === '--') args.shift()
return args.join(' ').trim()
}
export type PocInvocation =
| { kind: 'question'; question: string; scope?: QueryCorpusScope; pretty: boolean }
| { kind: 'tool'; toolName: string; args: Record<string, unknown>; pretty: boolean }
export const POC_TOOL_NAMES = [
'query_messages',
'search_messages',
'message_context',
'conversation_overview'
] as const
/**
* 解析 CLI 调用意图。任何参数错误都直接抛出,避免静默变成"提问"。
*/
export function parsePocInvocation(argv: readonly string[]): PocInvocation {
const args = [...argv]
if (args[0] === '--') args.shift()
const pretty = args.includes('--pretty')
const scopeIndex = args.indexOf('--scope')
let scope: QueryCorpusScope | undefined
if (scopeIndex >= 0) {
const rawScope = args[scopeIndex + 1]
if (!rawScope) throw new Error('缺少 --scope <json>')
let parsedScope: unknown
try {
parsedScope = JSON.parse(rawScope)
} catch {
throw new Error('--scope 不是合法 JSON')
}
if (!parsedScope || typeof parsedScope !== 'object' || Array.isArray(parsedScope)) {
throw new Error('--scope 必须是 JSON 对象')
}
const kind = (parsedScope as { kind?: unknown }).kind
if (kind !== 'all' && kind !== 'groups' && kind !== 'contact' && kind !== 'current') {
throw new Error('--scope.kind 必须是 all / groups / contact / current')
}
scope = parsedScope as QueryCorpusScope
if ((kind === 'contact' || kind === 'current') && !(parsedScope as { conversationId?: unknown }).conversationId) {
throw new Error('--scope.kind=contact|current 时必须给 conversationId')
}
}
const toolIndex = args.indexOf('--tool')
if (toolIndex < 0) {
const question =
scopeIndex < 0
? args.join(' ').trim()
: args
.filter((_, index) => index !== scopeIndex && index !== scopeIndex + 1)
.join(' ')
.trim()
return { kind: 'question', question, ...(scope ? { scope } : {}), pretty }
}
const toolName = (args[toolIndex + 1] || '').trim()
if (!POC_TOOL_NAMES.includes(toolName as (typeof POC_TOOL_NAMES)[number])) {
throw new Error(`不支持的诊断工具: ${toolName || '(空)'}(可选:${POC_TOOL_NAMES.join(' / ')})`)
}
const argsIndex = args.indexOf('--args')
const rawArgs = argsIndex >= 0 ? args[argsIndex + 1] : undefined
if (!rawArgs) throw new Error('缺少 --args <json>')
let parsed: unknown
try {
parsed = JSON.parse(rawArgs)
} catch {
throw new Error('--args 不是合法 JSON')
}
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
throw new Error('--args 必须是 JSON 对象')
}
return {
kind: 'tool',
toolName,
args: { ...(parsed as Record<string, unknown>), ...(scope ? { scope } : {}) },
pretty
}
}