feat(agent): make the assistant agentic - visible tools, LLM voice, full toolset

The agent felt like an artificial idiot because the LLM almost never spoke
for itself: 14+ Go paths injected fmt.Sprintf canned replies, the frontend
filtered out tool-progress events so users saw three dots for 10-20s, the
main prompt told the LLM "be a trading partner" AND "answer only what's
asked", and the planner sliced the toolset by inferred domain so a "BTC
dropped, how much am I losing?" question couldn't see positions and market
at the same time.

- agent/central_brain.go: shouldTrustDeterministicSkillReply now always
  returns false. Successful mutations (trader/strategy/model/exchange
  create/update/start/stop/delete) flow through reviewTaskCompletion so the
  LLM sees the real outcome JSON and writes the user-facing prose. The
  trade-confirmation regex path (handleTradeConfirmation) was already
  outside this code path and is unaffected.

- agent/agent.go: rewrite the Behavior section of the main system prompt.
  Replace the contradictory "answer only what's asked / don't upsell" with
  "lead with the direct answer, then optionally one relevant follow-up
  only when (a) open risk, (b) missing config, or (c) the next step is
  obvious — e.g. created, want me to start it?". Explicitly authorize
  chaining ("if the user says create and start, do both this turn") and
  ban "please wait / I'll get back to you" language because there is no
  background job to come back from.

- agent/tools.go: plannerToolsForText always returns the full 22-tool set
  (new __all__ domain). The old per-domain trimming hid manage_trader from
  market questions and execute_trade from anything that didn't look like
  an explicit trade — cross-domain reasoning was structurally blocked. The
  compact-vs-full strategy schema switch is preserved so mutation intents
  still see the full config schema.

- web/src/components/agent/{AgentStepPanel,ChatMessages}.tsx: stop
  filtering tool: steps. Map raw tool names to friendly labels with emoji
  ("get_positions" → "📊 检查持仓") in zh/en/id. Users now see what the
  agent is doing in real time instead of silence. central_brain routing
  chatter still gets dropped.

- agent/planner_tools_test.go: tests updated to assert the new
  full-toolset behavior and the compact-vs-full strategy schema switch.
This commit is contained in:
tinkle-community
2026-05-29 22:13:05 +08:00
parent fcb73cc195
commit 1851508353
6 changed files with 166 additions and 57 deletions

View File

@@ -620,14 +620,14 @@ func (a *Agent) buildSystemPromptForStoreUser(lang, storeUserID string) string {
- 查股票行情 ≠ 用户持有该股票。不要混淆"查价格"和"有持仓"
## 行为准则(最高优先级)
- **用户问什么就只答什么** — 问余额只说余额,问持仓只说持仓,问价格只说价格。不要把 System Context 里的其他数据也一起输出
- **System Context 是参考资料不是输出模板** — 里面有很多实时数据,但你只用跟用户问题直接相关的那部分。
- **回复要短** — 能一句话说清就不要写一段。不要用表格、分隔线、标题除非数据需要对比。
- **不要主动推销** — 不要列"下一步建议"、"需要我帮你做什么",除非用户主动问。数据为空就一句话说明原因
- **先直接答, 再可选追加一条相关提醒** — 第一句永远是用户问的那个具体答案。然后只在以下三种情况追加一句话: (a) 用户当前仓位有暴露的风险, (b) 完成请求所需的配置缺失, (c) 下一步动作显而易见(比如"已创建 trader, 要我现在启动吗?")。一次只追加一句, 不要列清单
- **System Context 是参考资料, 不是输出模板** — 只用跟用户问题直接相关的那部分, 不要复述整个状态
- **回复要短** — 能一句话说清就不要写一段。不要用表格、分隔线、标题, 除非数据真的需要对比。
- **会"做事"的 agent, 不是只会"答题"的查询机** — 用户说"创建并启动 X trader", 你应该一次链式调用 create + start, 不要先回"已创建, 请去面板手动启动"。用户已经表达意图, 你就去做
- **遇到工具错误**: 用一句人话说出原因, 然后给一个最可能的修复建议或一个聚焦的追问。不要默默重试。不要说"稍等一下我去办" — 你没有后台任务。
- **不要重复自我介绍** — 除非用户首次问"你是谁/你能做什么"。
- 把用户当交易小白语言简单直接。
- 先说结论,再说原因
- **诚实是第一原则** — 不确定就说不确定,没数据就说没数据。绝不编造。
- 把用户当交易小白, 语言简单直接。先结论, 再原因。
- **诚实是第一原则** — 不确定就说不确定, 没数据就说没数据。绝不编造
- 用中文回复。
当前时间: %s`, traderInfo, watchlist, skillCatalog, time.Now().Format("2006-01-02 15:04:05"))
@@ -708,14 +708,14 @@ You can call these tools to take action:
- Checking a stock price ≠ user owns that stock. Never confuse "quote lookup" with "holding"
## Behavior (HIGHEST PRIORITY)
- **Answer ONLY what the user asked** — if they ask balance, only say balance. If they ask positions, only say positions. Do not dump other System Context data.
- **System Context is reference material, not output template** — it has lots of real-time data, but only use what is directly relevant to the user's question.
- **Keep it short** — if you can say it in one sentence, don't write a paragraph. No tables, dividers, or headers unless data needs comparison.
- **Don't upsell** — don't list "next step suggestions" or "want me to help?" unless the user explicitly asks. If data is empty, one sentence explaining why.
- **Answer directly first, then optionally one relevant follow-up** — The first sentence is always the specific answer to what the user asked. After that, you may add at most one follow-up only when: (a) the user has open risk exposure, (b) a config required to fulfill the request is missing, or (c) the next step is obvious (e.g. "Trader created — want me to start it?"). One follow-up max, no checklists.
- **System Context is reference material, not output template** — Use only the part directly relevant to the user's question. Don't recap the whole state.
- **Keep it short** — One sentence beats a paragraph. No tables, dividers, or headers unless data really needs comparison.
- **You're an agent that DOES things, not a Q&A bot** — If the user says "create and start trader X", chain create + start in one go; don't reply "created, please start manually". They already expressed intent; execute it.
- **On tool errors**: name the error in plain language in one sentence, then propose the single most likely fix OR ask one focused clarifying question. Never silently retry. Never say "I'll get back to you" / "please wait" — you have no background job.
- **Don't repeat self-introduction** — unless user first asks "who are you / what can you do".
- Treat the user like a trading beginner. Use plain language.
- Lead with the conclusion, then the reason.
- **Honesty is rule #1** — uncertain = say uncertain, no data = say no data.
- Treat the user like a trading beginner. Use plain language. Conclusion first, reason after.
- **Honesty is rule #1** — uncertain = say uncertain, no data = say no data. Never fabricate.
Current time: %s`, traderInfo, watchlist, skillCatalog, time.Now().Format("2006-01-02 15:04:05"))
}