"""「我想加入 git 仓库怎么办」 has to reach the flow, not a dead end. The transcript this file exists for: an employee asked exactly that and the assistant answered 「公司知识库中没有关于如何加入 Git 仓库的说明文档」, while the company runs *Request to join the GitHub organization* with a named owner. Every test here is a step on the path from that question to that answer. """ from __future__ import annotations import uuid import pytest from app.assistant import flow_grounding as flows from app.domains.approval_registry import service as svc from app.identity.person_models import IdentityPerson async def _person(db_session, email, *, name="Flare Zuo"): person = IdentityPerson( id=uuid.uuid4(), work_email=email, display_name=name, employment_status="active", source_system="successfactors", hris_person_id=f"sf-{uuid.uuid4().hex[:10]}", ) db_session.add(person) await db_session.flush() await db_session.commit() return person async def _flow(db_session, name, *, owner=None, category="IT", desc=None): flow = await svc.create_flow( db_session, approval_code=str(uuid.uuid4()).upper(), name=name, category=category, description=desc, owner_person_id=owner.id if owner else None, ) await db_session.commit() return flow # ── The classifier ────────────────────────────────────────────────── @pytest.mark.parametrize("question", [ "我想加入git仓库怎么办", "怎么申请 VPN", "报销走什么流程", "这个流程谁负责", "how do I request access to the data warehouse", "能不能开通云账号", ]) def test_how_do_i_questions_are_recognised(question): assert flows.looks_like_a_how_do_i_question(question) is True @pytest.mark.parametrize("question", [ "我的电脑蓝屏了", "打印机卡纸", ]) def test_unrelated_questions_do_not_pull_the_register(question): """A false positive only costs a prompt block, but a register attached to every 「电脑坏了」 turn is noise the model has to read past on the questions it should be answering directly.""" assert flows.looks_like_a_how_do_i_question(question) is False # ── The grounding ─────────────────────────────────────────────────── @pytest.mark.asyncio async def test_the_github_question_reaches_the_english_flow(db_session): """The question is Chinese, the flow name is English, and they share no tokens — which is exactly why the grounding hands over the catalogue instead of trying to pick a winner by keyword.""" owner = await _person(db_session, f"flare-{uuid.uuid4().hex[:6]}@t.com") await _flow( db_session, "Request to join the GitHub organization", owner=owner, category="IT Services & Security", ) grounding = await flows.build_flow_grounding(db_session, "我想加入git仓库怎么办") assert grounding is not None assert "Request to join the GitHub organization" in grounding.system_context assert "Flare Zuo" in grounding.system_context names = [f["name"] for f in grounding.flows] assert "Request to join the GitHub organization" in names @pytest.mark.asyncio async def test_an_unconfirmed_owner_is_labelled_as_a_guess(db_session): """Handing over a colleague's name without saying how sure we are is how a machine's guess becomes folklore.""" owner = await _person(db_session, f"g-{uuid.uuid4().hex[:6]}@t.com", name="张三") flow = await _flow(db_session, "云账号申请", owner=owner) flow.owner_source = "inferred" flow.owner_confirmed_at = None await db_session.commit() grounding = await flows.build_flow_grounding(db_session, "怎么申请云账号") # Names WHICH machine read: since 2026-08-27 a candidate can also # come from Feishu's declared 流程管理员, and telling an employee we # guessed when we read the console is its own false claim. assert ( "张三(平台根据实际审批记录推断,尚未本人确认)" in grounding.system_context ) @pytest.mark.asyncio async def test_an_unclaimed_flow_points_at_it_rather_than_guessing(db_session): await _flow(db_session, "对外合同用印") grounding = await flows.build_flow_grounding(db_session, "用印怎么申请") assert "还没有人认领" in grounding.system_context @pytest.mark.asyncio async def test_an_unclaimed_flow_names_its_feishu_admins_when_there_are_any( db_session, ): """The register said 「还没有人认领(让员工找 IT)」 on 32 of 49 flows whose 流程管理员 Feishu names — sending the employee to IT for something IT would look up in the same console.""" flow = await _flow(db_session, "域名DNS解析申请") flow.feishu_admins = [ {"user_id": "u1", "name": "褚杰", "email": "a@t.com", "person_id": None, "active": True}, {"user_id": "u2", "name": "孟凡生", "email": "b@t.com", "person_id": None, "active": True}, ] await db_session.commit() grounding = await flows.build_flow_grounding(db_session, "域名解析怎么申请") assert "飞书流程管理员是 褚杰、孟凡生" in grounding.system_context assert "让员工找 IT" not in grounding.system_context @pytest.mark.asyncio async def test_a_departed_feishu_admin_is_not_offered_as_the_answer(db_session): flow = await _flow(db_session, "补卡") flow.feishu_admins = [ {"user_id": "u1", "name": "已离职的人", "email": "x@t.com", "person_id": None, "active": False}, ] await db_session.commit() grounding = await flows.build_flow_grounding(db_session, "补卡怎么申请") assert "已离职的人" not in grounding.system_context assert "还没有人认领" in grounding.system_context @pytest.mark.asyncio async def test_a_feishu_admin_owner_is_not_described_as_a_guess(db_session): """「平台推断」 on a name the Feishu console declares is the same false claim the ``stale`` branch guards against, pointed the other way: it tells the employee we guessed when we read.""" owner = await _person(db_session, f"fa-{uuid.uuid4().hex[:6]}@t.com", name="周盛虎") flow = await _flow(db_session, "TiDB Cloud Startup Credits", owner=owner) flow.owner_source = "feishu_admin" flow.owner_confirmed_at = None await db_session.commit() grounding = await flows.build_flow_grounding( db_session, "startup credits 怎么申请", ) assert "周盛虎(飞书审批定义里的流程管理员,尚未本人确认)" in grounding.system_context assert "平台推断" not in grounding.system_context @pytest.mark.asyncio async def test_an_empty_register_grounds_nothing(db_session): """An assistant that announces a directory nobody has filled in is worse than one that says nothing.""" assert await flows.build_flow_grounding(db_session, "怎么申请") is None @pytest.mark.asyncio async def test_relevant_flows_are_ordered_first_but_nothing_is_dropped( db_session, ): """Ordering is a convenience; filtering would be the bug. The right flow frequently scores zero because the question and the name are in different languages.""" owner = await _person(db_session, f"o-{uuid.uuid4().hex[:6]}@t.com") await _flow(db_session, "VPN 接入申请", owner=owner) await _flow(db_session, "Request to join the GitHub organization", owner=owner) grounding = await flows.build_flow_grounding(db_session, "VPN 怎么申请") names = [f["name"] for f in grounding.flows] assert names[0] == "VPN 接入申请" assert "Request to join the GitHub organization" in names @pytest.mark.asyncio async def test_a_stopped_flow_is_marked_not_hidden(db_session): """Dropping it would leave the employee wondering where it went; the model needs to be able to say 「那条已经停用了」.""" owner = await _person(db_session, f"s-{uuid.uuid4().hex[:6]}@t.com") flow = await _flow(db_session, "旧版报销", owner=owner) await svc.update_flow(db_session, flow, status="inactive") await db_session.commit() grounding = await flows.build_flow_grounding(db_session, "报销怎么申请") assert "旧版报销(已停用)" in grounding.system_context # ── The tool the model can call ───────────────────────────────────── @pytest.mark.asyncio async def test_the_copilot_tool_returns_flows_with_their_owners( db_session, employee_user, ): from app.domains.support.copilot import FLOW_TOOL_NAME, _dispatch_tool person, _ = employee_user owner = await _person(db_session, f"t-{uuid.uuid4().hex[:6]}@t.com") await _flow(db_session, "Request to join the GitHub organization", owner=owner) raw = await _dispatch_tool( FLOW_TOOL_NAME, {"query": "我想加入git仓库"}, db_session, person, ) import json payload = json.loads(raw) assert payload["flows"] hit = payload["flows"][0] assert hit["name"] == "Request to join the GitHub organization" assert hit["owner_name"] == "Flare Zuo" @pytest.mark.asyncio async def test_the_tool_says_the_register_is_empty_rather_than_denying( db_session, employee_user, ): """"There is no flow" and "nobody has published the list" are different answers, and only one of them is true here.""" from app.domains.support.copilot import FLOW_TOOL_NAME, _dispatch_tool person, _ = employee_user raw = await _dispatch_tool( FLOW_TOOL_NAME, {"query": "任何东西"}, db_session, person, ) import json payload = json.loads(raw) assert payload["flows"] == [] assert "empty" in payload["note"] def test_the_prompt_tells_the_model_the_register_exists(): """A tool the prompt never names is a tool the model does not reach for — which is how the original transcript happened.""" from app.assistant.prompt import feishu_system_prompt from app.domains.support.copilot import FLOW_TOOL_NAME prompt = feishu_system_prompt("zh") assert FLOW_TOOL_NAME in prompt assert "该走哪个流程" in prompt @pytest.mark.asyncio async def test_the_turn_actually_carries_the_register_into_the_prompt( db_session, employee_user, monkeypatch, ): """The grounding builder being correct is not the same as the turn using it. This asserts the wiring: the flow block has to reach the system prompt the model is given, or the whole preflight is dead code that passes its own unit tests. """ from app.assistant import conversation person, _ = employee_user owner = await _person(db_session, f"w-{uuid.uuid4().hex[:6]}@t.com") await _flow(db_session, "Request to join the GitHub organization", owner=owner) seen: dict[str, str] = {} async def _fake_turn(*args, **kwargs): seen["system_prompt"] = kwargs.get("system_prompt") or "" from app.domains.support.copilot import CopilotResult return CopilotResult(reply="ok", tool_calls=[], usage=None) monkeypatch.setattr( "app.domains.support.copilot.run_copilot_turn", _fake_turn, ) await conversation.simulate_employee_turn( db_session, person=person, question="我想加入git仓库怎么办", ) assert "COMPANY APPROVAL FLOWS" in seen["system_prompt"] assert "Request to join the GitHub organization" in seen["system_prompt"]