@danceiny/gotry 0.0.1-rc.7 → 0.0.1-rc.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +29 -74
- package/bin/gotry-inner.js +50 -1
- package/bin/gotry-stdio-ask.js +59 -0
- package/cordis.gotry-patch.yml +63 -5
- package/data/flights_2026.json +7 -4
- package/data/hotels_2026.json +102 -15
- package/data/time-slot-eval.json +384 -0
- package/data/yunnan-pack.json +2 -1
- package/dist/capabilities/agent-reach-bridge.py +90 -0
- package/dist/capabilities/anything.js +2 -2
- package/dist/capabilities/flyai.js +131 -0
- package/dist/capabilities/hbcli.js +11 -6
- package/dist/capabilities/incident-log.js +2 -1
- package/dist/capabilities/session/action-cache.js +120 -0
- package/dist/capabilities/session/adapters/ctrip-flight.js +83 -0
- package/dist/capabilities/session/adapters/meituan-local.js +73 -0
- package/dist/capabilities/session/extract.js +40 -0
- package/dist/capabilities/session/read-guard.js +56 -0
- package/dist/capabilities/session/transport.js +95 -0
- package/dist/capabilities/session-search.js +95 -0
- package/dist/scripts/action-cache-tests.js +107 -0
- package/dist/scripts/agent-reach-tests.js +1 -1
- package/dist/scripts/agent-reach-wrapper-tests.js +1 -1
- package/dist/scripts/async-collect.js +26 -7
- package/dist/scripts/companion-tests.js +112 -0
- package/dist/scripts/hbcli-tests.js +18 -2
- package/dist/scripts/ledger-tests.js +344 -0
- package/dist/scripts/ledger-workflow-crash.js +42 -0
- package/dist/scripts/memory-capture-tests.js +77 -0
- package/dist/scripts/memory-decay-tests.js +83 -0
- package/dist/scripts/memory-metrics.js +24 -0
- package/dist/scripts/nudge-digest.js +83 -0
- package/dist/scripts/probe-poi-tests.js +9 -0
- package/dist/scripts/replay.js +73 -1
- package/dist/scripts/session-attach-diagnose.js +31 -0
- package/dist/scripts/session-attach-poc.js +65 -0
- package/dist/scripts/session-attach-wait.js +41 -0
- package/dist/scripts/session-extract-tests.js +81 -0
- package/dist/scripts/session-login.js +48 -0
- package/dist/scripts/session-tests.js +162 -0
- package/dist/scripts/skeleton-check.js +3 -1
- package/dist/scripts/skills-contract-tests.js +85 -0
- package/dist/scripts/smoke.js +242 -4
- package/dist/scripts/state-cli-tests.js +136 -0
- package/dist/scripts/state-cli.js +234 -0
- package/dist/scripts/time-eval-tests.js +318 -0
- package/dist/scripts/travel-timeline-tests.js +123 -0
- package/dist/scripts/unified-tests.js +1 -1
- package/dist/src/companions.js +112 -0
- package/dist/src/dsh-llm.js +27 -7
- package/dist/src/index.js +600 -121
- package/dist/src/loop.js +89 -25
- package/dist/src/memory-capture.js +40 -0
- package/dist/src/memory-decay.js +31 -0
- package/dist/src/memory-utility.js +55 -0
- package/dist/src/mock-llm.js +6 -1
- package/dist/src/slot-spec.js +165 -0
- package/dist/src/state-ledger.js +848 -0
- package/dist/src/time-anchor.js +100 -0
- package/dist/src/tool-packet.js +12 -0
- package/dist/src/travel-slots.js +144 -0
- package/dist/src/travel-timeline.js +78 -0
- package/dist/src/unified.js +3 -1
- package/dist/src/wish-pool.js +27 -0
- package/package.json +6 -2
- package/ts/capabilities/hbcli.ts +6 -6
- package/ts/capabilities/incident-log.ts +6 -3
- package/ts/package.json +3 -0
- package/ts/scripts/skeleton-check.ts +5 -1
- package/ts/src/dsh-llm.ts +27 -7
- package/ts/src/index.ts +439 -91
- package/ts/src/loop.ts +109 -34
- package/ts/src/mock-llm.ts +13 -1
- package/ts/src/unified.ts +6 -1
- package/ts/cordis.gotry-patch.yml +0 -36
package/ts/src/loop.ts
CHANGED
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
|
|
9
9
|
import type { InterviewQuestion, TripState, TravelerProfile, Turn, CalendarState, SolveResult } from './contracts.ts'
|
|
10
10
|
import type { JourneySpecTS } from './unified.ts'
|
|
11
|
+
import type { TravelSlotExtraction } from './travel-slots.ts'
|
|
11
12
|
import { anythingSearch } from '../capabilities/anything.ts'
|
|
12
13
|
|
|
13
14
|
/** LLM 端口:S2 mock(确定性剧本) / S4 真(dsh 运行时)。循环不感知差异。 */
|
|
@@ -20,6 +21,12 @@ export interface LlmPort {
|
|
|
20
21
|
}>
|
|
21
22
|
/** 翻译:对话历史 → 统一模型 spec;约束未齐时返回 null(循环继续访谈) */
|
|
22
23
|
extractSpec(history: Turn[], state: TripState): Promise<JourneySpecTS | null>
|
|
24
|
+
/**
|
|
25
|
+
* 槽位抽取(travel_slot_extraction.v1):差旅 intake 面。时间表达逐字保留,
|
|
26
|
+
* 过期判定在确定性层(flagExpiredSlots),实现方负责注入时间锚点卡。
|
|
27
|
+
* now 可注入(评测用固定锚点保证确定性;生产省略取系统时钟)。
|
|
28
|
+
*/
|
|
29
|
+
extractSlots(history: Turn[], now?: Date): Promise<TravelSlotExtraction | null>
|
|
23
30
|
/** 问句润色(ADR-9:确定性驱动,LLM 只管语气) */
|
|
24
31
|
polishQuestion(q: InterviewQuestion): Promise<string>
|
|
25
32
|
/** 结果解释(S3 接 solve 后启用) */
|
|
@@ -235,7 +242,7 @@ export async function runTurn(
|
|
|
235
242
|
|
|
236
243
|
for (const q of blocking) parts.push(await llm.polishQuestion(q))
|
|
237
244
|
|
|
238
|
-
// 约束齐备(无阻塞问题)→ 翻译 spec →
|
|
245
|
+
// 约束齐备(无阻塞问题)→ 翻译 spec → **校验闸 + 日期一致性闸** → 求解 → 渲染
|
|
239
246
|
if (blocking.length === 0 && solve) {
|
|
240
247
|
const spec = await llm.extractSpec([...history, { role: 'user', text: userMsg }], state)
|
|
241
248
|
// 场景路由:erhai 候选标记 → 候选求解(洱海金标准的引擎判定)——纯 TS unify 路径
|
|
@@ -258,6 +265,20 @@ export async function runTurn(
|
|
|
258
265
|
} else {
|
|
259
266
|
const invalid = spec ? validateSpec(spec) : '翻译器未产出 spec'
|
|
260
267
|
if (spec && !invalid) {
|
|
268
|
+
// D-10 切片 C:spec↔槽位日期一致性闸(ADR-10 翻译≠造数的执行点)。
|
|
269
|
+
// LLM 翻译的 spec 日期与代码层换算的槽位日期分歧时不求解、不猜,追问确认——
|
|
270
|
+
// 日期是求解关键输入,错日期 = 错判决。槽位缺失/unresolved 不参与(不造假阳性)。
|
|
271
|
+
const ext = await llm.extractSlots([...history, { role: 'user', text: userMsg }])
|
|
272
|
+
if (ext) {
|
|
273
|
+
const { resolveSlots, specDateMismatches } = await import('./slot-spec.ts')
|
|
274
|
+
const { buildTimeAnchor } = await import('./time-anchor.ts')
|
|
275
|
+
const mismatches = specDateMismatches(spec, resolveSlots(ext, buildTimeAnchor()))
|
|
276
|
+
if (mismatches.length > 0) {
|
|
277
|
+
const lines = mismatches.map(m => `- ${m.specAt}:翻译说 ${m.specDate},你的原话换算是 ${m.slotDate}(${m.field})`)
|
|
278
|
+
parts.push(`⚠️ 日期分歧,确认后我再算(以你为准):\n${lines.join('\n')}`)
|
|
279
|
+
return { reply: parts.join('\n\n'), state }
|
|
280
|
+
}
|
|
281
|
+
}
|
|
261
282
|
state.spec = spec
|
|
262
283
|
state.solve = await solve(spec)
|
|
263
284
|
parts.push(llm.render ? await llm.render(state) : renderSolve(state))
|
|
@@ -271,66 +292,120 @@ export async function runTurn(
|
|
|
271
292
|
return { reply: parts.join('\n\n'), state }
|
|
272
293
|
}
|
|
273
294
|
|
|
274
|
-
// ---- 异步工单持久化(S5 编排半段):真正的「一小时后」必须跨进程存续 ----
|
|
275
|
-
//
|
|
276
|
-
//
|
|
295
|
+
// ---- 异步工单持久化(S5 编排半段 → ADR-15 durable 工单):真正的「一小时后」必须跨进程存续 ----
|
|
296
|
+
// 权威 = 账本 workflow_runs/workflow_steps(单事务;崩溃后 done 步骤不重执行);
|
|
297
|
+
// {id}.json / {id}.deliverable.md 降级为视图(AGENTS.md 清扫合同 + 人工检视),
|
|
298
|
+
// tmp+rename 原子写——此前裸 writeFile 的非原子缺口(RFC §1.1)就此关闭。
|
|
277
299
|
|
|
278
300
|
import { join } from 'node:path'
|
|
279
|
-
import { mkdir } from 'node:fs/promises'
|
|
280
|
-
import {
|
|
301
|
+
import { mkdir, rename, writeFile, readFile } from 'node:fs/promises'
|
|
302
|
+
import { ensureLedger, openLedgerIfExists, type StateLedger } from './state-ledger.ts'
|
|
281
303
|
|
|
282
|
-
|
|
304
|
+
async function asyncDir(stateRoot: string): Promise<string> {
|
|
305
|
+
const root = stateRoot === '.' ? process.cwd() : stateRoot
|
|
306
|
+
const dir = join(root, 'gotry-state', 'async')
|
|
307
|
+
await mkdir(dir, { recursive: true })
|
|
308
|
+
return dir
|
|
309
|
+
}
|
|
283
310
|
|
|
284
|
-
async function
|
|
285
|
-
|
|
286
|
-
|
|
311
|
+
async function atomicWrite(path: string, text: string): Promise<void> {
|
|
312
|
+
const tmp = `${path}.tmp`
|
|
313
|
+
await writeFile(tmp, text, 'utf-8')
|
|
314
|
+
await rename(tmp, path)
|
|
287
315
|
}
|
|
288
316
|
|
|
289
|
-
export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState): Promise<string> {
|
|
290
|
-
const
|
|
291
|
-
|
|
317
|
+
export async function persistAsyncTicket(ticket: AsyncTicket, state: TripState, stateRoot = '.'): Promise<string> {
|
|
318
|
+
const ledger = ensureLedger(stateRoot)
|
|
319
|
+
ledger.createWorkflowRun({ id: ticket.id, goal: ticket.objective, ticket, state })
|
|
320
|
+
const dir = await asyncDir(stateRoot)
|
|
321
|
+
const p = join(dir, `${ticket.id}.json`)
|
|
322
|
+
await atomicWrite(p, JSON.stringify({ ticket, state }, null, 2))
|
|
292
323
|
return p
|
|
293
324
|
}
|
|
294
325
|
|
|
295
|
-
export async function loadAsyncTicket(ticketId: string): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
|
|
326
|
+
export async function loadAsyncTicket(ticketId: string, stateRoot = '.'): Promise<{ ticket: AsyncTicket; state: TripState } | null> {
|
|
327
|
+
// 账本优先;无账本的 root 回退旧 json 文件(只读,兼容存量已交付工单)
|
|
328
|
+
const ledger = openLedgerIfExists(stateRoot)
|
|
329
|
+
const run = ledger?.getWorkflowRun(ticketId)
|
|
330
|
+
if (run) {
|
|
331
|
+
return {
|
|
332
|
+
ticket: JSON.parse(run.ticket_json) as AsyncTicket,
|
|
333
|
+
state: JSON.parse(run.state_json) as TripState,
|
|
334
|
+
}
|
|
335
|
+
}
|
|
296
336
|
try {
|
|
297
|
-
const p = await
|
|
337
|
+
const p = join(await asyncDir(stateRoot), `${ticketId}.json`)
|
|
298
338
|
return JSON.parse(await readFile(p, 'utf-8')) as { ticket: AsyncTicket; state: TripState }
|
|
299
339
|
} catch {
|
|
300
340
|
return null
|
|
301
341
|
}
|
|
302
342
|
}
|
|
303
343
|
|
|
304
|
-
export async function settleAsyncTicket(ticketId: string, reply: string): Promise<string> {
|
|
305
|
-
|
|
306
|
-
await
|
|
344
|
+
export async function settleAsyncTicket(ticketId: string, reply: string, stateRoot = '.'): Promise<string> {
|
|
345
|
+
openLedgerIfExists(stateRoot)?.settleWorkflowRun(ticketId, reply)
|
|
346
|
+
const dir = await asyncDir(stateRoot)
|
|
347
|
+
const p = join(dir, `${ticketId}.deliverable.md`)
|
|
348
|
+
await atomicWrite(p, reply)
|
|
307
349
|
return p
|
|
308
350
|
}
|
|
309
351
|
|
|
352
|
+
/**
|
|
353
|
+
* 可日志化 solve 端口(ADR-15 步骤日志,intent-before-execute):
|
|
354
|
+
* 先记 intent 再执行,done 后结果落账本——任意进程恢复时 done 的步骤直接复用
|
|
355
|
+
* 结果不重执行(exactly-once:LLM/求解调用不重复花钱),intent 悬挂的重试。
|
|
356
|
+
*/
|
|
357
|
+
export function makeJournaledSolvePort(ledger: StateLedger, runId: string, solve: SolvePort, opts?: { onRealSolve?: () => void }): SolvePort {
|
|
358
|
+
return async spec => {
|
|
359
|
+
const step = ledger.getWorkflowStep(runId, 'solve')
|
|
360
|
+
if (step?.status === 'done' && step.result) {
|
|
361
|
+
return JSON.parse(step.result) as Awaited<ReturnType<SolvePort>>
|
|
362
|
+
}
|
|
363
|
+
ledger.markStepIntent(runId, 'solve')
|
|
364
|
+
opts?.onRealSolve?.()
|
|
365
|
+
const r = await solve(spec)
|
|
366
|
+
ledger.markStepDone(runId, 'solve', r)
|
|
367
|
+
return r
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
|
|
310
371
|
/**
|
|
311
372
|
* probePoi:从 user msg 探测"查 POI/酒店" 信号,返回关键词。
|
|
312
|
-
*
|
|
373
|
+
* 触发的不是关键词,是**结构**;关键词抓取有方向性——
|
|
374
|
+
* 1) 显式搜索动词(查/搜/找/看看/推荐/告诉我/检索)+ 宾语在动词后;
|
|
375
|
+
* 2) 住宿名词(酒店/民宿/客栈/饭店):优先取名词**之后**的名称段(须含拉丁/数字,
|
|
376
|
+
* 「我订了酒店:The Title…」→ The Title…);退路是名词前 2-6 字紧邻地名
|
|
377
|
+
* (「大理酒店」→ 大理;「机票和」类垃圾后缀拒);
|
|
378
|
+
* 3) 开放问(有什么/玩什么/有哪些):取名词前的地名;
|
|
379
|
+
* 4) 短裸地名:≤12 字、无标点、无陈述动词(「大理」✓;「明天有空」「我的工作时间是…」✗——
|
|
380
|
+
* 2026-08-28 巡检收紧:旧版 ≤24 直通把访谈答案整句当关键词,垃圾 hbcli 调用+证据噪音)。
|
|
313
381
|
* 不是意图(改求解器),只是 datasources 编排层的"提早调 anything"提示。
|
|
314
382
|
*/
|
|
315
383
|
export function probePoi(msg: string): string | null {
|
|
316
384
|
const trimmed = msg.trim()
|
|
317
385
|
if (!trimmed) return null
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
const stripped = trimmed.replace(/^(查一下|查|搜|找|看看|推荐|告诉我|检索)\s*/, '').trim()
|
|
322
|
-
// 即使没 trigger 词,只要 stripped 仍是 ≥2 字符的非空串就当关键词
|
|
323
|
-
if (stripped.length >= 2) return stripped.slice(0, 24)
|
|
324
|
-
}
|
|
325
|
-
// 触发模式:查/搜/找/看看/推荐/告诉我/检索 + 地理/酒店类名词(贪吃但只切前 24 字符)
|
|
386
|
+
const cutAtPunct = (s: string): string => s.split(/[,,。.;;!!??]/)[0]?.trim() ?? ''
|
|
387
|
+
|
|
388
|
+
// 1) 显式搜索动词 + 宾语
|
|
326
389
|
const m = trimmed.match(/(查一下|查|搜|找|看看|推荐|告诉我|检索)\s*(.{2,24})/)
|
|
327
|
-
if (m
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
390
|
+
if (m?.[2]) {
|
|
391
|
+
const obj = cutAtPunct(m[2])
|
|
392
|
+
if (obj.length >= 2) return obj.slice(0, 24)
|
|
393
|
+
}
|
|
394
|
+
// 2) 住宿名词:名称段在名词后
|
|
395
|
+
if (/(酒店|民宿|客栈|饭店)/.test(trimmed)) {
|
|
396
|
+
const parts = trimmed.split(/(酒店|民宿|客栈|饭店)/)
|
|
397
|
+
const after = cutAtPunct((parts[2] ?? '').replace(/^[的::\s]+/, ''))
|
|
398
|
+
if (after.length >= 2 && /[A-Za-z0-9]/.test(after)) return after.slice(0, 24)
|
|
399
|
+
const nounAt = trimmed.search(/(酒店|民宿|客栈|饭店)/)
|
|
400
|
+
const pre = trimmed.slice(Math.max(0, nounAt - 6), nounAt).match(/[一-龥]{2,6}$/)
|
|
401
|
+
if (pre && !/(机票|车票|和|做|订|的)$/.test(pre[0])) return pre[0]
|
|
402
|
+
}
|
|
403
|
+
// 3) 开放问:地名在疑问词前
|
|
404
|
+
const q = trimmed.match(/([一-龥a-zA-Z]{2,12})\s*(?:有什么|玩什么|有哪些)/)
|
|
405
|
+
if (q?.[1]) return q[1]
|
|
406
|
+
// 4) 短裸地名(≤12 字,无标点,无陈述动词)
|
|
407
|
+
if (trimmed.length <= 12 && !/[,,。.;;!!??::]/.test(trimmed) && !/[是有在要想得到说订吧呢]/.test(trimmed)) {
|
|
408
|
+
return trimmed.slice(0, 24)
|
|
334
409
|
}
|
|
335
410
|
return null
|
|
336
411
|
}
|
package/ts/src/mock-llm.ts
CHANGED
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
import type { LlmPort } from './loop.ts'
|
|
8
8
|
import type { InterviewQuestion, TravelerProfile, TripState, Turn, CalendarState } from './contracts.ts'
|
|
9
9
|
import type { JourneySpecTS } from './unified.ts'
|
|
10
|
+
import type { TravelSlotExtraction } from './travel-slots.ts'
|
|
10
11
|
import { parseFlightPackToSpec } from './unified.ts'
|
|
11
12
|
|
|
12
13
|
interface ScriptStep {
|
|
@@ -16,6 +17,12 @@ interface ScriptStep {
|
|
|
16
17
|
profile?: Partial<TravelerProfile>
|
|
17
18
|
}
|
|
18
19
|
|
|
20
|
+
/** 槽位抽取剧本:评测 harness 把 golden 逐条挂进来,mock 原样回吐(管道自测用) */
|
|
21
|
+
export interface SlotScriptStep {
|
|
22
|
+
when: string
|
|
23
|
+
extraction: TravelSlotExtraction
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
/** 剧本:来自真实 Kimi 对话里用户给出关键事实的时刻 */
|
|
20
27
|
const SCRIPT: ScriptStep[] = [
|
|
21
28
|
{
|
|
@@ -48,7 +55,7 @@ const SCRIPT: ScriptStep[] = [
|
|
|
48
55
|
},
|
|
49
56
|
]
|
|
50
57
|
|
|
51
|
-
export function createMockLlm(flightPackPath?: string): LlmPort {
|
|
58
|
+
export function createMockLlm(flightPackPath?: string, slotScript: SlotScriptStep[] = []): LlmPort {
|
|
52
59
|
let stepIdx = 0
|
|
53
60
|
return {
|
|
54
61
|
async extractFacts(history: Turn[], _state: TripState) {
|
|
@@ -81,6 +88,11 @@ export function createMockLlm(flightPackPath?: string): LlmPort {
|
|
|
81
88
|
spec.budgetCny = 9000
|
|
82
89
|
return spec
|
|
83
90
|
},
|
|
91
|
+
async extractSlots(history: Turn[]): Promise<TravelSlotExtraction | null> {
|
|
92
|
+
const last = history[history.length - 1]?.text ?? ''
|
|
93
|
+
const hit = slotScript.find(s => last.includes(s.when))
|
|
94
|
+
return hit ? structuredClone(hit.extraction) : null
|
|
95
|
+
},
|
|
84
96
|
async polishQuestion(q: InterviewQuestion) {
|
|
85
97
|
return `【${q.key}】${q.text}\n(为什么问:${q.why})`
|
|
86
98
|
},
|
package/ts/src/unified.ts
CHANGED
|
@@ -28,6 +28,8 @@ export interface MoveSpecTS {
|
|
|
28
28
|
destTransferMin: number
|
|
29
29
|
redEye?: boolean
|
|
30
30
|
redEyeDurationMin?: number
|
|
31
|
+
/** D-6:红眼航段落地后接驳(机场→住处/办公室)的乘车补眠时长(分钟);机上已算,落地接驳未算 */
|
|
32
|
+
groundRecoveryMin?: number
|
|
31
33
|
/** D-5:目的地相对出发地的时差(KMG-BKK=+60,DXB-SZX=-240) */
|
|
32
34
|
tzOffsetMin?: number
|
|
33
35
|
/** M-1:出发地 UTC 偏移(工作窗口换算用) */
|
|
@@ -227,7 +229,10 @@ function evaluateOptionMove(segId: string, mv: MoveSpecTS): LegReport & { d2d_mi
|
|
|
227
229
|
let energy: number
|
|
228
230
|
if (mv.redEye && (mv.redEyeDurationMin ?? 0) > 0) {
|
|
229
231
|
const sleepH = ((mv.redEyeDurationMin ?? 0) - 60) / 60
|
|
230
|
-
|
|
232
|
+
// D-6 校准:机上睡眠上限 75;落地接驳(destTransferMin)乘车补眠回血(1h≈+5%,上限 80)
|
|
233
|
+
const groundH = (mv.groundRecoveryMin ?? mv.destTransferMin ?? 0) / 60
|
|
234
|
+
const recovery = Math.min(5, 5 * groundH)
|
|
235
|
+
energy = Math.max(30, Math.min(75 + recovery, 80, 30 + 8 * sleepH + recovery))
|
|
231
236
|
} else {
|
|
232
237
|
energy = 100 - 2 * 8
|
|
233
238
|
if (wake < 5 * 60) energy -= 30
|
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
# GoTry × dsh 成品组合(DeepSeek 原生——创始人官方 key)
|
|
2
|
-
# 用法(在 ts/dsh-runtime 下):
|
|
3
|
-
# DEEPSEEK_API_KEY=<key> node node_modules/@deepseek-ai/dsh/lib/bin.js \
|
|
4
|
-
# --profile headless --patch ../cordis.gotry-patch.yml "<任务>"
|
|
5
|
-
# (web 界面: 把 --profile headless 换成 web)
|
|
6
|
-
- insert:
|
|
7
|
-
- id: gotry-tools
|
|
8
|
-
name: '/Users/bytedance/work/gotry/ts/src/index.ts'
|
|
9
|
-
config:
|
|
10
|
-
pythonBin: '../../.venv/bin/python'
|
|
11
|
-
pythonPath: '../../py'
|
|
12
|
-
stateRoot: '.'
|
|
13
|
-
timeoutMs: 30000
|
|
14
|
-
preferInProcess: true
|
|
15
|
-
hbcliBin: 'hbcli'
|
|
16
|
-
|
|
17
|
-
# GoTry 成品人格:行为契约=产品语义
|
|
18
|
-
- id: system-prompt
|
|
19
|
-
config:
|
|
20
|
-
persona: >-
|
|
21
|
-
你是 GoTry——从出发到下一次出发的 AI 旅行伙伴(中国出境首发,workation 场景)。
|
|
22
|
-
今天是 {{current_date}}。行为契约:
|
|
23
|
-
(1)动机先行:首轮必问工作窗口(几点到几点/什么时区)与已订资源(航班/酒店);
|
|
24
|
-
(2)日历一次断言:日期换算 2026 年星期并记住,冲突让用户裁决,永不重算;
|
|
25
|
-
(3)翻译不造数:班次/价格只来自工具(gotry_feasibility_check/gotry_skeleton_check/gotry_hotel_search);
|
|
26
|
-
(4)判定归引擎:可行性/全成本由工具计算,你只解释;
|
|
27
|
-
(5)待决=选择题:2-3 个带 trade-off 选项;
|
|
28
|
-
(6)憧憬不被拒绝:不可行进 gotry_wish_pool_add(带条件);
|
|
29
|
-
(7)证据链:引用工具返回的标注([骨架:openflights]/[实时API:hbcli]/[静态包:估算])。
|
|
30
|
-
(8)时间感知:推荐季节/月份选项时,以今天为锚点——已过的月份不说「将」,
|
|
31
|
-
当下所在的季节直接点明(如「现在正是雨季」而非把当前月份当成远期选项)。
|
|
32
|
-
(9)兜底链:自身工具(可行性/酒店/天气/航班/Anything)答不上来时,用
|
|
33
|
-
gotry_agent_reach(100% follow Panniantong/Agent-Reach 路由表):web 读任意网页(r.jina.ai)/
|
|
34
|
-
rss/v2ex/youtube 字幕(yt-dlp)/github 搜索(gh)/bilibili/exa 语义搜索;需登录态的渠道
|
|
35
|
-
(twitter/reddit/xhs 等)会返回配置指引——如实转告用户,不编造。action=status 可跑
|
|
36
|
-
上游 `agent-reach doctor` 体检。答案标 [agent-reach:<渠道>@<ts>]。
|