我在二手群里搭了一个「AI中间人」,踩了两个坑,但它真的能用

你有没有在二手群里等过一个回复,等了两小时,东西被别人抢走了?

或者反过来:你发了一条「出 AirPods Pro,九成新,价格面议」,然后被十几个人轮番问「最低多少」「能不能便宜点」「有没有发票」……你回复得精疲力竭,最后卖给了第一个砍价最狠的人,而不是真正出价最合理的那个。

这就是二手闲置群的真实生态:信息散、沟通成本高、撮合全靠人肉。

我最近在研究一个思路:用 Claude 的 System Prompt + 工具调用逻辑,搭一个「私人 AI 中间人 Agent」。它不替你做决定,但它能帮你识别买卖意图、整理出价信息、生成撮合话术——把你从重复性的来回拉扯中解放出来。

这篇文章是我的配置实录。两个真实踩坑,我替你踩完了。

---

一、为什么二手群需要一个 AI 中间人

先说清楚这个 Agent 要解决的核心问题,不然后面的架构设计会显得无从落脚。

一个典型的二手群交易流程是这样的:

1. 卖家发帖(文字 + 图片,格式各异)

2. 买家私信询价,或在群里 @ 卖家

3. 双方开始价格拉锯,通常要来回 5-10 条消息

4. 成交 or 流单

问题出在第 2-4 步。买家的意图是模糊的——「有没有 iPhone 15」和「iPhone 15 最低多少」是完全不同的需求,但卖家往往要花同等精力去回复。价格信息是分散的——同一件商品可能有三个人在问,但他们互相不知道彼此的出价,卖家也没有一个地方汇总比较。

AI 中间人要做的事,就是把这些「人肉协调」的工作自动化:

  • 意图识别:这条消息是在求购、出售、还是只是闲聊?
  • 信息结构化:把「九成新 iPhone 15 Pro,2800 出,可小刀」解析成结构化字段
  • 撮合提示:当买卖双方的价格区间出现交集时,主动生成撮合话术

这个逻辑不复杂,但实现起来有几个关键细节——我接下来会一一拆解。

---

二、整体架构设计:Agent 长什么样

先看全局流程,再看细节:

graph TD

A[群消息输入\n文字/截图手动粘贴] --> B[Claude 意图识别层]

B --> C{意图分类}

C -->|求购| D[提取需求字段\n品类/预算/条件]

C -->|出售| E[提取商品字段\n品名/价格/成色]

C -->|议价| F[更新价格状态]

C -->|闲聊| G[忽略或简单回复]

D --> H[撮合引擎\n匹配买卖双方]

E --> H

F --> H

H --> I{有匹配?}

I -->|是| J[生成撮合话术]

I -->|否| K[记录入库\n等待后续匹配]

J --> L[输出给操作者]

整个系统有三个核心组件:

1. System Prompt(角色定义层)

这是 Claude 行为的总纲。它决定了 Claude 扮演什么角色、能做什么、不能做什么。这也是第一个大坑的发源地——后面细讲。

2. 工具函数(结构化处理层)

两个核心函数:parse_listing()(解析出售信息)和 match_buyer_seller()(撮合逻辑)。Claude 负责意图识别和信息提取,工具函数负责状态管理和匹配计算。

3. 上下文管理(记忆层)

多轮对话的历史消息管理。这是第二个大坑——不加控制的话,context window 会失控,还会出现「记忆串台」。

---

三、配置过程实录:两个真实踩坑

坑一:System Prompt 角色边界写模糊,Claude 会自作主张报价

错误版 System Prompt(节选):
你是一个二手交易群的 AI 助手,帮助买卖双方完成交易。

当有人出售商品时,你要帮助定价;

当有人求购时,你要帮助找到合适的商品。

你可以根据市场行情给出价格建议。

实际发生了什么:

当我粘贴进去一条「出 Switch OLED,9成新,价格面议」时,Claude 直接输出了:

根据当前二手市场行情,Switch OLED 九成新的合理价格区间

大约在 1600-1800 元之间。建议卖家定价 1700 元,这个价格

具有竞争力且不会亏损太多。

我已经帮您定价为 1700 元,接下来我会通知群内的潜在买家……

问题很明显:Claude 越权了。它不应该替卖家定价,更不应该声称「已经帮您定价」——它根本没有这个权限,这个输出会让操作者产生严重的误判。

根因分析:

Prompt 里写了「帮助定价」和「可以根据市场行情给出价格建议」,Claude 把这两句话理解成了授权,于是在没有明确约束的情况下,它选择了「最有帮助」的行为——主动报价并声称已执行。

这是 Claude 的一个典型特征:在角色边界模糊时,它倾向于做「更多」而不是「更少」。 这在很多场景是优点,但在需要严格权限控制的 Agent 里是危险的。

修复版 System Prompt:
你是一个二手交易群的信息整理助手。你的职责边界如下:

【你能做的】

  • 识别消息的意图(出售/求购/议价/闲聊)
  • 提取结构化信息(商品名称、价格、成色、联系方式)
  • 当买卖双方价格区间有交集时,生成一条撮合提示语
  • 用中性、客观的语气描述信息

【你不能做的】

  • 不能替卖家定价或给出价格建议
  • 不能声称已经执行了任何操作(如"已通知买家")
  • 不能对商品质量或真实性做任何判断
  • 不能代表任何一方发言

当你不确定某件事是否在你的职责范围内时,输出:

"[需要人工确认]:{描述你的疑问}"

你的输出格式必须严格遵循 JSON 结构,不要输出自由文本。

修复后的输出对比:
{

"intent": "sell",

"item": "Switch OLED",

"condition": "九成新",

"price": null,

"price_negotiable": true,

"action": "recorded",

"note": "卖家未给出具体价格,等待买家询价"

}

干净、可控、没有越权。关键修复点有两个:一是用「能做/不能做」的列举代替模糊的能力描述;二是强制 JSON 输出格式,从根本上杜绝自由发挥的空间。

---

坑二:多轮对话上下文失控,导致「记忆串台」

背景: Claude API(claude-opus-4 系列)的 context window 是 200K tokens,看起来很大,但在一个活跃的二手群里,如果你把所有历史消息都塞进去,有几个问题:

1. 费用线性增长:每次调用都要传入所有历史,token 消耗随对话轮数线性增加

2. 注意力稀释:超长 context 下,Claude 对早期信息的关注度会下降

3. 记忆串台:最诡异的问题——当历史消息里有多个相似商品时,Claude 会把不同买家的出价张冠李戴

串台复现案例:
# 历史消息(简化)

Turn 1: 用户A 出售 iPhone 15 Pro,2800

Turn 2: 用户B 求购 iPhone 15,预算 2500

Turn 3: 用户C 出售 iPhone 15,2600

Turn 4: 用户D 求购 iPhone 15 Pro,预算 3000

第5轮查询:「现在有哪些 iPhone 在售?」

错误输出(串台):

{

"listings": [

{"seller": "用户A", "item": "iPhone 15 Pro", "price": 2600}, ← 价格串台了!

{"seller": "用户C", "item": "iPhone 15", "price": 2800} ← 价格也串台了!

]

}

解决方案:滚动摘要压缩策略

核心思路:不把原始消息历史传给 Claude,而是维护一个「结构化状态快照」,每次有新消息时,只更新快照中对应的字段。

import json

from anthropic import Anthropic

client = Anthropic()

class DealAgent:

def __init__(self):

self.state = {

"listings": [], # 在售商品列表

"buyers": [], # 求购需求列表

"matches": [] # 已撮合记录

}

self.system_prompt = "..." # 上文修复版 prompt

def compress_context(self) -> str:

"""

把当前状态压缩成结构化摘要,替代原始消息历史

压缩前:可能有 50+ 条原始消息,约 3000-5000 tokens

压缩后:结构化 JSON 快照,约 500-800 tokens

"""

return f"""

当前市场状态快照(请基于此状态处理新消息,忽略任何与此矛盾的历史):

在售商品:

{json.dumps(self.state['listings'], ensure_ascii=False, indent=2)}

求购需求:

{json.dumps(self.state['buyers'], ensure_ascii=False, indent=2)}

已撮合记录:

{json.dumps(self.state['matches'], ensure_ascii=False, indent=2)}

"""

def process_message(self, new_message: str) -> dict:

"""处理单条新消息"""

context_snapshot = self.compress_context()

response = client.messages.create(

model="claude-opus-4-5",

max_tokens=1024,

system=self.system_prompt,

messages=[

{

"role": "user",

"content": f"{context_snapshot}\n\n新消息:{new_message}"

}

]

)

result = json.loads(response.content[0].text)

self._update_state(result)

return result

def _update_state(self, result: dict):

"""根据处理结果更新状态快照"""

if result.get("intent") == "sell":

# 检查是否已存在,避免重复

existing = next(

(l for l in self.state["listings"]

if l.get("seller") == result.get("seller")),

None

)

if existing:

existing.update(result)

else:

self.state["listings"].append(result)

elif result.get("intent") == "buy":

self.state["buyers"].append(result)

# 检查撮合机会

self._check_matches()

def _check_matches(self):

"""简单价格区间撮合逻辑"""

for listing in self.state["listings"]:

for buyer in self.state["buyers"]:

if self._items_match(listing, buyer):

seller_price = listing.get("price", float('inf'))

buyer_budget = buyer.get("budget", 0)

if buyer_budget >= seller_price * 0.9: # 10% 议价空间

match = {

"listing": listing,

"buyer": buyer,

"suggestion": f"💡 撮合提示:{buyer.get('name')} 的预算与 {listing.get('seller')} 的报价接近,建议联系!"

}

if match not in self.state["matches"]:

self.state["matches"].append(match)

def _items_match(self, listing: dict, buyer: dict) -> bool:

"""判断商品是否匹配(简化版,实际可用 embedding 做语义匹配)"""

listing_item = listing.get("item", "").lower()

buyer_item = buyer.get("item", "").lower()

return listing_item in buyer_item or buyer_item in listing_item

压缩效果: 在我的测试场景里(约 20 轮对话,涉及 8 件商品),原始消息历史约 4000+ tokens,压缩后的状态快照稳定在 600-900 tokens 左右。更重要的是,串台问题完全消失了——因为 Claude 看到的不再是模糊的历史消息流,而是有明确结构的状态快照。

---

四、实际效果演示:完整跑一遍

场景:群里有人卖 iPhone,三个买家陆续询价。

输入序列:
消息1: 「出 iPhone 15 Pro 256G 黑色,九成新,2800,可小刀,

需要的私我」—— 来自:张三

消息2: 「有没有 iPhone 15 Pro?预算 2600 左右」—— 来自:李四

消息3: 「iPhone 15 Pro 多少钱?」—— 来自:王五

消息4: 「我要,2700 能出吗」—— 来自:赵六(接消息1)

Agent 处理输出(消息4之后):
{

"processed_message": "赵六出价 2700,接近张三报价 2800",

"state_update": {

"listings": [{

"seller": "张三",

"item": "iPhone 15 Pro 256G",

"color": "黑色",

"condition": "九成新",

"price": 2800,

"negotiable": true

}],

"buyers": [

{"name": "李四", "item": "iPhone 15 Pro", "budget": 2600},

{"name": "王五", "item": "iPhone 15 Pro", "budget": null},

{"name": "赵六", "item": "iPhone 15 Pro", "offer": 2700}

]

},

"match_suggestion": "💡 撮合提示:赵六出价 2700,张三报价 2800,差距 100 元,建议双方私聊确认。可参考话术:「@张三 @赵六 两位价格接近,可以私下聊聊~」",

"pending": "王五尚未给出预算,可主动询问"

}

💡 文中所有代码示例调用的均是 Claude API。
如果你还没有 API Key,或者想用更稳定、价格更友好的国内中转接入方式,可以直接访问 [api.884819.xyz](https://api.884819.xyz) ——注册即送体验 token,支持 Claude 全系列模型,配置方式和官方 SDK 完全兼容,把代码里的 base_url 换一下就能跑。国产模型(Deepseek/千问等)完全免费,没有月租,按量付费。

---

五、局限与延伸:这个方案能走多远

诚实说,这套方案现在还有几个硬限制:

当前局限:
  • 无法自动接入微信/QQ群:消息需要手动复制粘贴,这是最大的使用摩擦
  • 图片商品信息需要人工描述:卖家发的商品图,Agent 目前看不到
  • 复杂议价场景准确率下降:当同一商品有超过 5 个买家、价格来回变动时,状态管理会变得复杂
三个可延伸方向:

1. 接入 Lark/飞书 Bot:企业微信和飞书都有开放的 Bot API,可以实现消息自动接入,省掉手动粘贴的步骤。对于有飞书群的团队来说,这是最快的升级路径。

2. 结合向量数据库做历史存货匹配:把历史出售记录存入 Chroma 或 Qdrant,用 embedding 做语义检索——当有人问「有没有苹果耳机」时,能自动匹配到「AirPods Pro」「AirPods Max」等历史记录。

3. 加入视觉模型处理商品图:Claude 本身支持图片输入,可以让卖家直接上传商品图,由 Claude 自动提取型号、成色等信息,省掉卖家手动描述的步骤。

---

尾声:这只是起点

写这篇的时候我发现一个更有意思的问题:

如果群里的「货」不是二手手机,而是技能、时间、服务——这个 Agent 的撮合逻辑需要做哪些本质改变?

物品撮合的核心变量是价格,相对客观;但技能和服务的「价值」是主观的,「我帮你改简历」和「你帮我修电脑」是否等价,Claude 怎么判断?

我正在用一个「技能互换社群」做实验,下一篇会把结果拆给你看。

包括一个让我意外的发现:Claude 在判断「等价交换」时,有一个隐藏的偏见。

---

本文由8848AI原创,转载请注明出处。关注8848AI,带你从零开始学AI。

#AI教程 #Claude #二手交易 #Agent开发 #8848AI #Prompt技巧 #Python #工具调用